attempt to fix profile picture error
This commit is contained in:
@@ -47,6 +47,7 @@ from src.agents.link_topic_builder import LinkTopicBuilderAgent
|
||||
from src.agents.strategy_importer import StrategyImporterAgent
|
||||
from src.services.post_insights_service import compute_post_insights, refresh_post_insights_for_account
|
||||
from src.services.insights_summary_service import generate_insights_summary
|
||||
from src.utils.post_cleanup import sanitize_post_content
|
||||
|
||||
# Router for user frontend
|
||||
user_router = APIRouter(tags=["user"])
|
||||
@@ -68,16 +69,16 @@ async def get_user_profile_picture(user_id: UUID) -> Optional[str]:
|
||||
|
||||
Note: session.linkedin_picture (OAuth login) should be checked by caller first.
|
||||
"""
|
||||
# Check for connected LinkedIn account first
|
||||
linkedin_account = await db.get_linkedin_account(user_id)
|
||||
if linkedin_account and linkedin_account.is_active and linkedin_account.linkedin_picture:
|
||||
return linkedin_account.linkedin_picture
|
||||
|
||||
# Fall back to profile picture from setup process
|
||||
# Prefer cached profile picture (Supabase) to avoid LinkedIn hotlink blocking
|
||||
profile = await db.get_profile(user_id)
|
||||
if profile and profile.profile_picture:
|
||||
return profile.profile_picture
|
||||
|
||||
# Fall back to connected LinkedIn account
|
||||
linkedin_account = await db.get_linkedin_account(user_id)
|
||||
if linkedin_account and linkedin_account.is_active and linkedin_account.linkedin_picture:
|
||||
return linkedin_account.linkedin_picture
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -103,6 +104,48 @@ async def get_user_avatar(session: UserSession, user_id: UUID) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
async def cache_linkedin_picture(
|
||||
picture_url: Optional[str],
|
||||
user_id: UUID,
|
||||
http_client: Optional["httpx.AsyncClient"] = None
|
||||
) -> Optional[str]:
|
||||
"""Download LinkedIn profile picture once and store in Supabase."""
|
||||
if not picture_url:
|
||||
return None
|
||||
|
||||
try:
|
||||
import httpx
|
||||
close_client = False
|
||||
client = http_client
|
||||
if client is None:
|
||||
client = httpx.AsyncClient(timeout=30.0)
|
||||
close_client = True
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
"Referer": "https://www.linkedin.com/",
|
||||
"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
|
||||
}
|
||||
response = await client.get(picture_url, headers=headers)
|
||||
if response.status_code != 200 or not response.content:
|
||||
return picture_url
|
||||
|
||||
content_type = response.headers.get("content-type", "image/jpeg")
|
||||
uploaded_url = await storage.upload_media(
|
||||
file_content=response.content,
|
||||
content_type=content_type,
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
if close_client:
|
||||
await client.aclose()
|
||||
|
||||
return uploaded_url or picture_url
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to cache LinkedIn picture: {e}")
|
||||
return picture_url
|
||||
|
||||
|
||||
|
||||
async def get_employee_permissions_or_default(user_id: UUID, company_id: UUID) -> dict:
|
||||
"""Get employee permissions for a company, returning all-true defaults if no row exists."""
|
||||
@@ -2926,6 +2969,7 @@ async def linkedin_callback(
|
||||
linkedin_user_id = userinfo.get("sub")
|
||||
linkedin_name = userinfo.get("name", "")
|
||||
linkedin_picture = userinfo.get("picture")
|
||||
cached_picture = await cache_linkedin_picture(linkedin_picture, UUID(session.user_id), http_client=client)
|
||||
|
||||
# Get vanity name if available (from profile API - optional)
|
||||
linkedin_vanity_name = None
|
||||
@@ -2955,7 +2999,7 @@ async def linkedin_callback(
|
||||
"linkedin_user_id": linkedin_user_id,
|
||||
"linkedin_vanity_name": linkedin_vanity_name,
|
||||
"linkedin_name": linkedin_name,
|
||||
"linkedin_picture": linkedin_picture,
|
||||
"linkedin_picture": cached_picture or linkedin_picture,
|
||||
"access_token": encrypted_access,
|
||||
"refresh_token": encrypted_refresh,
|
||||
"token_expires_at": datetime.now(timezone.utc) + timedelta(seconds=expires_in),
|
||||
@@ -2973,7 +3017,7 @@ async def linkedin_callback(
|
||||
linkedin_user_id=linkedin_user_id,
|
||||
linkedin_vanity_name=linkedin_vanity_name,
|
||||
linkedin_name=linkedin_name,
|
||||
linkedin_picture=linkedin_picture,
|
||||
linkedin_picture=cached_picture or linkedin_picture,
|
||||
access_token=encrypted_access,
|
||||
refresh_token=encrypted_refresh,
|
||||
token_expires_at=datetime.now(timezone.utc) + timedelta(seconds=expires_in),
|
||||
@@ -2982,6 +3026,12 @@ async def linkedin_callback(
|
||||
await db.create_linkedin_account(new_account)
|
||||
logger.info(f"Created LinkedIn account for user {session.user_id}")
|
||||
|
||||
if cached_picture:
|
||||
try:
|
||||
await db.update_profile(UUID(session.user_id), {"profile_picture": cached_picture})
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to update profile picture: {e}")
|
||||
|
||||
# Clear state cookie and redirect to settings
|
||||
response = RedirectResponse(url="/settings?success=linkedin_connected", status_code=302)
|
||||
response.delete_cookie("linkedin_oauth_state")
|
||||
@@ -4333,6 +4383,7 @@ async def chat_generate_post(request: Request):
|
||||
company_strategy=company_strategy,
|
||||
strategy_weight=post_type.strategy_weight
|
||||
)
|
||||
post_content = sanitize_post_content(post_content)
|
||||
|
||||
# Generate conversation ID
|
||||
import uuid
|
||||
@@ -4446,6 +4497,7 @@ async def chat_refine_post(request: Request):
|
||||
company_strategy=company_strategy,
|
||||
strategy_weight=getattr(post_type, 'strategy_weight', 0.5)
|
||||
)
|
||||
refined_post = sanitize_post_content(refined_post)
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
@@ -4470,7 +4522,7 @@ async def chat_save_post(request: Request):
|
||||
|
||||
try:
|
||||
data = await request.json()
|
||||
post_content = data.get("post_content", "").strip()
|
||||
post_content = sanitize_post_content(data.get("post_content", "").strip())
|
||||
post_type_id = data.get("post_type_id")
|
||||
chat_history = data.get("chat_history", [])
|
||||
|
||||
@@ -4501,7 +4553,7 @@ async def chat_save_post(request: Request):
|
||||
|
||||
for item in chat_history:
|
||||
if 'ai' in item and item['ai']:
|
||||
writer_versions.append(item['ai'])
|
||||
writer_versions.append(sanitize_post_content(item['ai']))
|
||||
# Store user feedback as "critic feedback"
|
||||
if 'user' in item and item['user']:
|
||||
critic_feedback_list.append({
|
||||
@@ -4620,7 +4672,7 @@ async def update_chat_post(request: Request, post_id: str):
|
||||
post_uuid = UUID(post_id)
|
||||
|
||||
data = await request.json()
|
||||
post_content = data.get("post_content", "").strip()
|
||||
post_content = sanitize_post_content(data.get("post_content", "").strip())
|
||||
chat_history = data.get("chat_history", [])
|
||||
|
||||
if not post_content:
|
||||
@@ -4641,7 +4693,7 @@ async def update_chat_post(request: Request, post_id: str):
|
||||
|
||||
for item in chat_history:
|
||||
if 'ai' in item and item['ai']:
|
||||
writer_versions.append(item['ai'])
|
||||
writer_versions.append(sanitize_post_content(item['ai']))
|
||||
# Store user feedback as "critic feedback"
|
||||
if 'user' in item and item['user']:
|
||||
critic_feedback_list.append({
|
||||
@@ -4751,6 +4803,7 @@ async def company_chat_generate_post(request: Request):
|
||||
company_strategy=company_strategy,
|
||||
strategy_weight=post_type.strategy_weight
|
||||
)
|
||||
post_content = sanitize_post_content(post_content)
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
@@ -4777,7 +4830,7 @@ async def company_chat_save_post(request: Request):
|
||||
try:
|
||||
data = await request.json()
|
||||
employee_id = data.get("employee_id")
|
||||
post_content = data.get("post_content", "").strip()
|
||||
post_content = sanitize_post_content(data.get("post_content", "").strip())
|
||||
post_type_id = data.get("post_type_id")
|
||||
chat_history = data.get("chat_history", [])
|
||||
topic_title = data.get("topic_title", post_content[:80] if post_content else "Chat Post")
|
||||
@@ -4794,7 +4847,7 @@ async def company_chat_save_post(request: Request):
|
||||
if not perms.get("can_create_posts", True):
|
||||
return JSONResponse({"success": False, "error": "Keine Berechtigung"}, status_code=403)
|
||||
|
||||
writer_versions = [item['ai'] for item in chat_history if 'ai' in item and item['ai']]
|
||||
writer_versions = [sanitize_post_content(item['ai']) for item in chat_history if 'ai' in item and item['ai']]
|
||||
critic_feedback_list = []
|
||||
for item in chat_history:
|
||||
if 'user' in item and item['user']:
|
||||
|
||||
Reference in New Issue
Block a user