Files
ViGent2/Docs/DevLogs/Day32.md
Kevin Wong 71b45852bf 更新
2026-03-04 17:35:59 +08:00

9.3 KiB
Raw Blame History

视频下载同源修复 + 安全漏洞第一批修复 (Day 32)

概述

今天的工作聚焦四件事:

  1. 修复首页与发布成功弹窗点击下载时被浏览器当作在线播放(新开标签页)的问题。
  2. 将下载修复开始后的开发内容从 Day31 拆分到 Day32,保持日志按天清晰归档。
  3. 根据安全审计报告(Temp/安全审计报告.md),实施第一批 6 项无功能风险的安全修复。
  4. 统一弹窗关闭交互:默认支持点空白关闭,发布成功清理弹窗保持强制留存。

1) 视频下载链路修复(避免新开标签页播放)

问题现象

  • 首页“下载视频”与发布成功弹窗“下载视频备份”在部分浏览器会打开新标签页播放视频,而不是直接触发下载。
  • 根因是跨域签名 URL 场景下,浏览器可能忽略 <a download>

修复方案

  • 后端新增同源下载接口:GET /api/videos/generated/{video_id}/download
    • 使用 FileResponse 返回本地视频文件
    • 显式返回 Content-Disposition: attachment
    • 浏览器直接进入保存文件流程
  • 发布成功弹窗下载改为传 videoId,不再依赖签名 URL。
  • 首页作品预览下载同步改为同源下载接口,下载行为与发布弹窗统一。
  • 兼容旧清理状态:CleanupContext 对旧 videoDownloadUrl 持久化字段做 videoId 解析回填。

2) 配套调整与文档拆分

前端联动

  • CleanupContext 继续沿用“清理失败不关弹窗、不清本地”的逻辑,下载链路仅替换为同源接口。
  • 首页 PreviewPanel 支持传入 generatedVideoId,下载按钮优先走 /api/videos/generated/{id}/download

日志归档

  • 将“下载修复开始后的内容”从 Day31 移出并归档到 Day32
  • Day31 保留 Day31 当日核心内容(到 cleanup 链路加固为止)。

3) 安全漏洞第一批修复6 项,无功能风险)

根据安全审计报告,实施第一批 6 项可直接修复的安全加固项。

3.1 JWT 默认密钥启动拦截

  • 文件backend/app/main.py
  • 新增 check_jwt_secret startup 事件(在 init_admin 之前)
  • JWT_SECRET_KEY 仍为默认值 "your-secret-key-change-in-production" 时:
    • 生产环境DEBUG=Falseraise RuntimeError 直接阻止服务启动
    • 开发环境DEBUG=True):输出 CRITICAL 级别日志告警,不阻止启动

3.2 AI / Tools 接口加认证

  • 文件backend/app/modules/ai/router.pybackend/app/modules/tools/router.py
  • AI 路由 3 个端点(/translate/generate-meta/rewrite)均增加 Depends(get_current_user)
  • Tools 路由 1 个端点(/extract-script)增加 Depends(get_current_user)
  • 前端 axios 已有 withCredentials: true401 自动跳登录页,无需前端改动

3.3 素材路径穿越修复

  • 文件backend/app/modules/materials/router.pybackend/app/modules/materials/service.py
  • streamdelete_materialrename_material 三处在 startswith(user_id) 校验之前新增 .. 拒绝
  • ..material_id 直接返回 400
  • delete_material 路由补充 except ValueError → 400原先仅 catch PermissionErrorValueError 会被 Exception 兜底返回 500

3.4 video_id 白名单校验

  • 文件backend/app/modules/videos/router.py
  • download_generateddelete_generated 两个端点在函数开头增加正则校验
  • 仅允许 ^[A-Za-z0-9_-]+$,不符合直接返回 400

3.5 上传/下载大小限制

  • materials/service.py(流式上传):在 chunk 累加后检查 MAX_UPLOAD_SIZE_MB(默认 500MB超限抛 ValueError
  • ref_audios/service.py(参考音频):await file.read() 后检查 5MB 上限
  • tools/service.py(文案提取文件上传):将 shutil.copyfileobj 替换为分块拷贝 + 500MB 限制
  • tools/service.pyURL 下载分支):_download_video 返回后检查文件体积,超 500MB 删除临时文件并拒绝

3.6 错误信息通用化

  • ai/router.py3 处 detail=str(e) 分别改为"翻译服务暂时不可用"、"生成标题标签失败"、"改写服务暂时不可用"
  • tools/router.py:保留 "Fresh cookies" 特定分支提示fallback 改为"文案提取失败,请稍后重试"
  • generated_audios/service.py:任务失败 error 字段从 traceback.format_exc() 改为 str(e)traceback 仅写入服务端日志

4) 弹窗关闭交互统一UX

目标

  • 保持统一交互预期:业务弹窗默认可通过 X 与点击遮罩关闭。
  • 保留关键流程保护:发布成功清理弹窗继续禁止遮罩关闭,避免误触导致流程中断。

调整内容

  • 文案提取弹窗(ScriptExtractionModal)支持点击遮罩关闭。
  • AI 改写弹窗(RewriteModal)支持点击遮罩关闭。
  • 发布页扫码登录弹窗支持点击遮罩关闭。
  • 修改密码弹窗支持点击遮罩关闭。
  • 录音弹窗采用动态策略:closeOnOverlay={!isRecording}
    • 未录音:允许遮罩关闭
    • 录音中:禁止遮罩关闭(防误触);X 关闭仍可用,且会先停止录音再关闭
  • 发布成功清理弹窗维持 closeOnOverlay=false,并且不提供 onClose(无右上角关闭按钮)。

📁 今日主要修改文件

文件 改动
backend/app/modules/videos/router.py 新增 GET /api/videos/generated/{video_id}/download,返回 attachment 下载响应;新增 video_id 白名单正则校验(^[A-Za-z0-9_-]+$
frontend/src/features/publish/model/usePublishController.ts 发布成功后 triggerCleanup()video.id(替换签名 URL
frontend/src/shared/contexts/CleanupContext.tsx 下载字段改为 videoId;兼容旧 videoDownloadUrl 回填;下载按钮改同源路径
frontend/src/features/home/ui/PreviewPanel.tsx 首页下载改为同源下载接口
frontend/src/features/home/ui/HomePage.tsx 透传 generatedVideoIdPreviewPanel
frontend/src/features/home/ui/ScriptExtractionModal.tsx 弹窗支持点击遮罩关闭(closeOnOverlay
frontend/src/features/home/ui/RewriteModal.tsx 弹窗支持点击遮罩关闭(closeOnOverlay
frontend/src/features/publish/ui/PublishPage.tsx 扫码登录弹窗支持点击遮罩关闭
frontend/src/components/AccountSettingsDropdown.tsx 修改密码弹窗支持点击遮罩关闭
frontend/src/features/home/ui/RefAudioPanel.tsx 录音弹窗改为 closeOnOverlay={!isRecording}(录音中禁遮罩关闭)
Docs/DevLogs/Day31.md 移除下载修复章节与对应验证/覆盖项(迁入 Day32
Docs/TASK_COMPLETE.md 新增 Day32 Current 区块Day31 取消 Current
Docs/BACKEND_README.md 补充 /api/videos/generated/{video_id}/download 接口说明
Docs/BACKEND_DEV.md 补充下载接口 attachment 约定
Docs/FRONTEND_README.md 补充首页/发布弹窗下载统一同源接口说明
Docs/FRONTEND_DEV.md 补充 CleanupContext 下载策略规范
Docs/PUBLISH_DEPLOY.md 补充发布成功后同源下载联动说明
README.md 补充”一键下载直达(同源 attachment”能力描述
backend/app/main.py check_jwt_secret startup 事件:生产环境(DEBUG=False)强拦截启动,开发环境 CRITICAL 告警
backend/app/modules/ai/router.py 3 个端点加 Depends(get_current_user) 认证;错误返回改为通用消息
backend/app/modules/tools/router.py extract-script 端点加 Depends(get_current_user) 认证;错误返回改为通用消息
backend/app/modules/materials/router.py stream 端点新增 .. 路径穿越拒绝;delete 端点补充 except ValueError → 400
backend/app/modules/materials/service.py delete_material / rename_material 新增 .. 路径穿越拒绝;流式上传增加 MAX_UPLOAD_SIZE_MB 大小限制
backend/app/modules/ref_audios/service.py 参考音频上传增加 5MB 大小限制
backend/app/modules/tools/service.py 文案提取文件上传替换为限大小分块拷贝500MBURL 下载分支增加下载后体积检查500MB
backend/app/modules/generated_audios/service.py 任务失败错误字段从 traceback.format_exc() 改为 str(e),避免泄露内部路径

🔍 验证记录

  • python -m py_compile backend/app/modules/videos/router.py
  • npm run buildfrontend
  • npm run buildfrontend弹窗关闭策略调整后复验
  • pm2 restart vigent2-frontend
  • pm2 restart vigent2-backend
  • curl http://127.0.0.1:8006/health 返回 {"status":"ok"}
  • 安全修复第一批语法验证:python -m py_compile backend/app/main.py backend/app/modules/materials/router.py backend/app/modules/tools/service.py backend/app/modules/ai/router.py backend/app/modules/tools/router.py backend/app/modules/materials/service.py backend/app/modules/ref_audios/service.py backend/app/modules/videos/router.py backend/app/modules/generated_audios/service.py
  • 未登录调用 /api/ai/translate → 返回 401
  • 未登录调用 /api/tools/extract-script → 返回 401
  • 收尾三刀语法验证:python -m py_compile backend/app/main.py backend/app/modules/materials/router.py backend/app/modules/tools/service.py