""" 分享功能路由 - 创建分享、验证密码、获取分享内容 """ import json import time import uuid from datetime import datetime, timezone from typing import Dict, List, Optional from fastapi import HTTPException from pydantic import BaseModel import sys from pathlib import Path sys.path.append(str(Path(__file__).parent.parent)) from database import get_db from core import log_error, log_info # ── 请求/响应模型 ───────────────────────────────────────────────────── class CreateShareRequest(BaseModel): """创建分享请求""" conversationIds: List[str] = [] messages: Optional[List[dict]] = None # 直接传递消息数据(消息分享模式) passwordHash: str expiresIn: Optional[int] = 604800 # 默认7天(秒) class VerifyShareRequest(BaseModel): """验证分享请求""" passwordHash: str # ── 分享处理器 ────────────────────────────────────────────────────────── async def create_share_handler(data: dict): """ 创建分享 请求体: { "conversationIds": ["conv-1", "conv-2"], "messages": [...], // 可选,消息分享模式 "passwordHash": "sha256-hash", "expiresIn": 604800 } """ try: conversation_ids = data.get("conversationIds", []) messages = data.get("messages") # 消息分享模式 password_hash = data.get("passwordHash", "") expires_in = data.get("expiresIn", 604800) if not password_hash: raise HTTPException(status_code=400, detail="请设置访问密码") # 获取数据库实例 db = get_db() # 计算过期时间 now = int(datetime.now(timezone.utc).timestamp() * 1000) expires_at = now + (expires_in * 1000) # 消息分享模式 if messages: if len(messages) == 0: raise HTTPException(status_code=400, detail="请选择要分享的消息") # 创建虚拟对话 virtual_conv_id = str(uuid.uuid4()) virtual_conversation = { "id": virtual_conv_id, "title": f"分享的消息 ({len(messages)}条)", "messages": messages, "createdAt": now, "updatedAt": now, } # 创建分享记录 share_data = { "conversationIds": [virtual_conv_id], "conversations": [virtual_conversation], "passwordHash": password_hash, "expiresAt": expires_at, } # 保存到数据库 share = db.create_share(share_data) log_info(f"[分享] 消息分享创建成功: {share['id']}, 消息数: {len(messages)}") return { "id": share["id"], "shareUrl": f"/chat-ui/share/{share['id']}", "expiresAt": share["expiresAt"], } # 对话分享模式 if not conversation_ids: raise HTTPException(status_code=400, detail="请选择要分享的对话") if len(conversation_ids) > 10: raise HTTPException(status_code=400, detail="最多分享10个对话") # 获取对话数据(快照) conversations = [] for conv_id in conversation_ids: conv = db.get_conversation(conv_id) if conv: # 创建对话快照(去除敏感信息) conv_snapshot = { "id": conv["id"], "title": conv["title"], "messages": conv.get("messages", []), "createdAt": conv["createdAt"], "updatedAt": conv["updatedAt"], } conversations.append(conv_snapshot) if not conversations: raise HTTPException(status_code=404, detail="未找到有效的对话") # 创建分享记录 share_data = { "conversationIds": conversation_ids, "conversations": conversations, "passwordHash": password_hash, "expiresAt": expires_at, } # 保存到数据库 share = db.create_share(share_data) log_info(f"[分享] 创建成功: {share['id']}, 对话数: {len(conversations)}") # 返回分享信息 return { "id": share["id"], "shareUrl": f"/chat-ui/share/{share['id']}", "expiresAt": share["expiresAt"], } except HTTPException: raise except Exception as e: log_error(f"[分享] 创建失败: {str(e)}") raise HTTPException(status_code=500, detail=f"创建分享失败: {str(e)}") async def get_share_handler(share_id: str): """ 获取分享基本信息(不含内容) 用于显示分享预览、检查是否过期等 """ db = get_db() share = db.get_share(share_id) if not share: raise HTTPException(status_code=404, detail="分享不存在") # 检查是否过期 now = int(datetime.now(timezone.utc).timestamp() * 1000) is_expired = now > share["expiresAt"] if is_expired: # 删除过期分享 db.delete_share(share_id) raise HTTPException(status_code=404, detail="分享已过期") return { "id": share["id"], "hasPassword": bool(share["passwordHash"]), "expiresAt": share["expiresAt"], "isExpired": is_expired, "conversationCount": len(share["conversationIds"]), } async def verify_share_handler(share_id: str, data: dict): """ 验证密码并获取分享内容 请求体: { "passwordHash": "sha256-hash" } """ db = get_db() share = db.get_share(share_id) if not share: raise HTTPException(status_code=404, detail="分享不存在") # 检查是否过期 now = int(datetime.now(timezone.utc).timestamp() * 1000) if now > share["expiresAt"]: db.delete_share(share_id) raise HTTPException(status_code=404, detail="分享已过期") # 验证密码 password_hash = data.get("passwordHash", "") if password_hash != share["passwordHash"]: log_info(f"[分享] 密码验证失败: {share_id}") return { "valid": False, } # 增加访问计数 view_count = db.update_share_view_count(share_id) log_info(f"[分享] 验证成功: {share_id}, 访问次数: {view_count}") # 返回分享内容 return { "valid": True, "share": { "id": share["id"], "conversationIds": share["conversationIds"], "conversations": share["conversations"], "createdAt": share["createdAt"], "expiresAt": share["expiresAt"], "viewCount": view_count, "hasPassword": bool(share["passwordHash"]), }, }