"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const auth_1 = require("../auth");
const config_1 = require("../config");
const session_manager_1 = require("../browser-chrome/session-manager");
const browser_proxy_1 = require("./browser-proxy");
const logger_1 = require("../logger");
const log = (0, logger_1.modLogger)('browser-chrome:route');
const router = (0, express_1.Router)();
function requireBearerAdmin(req, res, next) {
    const h = req.headers['authorization'];
    if (!h?.startsWith('Bearer ')) {
        res.status(401).json({ error: 'Bearer token required' });
        return;
    }
    const user = (0, auth_1.verifyToken)(h.slice(7));
    if (!user || !(0, config_1.isAdminUser)(user.username)) {
        res.status(403).json({ error: 'Admin only' });
        return;
    }
    req.user = user;
    next();
}
/**
 * POST /_session — create or reuse a per-user chromium session.
 * Returns { sid, token, viewport, url }. The token is required to open the
 * WebSocket stream (cannot use Authorization header in WS upgrade reliably).
 */
router.post('/_session', requireBearerAdmin, async (req, res) => {
    try {
        const session = await session_manager_1.browserChromeSessions.getOrCreate(req.user.username);
        const token = (0, session_manager_1.mintSessionToken)(session.sid, session.username);
        res.json({
            sid: session.sid,
            token,
            viewport: session.viewport,
            url: session.url,
        });
    }
    catch (err) {
        log.warn({ err }, 'create session failed');
        const msg = err instanceof Error ? err.message : 'Session create failed';
        res.status(500).json({ error: msg });
    }
});
/**
 * POST /:sid/nav { url } — navigate the session's page. SSRF guard reuses
 * the same allowlist as browser-proxy (RFC1918 + loopback only).
 */
router.post('/:sid/nav', requireBearerAdmin, async (req, res) => {
    const sid = req.params['sid'];
    const url = req.body?.url?.trim();
    if (!url) {
        res.status(400).json({ error: 'url required' });
        return;
    }
    let parsed;
    try {
        parsed = new URL(url);
    }
    catch {
        res.status(400).json({ error: 'Invalid URL' });
        return;
    }
    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
        res.status(400).json({ error: 'Only http: / https:' });
        return;
    }
    const allowedIp = await (0, browser_proxy_1.resolveAllowedTarget)(parsed.hostname);
    if (!allowedIp) {
        res.status(403).json({ error: 'Target not allowed — must resolve to RFC1918 / loopback' });
        return;
    }
    const session = session_manager_1.browserChromeSessions.get(sid, req.user.username);
    if (!session) {
        res.status(404).json({ error: 'Session not found' });
        return;
    }
    try {
        await session.page.goto(url, { timeout: 15000, waitUntil: 'load' });
        session.url = session.page.url();
        const title = await session.page.title().catch(() => '');
        res.json({ ok: true, url: session.url, title });
    }
    catch (err) {
        log.warn({ err, url }, 'nav failed');
        res.status(502).json({ error: 'Navigation failed', detail: err.message });
    }
});
/**
 * DELETE /:sid — explicit session teardown. Otherwise SessionManager idle
 * sweep eventually destroys it after 5 minutes of inactivity.
 */
router.delete('/:sid', requireBearerAdmin, async (req, res) => {
    const sid = req.params['sid'];
    const session = session_manager_1.browserChromeSessions.get(sid, req.user.username);
    if (!session) {
        res.status(404).json({ error: 'Session not found' });
        return;
    }
    await session_manager_1.browserChromeSessions.destroy(sid);
    res.status(204).end();
});
exports.default = router;
//# sourceMappingURL=browser-chrome.js.map