Files

14 KiB
Raw Permalink Blame History

2026-04-12

背景

  • GitHub Actions 构建 API 镜像时,pip install -r requirements.txt 多次出现 ReadTimeoutErrorfiles.pythonhosted.org / pypi.org)。

改动

  • 更新 api/Dockerfile
    • 新增构建参数 PIP_INDEX_URL(默认 https://pypi.org/simple)。
    • 新增构建参数 PIP_DEFAULT_TIMEOUT(默认 120)。
    • pip install 增加 --retries 8 --timeout "${PIP_DEFAULT_TIMEOUT}" -i "${PIP_INDEX_URL}"
    • 设置 PIP_DISABLE_PIP_VERSION_CHECK=1
  • 更新 .github/workflows/main.yml
    • API 镜像构建新增 build-args
      • PIP_INDEX_URL=${{ secrets.PIP_INDEX_URL || vars.PIP_INDEX_URL || 'https://pypi.org/simple' }}
      • PIP_DEFAULT_TIMEOUT=${{ vars.PIP_DEFAULT_TIMEOUT || '120' }}
  • 更新 api/requirements.txt
    • 从宽范围约束改为精确版本,减少 pip 解析回溯与重复下载。

验证

  • 本地触发 docker build -f api/Dockerfile api --build-arg PIP_INDEX_URL=https://pypi.org/simple --build-arg PIP_DEFAULT_TIMEOUT=120
  • 日志确认:
    • 新的 pip install 参数生效。
    • 发生网络超时时会触发 Retry(total=...) 重试逻辑,而非首次超时直接失败。

风险与备注

  • 受网络质量影响,构建时长可能显著增加。
  • 若目标环境访问 pypi.org 不稳定,需在 GitHub Secrets/Variables 配置更近的 PIP_INDEX_URL

追加修正(同日)

  • 触发问题:No matching distribution found for websockets>=10.4; extra == "standard"
  • 原因:uvicorn[standard] 会强依赖 websockets;在当前包源下解析失败。
  • 处理:
    • api/requirements.txtuvicorn[standard]==0.44.0 改为 uvicorn==0.44.0,并新增 wsproto==1.3.2
    • api/Dockerfile 启动命令追加 --ws wsproto,明确 WebSocket 协议实现。
  • 结果:构建日志不再出现 websockets>=10.4 依赖链,改为安装 wsproto

追加修正(部署脚本 YAML 解析)

  • 触发问题:部署阶段 docker compose 报错 yaml: line 2: mapping values are not allowed in this context,并伴随 DRONE_SSH_PREV_COMMAND_EXIT_CODE 变量告警。
  • 原因:appleboy/ssh-actionscript_stop: true 下会插入额外控制逻辑,和脚本中的 heredoc 组合后可能污染生成文件。
  • 处理:移除 workflow 中 appleboy/ssh-actionscript_stop: true,保留脚本内 set -euo pipefail 作为失败中断机制。

追加修正(API 构建超时 + 解析冲突)

  • 触发问题:
    • pip 拉取 files.pythonhosted.org 频繁 ReadTimeoutError
    • 在高延迟场景下,解析 pydantic 版本链时出现 ResolutionImpossible
  • 处理:
    • api/requirements.txt 新增显式锁定:
      • pydantic==2.12.5
      • pydantic-core==2.41.5
    • psycopg[binary]==3.3.3 改为显式双包:
      • psycopg==3.3.3
      • psycopg-binary==3.3.3
  • 验证:
    • docker compose build api --no-cache 成功。
    • 日志显示 pydantic-core-2.41.5psycopg-binary-3.3.3 均成功下载并安装,最终 fquiz-api Built

追加开发(文件管理一期主链)

  • 目标:
    • 在现有 FastAPI + SQLAlchemy + RBAC + Next.js App Router 结构上落地文件管理一期最小闭环。
    • 支持 VFS / S3 driver 抽象,先打通后台 /admin/files 骨架与核心目录操作。
  • 后端改动:
    • 新增模型:file_storage_backendsfile_storage_mountsfile_index_entriesapi/app/models/file_storage.py)。
    • init_db 增加 file_storage 模型加载,保证 create_all 生效。
    • 新增存储驱动层:VfsStorageDriverS3StorageDriver 与统一工厂(api/app/services/storage_driver.py)。
    • 新增文件服务:目录列表(带索引同步)、创建目录、删除路径(api/app/services/file_service.py)。
    • 新增 API/api/v1/admin/files/directories/deleteapi/app/api/v1/admin_files.py)。
    • 种子数据增加 file.read/file.manage 权限、admin.files 菜单、默认 VFS backend+mount 与 S3 backend 占位配置。
  • 前端改动:
    • 新增后台页面:web/src/app/admin/files/page.tsx
    • 提供挂载点切换、面包屑目录浏览、刷新、新建目录、删除。
    • 增补类型定义 FileListResponse 等,并在后台首页新增文件管理入口卡片。
  • 依赖与配置:
    • api/requirements.txt 增加 boto3==1.40.59S3 driver 依赖)。
    • .env.example 增加 FILE_VFS_ROOT(默认 ./data/vfs)。
  • 最小验证:
    • python3 -m compileall 覆盖新增/改动后端文件通过。
    • npx eslint(目标前端文件)通过。
    • npx tsc --noEmitweb)通过。
  • 风险与缺口:
    • 当前未提供存储后端/挂载点的可视化配置管理,S3 仍需手工写入 backend 配置并启用。
    • 仅实现目录浏览+建目录+删除,未包含上传、下载、移动、重命名、分享、回收站等完整网盘能力。

追加修复(API 容器 unhealthy + Docker Hub 超时)

  • 触发问题:
    • docker compose up 阶段 fquiz-api 持续 unhealthy(容器反复重启)。
    • 部署日志中 db 拉取 registry-1.docker.io 超时(Client.Timeout exceeded while awaiting headers)。
  • 根因:
    • api/app/services/admin_service.py 在模块导入期构建 selectinload(...) 常量,提前触发 SQLAlchemy mapper 配置;当时 User.audit_logs -> "AuditLog" 对应模型尚未注册,导致启动即崩溃。
    • 生产部署 compose 固定 postgres:16-alpine,对 Docker Hub 可达性强依赖。
  • 处理:
    • api/app/models/__init__.py 增加模型模块统一导入,保证关系类可解析。
    • api/app/services/admin_service.pyselectinload(...) 由模块级常量改为惰性构建,避免导入阶段触发 mapper 配置。
    • .github/workflows/main.yml 部署生成的 docker-compose.prod.yml 中 DB 镜像改为可配置:
      • image: ${POSTGRES_IMAGE:-docker.m.daocloud.io/library/postgres:16-alpine}
      • .env 模板新增 POSTGRES_IMAGE 默认值。
  • 验证:
    • 使用镜像源参数重建 API 成功:
      • docker compose build api --build-arg PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple --build-arg PIP_DEFAULT_TIMEOUT=600 --build-arg PIP_RETRIES=30
    • docker compose up -d api 后状态为 Up ... (healthy)
    • curl http://127.0.0.1:8000/health 返回 {"status":"ok","service":"fquiz-api","version":"0.1.0"}

追加修复(GitHub 发布 Run Command Timeout

  • 触发问题:
    • 发布阶段日志持续停留在 docker compose pull 的 layer 下载进度。
    • appleboy/ssh-action 最终报错:Run Command Timeout,作业退出码 1
  • 根因:
    • 远端拉取镜像速度慢时,SSH Action 的命令执行超时先触发,未等到 docker compose pull 自然完成。
  • 处理:
    • 更新 .github/workflows/main.yml 部署步骤:
      • appleboy/ssh-action 增加 timeout: 120scommand_timeout: 45m
      • 脚本内新增 DOCKER_CLIENT_TIMEOUT=600COMPOSE_HTTP_TIMEOUT=600 默认值。
      • 新增 pull_with_retry(最多 3 次)包装 docker compose pull,网络抖动时自动重试。
  • 验证建议:
    • 推送触发 main 发布,观察部署日志不再在固定时长点报 Run Command Timeout
    • 远端 docker compose ps 应显示 db/api/web 均为 Up(或 healthy)。

追加改造(后台视觉风格方案 A

  • 目标:
    • 将后台从默认黑白风格升级为更现代的 Slate + Cyan 视觉基线,保持业务逻辑不变。
  • 处理:
    • web/src/app/layout.tsx
      • 字体切换为 Space Grotesk(标题)+ Manrope(正文)+ JetBrains Mono(等宽)。
    • web/src/app/globals.css
      • 新增统一设计 token(背景/边框/强调色/文本层级)。
      • 新增通用样式类:surface-cardsurface-card-mutednoticebtn-*controltable-*
      • 增加浅色渐变背景与柔和光斑,提升整体层次。
    • web/src/app/admin/layout.tsx
      • 后台侧栏、顶部标题区改为半透明磨砂卡片风格,激活态采用青色高亮。
    • web/src/app/admin/**web/src/app/page.tsx
      • 统一替换卡片/按钮/表单/表格样式到新通用类,保持页面结构与交互逻辑不变。
  • 验证:
    • npm run lint:web 通过。
    • npm run build:web 失败(环境问题,非本次样式改动引入):
      • Turbopack 报 Can't resolve '@tanstack/react-query' / Can't resolve 'react',但 npm --workspace web ls react @tanstack/react-query --depth=0 可见依赖存在。
  • 风险:
    • 本次改动覆盖前端多个后台页面,主要风险是视觉回归与局部间距细节,需要联调页面人工验收。

追加修复(DB 端口冲突 + pgvector 基线)

  • 触发问题:
    • 远端启动 db 报错:listen tcp4 0.0.0.0:5432: bind: address already in use
    • 服务器已有旧 PostgreSQL 占用 5432,当前容器无法绑定。
  • 处理:
    • docker-compose.yml
      • DB 端口映射改为 ${POSTGRES_PORT:-5433}:5432
      • DB 默认镜像改为 docker.m.daocloud.io/pgvector/pgvector:pg16
    • .github/workflows/main.yml
      • 生产 compose 模板同步改为 ${POSTGRES_PORT:-5433}:5432
      • .env 自动模板新增 POSTGRES_PORT=5433
      • POSTGRES_IMAGE 默认改为 docker.m.daocloud.io/pgvector/pgvector:pg16
    • .env.example
      • 新增 POSTGRES_PORT=5433,并同步默认 POSTGRES_IMAGE 为 pgvector 镜像。
    • README.md
      • PostgreSQL 默认访问端口更新为 5433,并注明可通过 POSTGRES_PORT 覆盖。
  • 验证:
    • docker compose config 通过。
    • 展开结果确认:
      • db.ports.published5433
      • db.imagedocker.m.daocloud.io/pgvector/pgvector:pg16

追加优化(后台页面左右留白收敛)

  • 背景:
    • 用户反馈后台页面左右两侧 margin 偏大,宽屏下可用内容空间浪费。
  • 处理:
    • web/src/app/admin/layout.tsx 将主容器最大宽度由 max-w-[1360px] 提升到 max-w-[1760px]
    • 同时增加轻量响应式外层横向内边距(px-3 sm:px-4 xl:px-6),避免贴边。
    • 主内容区内边距从 p-6 md:p-8 收敛为 p-4 md:p-6,进一步释放表格可视宽度。
  • 验证:
    • cd web && npx eslint src/app/admin/layout.tsx 通过。

追加修复(CORS 跨域配置增强)

  • 触发问题:
    • 前端调用 API 出现浏览器 CORS 拦截,需要支持更灵活的来源配置。
  • 处理:
    • 后端 CORS 配置增强(api/app/core/config.py + api/app/main.py):
      • 新增 API_CORS_ORIGIN_REGEX 配置项(可选)。
      • API_CORS_ORIGINS 支持 * 与通配符域名(如 https://*.example.com),内部转换为正则匹配。
      • CORSMiddleware 增加 allow_origin_regex=settings.cors_origin_regex
    • 部署与环境模板同步:
      • .env.example 新增 API_CORS_ORIGIN_REGEX=
      • docker-compose.yml API 环境变量新增 API_CORS_ORIGIN_REGEX 透传。
      • .github/workflows/main.yml 生产 compose 模板与默认 .env 模板同步新增该变量。
    • 文档补充:
      • README.md 增加 CORS 配置说明与示例。
  • 验证:
    • python3 -m compileall api/app/core/config.py api/app/main.py 通过。
    • docker compose config 展开结果包含:
      • API_CORS_ORIGINS
      • API_CORS_ORIGIN_REGEX

追加修复(Web 镜像构建 TypeScript 阻塞)

  • 触发问题:
    • docker compose build webweb/DockerfileRUN npm run build 失败。
    • 报错定位在 web/src/app/page.tsx:190 附近(placeholder="Email" 上下文),实际错误为 Cannot find name 'title'
  • 根因:
    • 登录/注册表单标题渲染使用了未定义变量 title,导致 TypeScript 检查失败并中断 Next.js 构建。
  • 处理:
    • web/src/app/page.tsx{title} 改为基于 mode 的内联表达式:
      • 登录显示 登录
      • 注册显示 注册
  • 验证:
    • 复跑 docker compose build web,构建成功,路由静态生成完成,最终输出 fquiz-web Built

追加修复(浏览器 PNA 阻断导致登录失败)

  • 触发问题:
    • 浏览器报错:从 http://223.109.142.84:3000 访问 http://127.0.0.1:8000 被拦截,提示 The request client is not a secure context and the resource is in more-private address space loopback
  • 根因:
    • 前端构建时注入 NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8000,线上浏览器会把 127.0.0.1 解释为“访问者本机回环地址”,触发 PNA 安全策略阻断。
  • 处理:
    • 新增前端 API 基址运行时解析(web/src/lib/api.ts):
      • 浏览器端若发现配置为 loopback(127.0.0.1/localhost),且当前页面不在 loopback 主机,则自动改写为当前页面主机 + 原端口(默认 8000)。
    • 认证请求统一走运行时 API 基址(web/src/components/auth-provider.tsx)。
    • WS 连接统一走运行时 API 基址(web/src/components/ws-provider.tsx)。
    • 首页 Ping 与 API Base 展示改为运行时解析值(web/src/app/page.tsx)。
  • 验证:
    • cd web && npx eslint src/lib/api.ts src/components/auth-provider.tsx src/components/ws-provider.tsx src/app/page.tsx 通过。
    • cd web && npx tsc --noEmit 通过。

追加修复(需求创建 500

  • 触发问题:
    • 需求管理页面创建需求失败,接口 POST /api/v1/requirements 返回 500 Internal Server Error
  • 根因:
    • api/app/services/requirement_service.py_next_requirement_code() 使用 datetime.utcnow() 但文件缺少 datetime 导入,运行时报 NameError: name 'datetime' is not defined
  • 处理:
    • api/app/services/requirement_service.py 增加 from datetime import datetime
  • 验证:
    • python3 -m compileall api/app/services/requirement_service.py 通过。
    • docker compose up -d --build api 重建 API 容器成功。
    • 复测 POST /api/v1/requirements 返回 200,并成功创建需求(示例 code=REQ-2026-0001)。