fix: P0 安全加固 + 前端错误边界 + ESLint 修复

后端:
- 实现登出 API(清除 refresh token)
- 清除 videos.py 中已被 Celery 任务取代的死代码
- 添加速率限制中间件(60次/分钟,登录10次/分钟)
- 添加 SECRET_KEY/ENCRYPTION_KEY 默认值警告
- OSS STS 方法回退到 Policy 签名(不再抛异常)

前端:
- 添加全局 404/error/loading 页面
- 添加三端 error.tsx + loading.tsx 错误边界
- 修复 useId 条件调用违反 Hooks 规则
- 修复未转义引号和 Image 命名冲突
- 添加 ESLint 配置

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-02-09 17:18:04 +08:00
co-authored by Claude Opus 4.6
parent a8be7bbca9
commit 8eb8100cf4
25 changed files with 498 additions and 193 deletions
+7 -3
View File
@@ -5,6 +5,8 @@ from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.api.deps import get_current_user
from app.models.user import User
from app.schemas.auth import (
RegisterRequest,
LoginRequest,
@@ -234,13 +236,15 @@ async def refresh_token(
@router.post("/logout")
async def logout(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
# TODO: 添加认证依赖
):
"""
退出登录
- 清除 refresh token
- 清除 refresh token,使其失效
"""
# TODO: 实现退出登录
current_user.refresh_token = None
current_user.refresh_token_expires_at = None
await db.commit()
return {"message": "已退出登录"}
-176
View File
@@ -23,8 +23,6 @@ from app.schemas.review import (
ViolationSource,
SoftRiskWarning,
)
from app.services.ai_service import AIServiceFactory
from app.services.ai_client import OpenAICompatibleClient
router = APIRouter(prefix="/videos", tags=["videos"])
@@ -205,177 +203,3 @@ async def get_review_result(
violations=violations,
soft_warnings=soft_warnings,
)
# ==================== AI 辅助审核方法 ====================
async def _perform_ai_video_review(
task: ReviewTask,
ai_client: OpenAICompatibleClient,
text_model: str,
vision_model: str,
audio_model: str,
db: AsyncSession,
) -> dict:
"""
使用 AI 执行视频审核
流程:
1. 下载视频
2. ASR 转写
3. 提取关键帧
4. 视觉分析 (竞品 Logo)
5. OCR 字幕
6. 生成报告
"""
violations = []
score = 100
try:
# 更新进度: 开始处理
task.status = DBTaskStatus.PROCESSING
task.progress = 10
task.current_step = "下载视频"
await db.flush()
# TODO: 实际实现需要集成视频处理库
# 1. 下载视频
# video_path = await download_video(task.video_url)
# 2. ASR 转写
task.progress = 30
task.current_step = "语音转写"
await db.flush()
# asr_result = await ai_client.audio_transcription(
# audio_url=task.video_url, # 需要提取音频
# model=audio_model,
# )
# transcript = asr_result.content
# 3. 提取关键帧
task.progress = 50
task.current_step = "提取关键帧"
await db.flush()
# frames = await extract_keyframes(video_path)
# 4. 视觉分析
task.progress = 70
task.current_step = "视觉分析"
await db.flush()
# 检测竞品 Logo
# if task.competitors:
# vision_prompt = f"""
# 分析这些视频截图,检测是否包含以下竞品品牌的 Logo 或标识:
# 竞品列表: {task.competitors}
#
# 如果发现竞品,请返回:
# 1. 竞品名称
# 2. 出现的帧编号
# 3. 置信度 (0-1)
# """
# vision_result = await ai_client.vision_analysis(
# image_urls=frames,
# prompt=vision_prompt,
# model=vision_model,
# )
# 5. 文本综合分析
task.progress = 85
task.current_step = "综合分析"
await db.flush()
# analysis_prompt = f"""
# 作为广告合规审核专家,请分析以下视频脚本内容:
#
# 脚本内容:
# {transcript}
#
# 请检查:
# 1. 是否包含广告法违禁词(最好、第一、最佳等极限词)
# 2. 是否包含虚假功效宣称
# 3. 品牌信息是否正确
#
# 返回 JSON 格式:
# {{"violations": [...], "score": 0-100, "summary": "..."}}
# """
# analysis_result = await ai_client.chat_completion(
# messages=[{"role": "user", "content": analysis_prompt}],
# model=text_model,
# )
# 6. 完成审核
task.progress = 100
task.current_step = "审核完成"
task.status = DBTaskStatus.COMPLETED
task.score = score
task.summary = "审核完成,未发现违规" if not violations else f"发现 {len(violations)} 处违规"
task.violations = [v.model_dump() for v in violations] if violations else []
await db.flush()
return {
"score": score,
"summary": task.summary,
"violations": violations,
}
except Exception as e:
task.status = DBTaskStatus.FAILED
task.error_message = str(e)
await db.flush()
raise
# ==================== 后台任务入口 ====================
async def process_video_review_task(
review_id: str,
tenant_id: str,
db: AsyncSession,
):
"""
处理视频审核任务(由 Celery 或后台任务调用)
"""
# 获取任务
result = await db.execute(
select(ReviewTask).where(
and_(
ReviewTask.id == review_id,
ReviewTask.tenant_id == tenant_id,
)
)
)
task = result.scalar_one_or_none()
if not task:
return
# 获取 AI 客户端
ai_client = await AIServiceFactory.get_client(tenant_id, db)
if not ai_client:
# 没有配置 AI,使用规则引擎审核
task.status = DBTaskStatus.COMPLETED
task.score = 100
task.summary = "审核完成(规则引擎)"
task.progress = 100
task.current_step = "审核完成"
await db.flush()
return
# 获取模型配置
config = await AIServiceFactory.get_config(tenant_id, db)
models = config.models
# 执行 AI 审核
await _perform_ai_video_review(
task=task,
ai_client=ai_client,
text_model=models.get("text", "gpt-4o"),
vision_model=models.get("vision", "gpt-4o"),
audio_model=models.get("audio", "whisper-1"),
db=db,
)