13 KiB
tiku-supabase
面向数百租户、单租户十万级学生的 SaaS 题库工程。仓库采用 Supabase/PostgreSQL + Node.js API/Worker + Taro 4 React,学生端复用 H5、微信小程序和后续 App 的业务层;租户后台与平台后台首发为响应式 H5。
当前仓库是旧 PocketBase/SQLite 题库的重构目标,不是旧项目的镜像。旧代码和旧数据只作为迁移与功能对照来源。
当前状态
生产地基候选已在本地冻结验证,前端可以基于 apps/taro 开始正式设计与实现。它还不是“可直接全量放量”的生产版本:目标服务器上的真实配置、数据库迁移、Provider、首个平台超管、备份恢复、真实数据抽样、容量证据和生产上线证据仍必须完成。
2026-07-12 基线结果:
| 范围 | 结果 |
|---|---|
| clean-room PostgreSQL | 78/78 migrations;public RLS 140/140;tenant RLS 134/134 |
| 租户完整性 | 189/189 租户外键;单租户 100,000 学生基准通过 |
| 后端质量 | API、Worker、Importer、Taro TypeScript 通过 |
| 安全 | production runtime audit 0;仓库扫描 0 findings |
| API 容器 | Node 20.20.2/Alpine 3.23 固定摘要;非 root;约 53 MB |
| 三端 H5 | production 构建通过;静态烟测 25/25;Chrome 交互烟测 33/33 |
| 响应式 | 桌面与 390x844 移动探针通过;无运行时异常和页面级横向溢出 |
| 上线门禁 | 缺少真实生产证据时 fail closed,当前不会误放行 |
权威基线:
系统边界
学生 H5 / 微信小程序 / 后续 App
租户管理 H5
平台管理 H5
|
v
apps/api 统一鉴权、租户隔离和业务命令
|
+---- PostgreSQL / Supabase Auth / Storage
|
+---- apps/worker 显式队列任务和定时任务
- 前端业务数据默认只访问
apps/api,不得在页面中直写 Supabase 业务表。 x-tenant-id只提供租户上下文,不是身份凭证。- API 与 Worker 使用独立 PostgreSQL 角色
tiku_api、tiku_worker。 - 两个运行角色的
BYPASSRLS是受控后端边界,API 路由必须显式执行身份、租户和权限过滤;浏览器、Taro 和 Data API 不得获得这两个凭据。 - 微信小程序只发布学生端。批量导入、复杂财务和平台运营不强行迁入小程序。
目录
apps/api/ Node.js 业务 API
apps/taro/ Taro 4 React,学生/租户/平台三端
apps/worker/ 队列消费者、平台任务、导入导出和资源复检
packages/db/ PostgreSQL 连接池与共享数据库配置
packages/domain/ 共享领域常量和类型
supabase/migrations/ schema、RLS、索引、约束和安全边界
scripts/import-pocketbase/ PocketBase 导出、dry-run、导入和校验
scripts/deploy/ 服务器脚本、systemd、Nginx 和配置模板
docs/refactor/ 架构、迁移、容量、前端交接和生产证据文档
deploy.sh 新服务器推荐的 release/symlink 发布入口
本地开发
要求:
- Node.js 20+
- Docker Desktop 或兼容 Docker 环境
- Supabase CLI
- Chrome/Chromium,用于 H5 交互烟测
安装和启动:
npm ci --workspaces --include-workspace-root --include=dev
npm run supabase:start
npm run supabase:reset
npm run dev:api
supabase:reset 会清空本地数据库,只能用于本地或隔离测试库。需要集成测试数据时使用受保护命令:
npm run db:smoke-seed:test
脚本会同时检查数据库中的 app_private.environment_safety 标记和精确确认短语。不要对 staging/production 运行 supabase:reset、db:smoke-seed:test、test:api、test:rls 或会重建测试数据的 Worker 集成测试。
常用开发命令:
npm run dev:api
npm run dev:taro:h5
npm run dev:taro:weapp:student
Taro 具体页面、跨端能力和公开环境变量见 apps/taro/README.md。
验证
日常改动至少运行与改动范围对应的 TypeScript 和 contract 测试:
npm run check:api
npm run check:worker
npm run check:importer
npm run check:taro
npm run test:readiness
三端 H5 正式产物必须分别构建;根脚本 build:taro:h5 只等价于学生端构建:
npm run build:taro:h5:student
npm run build:taro:h5:tenant
npm run build:taro:h5:platform
npm run smoke:taro:h5
TARO_H5_INTERACTION_OUTPUT_DIR=/tmp/tiku-h5-smoke npm run smoke:taro:h5:interaction
npm run audit:taro:supply-chain
npm run guard:taro:visual
node scripts/taro-h5-release-guardrails-test.js --require-dist
npm run manifest:taro:h5 -- --require-dist
正式部署目录还必须放入真实的公开 runtime-config.json,并使用严格模式:
node scripts/taro-h5-release-guardrails-test.js --require-dist --require-runtime-config
npm run manifest:taro:h5 -- --require-dist --require-runtime-config
Taro 固定为 4.2.0。workspace postinstall 会按精确版本和源码 hash 原子应用 H5 Input/Button runtime patch;不要使用 npm ci --ignore-scripts。Taro CLI/构建工具链尚有已审核 allowlist 内的 audit 告警,任何新增高危依赖或进入 H5/小程序 bundle 的风险都必须重新审查。
Git 与 Gitea
远端仓库:
https://git.gongxue100.com/chenhaogxjy/tiku-supabase.git
Gitea SSH 端口为 2222:
git clone ssh://git@git.gongxue100.com:2222/chenhaogxjy/tiku-supabase.git
开发流程:
git switch main
git pull --ff-only origin main
git switch -c codex/<topic>
# 修改、验证、提交后
git push -u origin codex/<topic>
在 Gitea 创建 codex/<topic> -> main 的 PR。不要 force-push main。生产部署必须固定到已评审的 commit SHA,而不是在验证过程中继续改动分支。
本轮 production foundation 已按最终整体状态完成验证,可以作为一个受控 baseline 合入 main。合并前置不是继续等待功能开发,而是确保待提交文件完整、最终 commit 与验证/证据对应,并让服务器升级按新 runbook 执行。
凭据要求:
- 已在聊天、截图、工单或日志中出现的 token 必须立即吊销。
- 开发电脑优先使用 SSH key;HTTPS token 应交给系统 credential helper,不能写入 remote URL。
- 服务器只使用仓库只读 deploy key/token,不得复用开发者可写凭据。
- 真实
.env、deploy.env、上线 evidence 和 artifacts 已由既有忽略规则保护;真实 runtime config 只放服务器受控目录,仓库仅提交*.example.json模板。
部署选择
| 场景 | 推荐入口 | 配置位置 | 说明 |
|---|---|---|---|
| 全新测试/预生产服务器 | 部署手册“新测试服务器”分阶段 bootstrap | 独立 /opt/tiku-saas-staging、/srv/tiku-saas-staging、/etc/tiku-saas-staging |
当前没有绕过生产门禁的一键 staging 发布入口 |
| 全新生产服务器 | 根目录 deploy.sh |
/opt/tiku-saas/shared/ |
必须通过完整生产 evidence |
| 现有云服务器升级 | scripts/deploy/bin/deploy.sh 安装到 /opt/tiku-saas/bin/deploy.sh |
/etc/tiku-saas/ |
保留原命令,但必须先升级脚本、配置和 systemd units |
服务器持续更新命令仍可以是:
sudo -u deploy /opt/tiku-saas/bin/deploy.sh
但这次基础升级不能直接运行服务器上的旧脚本。旧脚本会原地构建、直接覆盖 H5,并重启已经废弃的 tiku-worker.service;新版 Worker 强制要求显式 --job,旧 unit 会失败。首次覆盖部署必须先按 服务器部署手册 完成一次性升级。
新版发布流程包含:
- 独立候选 release 构建,应用与三端 Web 原子切换。
- Taro 供应链、三端 runtime config、manifest、25 项静态与 33 项浏览器交互烟测。
- 生产 env readiness、可选 migration、迁移后的数据库 readiness。
- 与候选 commit 和 artifact SHA-256 绑定的真实 launch gate。
- API healthcheck、线上 H5 hash 校验和代码/Web 回滚。
脚本不会自动备份或回滚数据库、对象存储,也不会自动安装更新后的 systemd/Nginx 模板。涉及 migration、.service/.timer/.target、Nginx 或 env schema 变化时,必须先按 runbook 做人工变更和恢复演练。
测试服务器
推荐先部署一台生产等价的隔离测试服务器:
- 独立数据库、域名、对象存储 bucket、Auth 项目和 Provider 测试凭据。
- 数据库标记为
staging且allow_destructive_tests=false。 - API/Worker 仍以
NODE_ENV=production运行,验证生产 fail-fast,而不是用 development 配置绕过。 - 不导入生产密钥,不承接真实用户流量,不复用生产数据库或 bucket。
- 可以使用脱敏数据快照,但不得运行 destructive smoke seed。
当前两套部署器都是 fail-closed 的生产发布器,在 production 模式下强制要求完整 launch evidence。仓库目前没有经过验证的“一键 staging 发布模式”,因此不能通过关闭 gate、伪造 production evidence 或套用生产 /opt、/srv、/etc 路径来抢跑。
测试服务器当前采用分阶段 bootstrap:先在隔离目录 clone 固定 commit,完成安装、构建、数据库 bootstrap/migration/readiness、三端 runtime config 和真实测试环境证据;确认系统服务与 Web 路径后,再按照部署手册激活。若只想检查候选构建,停在构建和 smoke 阶段,不切换 Web 和 systemd。后续可以增加带独立路径和独立证据模型的 staging profile,但不能把它实现为生产门禁的弱化开关。
生产上线门禁
真实 evidence 从模板创建,但不提交 Git:
cp docs/refactor/production-launch-evidence.template.json /secure/path/production-launch-evidence.json
npm run launch:gate -- --evidence /secure/path/production-launch-evidence.json
注意:
- evidence 的
commit必须等于待发布 commit。 - 每项 artifact 必须存在且 SHA-256 匹配,默认要求在有效时间窗内。
- 相对 artifact 路径相对于 evidence 文件目录解析,必须把 evidence 与
launch-artifacts/作为完整 bundle 保存。 - 本地 mock、preview、clean-room 报告不能冒充生产证据。
- 三端线上 hash/runtime config 验证通过后才能解除回滚保护。
上线前硬阻断包括:数据库/对象存储备份恢复、runtime role bootstrap、完整 migration、真实 Auth/短信/支付/存储/CORS、首个平台超管、目标规格压测、真实数据抽样、Worker 调度、告警日志和所有人工 attestation。详细顺序见 生产地基基线。
首个平台超管
不要直接插入一个绕过 Auth 的管理员。先在真实 Supabase Auth 中创建或确认身份,再 dry-run:
DATABASE_URL='<tiku_api connection>' \
BOOTSTRAP_PLATFORM_ADMIN_AUTH_USER_ID='<auth.users UUID>' \
BOOTSTRAP_PLATFORM_ADMIN_USERNAME='platform_owner' \
BOOTSTRAP_PLATFORM_ADMIN_NAME='平台负责人' \
npm run bootstrap:platform-admin
确认输出后使用精确短语应用:
DATABASE_URL='<tiku_api connection>' \
BOOTSTRAP_PLATFORM_ADMIN_AUTH_USER_ID='<auth.users UUID>' \
BOOTSTRAP_PLATFORM_ADMIN_USERNAME='platform_owner' \
BOOTSTRAP_PLATFORM_ADMIN_NAME='平台负责人' \
npm run bootstrap:platform-admin -- \
--apply --confirm BOOTSTRAP_FIRST_PLATFORM_ADMIN
该脚本只允许创建首个 Auth-bound active platform admin;一旦存在有效绑定管理员,后续 bootstrap 会永久拒绝,日常员工管理应走平台后台权限流。
关键文档
安全红线
- 前端只允许持有 Supabase publishable key,禁止 service role、数据库密码、短信密钥、支付私钥和对象存储长期密钥。
- 生产 API/Worker 不允许
local_dev存储、mock Provider、legacy auth header 或平台本地管理 key。 - migration 只使用临时注入的标准 migration role;API/Worker 运行角色不能执行 DDL。
- 破坏性测试只允许明确标记为 local/test/ci 且
allow_destructive_tests=true的隔离数据库;绝不允许 staging/production,并且必须使用精确确认短语。 - migration 是前向操作,代码/Web 回滚不会回滚数据库;先备份、演练兼容性,再迁移。
- 生产证据、访问 token、支付/短信/存储密钥和用户隐私数据不得写入 Git、README 或构建日志。