feat: centralize application framework primitives
This commit is contained in:
@@ -1580,6 +1580,55 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
|
||||
});
|
||||
}
|
||||
|
||||
const authorization = (
|
||||
mod.authorization as
|
||||
| Record<
|
||||
string,
|
||||
| string
|
||||
| {
|
||||
permission: string;
|
||||
resource?: (ctx: Context) => unknown | Promise<unknown>;
|
||||
exposeReason?: boolean;
|
||||
mayDiscover?: boolean | ((ctx: Context) => boolean | Promise<boolean>);
|
||||
}
|
||||
>
|
||||
| undefined
|
||||
)?.[method];
|
||||
if (authorization) {
|
||||
if (!ctx.user) {
|
||||
return Response.json({ ok: false, error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
const rule =
|
||||
typeof authorization === "string" ? { permission: authorization } : authorization;
|
||||
const resource = rule.resource ? await rule.resource(ctx) : undefined;
|
||||
if (rule.resource && resource == null) {
|
||||
const mayDiscover =
|
||||
typeof rule.mayDiscover === "function"
|
||||
? await rule.mayDiscover(ctx)
|
||||
: Boolean(rule.mayDiscover);
|
||||
return Response.json(
|
||||
{ ok: false, error: mayDiscover ? "Not Found" : "Forbidden" },
|
||||
{ status: mayDiscover ? 404 : 403 },
|
||||
);
|
||||
}
|
||||
const decision = ctx.authz ? await ctx.authz.decide(rule.permission, resource) : undefined;
|
||||
const permissions = ctx.locals.permissions;
|
||||
const allowed = decision
|
||||
? decision.allowed
|
||||
: typeof permissions === "function"
|
||||
? await permissions(rule.permission, ctx)
|
||||
: Array.isArray(permissions) && permissions.includes(rule.permission);
|
||||
if (!allowed) {
|
||||
return Response.json(
|
||||
rule.exposeReason
|
||||
? { ok: false, error: "Forbidden", reason: decision?.reason }
|
||||
: { ok: false, error: "Forbidden" },
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
ctx.resource = resource;
|
||||
}
|
||||
|
||||
return (await handler(ctx)) as Response;
|
||||
}
|
||||
|
||||
@@ -1740,6 +1789,33 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
|
||||
const storeContainer = requestStoreContainer(ctx.req, matched.route.raw);
|
||||
|
||||
const mod = await loadModule(matched.route.file);
|
||||
ctx.params = matched.params;
|
||||
const pageSecurity = (mod.__wrnexusSecurity ?? {}) as Record<string, string>;
|
||||
if (/^(?:required|true)$/i.test(pageSecurity.auth ?? "") && !ctx.user) {
|
||||
if (ctx.req.method.toUpperCase() === "POST") {
|
||||
return Response.json({ error: "Authentication required" }, { status: 401 });
|
||||
}
|
||||
const login = new URL("/login", ctx.url);
|
||||
login.searchParams.set("returnTo", ctx.url.pathname + ctx.url.search);
|
||||
return Response.redirect(login, 302);
|
||||
}
|
||||
if (pageSecurity.permission) {
|
||||
const decision = ctx.authz ? await ctx.authz.decide(pageSecurity.permission) : undefined;
|
||||
const permissions = ctx.locals.permissions;
|
||||
const allowed = decision
|
||||
? decision.allowed
|
||||
: typeof permissions === "function"
|
||||
? await permissions(pageSecurity.permission, ctx)
|
||||
: Array.isArray(permissions) && permissions.includes(pageSecurity.permission);
|
||||
if (!allowed) {
|
||||
if (ctx.req.method.toUpperCase() === "POST") {
|
||||
return Response.json({ error: "Permission denied" }, { status: 403 });
|
||||
}
|
||||
const forbidden = new URL(pageSecurity.forbidden ?? "/forbidden", ctx.url);
|
||||
if (decision?.reason) forbidden.searchParams.set("reason", decision.reason);
|
||||
return Response.redirect(forbidden, 303);
|
||||
}
|
||||
}
|
||||
if (ctx.req.method.toUpperCase() === "POST") {
|
||||
const actionResponse = await handlePageAction(ctx, mod);
|
||||
if (actionResponse) return actionResponse;
|
||||
@@ -1749,7 +1825,6 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
|
||||
throw new Error(`Page ${matched.route.file} has no default export`);
|
||||
}
|
||||
|
||||
ctx.params = matched.params;
|
||||
const meta = (mod.meta ?? {}) as PageMeta;
|
||||
const pageNavigation = (mod.__wrnexusNavigation ?? {}) as { preserve?: string };
|
||||
const pageCache = (mod.__wrnexusCache ?? {}) as Record<string, string>;
|
||||
|
||||
Reference in New Issue
Block a user