import app from "./app"; import { logger } from "./lib/logger"; import bcrypt from "bcryptjs"; import { db, usersTable } from "@workspace/db"; import { sql } from "drizzle-orm"; const rawPort = process.env["PORT"]; if (!rawPort) { throw new Error( "PORT environment variable is required but was not provided.", ); } const port = Number(rawPort); if (Number.isNaN(port) || port <= 0) { throw new Error(`Invalid PORT value: "${rawPort}"`); } async function ensureSessionsTable(): Promise { try { const exists = await db.execute( sql`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'sessions')`, ); const rows = exists.rows as [{ exists: boolean }]; if (rows[0]?.exists) return; await db.execute( sql`CREATE TABLE "sessions" ("sid" varchar NOT NULL, "sess" json NOT NULL, "expire" timestamp(6) NOT NULL)`, ); await db.execute( sql`ALTER TABLE "sessions" ADD PRIMARY KEY ("sid")`, ); logger.info("Sessions table created"); } catch (err) { logger.error({ err }, "Failed to ensure sessions table"); } } async function seedAdminUser(): Promise { try { const [row] = await db.select({ count: sql`count(*)::int` }).from(usersTable); if ((row?.count ?? 0) > 0) return; const adminUsername = process.env.LOCAL_ADMIN_USERNAME || "admin"; const adminPassword = process.env.LOCAL_ADMIN_PASSWORD; if (!adminPassword) { logger.warn("LOCAL_ADMIN_PASSWORD is not set — skipping admin seed. Set it to enable local login."); return; } const passwordHash = await bcrypt.hash(adminPassword, 10); await db.insert(usersTable).values({ username: adminUsername, passwordHash, role: "admin", }); logger.info({ username: adminUsername }, "Admin user created"); } catch (err) { logger.error({ err }, "Failed to seed admin user"); } } async function start(): Promise { await ensureSessionsTable(); await seedAdminUser(); app.listen(port, (err) => { if (err) { logger.error({ err }, "Error listening on port"); process.exit(1); } logger.info({ port }, "Server listening"); }); } start().catch((err) => { logger.error({ err }, "Server startup failed"); process.exit(1); });