fix(admin): preserve revoked owners and 503 status

Owner recovery lived in the identity-only migration path and could clear historical revocations on conflict. Admin layout also rethrew authorization 503s, turning service-unavailable failures into 500 responses.\n\nMove recovery behind the RBAC migration sequence, exclude revoked candidates without mutating their history, and terminate layout redirects at a no-store 503 route.
This commit is contained in:
Jesse_Chen
2026-08-07 11:33:48 +08:00
parent 0d7e5a26d5
commit 754505d40a
7 changed files with 95 additions and 15 deletions
@@ -0,0 +1,9 @@
export function GET() {
return new Response("后台服务暂时不可用", {
status: 503,
headers: {
"cache-control": "no-store",
"content-type": "text/plain; charset=utf-8",
},
});
}
+1
View File
@@ -17,6 +17,7 @@ export default async function AdminLayout({ children }: { children: ReactNode })
const failure = resolveAdminPageAccessFailure(error.status);
if (failure.kind === "login") redirect(failure.location);
if (failure.kind === "forbidden") forbidden();
if (failure.kind === "unavailable") redirect(failure.location);
}
throw error;
}
+7 -2
View File
@@ -3,7 +3,7 @@ export type AdminPageAuthorizationStatus = 401 | 403 | 503;
export type AdminPageAccessFailure =
| { kind: "login"; location: "/login" }
| { kind: "forbidden"; status: 403; message: string }
| { kind: "unavailable"; status: 503; message: string };
| { kind: "unavailable"; status: 503; message: string; location: "/admin-unavailable" };
export function resolveAdminPageAccessFailure(
status: AdminPageAuthorizationStatus,
@@ -14,5 +14,10 @@ export function resolveAdminPageAccessFailure(
if (status === 403) {
return { kind: "forbidden", status, message: "无权访问后台" };
}
return { kind: "unavailable", status, message: "后台服务暂时不可用" };
return {
kind: "unavailable",
status,
message: "后台服务暂时不可用",
location: "/admin-unavailable",
};
}