brackt/server.ts

90 lines
2.7 KiB
TypeScript
Raw Permalink Normal View History

import compression from "compression";
import express from "express";
import type { Express, Request, Response, NextFunction } from "express";
import morgan from "morgan";
import { createServer } from "http";
import type { ViteDevServer } from "vite";
import path from "path";
import { fileURLToPath } from "url";
import { logger } from "./server/logger";
// ESM module resolution helpers
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Configuration
// Resolve paths relative to project root (one level up from dist/ in production, or current dir in dev)
const PROJECT_ROOT = path.resolve(__dirname, "..");
const BUILD_PATH = path.join(PROJECT_ROOT, "build/server/index.js");
const CLIENT_BUILD_PATH = path.join(PROJECT_ROOT, "build/client");
const CLIENT_ASSETS_PATH = path.join(PROJECT_ROOT, "build/client/assets");
const DEVELOPMENT = process.env.NODE_ENV === "development";
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
async function createAppServer(): Promise<void> {
const app: Express = express();
app.use(compression());
app.disable("x-powered-by");
if (DEVELOPMENT) {
logger.log("Starting development server");
// Dynamic import Vite only in development
const { createServer: createViteServer } = await import("vite");
const viteDevServer: ViteDevServer = await createViteServer({
server: { middlewareMode: true },
});
app.use(viteDevServer.middlewares);
app.use(async (req: Request, res: Response, next: NextFunction) => {
try {
const source = await viteDevServer.ssrLoadModule("./server/app.ts");
return await source.app(req, res, next);
} catch (error) {
if (error instanceof Error) {
viteDevServer.ssrFixStacktrace(error);
}
next(error);
}
});
} else {
logger.log("Starting production server");
app.use(
"/assets",
express.static(CLIENT_ASSETS_PATH, {
immutable: true,
maxAge: "1y"
})
);
app.use(morgan("tiny"));
app.use(express.static(CLIENT_BUILD_PATH, { maxAge: "1h" }));
// Import the built React Router app
const { app: productionApp } = await import(BUILD_PATH);
app.use(productionApp);
}
// Create HTTP server
const httpServer = createServer(app);
// Initialize Socket.IO
const { initializeSocketIO } = await import("./server/socket");
initializeSocketIO(httpServer);
// Start server
httpServer.listen(PORT, () => {
logger.log(`Server is running on http://localhost:${PORT}`);
});
}
// Start the server with error handling
Decouple database migrations from server startup (#265) * Use Docker Compose init service pattern for database migrations Replaces the fragile double-migration approach (server.ts startup + drizzle-kit CLI in CI) with a one-shot migrate service in Docker Compose that the app depends on via condition: service_completed_successfully. - Add scripts/migrate.mjs: programmatic drizzle-orm migration (uses production deps, not drizzle-kit which is devOnly and absent from image) - Dockerfile: copy scripts/ into image, remove drizzle.config.ts (only needed by drizzle-kit CLI) - server.ts: remove runMigrations() entirely; migrations are now handled by the migrate container before the app starts - deploy.yml: remove explicit docker run migration step; replace sleep 10 health check with a poll loop and explicit migrate exit-code check Production server's docker-compose.yaml needs a one-time manual update to add the migrate service — see plan for exact config. https://claude.ai/code/session_01ReaqH3o9NVH4QU4qE9WMMQ * Move drizzle-kit to devDependencies; use docker compose wait in deploy drizzle-kit is a dev-only tool (schema generation and local migrations). Now that production migrations run via the programmatic drizzle-orm API in the migrate init container, drizzle-kit has no runtime role. Also replaces the polling health check loop with docker compose wait, which blocks until the migrate service exits and returns its exit code cleanly — no sleep or manual status inspection needed. https://claude.ai/code/session_01ReaqH3o9NVH4QU4qE9WMMQ --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-04-06 00:07:36 -04:00
createAppServer().catch((error) => {
logger.error("Failed to start server:", error);
process.exit(1);
});