- scaffold hub/database-admin as SvelteKit 2 + Svelte 5 static SPA
with aurora/glass visual style (paths.base='/database')
- add lib/{api,session,org}.ts + Aurora.svelte component
- add routes: root redirect, /admin login page, /dashboard (OWNER/ADMIN only)
- backend: replace server-rendered HTML routes with /database/config JSON endpoint
- add hub/src/database/static.ts to serve SPA under /database/*
- wire registerDatabaseSpa into plugin.ts
- exempt /database/* from silo rate-limit (same treatment as /admin/*)
- add database:dev + database:build npm scripts; update deploy scripts
- update hub/src/database/README.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
src/database/
/database/* HTTP 面。代码写在这个目录里,hub.ts 通过 plugin.ts 挂载它,
所以服务器启动时能正确识别这些路由。
前后端分离:页面已迁到独立的 SvelteKit 静态 SPA hub/database-admin/
(与 hub/admin-web/ 同一套框架)。本目录的后端只保留三件事:鉴权透传、一个
免鉴权配置端点、以及把 SPA 构建产物托管出去。页面全部由 SPA 客户端渲染。
后端路由:
GET /database/config—— 免鉴权。返回{ siloOrganizationSlug, devLoginEnabled }, 给 SPA 登录页拼飞书链接、决定是否显示 dev 按钮用。不含任何敏感数据。GET /database/dev-login—— 仅开发。见下。GET /database、GET /database/*—— SPA shell / 客户端路由 fallback (static.ts的registerDatabaseSpa);资产在/database/_app/*。
SPA 页面(database-admin,paths.base='/database'):
/database/admin—— 飞书登录页。按钮指向/auth/feishu/<orgSlug>(slug 来自/database/config),回调由src/admin/routes/authRoutes.ts处理并种 session cookie。/database/dashboard—— 后台壳。未登录跳登录页;登录但非 OWNER/ADMIN 显示无权提示。
注册顺序要点:concrete 路由(
/database/config、/database/dev-login)必须在registerDatabaseSpa的/database/*fallback 之前注册(已在plugin.ts保证), 否则通配会 shadow 它们。
开发模式:用环境变量开启一键登录
本地开发没有真实飞书 app 时,可以用环境变量开启一键登录,跳过飞书 OAuth, 直接以现有 OWNER/ADMIN 身份登入后台。仅限开发,不是生产登录路径。
怎么开
在 hub/.env 里设:
HUB_DEV_LOGIN_BYPASS="true"
改完重启服务(npm run dev,或本地手动 npx tsx src/server.ts)。启动日志会
打印一行 DEV login bypass enabled: /database/dev-login ... 作为确认。
开启后:
/database/config返回devLoginEnabled: true,SPA 登录页据此显示 「⚡ 一键登录管理员」按钮- 后端注册
/database/dev-login端点:按钮就是打它,它签发一个和飞书 OAuth 回调完全一样的 session,然后跳到/database/dashboard
怎么关
把值设成 false(或 0 / no / off),或删掉这一行。关闭后按钮消失、
/database/dev-login 返回 404 —— 按钮和端点同进同退。
双重门禁(重要)
真正的开关是两个条件的与(判断在 plugin.ts):
allowDevLoginBypass = (NODE_ENV !== "production") && HUB_DEV_LOGIN_BYPASS 为真
即:只要 NODE_ENV=production,无论 HUB_DEV_LOGIN_BYPASS 设成什么,一键登录
都强制关闭。 生产始终只能走真实飞书 OAuth。
提醒:
HUB_DEV_LOGIN_BYPASS是敏感开关,别把开着它的.env带到任何联网 / 共享环境。整个旁路逻辑自包含在本目录(plugin.ts+routes/databaseRoutes.ts),src/admin的登录路由未受影响。
文件
| 文件 | 职责 |
|---|---|
plugin.ts |
模块对外入口,hub.ts 调 registerDatabasePlugin();先挂 concrete 路由再挂 SPA |
routes/databaseRoutes.ts |
后端 JSON / redirect 路由(/database/config、/database/dev-login),数据端点加在这里 |
static.ts |
registerDatabaseSpa:托管 database-admin/build 的 SPA + /database/* fallback |
新增一类数据端点时:要么直接往 databaseRoutes.ts 加 app.get("/database/..."),
要么新建 routes/xxxRoutes.ts 并在 databaseRoutes.ts 里 registerXxxRoutes(app, {...})
注册一次。页面则加在 database-admin/src/routes/ 下(SvelteKit 路由)。
SPA 构建与托管
- 前端在
hub/database-admin/,npm run build(或 hub 根的npm run database:build) 产出到database-admin/build/。hub 的npm run build会把两个 SPA 一起带出来。 static.ts默认从../../database-admin/build读产物;可用CPH_DATABASE_UI_DIR覆盖。产物缺失时降级:只 warn,不挂 SPA,/database/config与/database/dev-login仍可用。- 本地开发:hub 根
npm run database:dev起 Vite,它把/api、/auth、/database代理到127.0.0.1:8788。
约定(与 admin 面一致)
- 路由用绝对路径
"/database/...",不用 Fastify prefix —— 每条路由 grep 得到。 - guard 前置、fail closed:凡碰数据的端点第一行先跑
requireSession/requireOrgRole/requireProjectPermission(都在../admin/auth/guards.js)。 - 租户隔离(ADR-0020):每个 Prisma 查询都 scope 到
auth.organization.id, 不得跨 org。禁止无鉴权的数据路由。 - 数据库通过传入的
config.prisma访问(全进程单例,见../db.ts); 不要在这里new PrismaClient()。
为什么代码在 src/ 下
tsconfig.json 固定 rootDir: "src" 且 include: ["src/**/*.ts"]。只有
src/ 下的 .ts 会被 tsc 编译、被 tsx watch(npm run dev)加载。放在
src/ 之外的目录不会被构建,外部识别不到。