console.log("🚀 Starting KnowledgeBridge server..."); // Add global error handlers to catch crashes process.on('uncaughtException', (error) => { console.error('💥 Uncaught Exception:', error); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.error('💥 Unhandled Rejection at:', promise, 'reason:', reason); process.exit(1); }); import { config } from "dotenv"; import { join, dirname } from "path"; import { fileURLToPath } from "url"; import express, { type Request, Response, NextFunction } from "express"; import rateLimit from "express-rate-limit"; import helmet from "helmet"; import { body, validationResult } from "express-validator"; console.log("✅ Basic imports loaded successfully"); import { registerRoutes } from "./routes"; import { setupVite, serveStatic, log } from "./vite"; console.log("✅ All imports loaded successfully"); // Get the directory name for ES modules const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Load environment variables from .env file config({ path: join(__dirname, '../.env') }); const app = express(); // Trust proxy for Hugging Face Spaces infrastructure if (process.env.NODE_ENV === 'production') { app.set('trust proxy', true); } console.log("✅ Express app created"); console.log("✅ Server setup starting"); // Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: process.env.NODE_ENV === 'production' ? ["'self'", "'unsafe-inline'", "'unsafe-eval'"] : ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://replit.com"], // Allow Replit in dev styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "https://api.studio.nebius.ai", "https://api.github.com"], frameAncestors: process.env.NODE_ENV === 'production' ? ["'self'", "https://*.hf.space", "https://huggingface.co"] : ["'self'"], // Allow HF domains in production }, }, })); // Rate limiting configuration for production (HF Spaces) vs development const rateLimitConfig = process.env.NODE_ENV === 'production' ? { // For HF Spaces - configure for proxy environment windowMs: 15 * 60 * 1000, // 15 minutes max: 1000, // Higher limit since we can't reliably identify individual users message: { error: "Too many requests, please try again later." }, standardHeaders: true, legacyHeaders: false, // Use a combination of headers for better user identification in proxy environment keyGenerator: (req) => { return req.ip + '|' + (req.headers['x-forwarded-for'] || req.headers['cf-connecting-ip'] || req.connection.remoteAddress); }, // Skip validation that was causing the errors validate: false, } : { // For development - normal rate limiting windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: { error: "Too many requests from this IP, please try again later." }, standardHeaders: true, legacyHeaders: false, }; const limiter = rateLimit(rateLimitConfig); export const strictLimiter = process.env.NODE_ENV === 'production' ? rateLimit({ windowMs: 5 * 60 * 1000, // 5 minutes max: 50, // More generous limit for production message: { error: "Too many requests, please try again later." }, keyGenerator: (req) => { return req.ip + '|' + (req.headers['x-forwarded-for'] || req.headers['cf-connecting-ip'] || req.connection.remoteAddress); }, validate: false, }) : rateLimit({ windowMs: 1 * 60 * 1000, // 1 minute max: 10, // limit each IP to 10 requests per minute for sensitive endpoints message: { error: "Too many requests, please try again later." } }); app.use(limiter); // Body parsing with size limits app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: false, limit: '10mb' })); app.use((req, res, next) => { const start = Date.now(); const path = req.path; let capturedJsonResponse: Record | undefined = undefined; const originalResJson = res.json; res.json = function (bodyJson, ...args) { capturedJsonResponse = bodyJson; return originalResJson.apply(res, [bodyJson, ...args]); }; res.on("finish", () => { const duration = Date.now() - start; if (path.startsWith("/api")) { let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`; if (capturedJsonResponse) { logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`; } if (logLine.length > 80) { logLine = logLine.slice(0, 79) + "…"; } log(logLine); } }); next(); }); (async () => { const server = await registerRoutes(app); app.use((err: any, req: Request, res: Response, _next: NextFunction) => { const status = err.status || err.statusCode || 500; const message = status === 500 ? "Internal Server Error" : err.message || "Internal Server Error"; console.error(`Error ${status} on ${req.method} ${req.url}:`, err); // Only send response if not already sent if (!res.headersSent) { res.status(status).json({ message, ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); } // Log error but don't throw - throwing here crashes the server if (status === 500) { console.error('Internal server error:', err.stack); } }); // importantly only setup vite in development and after // setting up all the other routes so the catch-all route // doesn't interfere with the other routes console.log(`Environment: NODE_ENV=${process.env.NODE_ENV}, app.get('env')=${app.get('env')}`); // Add basic health endpoint before static serving to ensure server is responsive app.get("/ping", (_req, res) => { res.json({ status: "ok", timestamp: new Date().toISOString() }); }); if (process.env.NODE_ENV === "development") { console.log("Setting up Vite development server"); await setupVite(app, server); } else { console.log("Setting up static file serving for production"); serveStatic(app); } // Seed database with default documents for demo purposes console.log("🌱 Initializing database with default documents..."); try { const { seedDefaultDocuments } = await import('./seed-documents'); await seedDefaultDocuments(); } catch (error) { console.warn("⚠️ Failed to seed default documents:", error); } // Serve the app on the configured port (5000 for local, 7860 for HF Spaces) // this serves both the API and the client. const port = process.env.PORT ? parseInt(process.env.PORT) : (process.env.NODE_ENV === 'production' ? 7860 : 5000); server.listen({ port, host: "0.0.0.0", reusePort: true, }, () => { log(`serving on port ${port}`); }); })();