81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
try {
|
|
const [row] = await db.select({ count: sql<number>`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<void> {
|
|
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);
|
|
});
|