forked from wangziqi/gongxue-base
280 lines
13 KiB
Markdown
280 lines
13 KiB
Markdown
# 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,当前不会误放行 |
|
||
|
||
权威基线:
|
||
|
||
- [生产地基基线](docs/refactor/production-foundation-baseline-20260712.md)
|
||
- [clean-room 迁移审计](docs/refactor/clean-room-migration-audit-20260712.md)
|
||
- [Taro H5 浏览器 QA](docs/refactor/taro-h5-browser-qa-20260712.md)
|
||
- [Taro 供应链基线](docs/refactor/taro-supply-chain-baseline-20260712.md)
|
||
- [服务器部署手册](scripts/deploy/README.md)
|
||
|
||
## 系统边界
|
||
|
||
```text
|
||
学生 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 不得获得这两个凭据。
|
||
- 微信小程序只发布学生端。批量导入、复杂财务和平台运营不强行迁入小程序。
|
||
|
||
## 目录
|
||
|
||
```text
|
||
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 交互烟测
|
||
|
||
安装和启动:
|
||
|
||
```bash
|
||
npm ci --workspaces --include-workspace-root --include=dev
|
||
npm run supabase:start
|
||
npm run supabase:reset
|
||
npm run dev:api
|
||
```
|
||
|
||
`supabase:reset` 会清空本地数据库,只能用于本地或隔离测试库。需要集成测试数据时使用受保护命令:
|
||
|
||
```bash
|
||
npm run db:smoke-seed:test
|
||
```
|
||
|
||
脚本会同时检查数据库中的 `app_private.environment_safety` 标记和精确确认短语。不要对 staging/production 运行 `supabase:reset`、`db:smoke-seed:test`、`test:api`、`test:rls` 或会重建测试数据的 Worker 集成测试。
|
||
|
||
常用开发命令:
|
||
|
||
```bash
|
||
npm run dev:api
|
||
npm run dev:taro:h5
|
||
npm run dev:taro:weapp:student
|
||
```
|
||
|
||
Taro 具体页面、跨端能力和公开环境变量见 [apps/taro/README.md](apps/taro/README.md)。
|
||
|
||
## 验证
|
||
|
||
日常改动至少运行与改动范围对应的 TypeScript 和 contract 测试:
|
||
|
||
```bash
|
||
npm run check:api
|
||
npm run check:worker
|
||
npm run check:importer
|
||
npm run check:taro
|
||
npm run test:readiness
|
||
```
|
||
|
||
三端 H5 正式产物必须分别构建;根脚本 `build:taro:h5` 只等价于学生端构建:
|
||
|
||
```bash
|
||
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`,并使用严格模式:
|
||
|
||
```bash
|
||
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
|
||
|
||
远端仓库:
|
||
|
||
```text
|
||
https://git.gongxue100.com/chenhaogxjy/tiku-supabase.git
|
||
```
|
||
|
||
Gitea SSH 端口为 `2222`:
|
||
|
||
```bash
|
||
git clone ssh://git@git.gongxue100.com:2222/chenhaogxjy/tiku-supabase.git
|
||
```
|
||
|
||
开发流程:
|
||
|
||
```bash
|
||
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 |
|
||
|
||
服务器持续更新命令仍可以是:
|
||
|
||
```bash
|
||
sudo -u deploy /opt/tiku-saas/bin/deploy.sh
|
||
```
|
||
|
||
但这次基础升级不能直接运行服务器上的旧脚本。旧脚本会原地构建、直接覆盖 H5,并重启已经废弃的 `tiku-worker.service`;新版 Worker 强制要求显式 `--job`,旧 unit 会失败。首次覆盖部署必须先按 [服务器部署手册](scripts/deploy/README.md) 完成一次性升级。
|
||
|
||
新版发布流程包含:
|
||
|
||
- 独立候选 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:
|
||
|
||
```bash
|
||
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。详细顺序见 [生产地基基线](docs/refactor/production-foundation-baseline-20260712.md)。
|
||
|
||
## 首个平台超管
|
||
|
||
不要直接插入一个绕过 Auth 的管理员。先在真实 Supabase Auth 中创建或确认身份,再 dry-run:
|
||
|
||
```bash
|
||
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
|
||
```
|
||
|
||
确认输出后使用精确短语应用:
|
||
|
||
```bash
|
||
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 会永久拒绝,日常员工管理应走平台后台权限流。
|
||
|
||
## 关键文档
|
||
|
||
- [重构文档索引](docs/refactor/README.md)
|
||
- [架构](docs/refactor/architecture.md)
|
||
- [多租户鉴权安全契约](docs/refactor/multitenant-auth-security-contract.md)
|
||
- [前端交接索引](docs/refactor/frontend-handoff-index.md)
|
||
- [Taro H5 部署](docs/refactor/taro-h5-deployment.md)
|
||
- [PocketBase 真实数据迁移](docs/refactor/pocketbase-real-data-migration-runbook.md)
|
||
- [4C16G PostgreSQL 调优](docs/refactor/postgresql-4c16g-tuning.md)
|
||
- [容量压测](docs/refactor/performance-benchmark-runbook.md)
|
||
- [单租户十万学生容量](docs/refactor/tenant-student-capacity-runbook.md)
|
||
- [生产上线证据模板](docs/refactor/production-launch-evidence.template.json)
|
||
|
||
## 安全红线
|
||
|
||
- 前端只允许持有 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 或构建日志。
|