import { createHash } from "node:crypto"; import type { RequestHandler } from "express"; import { and, eq, isNull } from "drizzle-orm"; import type { Db } from "@paperclip/db"; import { agentApiKeys } from "@paperclip/db"; function hashToken(token: string) { return createHash("sha256").update(token).digest("hex"); } export function actorMiddleware(db: Db): RequestHandler { return async (req, _res, next) => { req.actor = { type: "board", userId: "board" }; const authHeader = req.header("authorization"); if (!authHeader?.toLowerCase().startsWith("bearer ")) { next(); return; } const token = authHeader.slice("bearer ".length).trim(); if (!token) { next(); return; } const tokenHash = hashToken(token); const key = await db .select() .from(agentApiKeys) .where(and(eq(agentApiKeys.keyHash, tokenHash), isNull(agentApiKeys.revokedAt))) .then((rows) => rows[0] ?? null); if (!key) { next(); return; } await db .update(agentApiKeys) .set({ lastUsedAt: new Date() }) .where(eq(agentApiKeys.id, key.id)); req.actor = { type: "agent", agentId: key.agentId, companyId: key.companyId, keyId: key.id, }; next(); }; } export function requireBoard(req: Express.Request) { return req.actor.type === "board"; }