192 lines
5.6 KiB
Python
192 lines
5.6 KiB
Python
"""
|
||
分享功能路由 - 创建分享、验证密码、获取分享内容
|
||
"""
|
||
|
||
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]
|
||
passwordHash: str
|
||
expiresIn: Optional[int] = 604800 # 默认7天(秒)
|
||
|
||
|
||
class VerifyShareRequest(BaseModel):
|
||
"""验证分享请求"""
|
||
passwordHash: str
|
||
|
||
|
||
# ── 分享处理器 ──────────────────────────────────────────────────────────
|
||
|
||
async def create_share_handler(data: dict):
|
||
"""
|
||
创建分享
|
||
|
||
请求体:
|
||
{
|
||
"conversationIds": ["conv-1", "conv-2"],
|
||
"passwordHash": "sha256-hash",
|
||
"expiresIn": 604800
|
||
}
|
||
"""
|
||
try:
|
||
conversation_ids = data.get("conversationIds", [])
|
||
password_hash = data.get("passwordHash", "")
|
||
expires_in = data.get("expiresIn", 604800)
|
||
|
||
if not conversation_ids:
|
||
raise HTTPException(status_code=400, detail="请选择要分享的对话")
|
||
|
||
if len(conversation_ids) > 10:
|
||
raise HTTPException(status_code=400, detail="最多分享10个对话")
|
||
|
||
if not password_hash:
|
||
raise HTTPException(status_code=400, detail="请设置访问密码")
|
||
|
||
# 获取数据库实例
|
||
db = get_db()
|
||
|
||
# 获取对话数据(快照)
|
||
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", [])[:100], # 限制消息数量
|
||
"createdAt": conv["createdAt"],
|
||
"updatedAt": conv["updatedAt"],
|
||
}
|
||
conversations.append(conv_snapshot)
|
||
|
||
if not conversations:
|
||
raise HTTPException(status_code=404, detail="未找到有效的对话")
|
||
|
||
# 计算过期时间
|
||
now = int(datetime.now(timezone.utc).timestamp() * 1000)
|
||
expires_at = now + (expires_in * 1000)
|
||
|
||
# 创建分享记录
|
||
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"/#/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"]),
|
||
},
|
||
} |