import express from "express"; import mongoose from "mongoose"; import "dotenv/config"; import { createRoutes } from "./routes"; import { AuthController, ChatController, EventController, httpLogger, } from "./controllers"; import { AuthService, ChatService, EventService } from "./services"; import { MongoUserRepository, MongoEventRepository, MongoChatRepository, } from "./repositories"; import { GPTAdapter } from "./ai"; import { logger } from "./logging"; import { MongoCaldavRepository } from "./repositories/mongo/MongoCaldavRepository"; import { CaldavService } from "./services/CaldavService"; import { CaldavController } from "./controllers/CaldavController"; const app = express(); const port = process.env.PORT || 3000; const mongoUri = process.env.MONGODB_URI || "mongodb://localhost:27017/calchat"; // Middleware app.use(express.json()); app.use(httpLogger); // CORS - only needed for web browser development // Native mobile apps don't send Origin headers and aren't affected by CORS if (process.env.NODE_ENV !== "production") { app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS", ); res.header( "Access-Control-Allow-Headers", "Content-Type, Authorization, X-User-Id", ); if (req.method === "OPTIONS") { res.sendStatus(200); return; } next(); }); } // Initialize repositories const userRepo = new MongoUserRepository(); const eventRepo = new MongoEventRepository(); const chatRepo = new MongoChatRepository(); const caldavRepo = new MongoCaldavRepository(); // Initialize AI provider const aiProvider = new GPTAdapter(); // Initialize services const authService = new AuthService(userRepo); const eventService = new EventService(eventRepo); const caldavService = new CaldavService(caldavRepo, eventService); const chatService = new ChatService(chatRepo, eventService, aiProvider); // Initialize controllers const authController = new AuthController(authService); const chatController = new ChatController(chatService, caldavService); const eventController = new EventController(eventService, caldavService); const caldavController = new CaldavController(caldavService); // Setup routes app.use( "/api", createRoutes({ authController, chatController, eventController, caldavController, }), ); // Health check app.get("/health", (_, res) => { res.json({ status: "ok" }); }); // Version endpoint app.get("/version", (_, res) => { res.json({ version: process.env.VERSION || "unknown", commit: process.env.COMMIT || "unknown", }); }); // AI Test endpoint (for development only) app.post("/api/ai/test", async (req, res) => { try { const { message } = req.body; if (!message) { res.status(400).json({ error: "message is required" }); return; } const result = await aiProvider.processMessage(message, { userId: "test-user", conversationHistory: [], currentDate: new Date(), fetchEventsInRange: async () => [], searchEvents: async () => [], fetchEventById: async () => null, }); res.json(result); } catch (error) { logger.error({ error }, "AI test error"); res.status(500).json({ error: String(error) }); } }); // Start server async function start() { try { await mongoose.connect(mongoUri); logger.info("Connected to MongoDB"); app.listen(port, () => { logger.info({ port }, "Server started"); }); } catch (error) { logger.fatal({ error }, "Failed to start server"); process.exit(1); } } start(); export default app;