"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startProxy = startProxy;
exports.stopProxy = stopProxy;
const node_http_1 = __importDefault(require("node:http"));
const node_crypto_1 = __importDefault(require("node:crypto"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const client_1 = require("@prisma/client");
const env_1 = require("../config/env");
const logger_1 = require("../utils/logger");
// HMAC secret — regenerated each server start (cookie sessions reset on restart)
const HMAC_SECRET = node_crypto_1.default.randomBytes(32);
const COOKIE_NAME = '_lmp_auth';
const COOKIE_MAX_AGE_S = 4 * 60 * 60; // 4 hours
// Extract Listmonk internal host/port from LISTMONK_URL (e.g. "http://listmonk-app:9000")
function parseListmonkTarget() {
    const url = new URL(env_1.env.LISTMONK_URL);
    return { hostname: url.hostname, port: parseInt(url.port || '9000', 10) };
}
// Basic auth header for Listmonk API user
function listmonkAuthHeader() {
    const creds = Buffer.from(`${env_1.env.LISTMONK_ADMIN_USER}:${env_1.env.LISTMONK_ADMIN_PASSWORD}`).toString('base64');
    return `Basic ${creds}`;
}
function signCookie(payload) {
    const data = Buffer.from(JSON.stringify(payload)).toString('base64url');
    const sig = node_crypto_1.default.createHmac('sha256', HMAC_SECRET).update(data).digest('base64url');
    return `${data}.${sig}`;
}
function verifyCookie(cookie) {
    const dot = cookie.indexOf('.');
    if (dot === -1)
        return null;
    const data = cookie.slice(0, dot);
    const sig = cookie.slice(dot + 1);
    const expected = node_crypto_1.default.createHmac('sha256', HMAC_SECRET).update(data).digest('base64url');
    if (!node_crypto_1.default.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
        return null;
    try {
        const payload = JSON.parse(Buffer.from(data, 'base64url').toString());
        if (payload.exp < Date.now() / 1000)
            return null;
        return payload;
    }
    catch {
        return null;
    }
}
function parseCookies(header) {
    const cookies = {};
    if (!header)
        return cookies;
    for (const pair of header.split(';')) {
        const eq = pair.indexOf('=');
        if (eq === -1)
            continue;
        const key = pair.slice(0, eq).trim();
        const val = pair.slice(eq + 1).trim();
        cookies[key] = val;
    }
    return cookies;
}
let server = null;
function startProxy() {
    const target = parseListmonkTarget();
    server = node_http_1.default.createServer((req, res) => {
        const url = new URL(req.url || '/', `http://${req.headers.host}`);
        // --- /auth?token=<jwt> — exchange JWT for proxy cookie ---
        if (url.pathname === '/auth') {
            const token = url.searchParams.get('token');
            if (!token) {
                res.writeHead(400, { 'Content-Type': 'text/plain' });
                res.end('Missing token parameter');
                return;
            }
            try {
                const payload = jsonwebtoken_1.default.verify(token, env_1.env.JWT_ACCESS_SECRET, { algorithms: ['HS256'] });
                const payloadRoles = payload.roles || [payload.role];
                if (!payloadRoles.includes(client_1.UserRole.SUPER_ADMIN)) {
                    res.writeHead(403, { 'Content-Type': 'text/plain' });
                    res.end('Forbidden — SUPER_ADMIN role required');
                    return;
                }
                const cookieVal = signCookie({
                    uid: payload.id,
                    exp: Math.floor(Date.now() / 1000) + COOKIE_MAX_AGE_S,
                });
                res.writeHead(302, {
                    Location: '/admin/',
                    'Set-Cookie': `${COOKIE_NAME}=${cookieVal}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${COOKIE_MAX_AGE_S}`,
                });
                res.end();
            }
            catch {
                res.writeHead(401, { 'Content-Type': 'text/plain' });
                res.end('Invalid or expired token');
            }
            return;
        }
        // --- All other routes: verify cookie, proxy to Listmonk ---
        const cookies = parseCookies(req.headers.cookie);
        const cookieVal = cookies[COOKIE_NAME];
        if (!cookieVal || !verifyCookie(cookieVal)) {
            res.writeHead(403, { 'Content-Type': 'text/plain' });
            res.end('Forbidden — authenticate at /auth?token=<jwt> first');
            return;
        }
        // Build proxy request headers (copy originals, inject auth, remove cookie)
        const proxyHeaders = { ...req.headers };
        delete proxyHeaders.host;
        delete proxyHeaders.cookie;
        proxyHeaders.authorization = listmonkAuthHeader();
        const proxyReq = node_http_1.default.request({
            hostname: target.hostname,
            port: target.port,
            path: req.url,
            method: req.method,
            headers: proxyHeaders,
        }, (proxyRes) => {
            // Strip frame-busting headers from Listmonk responses
            const responseHeaders = { ...proxyRes.headers };
            delete responseHeaders['x-frame-options'];
            delete responseHeaders['content-security-policy'];
            res.writeHead(proxyRes.statusCode || 502, responseHeaders);
            proxyRes.pipe(res);
        });
        proxyReq.on('error', (err) => {
            logger_1.logger.error('Listmonk proxy error:', err);
            if (!res.headersSent) {
                res.writeHead(502, { 'Content-Type': 'text/plain' });
            }
            res.end('Listmonk is unreachable');
        });
        // Pipe request body for POST/PUT/PATCH
        req.pipe(proxyReq);
    });
    server.listen(env_1.env.LISTMONK_PROXY_PORT, () => {
        logger_1.logger.info(`Listmonk proxy listening on port ${env_1.env.LISTMONK_PROXY_PORT}`);
    });
    return server;
}
function stopProxy() {
    return new Promise((resolve) => {
        if (server) {
            server.close(() => resolve());
        }
        else {
            resolve();
        }
    });
}
//# sourceMappingURL=listmonk-proxy.service.js.map