feat(filelib-web): 把 /database 各页从后端 HTML 拼接迁到 SvelteKit 路由

登录页、后台外壳与六个 tab 全部成为客户端路由:
  /database/admin              登录(迁自 renderLoginPage)
  /database/dashboard          概览(迁自 renderDashboard)
  .../library .../users .../groups .../search .../settings

六个 tab 是真 URL,不再是 location.hash + display:none —— 刷新不丢
位置,链接可分享。

BrowserShell 拆成 LibraryView,加 showUserFooter:老师端 /app 显示
身份/登出页脚,后台的文件库 tab 不显示(外层已有身份区)。

bootstrap 走 /database/config 而非 /database/api/login-info:后者由
teacherApp 在 silo org 查找成功后才注册,前者无条件注册,登录页在
org 未就绪时也必须能拿到配置。

后端拥有的链接(OAuth、DEV 一键登录)标 data-sveltekit-reload,
否则被客户端路由拦下。
This commit is contained in:
2026-07-26 20:17:43 +08:00
parent de9f846fd0
commit 3d0f4e5c2d
17 changed files with 576 additions and 61 deletions
+34
View File
@@ -0,0 +1,34 @@
/**
* 会话装载:GET /database/api/me 一次,结果进 `me` store。
* 老师端与管理后台共用 —— 两处的区别只是拿到 me 之后怎么用
* (老师端未登录显示登录视图;管理后台未登录跳 /database/admin,
* 非 isWebsiteAdmin 显示无权提示)。
*/
import { get } from "svelte/store";
import { api, UnauthenticatedError } from "./api.js";
import { me, authChecked } from "./stores.js";
import type { MeResponse } from "./types.js";
/** 幂等:已检查过就不再打请求(路由间切换不重复拉取)。 */
export async function loadSession(force = false): Promise<void> {
if (get(authChecked) && !force) return;
try {
me.set(await api<MeResponse>("/database/api/me"));
} catch (e) {
if (!(e instanceof UnauthenticatedError)) console.error(e);
me.set(null);
} finally {
authChecked.set(true);
}
}
/** 退出登录:清后端 cookie 再清前端 store。 */
export async function logout(): Promise<void> {
try {
await fetch("/auth/logout", { method: "POST", credentials: "same-origin" });
} catch {
/* 网络失败也照样清前端状态 */
}
me.set(null);
}