Merge GitHub upstream into Gitea primary
This commit is contained in:
@@ -3,30 +3,73 @@ import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
const migration = readFileSync(
|
||||
new URL("../supabase/migrations/20260727010000_refine_admin_redemption_audit.sql", import.meta.url),
|
||||
new URL("../supabase/migrations/20260805010000_reconcile_admin_redemption_audit.sql", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const auth = readFileSync(new URL("../src/lib/admin/auth.ts", import.meta.url), "utf8");
|
||||
const authPolicy = readFileSync(new URL("../src/lib/admin/auth-policy.ts", import.meta.url), "utf8");
|
||||
const adminUser = readFileSync(new URL("../src/lib/supabase/admin.ts", import.meta.url), "utf8");
|
||||
const codesRoute = readFileSync(new URL("../src/app/api/admin/codes/route.ts", import.meta.url), "utf8");
|
||||
const codeRoute = readFileSync(new URL("../src/app/api/admin/codes/[id]/route.ts", import.meta.url), "utf8");
|
||||
const providers = readFileSync(new URL("../src/lib/admin/providers.ts", import.meta.url), "utf8");
|
||||
const adminLayout = readFileSync(new URL("../src/app/admin/layout.tsx", import.meta.url), "utf8");
|
||||
const adminApp = readFileSync(new URL("../src/components/admin/admin-app.tsx", import.meta.url), "utf8");
|
||||
const adminRootRoute = readFileSync(new URL("../src/app/admin/route.ts", import.meta.url), "utf8");
|
||||
const readonlyRoutes = ["users", "credit-transactions", "consultations", "audit-logs"].map((resource) =>
|
||||
readFileSync(new URL(`../src/app/api/admin/${resource}/route.ts`, import.meta.url), "utf8"),
|
||||
);
|
||||
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
||||
|
||||
test("admin APIs use persisted Better Auth roles with admin and viewer boundaries", () => {
|
||||
test("admin APIs use persisted Better Auth roles with admin-only boundaries", () => {
|
||||
assert.match(auth, /requireIdentityUser/);
|
||||
assert.match(auth, /getIdentityAuthServices\(\)\.user\.api/);
|
||||
assert.match(authPolicy, /user\.role\.includes\("admin"\)/);
|
||||
assert.match(authPolicy, /user\.role\.includes\("viewer"\)/);
|
||||
assert.match(authPolicy, /access === "write" && role !== "admin"/);
|
||||
assert.doesNotMatch(authPolicy, /viewer/);
|
||||
assert.doesNotMatch(auth, /ADMIN_EMAILS|isAdminEmail/);
|
||||
assert.match(auth, /APP_ENV\?\.trim\(\) === "production"/);
|
||||
assert.match(codesRoute, /requireAdminSession\("write"\)/);
|
||||
assert.match(codeRoute, /requireAdminSession\("write"\)/g);
|
||||
});
|
||||
|
||||
test("self-hosted account entry checks only the persisted admin role", () => {
|
||||
const selfHostedBranch = adminUser.slice(
|
||||
adminUser.indexOf('process.env.AUTH_PROVIDER?.trim() === "self-hosted"'),
|
||||
adminUser.indexOf("if (isAdminEmail"),
|
||||
);
|
||||
|
||||
assert.match(selfHostedBranch, /queryAdminRows/);
|
||||
assert.match(selfHostedBranch, /select role from identity\.users where id = \$1 limit 1/);
|
||||
assert.match(selfHostedBranch, /role === "admin"/);
|
||||
assert.doesNotMatch(selfHostedBranch, /viewer|isAdminEmail|ADMIN_EMAILS/);
|
||||
assert.match(auth, /authorizeAdminAccess\(user, access\)/);
|
||||
});
|
||||
|
||||
test("admin navigation exposes payment and package resources", () => {
|
||||
assert.match(adminApp, /name: "payments", list: "\/admin\/payments", meta: \{ label: "支付管理"/);
|
||||
assert.match(adminApp, /name: "packages", list: "\/admin\/packages", meta: \{ label: "套餐管理"/);
|
||||
assert.match(adminApp, /CreditCardOutlined/);
|
||||
assert.match(adminApp, /ShoppingOutlined/);
|
||||
});
|
||||
|
||||
test("admin sider replaces logout with a collapsed-aware return-to-chat link", () => {
|
||||
assert.match(adminApp, /ThemedLayout Sider=\{AdminSider\}/);
|
||||
assert.match(adminApp, /ThemedSider/);
|
||||
assert.match(adminApp, /render=\{\(\{ items, collapsed \}\)/);
|
||||
assert.match(adminApp, /\{items\}/);
|
||||
assert.match(adminApp, /ArrowLeftOutlined/);
|
||||
assert.match(adminApp, /<Link href="\/" aria-label="返回对话">\{collapsed \? null : "返回对话"\}<\/Link>/);
|
||||
assert.doesNotMatch(adminApp, /logout\s*[,(}]|authProvider\.logout/);
|
||||
});
|
||||
|
||||
test("admin pages and root route are server-gated before rendering or redirecting", () => {
|
||||
assert.match(adminLayout, /await requireAdminSession\("read"\)/);
|
||||
assert.match(adminLayout, /error\.status === 401 \? "\/login" : "\/"/);
|
||||
assert.match(adminLayout, /redirect\(/);
|
||||
assert.match(adminRootRoute, /await requireAdminSession\("read"\)/);
|
||||
assert.match(adminRootRoute, /error\.status === 401 \? "\/login" : "\/"/);
|
||||
assert.match(adminRootRoute, /headers: \{ location: "\/admin\/codes" \}/);
|
||||
});
|
||||
|
||||
test("readonly resources cannot be mutated through Refine access control", () => {
|
||||
for (const resource of ["users", "credit-transactions", "consultations", "audit-logs"]) {
|
||||
assert.match(providers, new RegExp(`"${resource}"`));
|
||||
|
||||
Reference in New Issue
Block a user