const http = require('http'); const fs = require('fs'); const path = require('path'); const url = require('url'); const PORT = 80; const ROOT = '/app'; function corsHeaders(extra = {}) { return { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, HEAD', 'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With', ...extra }; } function sendFile(req, res, filePath, contentType) { if (!fs.existsSync(filePath)) { res.writeHead(404); return res.end('Not Found'); } const stat = fs.statSync(filePath); res.writeHead(200, { ...corsHeaders(), 'Content-Type': contentType, 'Content-Length': stat.size, 'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0' }); if (req.method !== 'HEAD') { fs.createReadStream(filePath).pipe(res); } else { res.end(); } } function contentType(file) { const ext = path.extname(file).toLowerCase(); const types = { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2' }; return types[ext] || 'application/octet-stream'; } const server = http.createServer((req, res) => { const parsed = url.parse(req.url); const pathname = parsed.pathname; // CORS preflight if (req.method === 'OPTIONS') { res.writeHead(200, corsHeaders()); return res.end(); } // Get client IP if (pathname === '/getIP') { const ip = req.headers['x-forwarded-for']?.split(',')[0].trim() || req.socket.remoteAddress || ''; res.writeHead(200, { ...corsHeaders(), 'Content-Type': 'text/plain', 'Cache-Control': 'no-store' }); return res.end(ip); } // Upload endpoint if (pathname === '/upload') { if (req.method !== 'POST') { res.writeHead(200, corsHeaders()); return res.end('OK'); } let bytes = 0; req.on('data', chunk => { bytes += chunk.length; }); req.on('end', () => { res.writeHead(200, { ...corsHeaders(), 'Content-Type': 'text/plain', 'Cache-Control': 'no-store' }); res.end('OK'); }); req.on('error', () => { if (!res.headersSent) { res.writeHead(500, corsHeaders()); } res.end('ERROR'); }); return; } // Download endpoint if (pathname === '/downloading') { const file = path.join(ROOT, 'downloading'); if (!fs.existsSync(file)) { res.writeHead(404); return res.end('Not Found'); } const stat = fs.statSync(file); res.writeHead(200, { ...corsHeaders(), 'Content-Type': 'application/octet-stream', 'Content-Length': stat.size, 'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0', 'Content-Disposition': 'inline' }); if (req.method !== 'HEAD') { fs.createReadStream(file).pipe(res); } else { res.end(); } return; } // Static files let requested = pathname === '/' ? '/index.html' : pathname; const filePath = path.normalize(path.join(ROOT, requested)); // Prevent path traversal if (!filePath.startsWith(ROOT)) { res.writeHead(403); return res.end('Forbidden'); } if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) { res.writeHead(404); return res.end('Not Found'); } sendFile(req, res, filePath, contentType(filePath)); }); server.listen(PORT, '0.0.0.0', () => { console.log(`Speedtest server listening on port ${PORT}`); });