avneesh123 commited on
Commit
5985e31
Β·
verified Β·
1 Parent(s): a5a9c7a

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +320 -112
server.js CHANGED
@@ -9,40 +9,41 @@ const app = express();
9
 
10
  const PORT = 7860;
11
  const VITE_PORT = 5173;
12
-
13
  const PROJECT_DIR = path.join(process.cwd(), "project");
14
  const MASTER_KEY = "AVNEESH_GOD_MODE_777";
15
 
16
  let viteProcess = null;
17
  let isBooting = false;
 
 
18
 
19
  /* ================= LOG STREAM ================= */
20
 
21
  let logs = [];
22
  let clients = new Set();
 
23
 
24
- function pushLog(message) {
25
-
26
  const entry = {
 
27
  time: new Date().toLocaleTimeString(),
28
- message
 
 
29
  };
30
 
31
  logs.push(entry);
32
-
33
- if (logs.length > 200) logs.shift();
34
 
35
  const payload = `data: ${JSON.stringify(entry)}\n\n`;
36
-
37
  clients.forEach(c => {
38
  try { c.write(payload); } catch { clients.delete(c); }
39
  });
40
 
41
- console.log(message);
42
-
43
  }
44
 
45
- /* ================= BASIC MIDDLEWARE ================= */
46
 
47
  app.use(cors());
48
  app.use(express.json({ limit: "50mb" }));
@@ -51,250 +52,457 @@ if (!fs.existsSync(PROJECT_DIR)) {
51
  fs.mkdirSync(PROJECT_DIR, { recursive: true });
52
  }
53
 
54
- /* ================= TOKEN SECURITY ================= */
55
 
56
- function verifyToken(req, res) {
57
 
58
- const token = req.body?.token || req.query?.token;
 
 
 
59
 
60
- if (token !== MASTER_KEY) {
61
 
62
- pushLog("[SECURITY] ACTION DENIED");
63
-
64
- res.status(403).json({
65
- error: "ACTION_DENIED"
66
- });
67
 
 
 
 
68
  return false;
69
  }
70
-
71
  return true;
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  /* ================= SSE LOG STREAM ================= */
76
 
77
  app.get("/api/logs", (req, res) => {
78
-
79
  res.setHeader("Content-Type", "text/event-stream");
80
  res.setHeader("Cache-Control", "no-cache");
81
  res.setHeader("Connection", "keep-alive");
 
82
 
83
- logs.forEach(l => {
 
 
84
  res.write(`data: ${JSON.stringify(l)}\n\n`);
85
  });
86
 
 
 
 
 
 
 
 
 
 
87
  clients.add(res);
88
 
89
  req.on("close", () => {
90
  clients.delete(res);
 
91
  });
92
-
93
  });
94
 
95
- /* ================= VITE START ================= */
96
 
97
- function startVite() {
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- if (viteProcess || isBooting) {
100
- pushLog("[SYSTEM] Vite already running");
 
101
  return;
102
  }
103
 
104
- pushLog("[SYSTEM] Starting Vite");
 
 
 
 
 
105
 
 
 
106
  isBooting = true;
107
 
108
  const nodeModules = path.join(PROJECT_DIR, "node_modules");
109
 
110
  const boot = () => {
111
-
112
- viteProcess = spawn("pnpm", ["run", "dev"], {
113
  cwd: PROJECT_DIR,
114
- shell: true
 
115
  });
116
 
117
  viteProcess.stdout.on("data", d => {
118
-
119
- const msg = d.toString();
120
-
121
- pushLog("[VITE] " + msg);
122
-
123
- if (msg.includes("Local:") || msg.includes("ready")) {
124
  isBooting = false;
 
 
125
  }
126
-
127
  });
128
 
129
  viteProcess.stderr.on("data", d => {
130
- pushLog("[VITE] " + d.toString());
 
 
131
  });
132
 
133
- viteProcess.on("close", () => {
134
-
135
- pushLog("[SYSTEM] Vite stopped");
136
-
137
  viteProcess = null;
138
-
139
  isBooting = false;
140
 
 
 
 
 
 
 
141
  });
142
-
143
  };
144
 
145
  if (!fs.existsSync(nodeModules)) {
146
-
147
- pushLog("[INSTALL] Installing dependencies");
148
 
149
  const install = spawn("pnpm", ["install"], {
150
  cwd: PROJECT_DIR,
151
  shell: true
152
  });
153
 
154
- install.stdout.on("data", d => {
155
- pushLog("[INSTALL] " + d.toString());
156
- });
157
-
158
- install.stderr.on("data", d => {
159
- pushLog("[INSTALL] " + d.toString());
160
- });
161
 
162
  install.on("close", code => {
163
-
164
  if (code === 0) {
165
-
166
- pushLog("[INSTALL] Done");
167
-
168
  boot();
169
-
170
  } else {
171
-
172
- pushLog("[ERROR] install failed");
173
-
174
  isBooting = false;
175
-
176
  }
177
-
178
  });
179
-
180
  } else {
181
-
182
  boot();
183
-
184
  }
185
-
186
  }
187
 
188
  /* ================= STATUS ================= */
189
 
190
  app.get("/api/status", (req, res) => {
191
-
192
- if (!verifyToken(req, res)) return;
193
-
194
  res.json({
195
- ready: !!viteProcess && !isBooting
 
 
 
 
 
 
196
  });
197
-
198
  });
199
 
200
  /* ================= FILE UPDATE ================= */
201
 
202
  app.post("/api/update", async (req, res) => {
203
-
204
  if (!verifyToken(req, res)) return;
 
205
 
206
- const { files } = req.body;
207
 
208
  try {
209
-
210
- if (files) {
211
 
212
  for (const [p, c] of Object.entries(files)) {
 
 
 
213
 
214
- const fp = path.join(PROJECT_DIR, p);
 
 
 
 
215
 
216
  await fs.ensureDir(path.dirname(fp));
 
 
 
217
 
218
- await fs.writeFile(fp, c);
 
219
 
220
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- pushLog("[FILES] Updated " + Object.keys(files).length + " files");
223
 
 
 
 
 
 
 
224
  }
225
 
226
- startVite();
 
 
227
 
228
- res.json({ success: true });
 
229
 
230
  } catch (e) {
 
 
 
231
 
232
- pushLog("[ERROR] " + e.message);
233
 
234
- res.status(500).json({ error: e.message });
 
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  }
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  });
239
 
240
  /* ================= COMMAND EXEC ================= */
241
 
242
- app.post("/api/execute", (req, res) => {
 
243
 
 
244
  if (!verifyToken(req, res)) return;
 
245
 
246
- const { command } = req.body;
247
 
248
- pushLog("[TERMINAL] " + command);
 
 
 
 
 
249
 
250
- exec(command, { cwd: PROJECT_DIR }, (err, stdout, stderr) => {
251
 
252
- res.json({
253
- success: !err,
254
- output: stdout || stderr || err?.message
255
- });
 
 
 
256
 
257
- });
 
 
 
 
258
 
 
 
259
  });
260
 
261
- /* ================= PROXY ================= */
262
 
263
- const proxy = httpProxy.createProxyServer({
264
- target: `http://127.0.0.1:${VITE_PORT}`,
265
- changeOrigin: true
 
 
 
266
  });
267
 
268
- proxy.on("error", (err, req, res) => {
269
-
270
- pushLog("[PROXY] Preview not ready");
271
-
272
- if (res && typeof res.writeHead === "function") {
273
 
274
- res.writeHead(503);
275
 
276
- res.end("Preview not ready");
 
277
 
 
 
 
 
 
 
 
 
 
278
  }
279
-
280
  });
281
 
282
- app.use((req, res, next) => {
 
 
 
 
 
 
 
 
 
 
283
 
284
- if (req.path.startsWith("/api")) {
285
 
286
- return next();
 
 
 
 
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
 
289
 
 
 
290
  proxy.web(req, res);
 
291
 
 
 
 
 
292
  });
293
 
294
- /* ================= SERVER START ================= */
 
 
295
 
296
- app.listen(PORT, () => {
297
 
298
- pushLog("AUTO DEV ENGINE RUNNING ON PORT " + PORT);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
 
 
300
  });
 
9
 
10
  const PORT = 7860;
11
  const VITE_PORT = 5173;
 
12
  const PROJECT_DIR = path.join(process.cwd(), "project");
13
  const MASTER_KEY = "AVNEESH_GOD_MODE_777";
14
 
15
  let viteProcess = null;
16
  let isBooting = false;
17
+ let buildRetryCount = 0;
18
+ const MAX_RETRIES = 3;
19
 
20
  /* ================= LOG STREAM ================= */
21
 
22
  let logs = [];
23
  let clients = new Set();
24
+ const logHistory = new Map(); // sessionId -> logs
25
 
26
+ function pushLog(message, type = "info") {
 
27
  const entry = {
28
+ id: Date.now() + Math.random(),
29
  time: new Date().toLocaleTimeString(),
30
+ timestamp: Date.now(),
31
+ message,
32
+ type // info | error | success | warning | system
33
  };
34
 
35
  logs.push(entry);
36
+ if (logs.length > 500) logs.shift();
 
37
 
38
  const payload = `data: ${JSON.stringify(entry)}\n\n`;
 
39
  clients.forEach(c => {
40
  try { c.write(payload); } catch { clients.delete(c); }
41
  });
42
 
43
+ console.log(`[${type.toUpperCase()}] ${message}`);
 
44
  }
45
 
46
+ /* ================= MIDDLEWARE ================= */
47
 
48
  app.use(cors());
49
  app.use(express.json({ limit: "50mb" }));
 
52
  fs.mkdirSync(PROJECT_DIR, { recursive: true });
53
  }
54
 
55
+ /* ================= RATE LIMITER ================= */
56
 
57
+ const rateLimitMap = new Map();
58
 
59
+ function rateLimit(req, res, maxRequests = 30, windowMs = 60000) {
60
+ const ip = req.ip || req.connection.remoteAddress;
61
+ const now = Date.now();
62
+ const windowStart = now - windowMs;
63
 
64
+ if (!rateLimitMap.has(ip)) rateLimitMap.set(ip, []);
65
 
66
+ const requests = rateLimitMap.get(ip).filter(t => t > windowStart);
67
+ requests.push(now);
68
+ rateLimitMap.set(ip, requests);
 
 
69
 
70
+ if (requests.length > maxRequests) {
71
+ pushLog(`[RATE LIMIT] IP ${ip} throttled`, "warning");
72
+ res.status(429).json({ error: "TOO_MANY_REQUESTS", retryAfter: 60 });
73
  return false;
74
  }
 
75
  return true;
76
+ }
77
 
78
+ // Clean rate limit map every 5 minutes
79
+ setInterval(() => {
80
+ const cutoff = Date.now() - 60000;
81
+ rateLimitMap.forEach((times, ip) => {
82
+ const filtered = times.filter(t => t > cutoff);
83
+ if (filtered.length === 0) rateLimitMap.delete(ip);
84
+ else rateLimitMap.set(ip, filtered);
85
+ });
86
+ }, 300000);
87
+
88
+ /* ================= TOKEN SECURITY ================= */
89
+
90
+ function verifyToken(req, res) {
91
+ const token = req.body?.token || req.query?.token || req.headers?.["x-api-key"];
92
+
93
+ if (token !== MASTER_KEY) {
94
+ pushLog(`[SECURITY] Unauthorized access attempt from ${req.ip}`, "error");
95
+ res.status(403).json({ error: "ACTION_DENIED", message: "Invalid token" });
96
+ return false;
97
+ }
98
+ return true;
99
  }
100
 
101
  /* ================= SSE LOG STREAM ================= */
102
 
103
  app.get("/api/logs", (req, res) => {
 
104
  res.setHeader("Content-Type", "text/event-stream");
105
  res.setHeader("Cache-Control", "no-cache");
106
  res.setHeader("Connection", "keep-alive");
107
+ res.setHeader("X-Accel-Buffering", "no");
108
 
109
+ // Send last N logs on connect
110
+ const last = parseInt(req.query.last) || 50;
111
+ logs.slice(-last).forEach(l => {
112
  res.write(`data: ${JSON.stringify(l)}\n\n`);
113
  });
114
 
115
+ // Heartbeat every 30s to keep connection alive
116
+ const heartbeat = setInterval(() => {
117
+ try {
118
+ res.write(`: heartbeat\n\n`);
119
+ } catch {
120
+ clearInterval(heartbeat);
121
+ }
122
+ }, 30000);
123
+
124
  clients.add(res);
125
 
126
  req.on("close", () => {
127
  clients.delete(res);
128
+ clearInterval(heartbeat);
129
  });
 
130
  });
131
 
132
+ /* ================= VITE MANAGER ================= */
133
 
134
+ function killVite() {
135
+ return new Promise((resolve) => {
136
+ if (!viteProcess) return resolve();
137
+ pushLog("[SYSTEM] Killing existing Vite process", "system");
138
+ viteProcess.kill("SIGTERM");
139
+ setTimeout(() => {
140
+ if (viteProcess) viteProcess.kill("SIGKILL");
141
+ viteProcess = null;
142
+ resolve();
143
+ }, 2000);
144
+ });
145
+ }
146
 
147
+ function startVite(forceRestart = false) {
148
+ if ((viteProcess || isBooting) && !forceRestart) {
149
+ pushLog("[SYSTEM] Vite already running", "system");
150
  return;
151
  }
152
 
153
+ if (forceRestart) {
154
+ killVite().then(() => bootVite());
155
+ } else {
156
+ bootVite();
157
+ }
158
+ }
159
 
160
+ function bootVite() {
161
+ pushLog("[SYSTEM] Starting Vite dev server...", "system");
162
  isBooting = true;
163
 
164
  const nodeModules = path.join(PROJECT_DIR, "node_modules");
165
 
166
  const boot = () => {
167
+ viteProcess = spawn("pnpm", ["run", "dev", "--", "--host", "0.0.0.0"], {
 
168
  cwd: PROJECT_DIR,
169
+ shell: true,
170
+ env: { ...process.env, FORCE_COLOR: "0" }
171
  });
172
 
173
  viteProcess.stdout.on("data", d => {
174
+ const msg = d.toString().trim();
175
+ if (!msg) return;
176
+ pushLog("[VITE] " + msg, "info");
177
+ if (msg.includes("Local:") || msg.includes("ready in") || msg.includes("localhost")) {
 
 
178
  isBooting = false;
179
+ buildRetryCount = 0;
180
+ pushLog("[SYSTEM] βœ… Vite is ready!", "success");
181
  }
 
182
  });
183
 
184
  viteProcess.stderr.on("data", d => {
185
+ const msg = d.toString().trim();
186
+ if (!msg) return;
187
+ pushLog("[VITE ERROR] " + msg, "error");
188
  });
189
 
190
+ viteProcess.on("close", (code) => {
191
+ pushLog(`[SYSTEM] Vite stopped (exit code: ${code})`, "system");
 
 
192
  viteProcess = null;
 
193
  isBooting = false;
194
 
195
+ // Auto-restart on unexpected crash (not manual kill)
196
+ if (code !== null && code !== 0 && buildRetryCount < MAX_RETRIES) {
197
+ buildRetryCount++;
198
+ pushLog(`[SYSTEM] Auto-restarting Vite (attempt ${buildRetryCount}/${MAX_RETRIES})...`, "warning");
199
+ setTimeout(() => bootVite(), 3000);
200
+ }
201
  });
 
202
  };
203
 
204
  if (!fs.existsSync(nodeModules)) {
205
+ pushLog("[INSTALL] node_modules not found. Installing dependencies...", "system");
 
206
 
207
  const install = spawn("pnpm", ["install"], {
208
  cwd: PROJECT_DIR,
209
  shell: true
210
  });
211
 
212
+ install.stdout.on("data", d => pushLog("[INSTALL] " + d.toString().trim(), "info"));
213
+ install.stderr.on("data", d => pushLog("[INSTALL] " + d.toString().trim(), "warning"));
 
 
 
 
 
214
 
215
  install.on("close", code => {
 
216
  if (code === 0) {
217
+ pushLog("[INSTALL] βœ… Dependencies installed successfully!", "success");
 
 
218
  boot();
 
219
  } else {
220
+ pushLog("[ERROR] ❌ pnpm install failed!", "error");
 
 
221
  isBooting = false;
 
222
  }
 
223
  });
 
224
  } else {
 
225
  boot();
 
226
  }
 
227
  }
228
 
229
  /* ================= STATUS ================= */
230
 
231
  app.get("/api/status", (req, res) => {
 
 
 
232
  res.json({
233
+ ready: !!viteProcess && !isBooting,
234
+ booting: isBooting,
235
+ viteRunning: !!viteProcess,
236
+ uptime: process.uptime(),
237
+ logCount: logs.length,
238
+ connectedClients: clients.size,
239
+ retryCount: buildRetryCount
240
  });
 
241
  });
242
 
243
  /* ================= FILE UPDATE ================= */
244
 
245
  app.post("/api/update", async (req, res) => {
 
246
  if (!verifyToken(req, res)) return;
247
+ if (!rateLimit(req, res, 20, 60000)) return;
248
 
249
+ const { files, restart = false } = req.body;
250
 
251
  try {
252
+ if (files && Object.keys(files).length > 0) {
253
+ const updatedFiles = [];
254
 
255
  for (const [p, c] of Object.entries(files)) {
256
+ // Security: prevent path traversal
257
+ const safePath = path.normalize(p).replace(/^(\.\.[\/\\])+/, "");
258
+ const fp = path.join(PROJECT_DIR, safePath);
259
 
260
+ // Only allow writes inside PROJECT_DIR
261
+ if (!fp.startsWith(PROJECT_DIR)) {
262
+ pushLog(`[SECURITY] Blocked path traversal: ${p}`, "error");
263
+ continue;
264
+ }
265
 
266
  await fs.ensureDir(path.dirname(fp));
267
+ await fs.writeFile(fp, c, "utf-8");
268
+ updatedFiles.push(safePath);
269
+ }
270
 
271
+ pushLog(`[FILES] βœ… Updated ${updatedFiles.length} files: ${updatedFiles.join(", ")}`, "success");
272
+ }
273
 
274
+ if (restart) {
275
+ startVite(true);
276
+ } else {
277
+ startVite();
278
+ }
279
+
280
+ res.json({ success: true, filesUpdated: Object.keys(files || {}).length });
281
+
282
+ } catch (e) {
283
+ pushLog("[ERROR] File update failed: " + e.message, "error");
284
+ res.status(500).json({ error: e.message });
285
+ }
286
+ });
287
+
288
+ /* ================= FILE READ ================= */
289
+
290
+ app.post("/api/read", async (req, res) => {
291
+ if (!verifyToken(req, res)) return;
292
 
293
+ const { filePath } = req.body;
294
 
295
+ try {
296
+ const safePath = path.normalize(filePath).replace(/^(\.\.[\/\\])+/, "");
297
+ const fp = path.join(PROJECT_DIR, safePath);
298
+
299
+ if (!fp.startsWith(PROJECT_DIR)) {
300
+ return res.status(403).json({ error: "Path traversal blocked" });
301
  }
302
 
303
+ if (!fs.existsSync(fp)) {
304
+ return res.status(404).json({ error: "File not found" });
305
+ }
306
 
307
+ const content = await fs.readFile(fp, "utf-8");
308
+ res.json({ success: true, content, path: safePath });
309
 
310
  } catch (e) {
311
+ res.status(500).json({ error: e.message });
312
+ }
313
+ });
314
 
315
+ /* ================= FILE LIST ================= */
316
 
317
+ app.post("/api/files", async (req, res) => {
318
+ if (!verifyToken(req, res)) return;
319
 
320
+ async function walkDir(dir, base = "") {
321
+ const items = [];
322
+ try {
323
+ const entries = await fs.readdir(dir, { withFileTypes: true });
324
+ for (const entry of entries) {
325
+ if (["node_modules", ".git", "dist", ".cache"].includes(entry.name)) continue;
326
+ const relPath = path.join(base, entry.name);
327
+ if (entry.isDirectory()) {
328
+ const children = await walkDir(path.join(dir, entry.name), relPath);
329
+ items.push({ name: entry.name, path: relPath, type: "dir", children });
330
+ } else {
331
+ items.push({ name: entry.name, path: relPath, type: "file" });
332
+ }
333
+ }
334
+ } catch {}
335
+ return items;
336
  }
337
 
338
+ const tree = await walkDir(PROJECT_DIR);
339
+ res.json({ success: true, tree });
340
+ });
341
+
342
+ /* ================= FILE DELETE ================= */
343
+
344
+ app.post("/api/delete", async (req, res) => {
345
+ if (!verifyToken(req, res)) return;
346
+
347
+ const { filePath } = req.body;
348
+
349
+ try {
350
+ const safePath = path.normalize(filePath).replace(/^(\.\.[\/\\])+/, "");
351
+ const fp = path.join(PROJECT_DIR, safePath);
352
+
353
+ if (!fp.startsWith(PROJECT_DIR)) {
354
+ return res.status(403).json({ error: "Path traversal blocked" });
355
+ }
356
+
357
+ await fs.remove(fp);
358
+ pushLog(`[FILES] Deleted: ${safePath}`, "warning");
359
+ res.json({ success: true });
360
+
361
+ } catch (e) {
362
+ res.status(500).json({ error: e.message });
363
+ }
364
  });
365
 
366
  /* ================= COMMAND EXEC ================= */
367
 
368
+ // Dangerous commands blocked
369
+ const BLOCKED_COMMANDS = ["rm -rf /", "mkfs", ":(){ :|:& };:", "shutdown", "reboot", "format"];
370
 
371
+ app.post("/api/execute", (req, res) => {
372
  if (!verifyToken(req, res)) return;
373
+ if (!rateLimit(req, res, 15, 60000)) return;
374
 
375
+ const { command, timeout = 30000 } = req.body;
376
 
377
+ // Block dangerous commands
378
+ const isBlocked = BLOCKED_COMMANDS.some(cmd => command.includes(cmd));
379
+ if (isBlocked) {
380
+ pushLog(`[SECURITY] Blocked dangerous command: ${command}`, "error");
381
+ return res.status(403).json({ error: "COMMAND_BLOCKED" });
382
+ }
383
 
384
+ pushLog("[TERMINAL] $ " + command, "system");
385
 
386
+ const child = exec(command, {
387
+ cwd: PROJECT_DIR,
388
+ timeout,
389
+ maxBuffer: 1024 * 1024 * 10 // 10MB
390
+ }, (err, stdout, stderr) => {
391
+ const output = stdout || stderr || err?.message || "";
392
+ const success = !err;
393
 
394
+ if (success) {
395
+ pushLog("[TERMINAL] βœ… Done", "success");
396
+ } else {
397
+ pushLog("[TERMINAL] ❌ " + (err?.message || "Command failed"), "error");
398
+ }
399
 
400
+ res.json({ success, output, exitCode: err?.code || 0 });
401
+ });
402
  });
403
 
404
+ /* ================= VITE CONTROL ================= */
405
 
406
+ app.post("/api/vite/restart", async (req, res) => {
407
+ if (!verifyToken(req, res)) return;
408
+ pushLog("[SYSTEM] Manual Vite restart requested", "system");
409
+ buildRetryCount = 0;
410
+ startVite(true);
411
+ res.json({ success: true, message: "Vite restart initiated" });
412
  });
413
 
414
+ app.post("/api/vite/stop", async (req, res) => {
415
+ if (!verifyToken(req, res)) return;
416
+ await killVite();
417
+ res.json({ success: true, message: "Vite stopped" });
418
+ });
419
 
420
+ /* ================= PROJECT RESET ================= */
421
 
422
+ app.post("/api/reset", async (req, res) => {
423
+ if (!verifyToken(req, res)) return;
424
 
425
+ try {
426
+ pushLog("[SYSTEM] πŸ”„ Resetting project...", "warning");
427
+ await killVite();
428
+ await fs.remove(PROJECT_DIR);
429
+ await fs.mkdirSync(PROJECT_DIR, { recursive: true });
430
+ pushLog("[SYSTEM] βœ… Project reset complete", "success");
431
+ res.json({ success: true });
432
+ } catch (e) {
433
+ res.status(500).json({ error: e.message });
434
  }
 
435
  });
436
 
437
+ /* ================= HEALTH CHECK ================= */
438
+
439
+ app.get("/health", (req, res) => {
440
+ res.json({
441
+ status: "ok",
442
+ uptime: process.uptime(),
443
+ memory: process.memoryUsage(),
444
+ vite: !!viteProcess,
445
+ timestamp: new Date().toISOString()
446
+ });
447
+ });
448
 
449
+ /* ================= PROXY ================= */
450
 
451
+ const proxy = httpProxy.createProxyServer({
452
+ target: `http://127.0.0.1:${VITE_PORT}`,
453
+ changeOrigin: true,
454
+ ws: true // WebSocket support for Vite HMR
455
+ });
456
 
457
+ proxy.on("error", (err, req, res) => {
458
+ if (res && typeof res.writeHead === "function") {
459
+ res.writeHead(503, { "Content-Type": "text/html" });
460
+ res.end(`
461
+ <html>
462
+ <body style="background:#0a0a0a;color:#fff;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:12px;">
463
+ <div style="font-size:2rem">⚑</div>
464
+ <div style="color:#facc15;font-size:1.1rem">AutoDev is booting up...</div>
465
+ <div style="color:#6b7280;font-size:0.8rem">Preview will be live in a moment</div>
466
+ <script>setTimeout(()=>location.reload(), 2000)</script>
467
+ </body>
468
+ </html>
469
+ `);
470
  }
471
+ });
472
 
473
+ app.use((req, res, next) => {
474
+ if (req.path.startsWith("/api") || req.path === "/health") return next();
475
  proxy.web(req, res);
476
+ });
477
 
478
+ // WebSocket proxy for Vite HMR
479
+ const server = app.listen(PORT, () => {
480
+ pushLog(`πŸš€ AutoDev Engine running on port ${PORT}`, "success");
481
+ pushLog(`πŸ“ Project dir: ${PROJECT_DIR}`, "system");
482
  });
483
 
484
+ server.on("upgrade", (req, socket, head) => {
485
+ proxy.ws(req, socket, head);
486
+ });
487
 
488
+ /* ================= GRACEFUL SHUTDOWN ================= */
489
 
490
+ process.on("SIGTERM", async () => {
491
+ pushLog("[SYSTEM] Shutting down gracefully...", "system");
492
+ await killVite();
493
+ process.exit(0);
494
+ });
495
+
496
+ process.on("SIGINT", async () => {
497
+ pushLog("[SYSTEM] Interrupted. Shutting down...", "system");
498
+ await killVite();
499
+ process.exit(0);
500
+ });
501
+
502
+ process.on("uncaughtException", (err) => {
503
+ pushLog("[CRITICAL] Uncaught Exception: " + err.message, "error");
504
+ });
505
 
506
+ process.on("unhandledRejection", (reason) => {
507
+ pushLog("[CRITICAL] Unhandled Rejection: " + reason, "error");
508
  });