Add user authentication and role management

This commit is contained in:
巫凤萍
2026-08-07 11:06:20 +08:00
parent 1f910c975d
commit c926a6a874
25 changed files with 2370 additions and 132 deletions

View File

@@ -8,8 +8,9 @@ test("builds the KOC LOOP product shell", async () => {
readFile(new URL("../app/admin-app.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/layout.tsx", import.meta.url), "utf8"),
]);
assert.match(page, /requireChatGPTUser/);
assert.match(page, /isAdminEmail/);
assert.match(page, /getUserFromCookieHeader/);
assert.match(page, /redirect\("\/login"\)/);
assert.match(page, /currentUser=\{user\}/);
assert.match(adminApp, /KOC LOOP/);
assert.match(adminApp, /内容分发闭环/);
assert.match(adminApp, /分发工作台/);
@@ -250,6 +251,63 @@ test("exports complete task recovery data to Excel with embedded images", async
assert.match(workbook, /oneCellAnchor/);
});
test("provides simple username-password login and three server-enforced roles", async () => {
const [
adminApp,
usersPage,
loginPage,
loginRoute,
logoutRoute,
usersRoute,
auth,
bootstrapRoute,
resourceExportRoute,
schema,
runtimeSchema,
migration,
] = await Promise.all([
readFile(new URL("../app/admin-app.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/users-page.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/login/page.tsx", import.meta.url), "utf8"),
readFile(new URL("../app/api/auth/login/route.ts", import.meta.url), "utf8"),
readFile(new URL("../app/api/auth/logout/route.ts", import.meta.url), "utf8"),
readFile(new URL("../app/api/users/route.ts", import.meta.url), "utf8"),
readFile(new URL("../lib/user-auth.ts", import.meta.url), "utf8"),
readFile(new URL("../app/api/bootstrap/route.ts", import.meta.url), "utf8"),
readFile(new URL("../app/api/resources-export/route.ts", import.meta.url), "utf8"),
readFile(new URL("../db/schema.ts", import.meta.url), "utf8"),
readFile(new URL("../lib/mvp-db.ts", import.meta.url), "utf8"),
readFile(new URL("../drizzle/0007_fantastic_sentinels.sql", import.meta.url), "utf8"),
]);
assert.match(loginPage, /登录账号/);
assert.match(loginPage, /当前|管理员分配|账号和密码/);
assert.match(loginRoute, /ensureInitialSuperAdmin/);
assert.match(loginRoute, /verifyPassword/);
assert.match(loginRoute, /Set-Cookie/);
assert.match(logoutRoute, /deleteSession/);
assert.match(logoutRoute, /clearSessionCookie/);
assert.match(auth, /PBKDF2/);
assert.match(auth, /HttpOnly/);
assert.match(auth, /SameSite=Lax/);
assert.match(auth, /SELECT id FROM users WHERE role = 'super_admin' LIMIT 1/);
assert.match(usersRoute, /currentUser\.role !== "super_admin" && role !== "user"/);
assert.match(usersRoute, /DELETE FROM auth_sessions WHERE user_id =/);
assert.match(usersPage, /不开放注册和个人改密/);
assert.match(usersPage, /普通用户/);
assert.match(usersPage, /管理员/);
assert.match(adminApp, /item\.key === "resources" && currentUser\.role === "user"/);
assert.match(adminApp, /item\.key === "users" && !isManager/);
assert.match(bootstrapRoute, /principal\.user\.role === "user"/);
assert.match(bootstrapRoute, /accounts: \[\]/);
assert.match(resourceExportRoute, /isManagerRequest/);
assert.match(schema, /authSessions/);
assert.match(schema, /users_single_super_admin_idx/);
assert.match(runtimeSchema, /CREATE TABLE IF NOT EXISTS auth_sessions/);
assert.match(migration, /CREATE TABLE `users`/);
assert.match(migration, /users_single_super_admin_idx/);
});
test("filters and exports the current KOC resource result set", async () => {
const [adminApp, exportRoute, workbook] = await Promise.all([
readFile(new URL("../app/admin-app.tsx", import.meta.url), "utf8"),
@@ -267,7 +325,7 @@ test("filters and exports the current KOC resource result set", async () => {
assert.match(exportRoute, /小红书号\/抖音号/);
assert.match(exportRoute, /历史合作来源/);
assert.match(exportRoute, /合作社资源 · 不可直联/);
assert.match(exportRoute, /isAdminRequest/);
assert.match(exportRoute, /isManagerRequest/);
assert.match(workbook, /relationships\/hyperlink/);
});