refactor(server): extract createApp and add auth/admin test seams
createApp() builds the Application without listening, so tests can call app.handle() directly. The listen call is gated behind import.meta.main so importing server.ts no longer boots a port. ADMIN_DIDS is read inside requireAdmin on each call (was frozen at module load), and a mutable authenticator indirection lets tests stub authenticateRequest via _setAuthenticatorForTest.
This commit is contained in:
44
server.ts
44
server.ts
@@ -19,13 +19,25 @@ type AuthCtx = {
|
|||||||
response: { status: number; body: unknown };
|
response: { status: number; body: unknown };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let authenticator: typeof authenticateRequest = authenticateRequest;
|
||||||
|
|
||||||
|
export const _setAuthenticatorForTest = (
|
||||||
|
fn: typeof authenticateRequest,
|
||||||
|
): void => {
|
||||||
|
authenticator = fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const _resetAuthenticatorForTest = (): void => {
|
||||||
|
authenticator = authenticateRequest;
|
||||||
|
};
|
||||||
|
|
||||||
const requireDidOwnership = async (
|
const requireDidOwnership = async (
|
||||||
ctx: AuthCtx,
|
ctx: AuthCtx,
|
||||||
did: string,
|
did: string,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
let verifiedDid: string;
|
let verifiedDid: string;
|
||||||
try {
|
try {
|
||||||
verifiedDid = await authenticateRequest(
|
verifiedDid = await authenticator(
|
||||||
ctx.request.headers.get("Authorization"),
|
ctx.request.headers.get("Authorization"),
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -41,17 +53,18 @@ const requireDidOwnership = async (
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ADMIN_DIDS = new Set(
|
const adminDids = (): Set<string> =>
|
||||||
|
new Set(
|
||||||
(Deno.env.get("ADMIN_DIDS") ?? "")
|
(Deno.env.get("ADMIN_DIDS") ?? "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((d) => d.trim())
|
.map((d) => d.trim())
|
||||||
.filter(Boolean),
|
.filter(Boolean),
|
||||||
);
|
);
|
||||||
|
|
||||||
const requireAdmin = async (ctx: AuthCtx): Promise<boolean> => {
|
const requireAdmin = async (ctx: AuthCtx): Promise<boolean> => {
|
||||||
let verifiedDid: string;
|
let verifiedDid: string;
|
||||||
try {
|
try {
|
||||||
verifiedDid = await authenticateRequest(
|
verifiedDid = await authenticator(
|
||||||
ctx.request.headers.get("Authorization"),
|
ctx.request.headers.get("Authorization"),
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -59,7 +72,7 @@ const requireAdmin = async (ctx: AuthCtx): Promise<boolean> => {
|
|||||||
ctx.response.body = { error: "Unauthorized" };
|
ctx.response.body = { error: "Unauthorized" };
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ADMIN_DIDS.has(verifiedDid)) {
|
if (!adminDids().has(verifiedDid)) {
|
||||||
ctx.response.status = 403;
|
ctx.response.status = 403;
|
||||||
ctx.response.body = { error: "Admin only" };
|
ctx.response.body = { error: "Admin only" };
|
||||||
return false;
|
return false;
|
||||||
@@ -260,9 +273,10 @@ router.delete("/:did/webhooks/:id", async (ctx) => {
|
|||||||
// ctx.response.status = 204;
|
// ctx.response.status = 204;
|
||||||
// })
|
// })
|
||||||
|
|
||||||
const app = new Application();
|
export const createApp = (): Application => {
|
||||||
|
const app = new Application();
|
||||||
|
|
||||||
app.use(async (ctx, next) => {
|
app.use(async (ctx, next) => {
|
||||||
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
|
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
|
||||||
ctx.response.headers.set(
|
ctx.response.headers.set(
|
||||||
"Access-Control-Allow-Methods",
|
"Access-Control-Allow-Methods",
|
||||||
@@ -277,10 +291,16 @@ app.use(async (ctx, next) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(router.routes());
|
app.use(router.routes());
|
||||||
app.use(router.allowedMethods());
|
app.use(router.allowedMethods());
|
||||||
|
|
||||||
log("[server] listening on port 8080");
|
return app;
|
||||||
app.listen({ port: 8080 });
|
};
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
const app = createApp();
|
||||||
|
log("[server] listening on port 8080");
|
||||||
|
app.listen({ port: 8080 });
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user