File size: 6,944 Bytes
7c012de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
999687d
 
 
 
 
7c012de
 
951fb3f
7c012de
 
 
 
 
 
7127914
 
 
7c012de
 
 
8a11413
 
 
7c012de
 
 
 
0e0c0a2
 
dd22d79
 
 
 
 
 
 
 
 
 
 
 
 
 
0e0c0a2
 
7c012de
 
 
 
 
 
 
0e0c0a2
 
 
 
 
dd22d79
 
 
 
 
 
 
 
 
 
 
0e0c0a2
 
 
 
 
 
 
7c012de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7841477
 
 
 
7c012de
 
 
 
 
 
 
 
 
e57738d
 
 
 
 
 
 
 
 
7c012de
 
af2dad0
7c012de
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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<string, any> | 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}`);
  });
})();