Files
gongxue-base/README.md
2026-07-12 19:26:57 +08:00

13 KiB
Raw Blame History

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 migrationspublic RLS 140/140tenant 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/25Chrome 交互烟测 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_apitiku_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:resetdb:smoke-seed:testtest:apitest: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 keyHTTPS token 应交给系统 credential helper不能写入 remote URL。
  • 服务器只使用仓库只读 deploy key/token不得复用开发者可写凭据。
  • 真实 .envdeploy.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 测试凭据。
  • 数据库标记为 stagingallow_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 roleAPI/Worker 运行角色不能执行 DDL。
  • 破坏性测试只允许明确标记为 local/test/ci 且 allow_destructive_tests=true 的隔离数据库;绝不允许 staging/production并且必须使用精确确认短语。
  • migration 是前向操作,代码/Web 回滚不会回滚数据库;先备份、演练兼容性,再迁移。
  • 生产证据、访问 token、支付/短信/存储密钥和用户隐私数据不得写入 Git、README 或构建日志。