Files
CapaKraken/apps/web/src/app/api/trpc/[trpc]/route.ts
T

68 lines
2.0 KiB
TypeScript

import { createTRPCContext, loadRoleDefaults } from "@capakraken/api";
import { appRouter } from "@capakraken/api/router";
import { prisma } from "@capakraken/db";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import type { NextRequest } from "next/server";
import { auth } from "~/server/auth.js";
// Throttle lastActiveAt updates: max once per 60s per user
const lastActiveCache = new Map<string, number>();
const ACTIVITY_THROTTLE_MS = 60_000;
function trackActivity(userId: string) {
const now = Date.now();
const last = lastActiveCache.get(userId) ?? 0;
if (now - last < ACTIVITY_THROTTLE_MS) return;
lastActiveCache.set(userId, now);
prisma.user.update({
where: { id: userId },
data: { lastActiveAt: new Date(now) },
}).catch(() => {/* ignore */});
}
const handler = async (req: NextRequest) => {
const session = await auth();
const dbUser = session?.user?.email
? await prisma.user.findUnique({
where: { email: session.user.email },
select: { id: true, systemRole: true, permissionOverrides: true },
})
: null;
// Track user activity (throttled, fire-and-forget)
if (dbUser) trackActivity(dbUser.id);
// Load configurable role defaults (cached, 60s TTL)
const roleDefaults = await loadRoleDefaults();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options: any = {
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => createTRPCContext({ session, dbUser, roleDefaults }),
};
if (process.env["NODE_ENV"] === "development") {
options.onError = ({
path,
error,
}: {
path?: string;
error: { message: string; code?: string };
}) => {
const label = `tRPC ${path ?? "<no-path>"}`;
if (error.code === "NOT_FOUND") {
console.warn(`⚠️ ${label}: ${error.message}`);
return;
}
console.error(`${label}: ${error.message}`);
};
}
return fetchRequestHandler(options);
};
export { handler as GET, handler as POST };