12 KiB
12 KiB
2026-04-12
背景
- GitHub Actions 构建 API 镜像时,
pip install -r requirements.txt多次出现ReadTimeoutError(files.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 镜像构建新增
- 更新
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.txt将uvicorn[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-action在script_stop: true下会插入额外控制逻辑,和脚本中的 heredoc 组合后可能污染生成文件。 - 处理:移除 workflow 中
appleboy/ssh-action的script_stop: true,保留脚本内set -euo pipefail作为失败中断机制。
追加修正(API 构建超时 + 解析冲突)
- 触发问题:
pip拉取files.pythonhosted.org频繁ReadTimeoutError。- 在高延迟场景下,解析
pydantic版本链时出现ResolutionImpossible。
- 处理:
api/requirements.txt新增显式锁定:pydantic==2.12.5pydantic-core==2.41.5
- 将
psycopg[binary]==3.3.3改为显式双包:psycopg==3.3.3psycopg-binary==3.3.3
- 验证:
docker compose build api --no-cache成功。- 日志显示
pydantic-core-2.41.5与psycopg-binary-3.3.3均成功下载并安装,最终fquiz-api Built。
追加开发(文件管理一期主链)
- 目标:
- 在现有 FastAPI + SQLAlchemy + RBAC + Next.js App Router 结构上落地文件管理一期最小闭环。
- 支持 VFS / S3 driver 抽象,先打通后台
/admin/files骨架与核心目录操作。
- 后端改动:
- 新增模型:
file_storage_backends、file_storage_mounts、file_index_entries(api/app/models/file_storage.py)。 init_db增加file_storage模型加载,保证create_all生效。- 新增存储驱动层:
VfsStorageDriver、S3StorageDriver与统一工厂(api/app/services/storage_driver.py)。 - 新增文件服务:目录列表(带索引同步)、创建目录、删除路径(
api/app/services/file_service.py)。 - 新增 API:
/api/v1/admin/files、/directories、/delete(api/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.59(S3 driver 依赖)。.env.example增加FILE_VFS_ROOT(默认./data/vfs)。
- 最小验证:
python3 -m compileall覆盖新增/改动后端文件通过。npx eslint(目标前端文件)通过。npx tsc --noEmit(web)通过。
- 风险与缺口:
- 当前未提供存储后端/挂载点的可视化配置管理,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.py将selectinload(...)由模块级常量改为惰性构建,避免导入阶段触发 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"}。
- 使用镜像源参数重建 API 成功:
追加修复(GitHub 发布 Run Command Timeout)
- 触发问题:
- 发布阶段日志持续停留在
docker compose pull的 layer 下载进度。 appleboy/ssh-action最终报错:Run Command Timeout,作业退出码1。
- 发布阶段日志持续停留在
- 根因:
- 远端拉取镜像速度慢时,SSH Action 的命令执行超时先触发,未等到
docker compose pull自然完成。
- 远端拉取镜像速度慢时,SSH Action 的命令执行超时先触发,未等到
- 处理:
- 更新
.github/workflows/main.yml部署步骤:- 为
appleboy/ssh-action增加timeout: 120s与command_timeout: 45m。 - 脚本内新增
DOCKER_CLIENT_TIMEOUT=600、COMPOSE_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-card、surface-card-muted、notice、btn-*、control、table-*。 - 增加浅色渐变背景与柔和光斑,提升整体层次。
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可见依赖存在。
- Turbopack 报
- 风险:
- 本次改动覆盖前端多个后台页面,主要风险是视觉回归与局部间距细节,需要联调页面人工验收。
追加修复(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。
- DB 端口映射改为
.github/workflows/main.yml:- 生产 compose 模板同步改为
${POSTGRES_PORT:-5433}:5432。 .env自动模板新增POSTGRES_PORT=5433。POSTGRES_IMAGE默认改为docker.m.daocloud.io/pgvector/pgvector:pg16。
- 生产 compose 模板同步改为
.env.example:- 新增
POSTGRES_PORT=5433,并同步默认POSTGRES_IMAGE为 pgvector 镜像。
- 新增
README.md:- PostgreSQL 默认访问端口更新为
5433,并注明可通过POSTGRES_PORT覆盖。
- PostgreSQL 默认访问端口更新为
- 验证:
docker compose config通过。- 展开结果确认:
db.ports.published为5433。db.image为docker.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.ymlAPI 环境变量新增API_CORS_ORIGIN_REGEX透传。.github/workflows/main.yml生产 compose 模板与默认.env模板同步新增该变量。
- 文档补充:
README.md增加 CORS 配置说明与示例。
- 后端 CORS 配置增强(
- 验证:
python3 -m compileall api/app/core/config.py api/app/main.py通过。docker compose config展开结果包含:API_CORS_ORIGINSAPI_CORS_ORIGIN_REGEX
追加修复(Web 镜像构建 TypeScript 阻塞)
- 触发问题:
docker compose build web在web/Dockerfile的RUN 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。
- 复跑