avneesh123's picture
Update server.js
e079e39 verified
import express from "express";
import cors from "cors";
import fs from "fs-extra";
import path from "path";
import { spawn, exec } from "child_process";
import httpProxy from "http-proxy";
const app = express();
/* ================= CONFIGURATION ================= */
const PORT = 7860;
const VITE_PORT = parseInt(process.env.VITE_PORT || "5173");
const PROJECT_DIR = path.join(process.cwd(), "project");
const MASTER_KEY = "AVNEESH_GOD_MODE_777";
const ALLOWED_ORIGINS = [
"https://avneesh123-autodev-mark2.hf.space",
"https://avneesh123-autodev-mark2-engine.hf.space",
"https://avneesh123-autodev-mark2.hf.space/chat",
"http://localhost:5173",
"http://localhost:3000",
"http://127.0.0.1:5173",
];
// ================= SECURE IFRAME LOCK =================
app.use((req, res, next) => {
res.removeHeader("X-Frame-Options");
res.setHeader(
"Content-Security-Policy",
"frame-ancestors 'self' https://*.hf.space http://localhost:* http://127.0.0.1:*"
);
next();
});
// --- Process Managers ---
let viteProcess = null;
let isBooting = false;
let buildRetryCount = 0;
const MAX_RETRIES = 3;
const bgProcesses = new Map();
// ================= PERSISTENT LOCK FILES =================
const FOUNDATION_LOCK = path.join(process.cwd(), ".foundation_lock");
const DEPS_LOCK = path.join(process.cwd(), ".deps_lock");
/* ================= BINARY FILE EXTENSIONS ================= */
const BINARY_EXTS = [
".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".svg",
".pdf", ".zip", ".tar", ".gz",
".ttf", ".woff", ".woff2", ".otf", ".eot",
".mp3", ".mp4", ".wav"
];
function isBinaryFile(filePath) {
const ext = path.extname(filePath).toLowerCase();
return BINARY_EXTS.includes(ext);
}
/* ================= IDLE TRACKER ================= */
let lastActivityTime = Date.now();
const IDLE_TIMEOUT = 10 * 60 * 1000;
const IGNORE_ACTIVITY_PATHS = ["/api/logs", "/health", "/api/status"];
function updateActivity(req) {
if (!IGNORE_ACTIVITY_PATHS.some(p => req.path.startsWith(p))) {
lastActivityTime = Date.now();
}
}
setInterval(async () => {
const idleMs = Date.now() - lastActivityTime;
if (idleMs > IDLE_TIMEOUT && viteProcess) {
pushLog(`[IDLE] No activity for ${Math.round(idleMs / 60000)} min β€” stopping Vite to save resources`, "warning");
await killVite();
bgProcesses.forEach((_, id) => killBgProcess(id));
}
}, 60 * 1000);
/* ================= LOG STREAM ================= */
let logs = [];
let clients = new Set();
function pushLog(message, type = "info") {
const entry = {
id: Date.now() + Math.random(),
time: new Date().toLocaleTimeString(),
timestamp: Date.now(),
message,
type
};
logs.push(entry);
if (logs.length > 500) logs.shift();
const payload = `data: ${JSON.stringify(entry)}\n\n`;
clients.forEach(c => {
try { c.write(payload); } catch { clients.delete(c); }
});
console.log(`[${type.toUpperCase()}] ${message}`);
}
/* ================= MIDDLEWARE ================= */
app.use(cors({
origin: (origin, callback) => {
if (!origin) return callback(null, true);
if (ALLOWED_ORIGINS.includes(origin) || origin.endsWith(".hf.space")) return callback(null, true);
pushLog(`[SECURITY] CORS DENIED β€” origin: ${origin}`, "error");
callback(new Error("ACTION_DENIED"));
},
credentials: true
}));
app.use(express.json({ limit: "50mb" }));
app.use((err, req, res, next) => {
if (err.message === "ACTION_DENIED") {
return res.status(403).json({ error: "ACTION_DENIED", message: "Origin not allowed" });
}
next(err);
});
if (!fs.existsSync(PROJECT_DIR)) {
fs.mkdirSync(PROJECT_DIR, { recursive: true });
}
/* ================= RATE LIMITER & TOKEN SECURITY ================= */
const rateLimitMap = new Map();
function rateLimit(req, res, maxRequests = 30, windowMs = 60000) {
const ip = req.ip || req.socket.remoteAddress;
const now = Date.now();
const windowStart = now - windowMs;
if (!rateLimitMap.has(ip)) rateLimitMap.set(ip, []);
const requests = rateLimitMap.get(ip).filter(t => t > windowStart);
requests.push(now);
rateLimitMap.set(ip, requests);
if (requests.length > maxRequests) {
pushLog(`[RATE LIMIT] IP ${ip} throttled`, "warning");
res.status(429).json({ error: "TOO_MANY_REQUESTS", retryAfter: 60 });
return false;
}
return true;
}
function verifyToken(req, res) {
const token = req.body?.token || req.query?.token || req.headers?.["x-api-key"];
if (token !== MASTER_KEY) {
pushLog(`[SECURITY] Unauthorized access attempt from ${req.ip}`, "error");
res.status(403).json({ error: "ACTION_DENIED", message: "Invalid token" });
return false;
}
return true;
}
/* ================= SSE LOG STREAM ================= */
app.get("/api/logs", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
// BUG FIX #1: X-Accel-Buffering missing tha β€” HF/nginx ke peeche SSE buffer
// ho jaata tha, logs real-time nahi aate the client tak
res.setHeader("X-Accel-Buffering", "no");
const last = parseInt(req.query.last) || 50;
logs.slice(-last).forEach(l => res.write(`data: ${JSON.stringify(l)}\n\n`));
// Heartbeat β€” 30s mein connection alive rakhne ke liye
const heartbeat = setInterval(() => {
try { res.write(`: heartbeat\n\n`); } catch { clearInterval(heartbeat); }
}, 30000);
clients.add(res);
req.on("close", () => {
clients.delete(res);
clearInterval(heartbeat);
});
});
/* ================= MULTI-SERVER MANAGER ================= */
function startBgProcess(id, command, port = null) {
if (bgProcesses.has(id)) {
pushLog(`[SERVER] Process ${id} is already running`, "warning");
return false;
}
pushLog(`[SERVER] Starting background process [${id}]: ${command}`, "system");
const proc = spawn(command, {
cwd: PROJECT_DIR,
shell: true,
env: { ...process.env, PORT: port ? String(port) : undefined }
});
proc.stdout.on("data", d => pushLog(`[${id.toUpperCase()}] ${d.toString().trim()}`, "info"));
proc.stderr.on("data", d => pushLog(`[${id.toUpperCase()} ERROR] ${d.toString().trim()}`, "error"));
proc.on("close", code => {
pushLog(`[SERVER] Process ${id} stopped (code: ${code})`, "system");
bgProcesses.delete(id);
});
bgProcesses.set(id, { process: proc, command, port });
return true;
}
function killBgProcess(id) {
if (!bgProcesses.has(id)) return false;
const { process: proc } = bgProcesses.get(id);
proc.kill("SIGKILL");
bgProcesses.delete(id);
pushLog(`[SERVER] Killed background process: ${id}`, "warning");
return true;
}
app.post("/api/process/start", (req, res) => {
if (!verifyToken(req, res)) return;
const { id, command, port } = req.body;
if (!id || !command) return res.status(400).json({ error: "ID and command required" });
const success = startBgProcess(id, command, port);
res.json({ success, message: success ? "Process started" : "Process already running" });
});
app.post("/api/process/stop", (req, res) => {
if (!verifyToken(req, res)) return;
const { id } = req.body;
const success = killBgProcess(id);
res.json({ success, message: success ? "Process stopped" : "Process not found" });
});
/* ================= VITE MANAGER ================= */
function killVite() {
return new Promise((resolve) => {
if (viteProcess) {
pushLog("[SYSTEM] Killing existing Vite process", "system");
viteProcess.kill("SIGKILL");
viteProcess = null;
}
const killCmd = process.platform === "win32"
? `for /f "tokens=5" %a in ('netstat -aon ^| findstr :${VITE_PORT}') do taskkill /F /PID %a`
: `fuser -k ${VITE_PORT}/tcp`;
exec(killCmd, () => setTimeout(resolve, 1500));
});
}
function startVite(forceRestart = false) {
if (isBooting && !forceRestart) return;
if (viteProcess && !forceRestart) return;
if (forceRestart) {
isBooting = true;
killVite().then(() => bootVite());
} else {
bootVite();
}
}
function bootVite() {
pushLog("[SYSTEM] Starting Vite dev server...", "system");
isBooting = true;
const nodeModules = path.join(PROJECT_DIR, "node_modules");
const boot = () => {
viteProcess = spawn("pnpm", ["run", "dev", "--", "--host", "0.0.0.0", "--port", String(VITE_PORT)], {
cwd: PROJECT_DIR,
shell: true,
env: { ...process.env, FORCE_COLOR: "0" }
});
viteProcess.stdout.on("data", d => {
const msg = d.toString().trim();
if (!msg) return;
pushLog("[VITE] " + msg, "info");
if (msg.includes("Local:") || msg.includes("ready in") || msg.includes("localhost")) {
isBooting = false;
buildRetryCount = 0;
pushLog("[SYSTEM] βœ… Vite is ready!", "success");
}
});
viteProcess.stderr.on("data", d => {
const msg = d.toString().trim();
if (!msg) return;
pushLog("[VITE ERROR] " + msg, "error");
});
viteProcess.on("close", (code) => {
pushLog(`[SYSTEM] Vite stopped (exit code: ${code})`, "system");
viteProcess = null;
isBooting = false;
if (code !== null && code !== 0 && buildRetryCount < MAX_RETRIES) {
buildRetryCount++;
pushLog(`[SYSTEM] Auto-restarting Vite (attempt ${buildRetryCount}/${MAX_RETRIES})...`, "warning");
setTimeout(() => killVite().then(() => bootVite()), 3000);
}
});
};
if (fs.existsSync(DEPS_LOCK) && fs.existsSync(nodeModules)) {
pushLog("[INSTALL] βœ… Dependencies locked β€” skipping install", "success");
boot();
return;
}
pushLog("[INSTALL] Installing dependencies...", "system");
const install = spawn("pnpm", ["install"], { cwd: PROJECT_DIR, shell: true });
install.stdout.on("data", d => pushLog("[INSTALL] " + d.toString().trim(), "info"));
install.stderr.on("data", d => pushLog("[INSTALL] " + d.toString().trim(), "warning"));
install.on("close", code => {
if (code === 0) {
fs.writeFileSync(DEPS_LOCK, new Date().toISOString());
pushLog("[INSTALL] βœ… Dependencies installed!", "success");
boot();
} else {
pushLog("[ERROR] ❌ pnpm install failed!", "error");
isBooting = false;
}
});
}
/* ================= STATUS ================= */
app.get("/api/status", (req, res) => {
res.json({
ready: !!viteProcess && !isBooting,
booting: isBooting,
viteRunning: !!viteProcess,
activeBackgroundProcesses: Array.from(bgProcesses.keys()),
uptime: process.uptime(),
logCount: logs.length
});
});
/* ================= FILE UPDATE ================= */
app.post("/api/update", async (req, res) => {
if (!verifyToken(req, res)) return;
if (!rateLimit(req, res, 20, 60000)) return;
updateActivity(req);
const foundationAlreadyWritten = fs.existsSync(FOUNDATION_LOCK);
const { files, restart = false } = req.body;
try {
if (files && Object.keys(files).length > 0) {
const updatedFiles = [];
for (const [p, contentObj] of Object.entries(files)) {
const safePath = path.normalize(p).replace(/^(\.\.[\/\\])+/, "");
const fp = path.join(PROJECT_DIR, safePath);
if (!fp.startsWith(PROJECT_DIR)) {
pushLog(`[SECURITY] Blocked path traversal: ${p}`, "error");
continue;
}
await fs.ensureDir(path.dirname(fp));
// BUG FIX #2: contentObj null/undefined hone par crash hota tha
// typeof check se pehle null guard lagao
const fileContent = contentObj == null
? ""
: typeof contentObj === "string"
? contentObj
: contentObj.content ?? "";
const isBase64 = contentObj?.isBase64 || isBinaryFile(safePath);
if (isBase64 && fileContent) {
await fs.writeFile(fp, Buffer.from(fileContent, "base64"));
} else {
await fs.writeFile(fp, fileContent, "utf-8");
}
updatedFiles.push(safePath);
}
if (!foundationAlreadyWritten) {
fs.writeFileSync(FOUNDATION_LOCK, new Date().toISOString());
}
pushLog(`[FILES] βœ… Updated ${updatedFiles.length} files`, "success");
}
if (restart) startVite(true);
else startVite();
res.json({ success: true, filesUpdated: Object.keys(files || {}).length });
} catch (e) {
pushLog("[ERROR] File update failed: " + e.message, "error");
res.status(500).json({ error: e.message });
}
});
/* ================= FILE READ ================= */
app.post("/api/read", async (req, res) => {
if (!verifyToken(req, res)) return;
updateActivity(req);
const { filePath } = req.body;
try {
const safePath = path.normalize(filePath).replace(/^(\.\.[\/\\])+/, "");
const fp = path.join(PROJECT_DIR, safePath);
if (!fp.startsWith(PROJECT_DIR)) return res.status(403).json({ error: "Path traversal blocked" });
if (!fs.existsSync(fp)) return res.status(404).json({ error: "File not found" });
const isBinary = isBinaryFile(safePath);
const content = isBinary
? await fs.readFile(fp, "base64")
: await fs.readFile(fp, "utf-8");
res.json({ success: true, content, path: safePath, isBase64: isBinary });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
/* ================= FILE LIST & DELETE ================= */
app.post("/api/files", async (req, res) => {
if (!verifyToken(req, res)) return;
updateActivity(req);
async function walkDir(dir, base = "") {
const items = [];
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
if (["node_modules", ".git", "dist", ".cache"].includes(entry.name)) continue;
const relPath = path.join(base, entry.name);
if (entry.isDirectory()) {
const children = await walkDir(path.join(dir, entry.name), relPath);
items.push({ name: entry.name, path: relPath, type: "dir", children });
} else {
items.push({ name: entry.name, path: relPath, type: "file" });
}
}
} catch {}
return items;
}
const tree = await walkDir(PROJECT_DIR);
res.json({ success: true, tree });
});
app.post("/api/delete", async (req, res) => {
if (!verifyToken(req, res)) return;
updateActivity(req);
const { filePath } = req.body;
try {
const safePath = path.normalize(filePath).replace(/^(\.\.[\/\\])+/, "");
const fp = path.join(PROJECT_DIR, safePath);
if (!fp.startsWith(PROJECT_DIR)) return res.status(403).json({ error: "Path traversal blocked" });
await fs.remove(fp);
pushLog(`[FILES] Deleted: ${safePath}`, "warning");
res.json({ success: true });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
/* ================= COMMAND EXEC ================= */
const BLOCKED_COMMANDS = ["rm -rf /", "mkfs", ":(){ :|:& };:", "shutdown", "reboot", "format"];
app.post("/api/execute", (req, res) => {
if (!verifyToken(req, res)) return;
if (!rateLimit(req, res, 15, 60000)) return;
updateActivity(req);
const { command, timeout = 30000 } = req.body;
const isBlocked = BLOCKED_COMMANDS.some(cmd => command.includes(cmd));
if (isBlocked) {
pushLog(`[SECURITY] Blocked dangerous command: ${command}`, "error");
return res.status(403).json({ error: "COMMAND_BLOCKED" });
}
pushLog("[TERMINAL] $ " + command, "system");
const child = exec(command, { cwd: PROJECT_DIR, timeout, maxBuffer: 1024 * 1024 * 10 }, (err, stdout, stderr) => {
const output = stdout || stderr || err?.message || "";
const success = !err;
const exitCode = child.exitCode ?? (err ? 1 : 0);
if (success) pushLog("[TERMINAL] βœ… Done", "success");
else pushLog("[TERMINAL] ❌ " + (err?.message || "Command failed"), "error");
res.json({ success, output, exitCode });
});
});
/* ================= HEALTH CHECK ================= */
app.get("/health", (req, res) => {
res.json({
status: "ok",
uptime: process.uptime(),
memory: process.memoryUsage(),
vite: !!viteProcess,
timestamp: new Date().toISOString()
});
});
/* ================= VITE CONTROL & RESET ================= */
app.post("/api/vite/restart", async (req, res) => {
if (!verifyToken(req, res)) return;
updateActivity(req);
pushLog("[SYSTEM] Manual Vite restart requested", "system");
buildRetryCount = 0;
startVite(true);
res.json({ success: true, message: "Vite restart initiated" });
});
app.post("/api/vite/stop", async (req, res) => {
if (!verifyToken(req, res)) return;
await killVite();
res.json({ success: true, message: "Vite stopped" });
});
app.post("/api/reset", async (req, res) => {
if (!verifyToken(req, res)) return;
try {
pushLog("[SYSTEM] Resetting project...", "warning");
await killVite();
bgProcesses.forEach((_, id) => killBgProcess(id));
await fs.remove(PROJECT_DIR);
fs.mkdirSync(PROJECT_DIR, { recursive: true });
if (fs.existsSync(FOUNDATION_LOCK)) fs.removeSync(FOUNDATION_LOCK);
if (fs.existsSync(DEPS_LOCK)) fs.removeSync(DEPS_LOCK);
buildRetryCount = 0;
pushLog("[SYSTEM] βœ… Project reset complete", "success");
res.json({ success: true });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
/* ================= PROXY ================= */
const proxy = httpProxy.createProxyServer({
target: `http://127.0.0.1:${VITE_PORT}`,
changeOrigin: true,
ws: true
});
const PREVIEW_SECRET = "AUTODEV_PREVIEW_777";
const ACCESS_DENIED_PAGE = `<!DOCTYPE html><html><head><title>Access Denied</title><style>body{background:#09090b;color:#fff;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;flex-direction:column;gap:16px;}.title{color:#ef4444;font-size:1.4rem;font-weight:bold;}</style></head><body><div class="title">ACCESS DENIED</div><div>This preview is protected</div></body></html>`;
const BOOTING_PAGE = `<!DOCTYPE html><html><head><title>Booting...</title><style>body{background:#09090b;color:#facc15;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;}</style><script>setTimeout(()=>location.reload(), 2000)</script></head><body>⚑ AutoDev is booting up...</body></html>`;
// BUG FIX #3: proxyRes handler WRONG tha β€” http-proxy already headers forward
// kar chuka hota hai jab proxyRes fire hota hai, toh wahan set karna useless tha.
// Sahi fix: upar wala app.use iframe middleware engine responses cover karta hai.
// proxyRes handler hata diya.
proxy.on("error", (err, req, res) => {
if (res && typeof res.writeHead === "function") {
try {
res.writeHead(503, { "Content-Type": "text/html" });
res.end(BOOTING_PAGE);
} catch {}
} else if (res && typeof res.destroy === "function") {
res.destroy();
}
});
app.use((req, res, next) => {
if (req.path.startsWith("/api") || req.path === "/health") return next();
const isRootRequest = req.path === "/" || req.path === "";
if (isRootRequest) {
const previewHeader = req.headers["x-autodev-preview"];
const previewParam = req.query["__autodev"];
if (previewHeader !== PREVIEW_SECRET && previewParam !== PREVIEW_SECRET) {
pushLog(`[SECURITY] Preview blocked β€” direct access attempt`, "warning");
return res.status(403).send(ACCESS_DENIED_PAGE);
}
}
updateActivity(req);
if (!viteProcess && !isBooting) startVite();
proxy.web(req, res);
});
const server = app.listen(PORT, () => {
pushLog(`[SYSTEM] AutoDev Engine running on port ${PORT}`, "system");
});
server.on("upgrade", (req, socket, head) => proxy.ws(req, socket, head));
/* ================= GRACEFUL SHUTDOWN ================= */
process.on("SIGTERM", async () => {
pushLog("[SYSTEM] Shutting down...", "system");
await killVite();
bgProcesses.forEach((_, id) => killBgProcess(id));
process.exit(0);
});
process.on("SIGINT", async () => {
await killVite();
bgProcesses.forEach((_, id) => killBgProcess(id));
process.exit(0);
});
process.on("uncaughtException", err => pushLog("[CRITICAL] Uncaught Exception: " + err.message, "error"));
process.on("unhandledRejection", reason => pushLog("[CRITICAL] Unhandled Rejection: " + reason, "error"));