DUNGEONS-30: add access gateway with Telegram and Discord approvals

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-09-05 17:09:53 +05:00
commit b87a8ef70e
30 changed files with 1838 additions and 0 deletions

47
src/http.js Normal file
View File

@@ -0,0 +1,47 @@
import { isIP } from 'node:net';
export const COOKIE = 'dng_access';
export function readCookie(req) {
const matches = (req.headers.cookie || '').split(';').map(v => v.trim()).filter(v => v.startsWith(`${COOKIE}=`));
return matches.length === 1 ? matches[0].slice(COOKIE.length + 1) : '';
}
export function sessionCookie(config, token, maxAge = 0) {
return `${COOKIE}=${token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${maxAge}${config.secureCookie ? '; Secure' : ''}`;
}
export function safeNext(value) {
if (typeof value !== 'string' || value.length > 4096 || !value.startsWith('/') || value.startsWith('//') || /[\\\u0000-\u0020]/.test(value)) return '/';
try {
const decoded = decodeURIComponent(value);
if (/[\\\u0000-\u001f]/.test(decoded)) return '/';
const url = new URL(value, 'http://gateway.local');
if (url.origin !== 'http://gateway.local' || url.pathname.startsWith('/_access')) return '/';
return url.pathname + url.search + url.hash;
} catch { return '/'; }
}
export function clientIp(req, config) {
const forwarded = req.headers['x-forwarded-for'];
// Accept a single IP only. Forwarded chains require explicit edge normalization.
if (config.trustProxy && typeof forwarded === 'string' && isIP(forwarded.trim())) return forwarded.trim();
return req.socket.remoteAddress || 'unknown';
}
export function sendJson(res, status, body, headers = {}) {
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store', 'x-content-type-options': 'nosniff', ...headers });
res.end(JSON.stringify(body));
}
export async function readBody(req, maximum = 4096) {
if (Number(req.headers['content-length']) > maximum) { const error = new Error('Request is too large.'); error.status = 413; throw error; }
const chunks = [];
let size = 0;
for await (const chunk of req) {
size += chunk.length;
if (size > maximum) { const error = new Error('Request is too large.'); error.status = 413; throw error; }
chunks.push(chunk);
}
return Buffer.concat(chunks);
}