Files
publisher-dashboard/apps/api-server/src/context.ts
igm 25c8bab741 Add orgMemberMiddleware for org-scoped procedures
- Add OrgInfo, OrgMembership, OrgMemberContext types to context.ts
- Create org-member.ts middleware that:
  - Chains with authMiddleware
  - Takes input with org slug
  - Looks up org and verifies membership
  - Adds org and membership info to context
- Export from middlewares/index.ts and procedures/base.ts

Also simplify superuserMiddleware to use authMiddleware.concat()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:49:03 +08:00

149 lines
3.1 KiB
TypeScript

/**
* API context types for oRPC handlers
*/
import type { Database } from "@reviq/db-schema";
import type { EmailClient } from "@reviq/emails";
import type { Kysely } from "kysely";
/**
* Email configuration for the API
*/
export interface EmailConfig {
client: EmailClient;
fromAddress: string;
baseUrl: string;
}
/**
* Base API context available to all handlers
*/
export interface APIContext {
/** Database client */
db: Kysely<Database>;
/** Request origin (e.g., "http://localhost:6827") */
origin: string;
/** Allowed WebAuthn origins */
allowedOrigins: string[];
/** Relying party name for WebAuthn */
rpName: string;
/** Request headers (for reading cookies, auth headers) */
reqHeaders: Headers;
/** Response headers (for setting cookies) */
resHeaders: Headers;
/** Client IP address from direct connection (fallback when no proxy headers) */
clientIP?: string | null;
/** Email client and configuration */
email: EmailConfig;
}
/**
* User information from the session
*/
export interface SessionUser {
id: number;
email: string;
displayName: string | null;
emailVerifiedAt: Date | null;
isSuperuser: boolean;
}
/**
* Session information
*/
export interface Session {
/** Session ID (stored as bigint in DB, returned as string) */
id: string;
trustedMode: boolean;
createdAt: Date;
}
/**
* API token authentication info
*/
export interface ApiTokenAuth {
method: "api_token";
tokenId: string;
tokenName: string;
expiresAt: Date;
lastUsedAt: Date | null;
createdAt: Date;
}
/**
* Session authentication info
*/
export interface SessionAuth {
method: "session";
sessionId: string;
expiresAt: Date;
createdAt: Date;
}
/**
* Union type for authentication method info
*/
export type AuthInfo = ApiTokenAuth | SessionAuth;
/**
* Authenticated API context for protected handlers
*/
export interface AuthenticatedContext extends APIContext {
/** Current user from session */
user: SessionUser;
/** Current session */
session: Session;
/** Authentication method and details */
auth: AuthInfo;
}
/**
* Login request context (used during login flow)
*/
export interface LoginRequestContext extends APIContext {
/** Login request ID from cookie */
loginRequestId: number;
/** User associated with the login request */
user: SessionUser;
}
/**
* Superuser context for admin procedures
* Requires user to have is_superuser = true
*/
export interface SuperuserContext extends AuthenticatedContext {
/** User with superuser privileges */
user: SessionUser & { isSuperuser: true };
}
/**
* Organization info in context
*/
export interface OrgInfo {
id: number;
slug: string;
displayName: string;
logoUrl: string | null;
createdAt: Date;
}
/**
* User's membership in an org
*/
export interface OrgMembership {
id: number;
role: "owner" | "admin" | "member";
createdAt: Date;
}
/**
* Org member context for org-scoped procedures
* Requires user to be a member of the org
*/
export interface OrgMemberContext extends AuthenticatedContext {
/** The organization */
org: OrgInfo;
/** User's membership in the org */
membership: OrgMembership;
}