feat: centralize application framework primitives
This commit is contained in:
@@ -33,6 +33,10 @@ export interface EndpointDefinition<I, O> {
|
||||
input?: SchemaLike<I> | OutputSchemaLike<I>;
|
||||
output?: SchemaLike<O> | OutputSchemaLike<O>;
|
||||
auth?: "optional" | "required";
|
||||
/** Permission checked through an installed @wrnexus/authz middleware. */
|
||||
permission?: string;
|
||||
/** Resource supplied to bound authorization policies. */
|
||||
resource?: (ctx: Context, input: I) => unknown | Promise<unknown>;
|
||||
description?: string;
|
||||
tags?: string[];
|
||||
handler(input: I, ctx: Context): O | Promise<O>;
|
||||
@@ -111,6 +115,31 @@ export function defineEndpoint(
|
||||
: await ctx.req.json().catch(() => ({}));
|
||||
input = schemaValue(definition.input, resolvedInput);
|
||||
}
|
||||
if (definition.permission) {
|
||||
const authz = (
|
||||
ctx as Context & {
|
||||
authz?: {
|
||||
decide(
|
||||
permission: string,
|
||||
resource?: unknown,
|
||||
): Promise<{ allowed: boolean; reason?: string }>;
|
||||
};
|
||||
}
|
||||
).authz;
|
||||
if (!authz) {
|
||||
throw new EndpointError(
|
||||
500,
|
||||
"AUTHZ_NOT_CONFIGURED",
|
||||
"Authorization middleware is not configured.",
|
||||
);
|
||||
}
|
||||
const resource = definition.resource ? await definition.resource(ctx, input) : undefined;
|
||||
const decision = await authz.decide(definition.permission, resource);
|
||||
if (!decision.allowed) {
|
||||
throw new EndpointError(403, "FORBIDDEN", "Permission denied.");
|
||||
}
|
||||
ctx.resource = resource;
|
||||
}
|
||||
const rawOutput = await definition.handler(input, ctx);
|
||||
const output = definition.output ? schemaValue(definition.output, rawOutput) : rawOutput;
|
||||
return output instanceof Response ? output : json({ data: output });
|
||||
|
||||
Reference in New Issue
Block a user