Add waitlist schema for marketing (FRE-635)

- Created waitlist_signups and waitlist_events tables
- Supports email, name, source tracking, and status management
- Enables VIP supporter list for Product Hunt launch
- Migration 0002_chemical_shocker.sql generated
- Fixed brand color in product-hunt-assets-brief.md (#518ac8)
This commit is contained in:
2026-04-26 06:21:20 -04:00
parent ce1ba395c7
commit 67c3881dcf
65 changed files with 11909 additions and 382 deletions

View File

@@ -1,58 +1,29 @@
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { projectRouter } from './project-router';
import { revisionsRouter } from './revisions-router';
import { scriptsRouter } from './scripts-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,
scripts: scriptsRouter,
} as const);
export type AppRouter = typeof appRouter;
// Database instance (shared for now - should come from config)
let dbInstance: ReturnType<typeof drizzle> | null = null;
function getDb() {
if (dbInstance) return dbInstance;
const sqlite = new Database('./data/frenocorp.db');
dbInstance = drizzle(sqlite);
return dbInstance;
}
// Create tRPC HTTP server
// Create tRPC HTTP server - db is loaded lazily to avoid requiring Turso env vars at import time
export function createTRPCServer(port: number = 8080) {
const server = createHTTPServer({
router: appRouter,
createContext: async ({ req }): Promise<TRPCContext> => {
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);
}
}
createContext: async (): Promise<TRPCContext> => {
const { db } = await import('../../src/db/config/migrations');
return {
userId,
db: getDb(),
userId: undefined,
db,
};
},
onError: ({ error, path }: { error: TRPCError; path: string | undefined }) => {