diff --git a/express/app.ts b/express/app.ts index 14da28e..1967cd3 100644 --- a/express/app.ts +++ b/express/app.ts @@ -3,6 +3,7 @@ import express, { type Response as ExpressResponse, } from "express"; import { match } from "path-to-regexp"; +import { Session } from "./auth"; import { cli } from "./cli"; import { contentTypes } from "./content-types"; import { httpCodes } from "./http-codes"; @@ -68,7 +69,7 @@ routes.forEach((route: Route, _idx: number, _allRoutes: Route[]) => { parameters: { one: 1, two: 2 }, request, user: auth.user, - session: auth.session, + session: new Session(auth.session, auth.user), }; try { diff --git a/express/auth/index.ts b/express/auth/index.ts index d68d1fc..c81666f 100644 --- a/express/auth/index.ts +++ b/express/auth/index.ts @@ -10,4 +10,11 @@ export { hashPassword, verifyPassword } from "./password"; export { AuthService, type AuthResult } from "./service"; export { type AuthStore, InMemoryAuthStore } from "./store"; export { generateToken, hashToken, SESSION_COOKIE_NAME } from "./token"; -export * from "./types"; +export { + type AuthMethod, + type SessionData, + type TokenId, + type TokenType, + Session, + tokenLifetimes, +} from "./types"; diff --git a/express/auth/types.ts b/express/auth/types.ts index bcc90f6..c8112dd 100644 --- a/express/auth/types.ts +++ b/express/auth/types.ts @@ -62,3 +62,35 @@ export const tokenLifetimes: Record = { password_reset: 1 * 60 * 60 * 1000, // 1 hour email_verify: 24 * 60 * 60 * 1000, // 24 hours }; + +// Import here to avoid circular dependency at module load time +import { AnonymousUser, type MaybeUser } from "../user"; + +// Session wrapper class providing a consistent interface for handlers. +// Always present on Call (never null), but may represent an anonymous session. +export class Session { + constructor( + private readonly data: SessionData | null, + private readonly user: MaybeUser, + ) {} + + getUser(): MaybeUser { + return this.user; + } + + getData(): SessionData | null { + return this.data; + } + + isAuthenticated(): boolean { + return this.user !== AnonymousUser; + } + + get tokenId(): string | undefined { + return this.data?.tokenId; + } + + get userId(): string | undefined { + return this.data?.userId; + } +} diff --git a/express/routes.ts b/express/routes.ts index 3e31e7f..7d2504a 100644 --- a/express/routes.ts +++ b/express/routes.ts @@ -51,19 +51,35 @@ const routes: Route[] = [ return ret; }; + const rrr = lr(routes) + + const template=` + + + + + + +` +const result = nunjucks.renderString(template,{rrr}) + const listing = lr(routes).join(", "); return { code, - result: listing + "\n", - contentType: contentTypes.text.plain, + result, + contentType: contentTypes.text.html, }; }, }, { path: "/whoami", methods: ["GET"], - handler: async (_call: Call): Promise => { - const me = services.session.getUser(); + handler: async (call: Call): Promise => { + const me = call.session.getUser(); const template = ` diff --git a/express/types.ts b/express/types.ts index a1ed455..ce9b917 100644 --- a/express/types.ts +++ b/express/types.ts @@ -8,7 +8,7 @@ import { } from "express"; import type { MatchFunction } from "path-to-regexp"; import { z } from "zod"; -import type { SessionData } from "./auth/types"; +import type { Session } from "./auth/types"; import { type ContentType, contentTypes } from "./content-types"; import { type HttpCode, httpCodes } from "./http-codes"; import { @@ -40,7 +40,7 @@ export type Call = { parameters: object; request: ExpressRequest; user: MaybeUser; - session: SessionData | null; + session: Session; }; export type InternalHandler = (req: ExpressRequest) => Promise;