Add request-scoped context for session.getUser()
Use AsyncLocalStorage to provide request context so services can access the current user without needing Call passed through every function. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { match } from "path-to-regexp";
|
|||||||
import { Session } from "./auth";
|
import { Session } from "./auth";
|
||||||
import { cli } from "./cli";
|
import { cli } from "./cli";
|
||||||
import { contentTypes } from "./content-types";
|
import { contentTypes } from "./content-types";
|
||||||
|
import { runWithContext } from "./context";
|
||||||
import { httpCodes } from "./http-codes";
|
import { httpCodes } from "./http-codes";
|
||||||
import { routes } from "./routes";
|
import { routes } from "./routes";
|
||||||
import { services } from "./services";
|
import { services } from "./services";
|
||||||
@@ -75,7 +76,10 @@ routes.forEach((route: Route, _idx: number, _allRoutes: Route[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const retval = await route.handler(req);
|
const retval = await runWithContext(
|
||||||
|
{ user: auth.user },
|
||||||
|
() => route.handler(req),
|
||||||
|
);
|
||||||
return retval;
|
return retval;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle authentication errors
|
// Handle authentication errors
|
||||||
|
|||||||
27
express/context.ts
Normal file
27
express/context.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// context.ts
|
||||||
|
//
|
||||||
|
// Request-scoped context using AsyncLocalStorage.
|
||||||
|
// Allows services to access request data (like the current user) without
|
||||||
|
// needing to pass Call through every function.
|
||||||
|
|
||||||
|
import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
|
import { AnonymousUser, type MaybeUser } from "./user";
|
||||||
|
|
||||||
|
type RequestContext = {
|
||||||
|
user: MaybeUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
const asyncLocalStorage = new AsyncLocalStorage<RequestContext>();
|
||||||
|
|
||||||
|
// Run a function within a request context
|
||||||
|
function runWithContext<T>(context: RequestContext, fn: () => T): T {
|
||||||
|
return asyncLocalStorage.run(context, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current user from context, or AnonymousUser if not in a request
|
||||||
|
function getCurrentUser(): MaybeUser {
|
||||||
|
const context = asyncLocalStorage.getStore();
|
||||||
|
return context?.user ?? AnonymousUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getCurrentUser, runWithContext, type RequestContext };
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
// services.ts
|
// services.ts
|
||||||
|
|
||||||
import { AuthService } from "../auth";
|
import { AuthService } from "../auth";
|
||||||
|
import { getCurrentUser } from "../context";
|
||||||
import { db, migrate, migrationStatus, PostgresAuthStore } from "../database";
|
import { db, migrate, migrationStatus, PostgresAuthStore } from "../database";
|
||||||
import { getLogs, log } from "../logging";
|
import { getLogs, log } from "../logging";
|
||||||
import { anonymousUser, type User } from "../user";
|
import type { MaybeUser } from "../user";
|
||||||
|
|
||||||
const database = {
|
const database = {
|
||||||
db,
|
db,
|
||||||
@@ -29,8 +30,8 @@ const misc = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const session = {
|
const session = {
|
||||||
getUser: (): User => {
|
getUser: (): MaybeUser => {
|
||||||
return anonymousUser;
|
return getCurrentUser();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user