fix(admin): fail safe across migration and auth boundaries

The recovery migration crossed the identity and RBAC ledgers without guarding schema prerequisites, while unknown configuration, provider, and database failures escaped the admin authorization boundary as 500s. Keep recovery in the DB ledger with explicit prerequisite no-ops, and sanitize unknown authorization failures to the existing 503 path.
This commit is contained in:
Jesse_Chen
2026-08-07 12:08:58 +08:00
parent 754505d40a
commit 66a6c96a5c
9 changed files with 312 additions and 108 deletions
+3 -18
View File
@@ -16,7 +16,9 @@ import {
verifyHighRiskAdminProof,
type AdminMfaStatus,
} from "./auth-policy";
import { isPostgresError } from "./database";
import { adminErrorResponse } from "./admin-error-response";
export { adminErrorResponse } from "./admin-error-response";
export const listQuerySchema = z.object({
page: z.coerce.number().int().min(1).default(1),
@@ -40,23 +42,6 @@ export function requestId(request: Request): string {
return supplied && supplied.length <= 200 ? supplied : crypto.randomUUID();
}
export function adminErrorResponse(error: unknown) {
if (error instanceof AdminAuthorizationError) {
return NextResponse.json({ error: error.message }, { status: error.status });
}
if (isPostgresError(error)) {
if (error.code === "42501") return NextResponse.json({ error: "无权执行此操作" }, { status: 403 });
if (error.code === "40001") return NextResponse.json({ error: "资源已被其他管理员修改,请刷新后重试" }, { status: 409 });
if (error.code === "22023" || error.code === "23514" || error.code === "23505") {
return NextResponse.json({ error: "提交内容不符合业务约束" }, { status: 400 });
}
}
return NextResponse.json(
{ error: "后台服务暂时不可用" },
{ status: 500 },
);
}
export async function requireAdminMutation(request: Request, permission: AdminPermission) {
if (!isSameOriginAdminMutation(request.headers.get("origin"), request.url)) {
throw new AdminAuthorizationError("请求来源不可信", 403);