attempt to fix profile picture error
This commit is contained in:
BIN
profile_pictures/olivia.kibele@onyva.de.jpeg
Normal file
BIN
profile_pictures/olivia.kibele@onyva.de.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
profile_pictures/timo.uttenweiler@onyva.de.jpeg
Normal file
BIN
profile_pictures/timo.uttenweiler@onyva.de.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
116
scripts/update_profile_pictures.py
Normal file
116
scripts/update_profile_pictures.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Upload local profile pictures to Supabase and update user/profile records.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python scripts/update_profile_pictures.py --dir profile_pictures --apply
|
||||||
|
python scripts/update_profile_pictures.py --dir profile_pictures # dry run
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import asyncio
|
||||||
|
import mimetypes
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from src.database import db
|
||||||
|
from src.services.storage_service import storage
|
||||||
|
|
||||||
|
|
||||||
|
def guess_content_type(path: Path) -> str:
|
||||||
|
content_type, _ = mimetypes.guess_type(str(path))
|
||||||
|
return content_type or "image/jpeg"
|
||||||
|
|
||||||
|
|
||||||
|
async def process_directory(directory: Path, apply: bool) -> None:
|
||||||
|
if not directory.exists() or not directory.is_dir():
|
||||||
|
raise ValueError(f"Directory not found: {directory}")
|
||||||
|
|
||||||
|
files = sorted([p for p in directory.iterdir() if p.is_file()])
|
||||||
|
if not files:
|
||||||
|
print("No files found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
updated = 0
|
||||||
|
skipped = 0
|
||||||
|
|
||||||
|
for path in files:
|
||||||
|
email = path.stem.strip()
|
||||||
|
if "@" not in email:
|
||||||
|
print(f"Skip (no email in filename): {path.name}")
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
user = await db.get_user_by_email(email)
|
||||||
|
if not user:
|
||||||
|
print(f"User not found for email: {email}")
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
content_type = guess_content_type(path)
|
||||||
|
file_bytes = path.read_bytes()
|
||||||
|
|
||||||
|
print(f"\n{path.name} -> {email} ({content_type}, {len(file_bytes)} bytes)")
|
||||||
|
|
||||||
|
if not apply:
|
||||||
|
print("DRY RUN: would upload and update profile.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
uploaded_url = await storage.upload_media(
|
||||||
|
file_content=file_bytes,
|
||||||
|
content_type=content_type,
|
||||||
|
user_id=str(user.id),
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.update_profile(user.id, {"profile_picture": uploaded_url})
|
||||||
|
linkedin_account = await db.get_linkedin_account(user.id)
|
||||||
|
if linkedin_account:
|
||||||
|
await db.update_linkedin_account(
|
||||||
|
linkedin_account.id,
|
||||||
|
{"linkedin_picture": uploaded_url}
|
||||||
|
)
|
||||||
|
|
||||||
|
if db.admin_client:
|
||||||
|
try:
|
||||||
|
await asyncio.to_thread(
|
||||||
|
lambda: db.admin_client.auth.admin.update_user_by_id(
|
||||||
|
str(user.id),
|
||||||
|
{
|
||||||
|
"user_metadata": {
|
||||||
|
"picture": uploaded_url,
|
||||||
|
"linkedin_picture": uploaded_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning(f"Failed to update auth user metadata: {exc}")
|
||||||
|
else:
|
||||||
|
logger.warning("No service role key available; cannot update auth.users metadata.")
|
||||||
|
|
||||||
|
updated += 1
|
||||||
|
print(f"Updated profile picture: {uploaded_url}")
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error(f"Failed to update {email}: {exc}")
|
||||||
|
skipped += 1
|
||||||
|
|
||||||
|
print(f"\nDone. Updated: {updated}, Skipped: {skipped}")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="Upload profile pictures to Supabase and update users.")
|
||||||
|
parser.add_argument("--dir", default="profile_pictures", help="Directory with profile pictures")
|
||||||
|
parser.add_argument("--apply", action="store_true", help="Apply changes (otherwise dry run)")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parse_args()
|
||||||
|
asyncio.run(process_directory(Path(args.dir), apply=args.apply))
|
||||||
@@ -21,6 +21,7 @@ from src.agents.style_validator import StyleValidator
|
|||||||
from src.agents.readability_checker import ReadabilityChecker
|
from src.agents.readability_checker import ReadabilityChecker
|
||||||
from src.agents.quality_refiner import QualityRefinerAgent
|
from src.agents.quality_refiner import QualityRefinerAgent
|
||||||
from src.database.models import PostType
|
from src.database.models import PostType
|
||||||
|
from src.utils.post_cleanup import sanitize_post_content
|
||||||
|
|
||||||
|
|
||||||
class WorkflowOrchestrator:
|
class WorkflowOrchestrator:
|
||||||
@@ -658,6 +659,7 @@ class WorkflowOrchestrator:
|
|||||||
company_strategy=company_strategy, # Pass company strategy
|
company_strategy=company_strategy, # Pass company strategy
|
||||||
strategy_weight=strategy_weight # NEW: Pass strategy weight
|
strategy_weight=strategy_weight # NEW: Pass strategy weight
|
||||||
)
|
)
|
||||||
|
current_post = sanitize_post_content(current_post)
|
||||||
else:
|
else:
|
||||||
# Revision based on feedback - pass full critic result for structured changes
|
# Revision based on feedback - pass full critic result for structured changes
|
||||||
report_progress("Writer überarbeitet Post...", iteration, None, writer_versions, critic_feedback_list)
|
report_progress("Writer überarbeitet Post...", iteration, None, writer_versions, critic_feedback_list)
|
||||||
@@ -677,8 +679,9 @@ class WorkflowOrchestrator:
|
|||||||
company_strategy=company_strategy, # Pass company strategy
|
company_strategy=company_strategy, # Pass company strategy
|
||||||
strategy_weight=strategy_weight # NEW: Pass strategy weight
|
strategy_weight=strategy_weight # NEW: Pass strategy weight
|
||||||
)
|
)
|
||||||
|
current_post = sanitize_post_content(current_post)
|
||||||
|
|
||||||
writer_versions.append(current_post)
|
writer_versions.append(sanitize_post_content(current_post))
|
||||||
logger.info(f"Writer produced version {iteration}")
|
logger.info(f"Writer produced version {iteration}")
|
||||||
|
|
||||||
# Report progress with new version
|
# Report progress with new version
|
||||||
@@ -750,7 +753,7 @@ class WorkflowOrchestrator:
|
|||||||
profile_analysis=profile_analysis.full_analysis,
|
profile_analysis=profile_analysis.full_analysis,
|
||||||
example_posts=example_post_texts
|
example_posts=example_post_texts
|
||||||
)
|
)
|
||||||
current_post = polished_post
|
current_post = sanitize_post_content(polished_post)
|
||||||
logger.info("✅ Post polished (Formatierung erhalten)")
|
logger.info("✅ Post polished (Formatierung erhalten)")
|
||||||
else:
|
else:
|
||||||
logger.info("✅ No quality issues, skipping polish")
|
logger.info("✅ No quality issues, skipping polish")
|
||||||
@@ -772,7 +775,7 @@ class WorkflowOrchestrator:
|
|||||||
generated_post = GeneratedPost(
|
generated_post = GeneratedPost(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
topic_title=topic.get("title", "Unknown"),
|
topic_title=topic.get("title", "Unknown"),
|
||||||
post_content=current_post,
|
post_content=sanitize_post_content(current_post),
|
||||||
iterations=iteration,
|
iterations=iteration,
|
||||||
writer_versions=writer_versions,
|
writer_versions=writer_versions,
|
||||||
critic_feedback=critic_feedback_list,
|
critic_feedback=critic_feedback_list,
|
||||||
|
|||||||
22
src/utils/post_cleanup.py
Normal file
22
src/utils/post_cleanup.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"""Utilities to sanitize generated post content."""
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_post_content(text: str) -> str:
|
||||||
|
"""Remove markdown bold and leading 'Post' labels from generated content."""
|
||||||
|
if not text:
|
||||||
|
return text
|
||||||
|
|
||||||
|
cleaned = text.strip()
|
||||||
|
|
||||||
|
# Remove leading "Post" or "**Post**" labels
|
||||||
|
cleaned = re.sub(r'^\s*(\*\*post\*\*|post)\s*[:\-–—]*\s*', '', cleaned, flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
# Remove markdown bold markers, keep inner text
|
||||||
|
cleaned = re.sub(r'\*\*(.+?)\*\*', r'\1', cleaned)
|
||||||
|
cleaned = re.sub(r'__(.+?)__', r'\1', cleaned)
|
||||||
|
|
||||||
|
# Remove any leftover bold markers
|
||||||
|
cleaned = cleaned.replace('**', '').replace('__', '')
|
||||||
|
|
||||||
|
return cleaned.strip()
|
||||||
@@ -70,8 +70,8 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
|||||||
"default-src 'self'; "
|
"default-src 'self'; "
|
||||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; "
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; "
|
||||||
"style-src 'self' 'unsafe-inline'; "
|
"style-src 'self' 'unsafe-inline'; "
|
||||||
"img-src 'self' data: blob: https://*.supabase.co https://*.linkedin.com https://media.licdn.com; "
|
"img-src 'self' data: blob: https://*.supabase.co https://supabase.onyva.dev https://*.linkedin.com https://media.licdn.com; "
|
||||||
"connect-src 'self' https://*.supabase.co; "
|
"connect-src 'self' https://*.supabase.co https://supabase.onyva.dev; "
|
||||||
"font-src 'self' data:; "
|
"font-src 'self' data:; "
|
||||||
"frame-ancestors 'none'; "
|
"frame-ancestors 'none'; "
|
||||||
"base-uri 'self'; "
|
"base-uri 'self'; "
|
||||||
|
|||||||
@@ -78,8 +78,8 @@
|
|||||||
<div class="flex items-center justify-between p-4 bg-brand-bg border border-gray-600 rounded-lg">
|
<div class="flex items-center justify-between p-4 bg-brand-bg border border-gray-600 rounded-lg">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="w-10 h-10 rounded-full bg-brand-highlight/20 flex items-center justify-center overflow-hidden">
|
<div class="w-10 h-10 rounded-full bg-brand-highlight/20 flex items-center justify-center overflow-hidden">
|
||||||
{% if employee.linkedin_picture %}
|
{% if employee.profile_picture or employee.linkedin_picture %}
|
||||||
<img src="{{ employee.linkedin_picture }}" alt="" class="w-10 h-10 rounded-full">
|
<img src="{{ employee.profile_picture or employee.linkedin_picture }}" alt="" class="w-10 h-10 rounded-full">
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-brand-highlight font-bold">{{ (employee.display_name or employee.linkedin_name or employee.email)[0] | upper }}</span>
|
<span class="text-brand-highlight font-bold">{{ (employee.display_name or employee.linkedin_name or employee.email)[0] | upper }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
<a href="/company/manage?employee_id={{ emp.id }}"
|
<a href="/company/manage?employee_id={{ emp.id }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 rounded-lg transition-colors {% if selected_employee and selected_employee.id == emp.id %}bg-brand-highlight text-brand-bg-dark{% else %}bg-brand-bg hover:bg-brand-bg-light text-white{% endif %}">
|
class="flex items-center gap-3 px-4 py-3 rounded-lg transition-colors {% if selected_employee and selected_employee.id == emp.id %}bg-brand-highlight text-brand-bg-dark{% else %}bg-brand-bg hover:bg-brand-bg-light text-white{% endif %}">
|
||||||
<div class="w-8 h-8 rounded-full {% if selected_employee and selected_employee.id == emp.id %}bg-brand-bg-dark/20{% else %}bg-brand-highlight/20{% endif %} flex items-center justify-center">
|
<div class="w-8 h-8 rounded-full {% if selected_employee and selected_employee.id == emp.id %}bg-brand-bg-dark/20{% else %}bg-brand-highlight/20{% endif %} flex items-center justify-center">
|
||||||
{% if emp.linkedin_picture %}
|
{% if emp.profile_picture or emp.linkedin_picture %}
|
||||||
<img src="{{ emp.linkedin_picture }}" alt="" class="w-8 h-8 rounded-full">
|
<img src="{{ emp.profile_picture or emp.linkedin_picture }}" alt="" class="w-8 h-8 rounded-full">
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="{% if selected_employee and selected_employee.id == emp.id %}text-brand-bg-dark{% else %}text-brand-highlight{% endif %} text-sm font-medium">{{ (emp.display_name or emp.email)[0] | upper }}</span>
|
<span class="{% if selected_employee and selected_employee.id == emp.id %}text-brand-bg-dark{% else %}text-brand-highlight{% endif %} text-sm font-medium">{{ (emp.display_name or emp.email)[0] | upper }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -44,8 +44,8 @@
|
|||||||
<div class="card-bg border rounded-xl p-6 mb-8">
|
<div class="card-bg border rounded-xl p-6 mb-8">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="w-16 h-16 rounded-full bg-brand-highlight/20 flex items-center justify-center overflow-hidden">
|
<div class="w-16 h-16 rounded-full bg-brand-highlight/20 flex items-center justify-center overflow-hidden">
|
||||||
{% if selected_employee.linkedin_picture %}
|
{% if selected_employee.profile_picture or selected_employee.linkedin_picture %}
|
||||||
<img src="{{ selected_employee.linkedin_picture }}" alt="" class="w-16 h-16 rounded-full">
|
<img src="{{ selected_employee.profile_picture or selected_employee.linkedin_picture }}" alt="" class="w-16 h-16 rounded-full">
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-brand-highlight text-2xl font-medium">{{ (selected_employee.display_name or selected_employee.email)[0] | upper }}</span>
|
<span class="text-brand-highlight text-2xl font-medium">{{ (selected_employee.display_name or selected_employee.email)[0] | upper }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ from src.agents.link_topic_builder import LinkTopicBuilderAgent
|
|||||||
from src.agents.strategy_importer import StrategyImporterAgent
|
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.post_insights_service import compute_post_insights, refresh_post_insights_for_account
|
||||||
from src.services.insights_summary_service import generate_insights_summary
|
from src.services.insights_summary_service import generate_insights_summary
|
||||||
|
from src.utils.post_cleanup import sanitize_post_content
|
||||||
|
|
||||||
# Router for user frontend
|
# Router for user frontend
|
||||||
user_router = APIRouter(tags=["user"])
|
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.
|
Note: session.linkedin_picture (OAuth login) should be checked by caller first.
|
||||||
"""
|
"""
|
||||||
# Check for connected LinkedIn account first
|
# Prefer cached profile picture (Supabase) to avoid LinkedIn hotlink blocking
|
||||||
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
|
|
||||||
profile = await db.get_profile(user_id)
|
profile = await db.get_profile(user_id)
|
||||||
if profile and profile.profile_picture:
|
if profile and profile.profile_picture:
|
||||||
return 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
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -103,6 +104,48 @@ async def get_user_avatar(session: UserSession, user_id: UUID) -> Optional[str]:
|
|||||||
return None
|
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:
|
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."""
|
"""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_user_id = userinfo.get("sub")
|
||||||
linkedin_name = userinfo.get("name", "")
|
linkedin_name = userinfo.get("name", "")
|
||||||
linkedin_picture = userinfo.get("picture")
|
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)
|
# Get vanity name if available (from profile API - optional)
|
||||||
linkedin_vanity_name = None
|
linkedin_vanity_name = None
|
||||||
@@ -2955,7 +2999,7 @@ async def linkedin_callback(
|
|||||||
"linkedin_user_id": linkedin_user_id,
|
"linkedin_user_id": linkedin_user_id,
|
||||||
"linkedin_vanity_name": linkedin_vanity_name,
|
"linkedin_vanity_name": linkedin_vanity_name,
|
||||||
"linkedin_name": linkedin_name,
|
"linkedin_name": linkedin_name,
|
||||||
"linkedin_picture": linkedin_picture,
|
"linkedin_picture": cached_picture or linkedin_picture,
|
||||||
"access_token": encrypted_access,
|
"access_token": encrypted_access,
|
||||||
"refresh_token": encrypted_refresh,
|
"refresh_token": encrypted_refresh,
|
||||||
"token_expires_at": datetime.now(timezone.utc) + timedelta(seconds=expires_in),
|
"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_user_id=linkedin_user_id,
|
||||||
linkedin_vanity_name=linkedin_vanity_name,
|
linkedin_vanity_name=linkedin_vanity_name,
|
||||||
linkedin_name=linkedin_name,
|
linkedin_name=linkedin_name,
|
||||||
linkedin_picture=linkedin_picture,
|
linkedin_picture=cached_picture or linkedin_picture,
|
||||||
access_token=encrypted_access,
|
access_token=encrypted_access,
|
||||||
refresh_token=encrypted_refresh,
|
refresh_token=encrypted_refresh,
|
||||||
token_expires_at=datetime.now(timezone.utc) + timedelta(seconds=expires_in),
|
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)
|
await db.create_linkedin_account(new_account)
|
||||||
logger.info(f"Created LinkedIn account for user {session.user_id}")
|
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
|
# Clear state cookie and redirect to settings
|
||||||
response = RedirectResponse(url="/settings?success=linkedin_connected", status_code=302)
|
response = RedirectResponse(url="/settings?success=linkedin_connected", status_code=302)
|
||||||
response.delete_cookie("linkedin_oauth_state")
|
response.delete_cookie("linkedin_oauth_state")
|
||||||
@@ -4333,6 +4383,7 @@ async def chat_generate_post(request: Request):
|
|||||||
company_strategy=company_strategy,
|
company_strategy=company_strategy,
|
||||||
strategy_weight=post_type.strategy_weight
|
strategy_weight=post_type.strategy_weight
|
||||||
)
|
)
|
||||||
|
post_content = sanitize_post_content(post_content)
|
||||||
|
|
||||||
# Generate conversation ID
|
# Generate conversation ID
|
||||||
import uuid
|
import uuid
|
||||||
@@ -4446,6 +4497,7 @@ async def chat_refine_post(request: Request):
|
|||||||
company_strategy=company_strategy,
|
company_strategy=company_strategy,
|
||||||
strategy_weight=getattr(post_type, 'strategy_weight', 0.5)
|
strategy_weight=getattr(post_type, 'strategy_weight', 0.5)
|
||||||
)
|
)
|
||||||
|
refined_post = sanitize_post_content(refined_post)
|
||||||
|
|
||||||
return JSONResponse({
|
return JSONResponse({
|
||||||
"success": True,
|
"success": True,
|
||||||
@@ -4470,7 +4522,7 @@ async def chat_save_post(request: Request):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
data = await request.json()
|
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")
|
post_type_id = data.get("post_type_id")
|
||||||
chat_history = data.get("chat_history", [])
|
chat_history = data.get("chat_history", [])
|
||||||
|
|
||||||
@@ -4501,7 +4553,7 @@ async def chat_save_post(request: Request):
|
|||||||
|
|
||||||
for item in chat_history:
|
for item in chat_history:
|
||||||
if 'ai' in item and item['ai']:
|
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"
|
# Store user feedback as "critic feedback"
|
||||||
if 'user' in item and item['user']:
|
if 'user' in item and item['user']:
|
||||||
critic_feedback_list.append({
|
critic_feedback_list.append({
|
||||||
@@ -4620,7 +4672,7 @@ async def update_chat_post(request: Request, post_id: str):
|
|||||||
post_uuid = UUID(post_id)
|
post_uuid = UUID(post_id)
|
||||||
|
|
||||||
data = await request.json()
|
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", [])
|
chat_history = data.get("chat_history", [])
|
||||||
|
|
||||||
if not post_content:
|
if not post_content:
|
||||||
@@ -4641,7 +4693,7 @@ async def update_chat_post(request: Request, post_id: str):
|
|||||||
|
|
||||||
for item in chat_history:
|
for item in chat_history:
|
||||||
if 'ai' in item and item['ai']:
|
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"
|
# Store user feedback as "critic feedback"
|
||||||
if 'user' in item and item['user']:
|
if 'user' in item and item['user']:
|
||||||
critic_feedback_list.append({
|
critic_feedback_list.append({
|
||||||
@@ -4751,6 +4803,7 @@ async def company_chat_generate_post(request: Request):
|
|||||||
company_strategy=company_strategy,
|
company_strategy=company_strategy,
|
||||||
strategy_weight=post_type.strategy_weight
|
strategy_weight=post_type.strategy_weight
|
||||||
)
|
)
|
||||||
|
post_content = sanitize_post_content(post_content)
|
||||||
|
|
||||||
return JSONResponse({
|
return JSONResponse({
|
||||||
"success": True,
|
"success": True,
|
||||||
@@ -4777,7 +4830,7 @@ async def company_chat_save_post(request: Request):
|
|||||||
try:
|
try:
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
employee_id = data.get("employee_id")
|
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")
|
post_type_id = data.get("post_type_id")
|
||||||
chat_history = data.get("chat_history", [])
|
chat_history = data.get("chat_history", [])
|
||||||
topic_title = data.get("topic_title", post_content[:80] if post_content else "Chat Post")
|
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):
|
if not perms.get("can_create_posts", True):
|
||||||
return JSONResponse({"success": False, "error": "Keine Berechtigung"}, status_code=403)
|
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 = []
|
critic_feedback_list = []
|
||||||
for item in chat_history:
|
for item in chat_history:
|
||||||
if 'user' in item and item['user']:
|
if 'user' in item and item['user']:
|
||||||
|
|||||||
Reference in New Issue
Block a user