"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildApiKeyAuth = buildApiKeyAuth;
exports.resolveApiKey = resolveApiKey;
// ─── Cache de API key (evita Prisma query em cada requisição) ────────────────
// Chaves são imutáveis durante a vida da sessão — TTL de 5 min é suficiente.
const KEY_CACHE_TTL = 5 * 60000;
const keyCache = new Map();
setInterval(() => {
    const now = Date.now();
    for (const [k, v] of keyCache.entries()) {
        if (now - v.cachedAt > KEY_CACHE_TTL)
            keyCache.delete(k);
    }
}, KEY_CACHE_TTL);
// ─── Rate limiter simples em memória (por chave) ─────────────────────────────
const RATE_WINDOW_MS = 60000;
const RATE_MAX = 120;
const rateMap = new Map();
function checkRateLimit(key) {
    const now = Date.now();
    let entry = rateMap.get(key);
    if (!entry || now > entry.resetAt) {
        entry = { count: 1, resetAt: now + RATE_WINDOW_MS };
        rateMap.set(key, entry);
        return true;
    }
    entry.count++;
    return entry.count <= RATE_MAX;
}
// Limpeza periódica para não vazar memória
setInterval(() => {
    const now = Date.now();
    for (const [k, v] of rateMap.entries()) {
        if (now > v.resetAt)
            rateMap.delete(k);
    }
}, RATE_WINDOW_MS * 2);
// ─── Factory ─────────────────────────────────────────────────────────────────
function buildApiKeyAuth(prisma) {
    return async function apiKeyAuth(req, res, next) {
        const key = req.headers['x-nw-key']?.trim();
        if (!key) {
            res.status(401).json({ error: 'Header x-nw-key ausente' });
            return;
        }
        if (!checkRateLimit(key)) {
            res.status(429).json({ error: 'Rate limit excedido (120 req/min por chave)' });
            return;
        }
        // Verifica cache antes de ir ao banco
        const cached = keyCache.get(key);
        if (cached && Date.now() - cached.cachedAt < KEY_CACHE_TTL) {
            if (cached.expiresAt && cached.expiresAt < new Date()) {
                res.status(401).json({ error: 'Chave expirada' });
                return;
            }
            req.extTenantId = cached.tenantId;
            next();
            return;
        }
        const apiKey = await prisma.apiKey.findUnique({
            where: { key },
            select: { tenantId: true, isActive: true, expiresAt: true },
        });
        if (!apiKey || !apiKey.isActive) {
            res.status(401).json({ error: 'Chave inválida ou inativa' });
            return;
        }
        if (apiKey.expiresAt && apiKey.expiresAt < new Date()) {
            res.status(401).json({ error: 'Chave expirada' });
            return;
        }
        keyCache.set(key, { tenantId: apiKey.tenantId, expiresAt: apiKey.expiresAt, cachedAt: Date.now() });
        req.extTenantId = apiKey.tenantId;
        next();
    };
}
/**
 * Versão standalone que valida a chave sem passar pelo Express.
 * Usada no handshake do WS (upgrade request).
 */
async function resolveApiKey(prisma, key) {
    if (!key)
        return null;
    const cached = keyCache.get(key);
    if (cached && Date.now() - cached.cachedAt < KEY_CACHE_TTL) {
        if (cached.expiresAt && cached.expiresAt < new Date())
            return null;
        return cached.tenantId;
    }
    const apiKey = await prisma.apiKey.findUnique({
        where: { key },
        select: { tenantId: true, isActive: true, expiresAt: true },
    });
    if (!apiKey || !apiKey.isActive)
        return null;
    if (apiKey.expiresAt && apiKey.expiresAt < new Date())
        return null;
    keyCache.set(key, { tenantId: apiKey.tenantId, expiresAt: apiKey.expiresAt, cachedAt: Date.now() });
    return apiKey.tenantId;
}
//# sourceMappingURL=apikey-auth.js.map