diff --git a/hub/.gitignore b/hub/.gitignore
index 9830097..b5e8e21 100644
--- a/hub/.gitignore
+++ b/hub/.gitignore
@@ -12,3 +12,6 @@ dist/
admin-web/node_modules/
admin-web/build/
admin-web/.svelte-kit/
+filelib-web/node_modules/
+filelib-web/build/
+filelib-web/.svelte-kit/
diff --git a/hub/filelib-web/index.html b/hub/filelib-web/index.html
deleted file mode 100644
index e395a5c..0000000
--- a/hub/filelib-web/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- 文件库
-
-
-
-
-
-
diff --git a/hub/filelib-web/package-lock.json b/hub/filelib-web/package-lock.json
index 8a6f9c7..f306d41 100644
--- a/hub/filelib-web/package-lock.json
+++ b/hub/filelib-web/package-lock.json
@@ -8,6 +8,8 @@
"name": "filelib-web",
"version": "0.1.0",
"devDependencies": {
+ "@sveltejs/adapter-static": "^3.0.10",
+ "@sveltejs/kit": "^2.63.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/vite": "^4.3.2",
"svelte": "^5.56.1",
@@ -130,6 +132,13 @@
"url": "https://github.com/sponsors/Boshen"
}
},
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.29",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
@@ -412,6 +421,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@sveltejs/acorn-typescript": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.11.tgz",
@@ -422,6 +438,58 @@
"acorn": "^8.9.0"
}
},
+ "node_modules/@sveltejs/adapter-static": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz",
+ "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.0.0"
+ }
+ },
+ "node_modules/@sveltejs/kit": {
+ "version": "2.70.1",
+ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.70.1.tgz",
+ "integrity": "sha512-nY9SPHGOZro3doud9vZXDBwl9tCZIouuJztjgSHs6PAIrv9M/z5O7eOhPV5xU7CgVHA976Jwu3BA1hIFvXztkA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@sveltejs/acorn-typescript": "^1.0.9",
+ "@types/cookie": "^0.6.0",
+ "acorn": "^8.16.0",
+ "cookie": "^0.6.0",
+ "devalue": "^5.8.1",
+ "esm-env": "^1.2.2",
+ "kleur": "^4.1.5",
+ "magic-string": "^0.30.5",
+ "mrmime": "^2.0.0",
+ "set-cookie-parser": "^3.0.0",
+ "sirv": "^3.0.0"
+ },
+ "bin": {
+ "svelte-kit": "svelte-kit.js"
+ },
+ "engines": {
+ "node": ">=18.13"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0",
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "typescript": "^5.3.3 || ^6.0.0",
+ "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@sveltejs/load-config": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz",
@@ -747,6 +815,13 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -820,6 +895,16 @@
"node": ">=6"
}
},
+ "node_modules/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -946,6 +1031,16 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/lightningcss": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
@@ -1246,6 +1341,16 @@
"node": ">=4"
}
},
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
@@ -1389,6 +1494,28 @@
"node": ">=6"
}
},
+ "node_modules/set-cookie-parser": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz",
+ "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sirv": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
+ "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1490,6 +1617,16 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
diff --git a/hub/filelib-web/package.json b/hub/filelib-web/package.json
index 43c706a..08320e6 100644
--- a/hub/filelib-web/package.json
+++ b/hub/filelib-web/package.json
@@ -4,12 +4,15 @@
"version": "0.1.0",
"type": "module",
"scripts": {
- "dev": "vite",
+ "dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
- "check": "svelte-check --tsconfig ./tsconfig.json"
+ "prepare": "svelte-kit sync || echo ''",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
+ "@sveltejs/adapter-static": "^3.0.10",
+ "@sveltejs/kit": "^2.63.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/vite": "^4.3.2",
"svelte": "^5.56.1",
diff --git a/hub/filelib-web/src/App.svelte b/hub/filelib-web/src/App.svelte
deleted file mode 100644
index cc69f92..0000000
--- a/hub/filelib-web/src/App.svelte
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-{#if !$authChecked}
- 加载中…
-{:else if $me}
-
-{:else}
-
-{/if}
-
-
diff --git a/hub/filelib-web/src/app.d.ts b/hub/filelib-web/src/app.d.ts
new file mode 100644
index 0000000..e1551fc
--- /dev/null
+++ b/hub/filelib-web/src/app.d.ts
@@ -0,0 +1,12 @@
+// See https://svelte.dev/docs/kit/types#app
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/hub/filelib-web/src/app.html b/hub/filelib-web/src/app.html
new file mode 100644
index 0000000..1e7531f
--- /dev/null
+++ b/hub/filelib-web/src/app.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/hub/filelib-web/src/main.ts b/hub/filelib-web/src/main.ts
deleted file mode 100644
index 727d356..0000000
--- a/hub/filelib-web/src/main.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { mount } from "svelte";
-import App from "./App.svelte";
-import "./app.css";
-
-export default mount(App, { target: document.getElementById("app")! });
diff --git a/hub/filelib-web/svelte.config.js b/hub/filelib-web/svelte.config.js
new file mode 100644
index 0000000..87af393
--- /dev/null
+++ b/hub/filelib-web/svelte.config.js
@@ -0,0 +1,35 @@
+import adapter from '@sveltejs/adapter-static';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+/**
+ * 老师端 /app 与管理后台 /database/* 共用这一份 SPA 构建产物,由 hub 后端静态托管
+ * (见 hub/src/database/static.ts)。服务端不渲染任何页面,只提供 /database/api/*。
+ *
+ * 两个关键配置:
+ *
+ * - `appDir: '_filelib'` —— 默认 `_app` 会与 admin-web 在同一个 Fastify 实例上注册的
+ * 根 `/_app/*` 资源路由撞车(见 hub/src/admin/static.ts),Fastify 重复路由会直接
+ * 在启动时抛错。改名后两套 SPA 的资源路径互不干扰。
+ *
+ * - `paths.relative: false` —— 同一份 index.html 会在不同深度的 URL 下被送出
+ * (`/app`、`/database/dashboard/users`),相对资源路径会解析错。必须用绝对路径。
+ */
+const config = {
+ preprocess: vitePreprocess(),
+ kit: {
+ adapter: adapter({
+ pages: 'build',
+ assets: 'build',
+ fallback: 'index.html',
+ precompress: false,
+ strict: false,
+ }),
+ appDir: '_filelib',
+ paths: {
+ base: '',
+ relative: false,
+ },
+ },
+};
+
+export default config;
diff --git a/hub/filelib-web/tsconfig.json b/hub/filelib-web/tsconfig.json
index 80e1189..0d61fe3 100644
--- a/hub/filelib-web/tsconfig.json
+++ b/hub/filelib-web/tsconfig.json
@@ -1,4 +1,5 @@
{
+ "extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
@@ -12,6 +13,5 @@
"resolveJsonModule": true,
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"]
- },
- "include": ["src/**/*.ts", "src/**/*.svelte"]
+ }
}
diff --git a/hub/filelib-web/vite.config.ts b/hub/filelib-web/vite.config.ts
index 9e4b9c6..ef7e8d4 100644
--- a/hub/filelib-web/vite.config.ts
+++ b/hub/filelib-web/vite.config.ts
@@ -1,25 +1,27 @@
-import { defineConfig } from "vite";
-import { svelte } from "@sveltejs/vite-plugin-svelte";
+import { sveltekit } from "@sveltejs/kit/vite";
import tailwindcss from "@tailwindcss/vite";
+import { defineConfig } from "vite";
-// 老师端独立 SPA:构建产物由 hub 后端静态托管于 /app;
-// 开发时 vite dev(:5173)把 API/认证请求代理到后端(:8788)。
+// 老师端 /app + 管理后台 /database/* 的唯一前端工程;构建产物由 hub 后端静态托管。
+// 开发时 vite dev(:5173)把 API/认证/一键登录请求代理到后端(:8788);
+// 页面路由全部由 SvelteKit 客户端路由处理,后端不参与。
const backend = "http://127.0.0.1:8788";
export default defineConfig({
- plugins: [svelte(), tailwindcss()],
- base: "/app/",
- build: {
- outDir: "dist",
- emptyOutDir: true,
- },
+ plugins: [tailwindcss(), sveltekit()],
server: {
port: 5173,
proxy: {
"/database/api": backend,
+ // 免鉴权 bootstrap(org slug + dev 开关);登录页和用户管理页都靠它。
+ "/database/config": backend,
"/auth": backend,
+ // 后端拥有的 DEV 一键登录端点(签 cookie 后 302);不代理会被 SPA 回退吃掉。
+ "/database/dev-login": backend,
"/app/dev-login": backend,
"/app/dev-login-teacher": backend,
+ // 平台层 org 成员 API(用户管理面板)。
+ "/api/org": backend,
},
},
});