import { createHTTPServer } from '@trpc/server/adapters/standalone'; import { projectRouter } from './project-router'; import { revisionsRouter } from './revisions-router'; import type { TRPCContext } from './types'; import type { TRPCError } from '@trpc/server'; import { t } from './router'; import { drizzle } from 'drizzle-orm/better-sqlite3'; import Database from 'better-sqlite3'; import { projects, characters, scenes, characterRelationships, sceneCharacters } from '../../src/db/schema'; import { verifyToken } from '@clerk/backend'; // App router combining all routers export const appRouter = t.router({ project: projectRouter, revisions: revisionsRouter, } as const); export type AppRouter = typeof appRouter; // Database instance (shared for now - should come from config) let dbInstance: ReturnType | null = null; function getDb() { if (dbInstance) return dbInstance; const sqlite = new Database('./data/frenocorp.db'); dbInstance = drizzle(sqlite); return dbInstance; } // Create tRPC HTTP server export function createTRPCServer(port: number = 8080) { const server = createHTTPServer({ router: appRouter, createContext: async ({ req }): Promise => { const authHeader = req.headers.authorization; let userId: number | undefined = undefined; if (authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.substring(7); try { const clerkSecretKey = process.env.CLERK_SECRET_KEY; if (!clerkSecretKey) { console.warn('CLERK_SECRET_KEY not set, skipping token verification'); } else { const payload = await verifyToken(token, { secretKey: clerkSecretKey }); userId = payload.sub ? parseInt(payload.sub, 10) : undefined; } } catch (error) { console.error('Failed to verify Clerk token:', error); } } return { userId, db: getDb(), }; }, onError: ({ error, path }: { error: TRPCError; path: string | undefined }) => { console.error(`tRPC error on ${path}:`, error.message); }, }); server.listen(port, () => { console.log(`tRPC server listening on port ${port}`); }); return server; } export default appRouter;