2026-04-12 16:00:20 +08:00
|
|
|
|
# 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/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` 作为失败中断机制。
|
2026-04-12 20:56:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 追加修正(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.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"}`。
|
2026-04-12 21:19:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 追加修复(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: 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`)。
|
2026-04-12 21:46:31 +08:00
|
|
|
|
|
2026-04-12 22:00:48 +08:00
|
|
|
|
## 追加改造(后台视觉风格方案 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` 可见依赖存在。
|
|
|
|
|
|
- 风险:
|
|
|
|
|
|
- 本次改动覆盖前端多个后台页面,主要风险是视觉回归与局部间距细节,需要联调页面人工验收。
|
|
|
|
|
|
|
2026-04-12 21:46:31 +08:00
|
|
|
|
## 追加修复(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.published` 为 `5433`。
|
|
|
|
|
|
- `db.image` 为 `docker.m.daocloud.io/pgvector/pgvector:pg16`。
|
2026-04-12 22:45:38 +08:00
|
|
|
|
|
|
|
|
|
|
## 追加优化(后台页面左右留白收敛)
|
|
|
|
|
|
|
|
|
|
|
|
- 背景:
|
|
|
|
|
|
- 用户反馈后台页面左右两侧 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` 通过。
|
|
|
|
|
|
|
2026-04-12 23:00:19 +08:00
|
|
|
|
## 追加修复(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`
|
|
|
|
|
|
|
2026-04-12 22:45:38 +08:00
|
|
|
|
## 追加修复(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`。
|