refactor(database)!: 后端不再渲染任何 HTML,只出 JSON

删掉约 1770 行服务端模板拼接:renderDashboard / renderLoginPage
(databaseRoutes)、adminPanels、libraryBrowser、uiTheme,以及
libraryPage —— 后者迁移前已是无人引用的死代码。

新增两个端点承接原先在 page handler 里 inline 算的东西:
  GET /database/config      免鉴权 bootstrap(org slug + dev 开关);
                            注册位置刻意早于 silo org 的提前返回,
                            org 未就绪时登录页仍要能渲染。
  GET /database/api/stats   概览统计,要求 silo org OWNER/ADMIN ——
                            它聚合的是 org 级计数与审计流,不是
                            单节点权限视图。

静态托管收敛到 static.ts:一份 filelib-web 构建产物挂 /app 与
/database 两个前缀,资源路由只注册一次。并发症是路由顺序成了硬约束
—— 具体页面路由必须先于 SPA 通配注册,否则重演 /database/dashboard
盖住 SPA 的老 bug(ADR-0029)。

/database/library 改为 302 到 /database/dashboard/library。

BREAKING: 部署需先构建 filelib-web,否则 static.ts 的 existsSync
守卫会让 /app 与 /database 全部 404。
This commit is contained in:
2026-07-26 20:17:59 +08:00
parent 3d0f4e5c2d
commit d159e372d2
8 changed files with 109 additions and 2630 deletions
+26 -17
View File
@@ -3,26 +3,35 @@
`/database/*` HTTP 面。代码写在这个目录里,`hub.ts` 通过 `plugin.ts` 挂载它,
所以服务器启动时能正确识别这些路由。
**前后端分离**:页面已迁到独立的 SvelteKit 静态 SPA `hub/database-admin/`
(与 `hub/admin-web/` 同一套框架)。本目录的后端只保留三件事:鉴权透传、一个
免鉴权配置端点、以及把 SPA 构建产物托管出去。页面全部由 SPA 客户端渲染。
**前后端分离**:页面全部在 SvelteKit 静态 SPA `hub/filelib-web/`(与 `hub/admin-web/`
同一套框架)。**老师端 `/app` 与管理后台 `/database` 共用这一份工程和这一份构建产物** ——
两个挂载前缀,一个 SPA。本目录的后端只保留三件事:鉴权透传、JSON 数据端点、
以及把构建产物托管出去。服务端不渲染任何 HTML。
后端路由:
- `GET /database/config` —— 免鉴权。返回 `{ siloOrganizationSlug, devLoginEnabled }`
- `GET /database/config` —— 免鉴权。返回 `{ orgSlug, devLoginEnabled }`
给 SPA 登录页拼飞书链接、决定是否显示 dev 按钮用。不含任何敏感数据。
`/database/api/login-info` 是同形状的既有端点,由 `routes/teacherApp.ts` 注册。)
- `GET /database/api/stats` —— 概览页统计。需登录 **且** 是 silo org OWNER/ADMIN。
- `GET /database/dev-login` —— 仅开发。见下。
- `GET /database``GET /database/*` —— SPA shell / 客户端路由 fallback
`static.ts``registerDatabaseSpa`;资产在 `/database/_app/*`
- `GET /database``GET /database/*``GET /app``GET /app/*` —— SPA shell /
客户端路由 fallback`static.ts``registerDatabaseSpa`)。
- `GET /_filelib/*` —— 构建产物资源。SvelteKit 的 `appDir` 改名为 `_filelib`
以避开 `admin-web` 在根上注册的 `/_app/*`(同名会让 Fastify 启动即抛重复路由)。
SPA 页面(`database-admin``paths.base='/database'`):
SPA 页面(`filelib-web`,真 URL 路由、无 hash):
- `/database/admin` —— 飞书登录页。按钮指向 `/auth/feishu/<orgSlug>`slug 来自
`/database/config`),回调由 `src/admin/routes/authRoutes.ts` 处理并种 session cookie。
- `/database/dashboard` —— 后台壳。未登录跳登录页;**登录但非 OWNER/ADMIN 显示无权提示**
- `/app` —— 老师端文件库。未登录显示登录卡片。
- `/database/admin` —— 管理员飞书登录页。按钮指向 `/auth/feishu/<orgSlug>`
回调由 `src/admin/routes/authRoutes.ts` 处理并种 session cookie
- `/database/dashboard` —— 后台外壳(侧栏 + 权限门)。未登录跳登录页;
**登录但非 OWNER/ADMIN 显示无权提示**。六个 tab 都是子路由:
`/database/dashboard`(概览)、`/library``/users``/groups``/search``/settings`
> **注册顺序要点**concrete 路由(`/database/config`、`/database/dev-login`)必须在
> `registerDatabaseSpa` 的 `/database/*` fallback 之前注册(已在 `plugin.ts` 保证),
> **注册顺序要点**concrete 路由(`/database/config`、`/database/api/*`、
> `/database/dev-login`、`/app/dev-login-teacher`)必须在 `registerDatabaseSpa` 的
> `/database/*`、`/app/*` fallback 之前注册(已在 `plugin.ts` 保证),
> 否则通配会 shadow 它们。
## 开发模式:用环境变量开启一键登录
@@ -73,17 +82,17 @@ allowDevLoginBypass = (NODE_ENV !== "production") && HUB_DEV_LOGIN_BYPASS 为真
| 文件 | 职责 |
|------|------|
| `plugin.ts` | 模块对外入口,`hub.ts``registerDatabasePlugin()` |
| `routes/databaseRoutes.ts` | 登录页/dashboard + 各子路由装配点 |
| `routes/databaseRoutes.ts` | `/database/config``/database/api/stats`、dev 旁路 + 各子路由装配点 |
| `routes/filelibRoutes.ts` | 文件库 树/授权 API |
| `routes/fileRoutes.ts` | 文件库 文件内容/导出 API |
| `routes/memberGroupRoutes.ts` | 成员组管理 API + `/groups/search` + `/users/search`(ADR-0028) |
| `routes/adminPanels.ts` | dashboard「用户管理」(org 成员)/「Group 管理」(MemberGroup 嵌套树)面板 |
| `routes/libraryPage.ts` | `/database/library` 文件库浏览页 |
| `routes/teacherApp.ts` | `/database/api/login-info` + 老师端 DEV 一键登录 |
| `static.ts` | filelib-web 构建产物托管:`/_filelib/*` 资源 + `/app``/database` 两个 SPA 回退 |
| `filelib/` | 文件库领域层(见下) |
新增一类**数据**端点时:要么直接往 `databaseRoutes.ts``app.get("/database/...")`
新增一类**数据**端点时:要么直接往 `databaseRoutes.ts``app.get("/database/api/...")`
要么新建 `routes/xxxRoutes.ts` 并在 `databaseRoutes.ts``registerXxxRoutes(app, {...})`
注册一次。
注册一次。**不要在后端拼 HTML** —— 页面一律加在 `hub/filelib-web/src/routes/` 下。
## 文件库(filelib/)