import http from 'http'; import { Readable } from 'stream'; const PORT = process.env.PORT || 7860; process.on('uncaughtException', (err) => console.error('CRASH ÉVITÉ:', err)); process.on('unhandledRejection', (err) => console.error('PROMESSE REFUSÉE:', err)); const trafficStats = {}; function getClientStats(req) { const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || '127.0.0.1'; if (!trafficStats[ip]) { trafficStats[ip] = { up: 0, down: 0 }; } return trafficStats[ip]; } const BROWSER_HOOK_SCRIPT = ` `; const LANDING_PAGE = ` CyberProxy Pro

🚀 CyberProxy Pro

Accède à Modrinth et contourne les restrictions d'assets en temps réel

`; function rewriteCss(css, targetUrl) { const proto = targetUrl.protocol.replace(':', ''); let rewritten = css.replace(/url\(['"]?(https?:\/\/[^'")]+)['"]?\)/g, (match, url) => { try { const u = new URL(url); return `url(/p/${u.protocol.replace(':', '')}/${u.host}${u.pathname}${u.search})`; } catch(e) { return match; } }); rewritten = rewritten.replace(/url\(['"]?\/((?!p\/)[^'")]+)['"]?\)/g, (match, path) => { return `url(/p/${proto}/${targetUrl.host}/${path})`; }); return rewritten; } const server = http.createServer(async (req, res) => { const clientStats = getClientStats(req); // --- PRIORITÉ ABSOLUE AUX ROUTES INTERNES --- if (req.url === '/' || req.url === '') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); return res.end(LANDING_PAGE); } if (req.url === '/proxy-stats') { res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store, max-age=0' }); return res.end(JSON.stringify({ up: (clientStats.up / (1024 * 1024)).toFixed(2), down: (clientStats.down / (1024 * 1024)).toFixed(2) })); } // --- LOGIQUE DU PROXY --- let targetUrlStr = null; if (req.url.startsWith('/p/')) { const match = req.url.match(/^\/p\/([^/]+)\/([^/]+)(.*)$/); if (match) { const [_, proto, domain, path] = match; targetUrlStr = `${proto}://${domain}${path}`; } } else if (req.headers.referer) { try { const refUrl = new URL(req.headers.referer); if (refUrl.pathname.startsWith('/p/')) { const match = refUrl.pathname.match(/^\/p\/([^/]+)\/([^/]+)/); if (match) { targetUrlStr = `${match[1]}://${match[2]}${req.url}`; } } } catch (e) {} } if (!targetUrlStr && req.headers.cookie) { const match = req.headers.cookie.match(/proxy_fallback_origin=([^;]+)/); if (match) targetUrlStr = decodeURIComponent(match[1]) + req.url; } if (!targetUrlStr) { res.writeHead(404); return res.end(); } try { const targetUrl = new URL(targetUrlStr); const headers = new Headers(); for (const [key, value] of Object.entries(req.headers)) { if (!['host', 'cookie', 'accept-encoding', 'connection', 'origin', 'referer'].includes(key.toLowerCase())) { headers.set(key, value); } } headers.set('Host', targetUrl.host); headers.set('User-Agent', req.headers['user-agent'] || 'Mozilla/5.0'); if (req.headers['origin']) headers.set('Origin', targetUrl.origin); if (req.headers['referer']) headers.set('Referer', targetUrl.origin); if (req.headers.cookie) { const cleanCookies = req.headers.cookie.split(';').map(c => c.trim()).filter(c => !c.startsWith('proxy_fallback_origin=')).join('; '); if (cleanCookies) headers.set('Cookie', cleanCookies); } let body = null; if (req.method !== 'GET' && req.method !== 'HEAD') { const buffers = []; for await (const chunk of req) { buffers.push(chunk); clientStats.up += chunk.length; } body = Buffer.concat(buffers); } const response = await fetch(targetUrl.href, { method: req.method, headers: headers, body: body, redirect: 'manual' }); const stripHeaders = ['connection', 'keep-alive', 'transfer-encoding', 'content-encoding', 'location', 'content-security-policy', 'strict-transport-security', 'content-length']; const resHeaders = {}; response.headers.forEach((value, key) => { if (!stripHeaders.includes(key.toLowerCase())) resHeaders[key] = value; }); const setCookies = [`proxy_fallback_origin=${encodeURIComponent(targetUrl.origin)}; Path=/; HttpOnly; SameSite=Lax`]; const originalSetCookies = response.headers.getSetCookie ? response.headers.getSetCookie() : []; originalSetCookies.forEach(c => setCookies.push(c)); resHeaders['Set-Cookie'] = setCookies; if (response.status >= 300 && response.status < 400) { let location = response.headers.get('location'); if (location) { const absoluteLocation = new URL(location, targetUrl.href); resHeaders['Location'] = `/p/${absoluteLocation.protocol.replace(':', '')}/${absoluteLocation.host}${absoluteLocation.pathname}${absoluteLocation.search}`; } res.writeHead(response.status, resHeaders); return res.end(); } const contentType = response.headers.get('content-type') || ''; if (contentType.includes('text/html')) { let text = await response.text(); text = text.replace('', '' + BROWSER_HOOK_SCRIPT); text = text.replace(/(href|src)=["'](https?:\/\/)([^"']+)["']/g, (m, attr, proto, rest) => `${attr}="/p/${proto.replace('://', '')}/${rest}"`); text = text.replace(/(href|src)=["']\/((?!p\/)[^"']+)["']/g, (m, attr, path) => `${attr}="/p/${targetUrl.protocol.replace(':', '')}/${targetUrl.host}/${path}"`); const bodyBuffer = Buffer.from(text, 'utf-8'); clientStats.down += bodyBuffer.length; resHeaders['content-length'] = bodyBuffer.length; resHeaders['content-type'] = 'text/html; charset=utf-8'; res.writeHead(response.status, resHeaders); return res.end(bodyBuffer); } if (contentType.includes('text/css')) { let text = await response.text(); text = rewriteCss(text, targetUrl); const bodyBuffer = Buffer.from(text, 'utf-8'); clientStats.down += bodyBuffer.length; resHeaders['content-length'] = bodyBuffer.length; res.writeHead(response.status, resHeaders); return res.end(bodyBuffer); } res.writeHead(response.status, resHeaders); if (response.body) { const stream = Readable.fromWeb(response.body); stream.on('data', (chunk) => { clientStats.down += chunk.length; }); stream.on('error', () => res.end()); stream.pipe(res); } else { res.end(); } } catch (err) { if (!res.headersSent) { res.writeHead(502); res.end(`Erreur de communication.`); } } }); server.on('upgrade', (req, socket, head) => { let targetUrlStr = null; if (req.url.startsWith('/p/')) { const match = req.url.match(/^\/p\/([^/]+)\/([^/]+)(.*)$/); if (match) targetUrlStr = `${match[1] === 'https' ? 'wss' : 'ws'}://${match[2]}${match[3]}`; } if (!targetUrlStr) return socket.end(); const target = new URL(targetUrlStr); const options = { hostname: target.hostname, port: target.port || (target.protocol === 'wss:' ? 443 : 80), method: req.method, path: req.url, headers: { ...req.headers, host: target.host } }; const proxyReq = http.request(options); proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => { socket.write('HTTP/1.1 101 Switching Protocols\r\n'); for (const [key, value] of Object.entries(proxyRes.headers)) socket.write(`${key}: ${value}\r\n`); socket.write('\r\n'); proxySocket.write(proxyHead); proxySocket.pipe(socket); socket.pipe(proxySocket); }); proxyReq.on('error', () => socket.end()); proxyReq.end(); }); server.listen(PORT, '0.0.0.0', () => console.log(`CyberProxy Pro actif sur le port ${PORT}`));