import express from "express"; import cors from "cors"; import helmet from "helmet"; import morgan from "morgan"; import rateLimit from "express-rate-limit"; import { createServer } from "http"; import { Server } from "socket.io"; import dotenv from "dotenv"; import fs from "fs"; import path from "path"; const envPath = ["../.env", "../../.env", ".env"] .map((p) => path.resolve(process.cwd(), p)) .find((p) => fs.existsSync(p)) || ".env"; dotenv.config({ path: envPath }); import { prisma } from "./utils/prisma"; import { errorHandler } from "./middleware/errorHandler"; import { authRouter } from "./routes/auth"; import { userRouter } from "./routes/users"; import { walletRouter } from "./routes/wallet"; import { transactionsRouter } from "./routes/transactions"; import { adminRouter } from "./routes/admin"; import { tautulliRouter } from "./routes/tautulli"; import { overseerRouter } from "./routes/overseer"; import { webhookRouter } from "./routes/webhooks"; import { setupSocketHandlers } from "./services/socket"; const app = express(); const httpServer = createServer(app); const allowedOrigins = [ process.env.FRONTEND_URL, "https://coop.hobokenchicken.com", "http://172.20.1.238:3000", ].filter(Boolean) as string[]; const io = new Server(httpServer, { cors: { origin: allowedOrigins, credentials: true, }, }); // Security middleware app.use( helmet({ crossOriginResourcePolicy: { policy: "cross-origin" }, }), ); app.use( cors({ origin: allowedOrigins, credentials: true, }), ); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs }); app.use(limiter); // Body parsing app.use(express.json({ limit: "10mb" })); app.use(express.urlencoded({ extended: true })); // Logging app.use(morgan("combined")); // Health check app.get("/health", (_req, res) => { res.json({ status: "ok", timestamp: new Date().toISOString() }); }); // API routes app.use("/api/auth", authRouter); app.use("/api/users", userRouter); app.use("/api/wallet", walletRouter); app.use("/api/transactions", transactionsRouter); app.use("/api/admin", adminRouter); app.use("/api/tautulli", tautulliRouter); app.use("/api/overseer", overseerRouter); app.use("/webhooks", webhookRouter); // Error handling app.use(errorHandler); // Setup WebSocket handlers setupSocketHandlers(io); // Start server const PORT = process.env.PORT || 3001; async function startServer() { try { // Connect to database await prisma.$connect(); console.log("✅ Connected to database"); // Ensure default settings exist const settings = await prisma.systemSettings.findFirst(); if (!settings) { await prisma.systemSettings.create({ data: { id: "default", }, }); console.log("✅ Created default system settings"); } httpServer.listen(PORT, () => { console.log(`🚀 Server running on port ${PORT}`); console.log( `📊 API URL: ${process.env.API_URL || `http://localhost:${PORT}`}`, ); }); } catch (error) { console.error("Failed to start server:", error); process.exit(1); } } // Graceful shutdown process.on("SIGTERM", async () => { console.log("SIGTERM received, shutting down gracefully"); await prisma.$disconnect(); httpServer.close(() => { console.log("Server closed"); process.exit(0); }); }); process.on("SIGINT", async () => { console.log("SIGINT received, shutting down gracefully"); await prisma.$disconnect(); httpServer.close(() => { console.log("Server closed"); process.exit(0); }); }); startServer(); export { io };