← back to Claude Webdev Accelerator
scripts/gate-proxy.js
35 lines
// Zero-dep Basic-auth reverse proxy for public previews.
// Locks the ENTIRE upstream app behind one credential so a public tunnel URL
// requires auth regardless of how the app scopes its own auth.
// Usage: PORT=4901 UPSTREAM=127.0.0.1:3901 GATE_AUTH=admin:DW2024! node gate-proxy.js
const http = require('http');
const PORT = parseInt(process.env.PORT || '4900', 10);
const UPSTREAM = process.env.UPSTREAM || '127.0.0.1:3900';
const GATE_AUTH = process.env.GATE_AUTH || 'admin:DW2024!';
const [UHOST, UPORT] = UPSTREAM.split(':');
function authed(req) {
const m = (req.headers.authorization || '').match(/^Basic\s+(.+)$/i);
if (!m) return false;
try { return Buffer.from(m[1], 'base64').toString() === GATE_AUTH; } catch { return false; }
}
http.createServer((req, res) => {
if (!authed(req)) {
res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="preview"' });
return res.end('preview: auth required');
}
const opts = {
host: UHOST, port: parseInt(UPORT, 10), method: req.method, path: req.url,
headers: { ...req.headers, host: `${UHOST}:${UPORT}` },
};
const up = http.request(opts, (ur) => {
res.writeHead(ur.statusCode, ur.headers);
ur.pipe(res);
});
up.on('error', (e) => { res.writeHead(502); res.end('upstream error: ' + e.message); });
req.pipe(up);
}).listen(PORT, '127.0.0.1', function () {
console.log(`[gate-proxy] :${PORT} -> ${UPSTREAM} (gated)`);
});