/**
 * Security Center data gathering (async, DB + env I/O).
 *
 * Reads real auth/session/secret/audit state and composes the
 * {@link SecurityCenterData} the admin tab renders. Never returns secret
 * values — only configured/missing verdicts.
 */
import { getActuateConfig } from '../../config/runtime.js';
import { validateEnvShape } from '../../diagnostics/env.js';
import { isIpAllowed } from '../ip-allowlist.js';
import { parseSecuritySettings } from './settings.js';
import { computeSecurityStatus } from './status.js';
import { summarizeApiKeys, summarizeUsers } from './summaries.js';
function hasModel(db, model) {
    return Boolean(db && typeof db === 'object' && db[model]);
}
function isProductionEnv() {
    return (process.env.VERCEL_ENV ?? process.env.NODE_ENV) === 'production';
}
function envSeverityToCheck(s) {
    switch (s) {
        case 'ok':
            return 'pass';
        case 'warn':
            return 'warning';
        case 'error':
        case 'missing':
            return 'fail';
        default:
            return 'unknown';
    }
}
/** Read the persisted, enforced security policy from the `settings` global. */
export async function getStoredSecuritySettings(db) {
    const d = db;
    if (!hasModel(d, 'document'))
        return parseSecuritySettings({});
    try {
        const row = await d.document.findFirst({
            where: { collection: 'settings', deletedAt: null },
            select: { data: true },
        });
        return parseSecuritySettings(row?.data ?? {});
    }
    catch {
        return parseSecuritySettings({});
    }
}
export async function getSecurityCenterData(opts) {
    const d = opts.db;
    const isProduction = isProductionEnv();
    const now = Date.now();
    const settings = await getStoredSecuritySettings(opts.db);
    const userRows = hasModel(d, 'user')
        ? await d
            .user.findMany({ select: { role: true, isActive: true, totpEnabled: true } })
            .catch(() => [])
        : [];
    const users = summarizeUsers(userRows);
    let viewerHasMfa = false;
    if (opts.userId && hasModel(d, 'user')) {
        const viewer = await d
            .user.findUnique({
            where: { id: opts.userId },
            select: { totpEnabled: true },
        })
            .catch(() => null);
        viewerHasMfa = Boolean(viewer?.totpEnabled);
    }
    const activeSessions = hasModel(d, 'session')
        ? await d
            .session.count({ where: { revokedAt: null, expiresAt: { gt: new Date() } } })
            .catch(() => 0)
        : 0;
    const apiKeyRows = hasModel(d, 'apiKey')
        ? await d
            .apiKey.findMany({
            select: { scopes: true, expiresAt: true, lastUsedAt: true, revokedAt: true },
        })
            .catch(() => [])
        : [];
    const apiKeys = summarizeApiKeys(apiKeyRows, now);
    const since = new Date(now - 24 * 60 * 60 * 1000);
    const failedLogins24h = hasModel(d, 'auditLog')
        ? await d
            .auditLog.count({ where: { event: 'login_failed', timestamp: { gte: since } } })
            .catch(() => 0)
        : 0;
    // ── Secrets / encryption (verdicts only, never values) ───────────────────────
    const env = validateEnvShape();
    const byName = new Map(env.checks.map((c) => [c.name, c]));
    const config = getActuateConfig();
    const configSecret = typeof config?.secret === 'string' && config.secret.length >= 32;
    let cmsSecret = envSeverityToCheck(byName.get('CMS_SECRET')?.status ?? 'missing');
    if (cmsSecret === 'fail' && configSecret)
        cmsSecret = 'pass';
    const encRaw = byName.get('CMS_ENCRYPTION_KEY')?.status ?? 'warn';
    // A *missing* key is only a hard failure in production; elsewhere it's a warning.
    let encryptionKey = envSeverityToCheck(encRaw);
    if (encRaw === 'warn' && isProduction)
        encryptionKey = 'fail';
    const upstash = byName.get('UPSTASH_REDIS_REST_URL')?.status;
    const rateLimitBackend = upstash === 'ok' ? 'distributed' : 'in_memory';
    const secrets = {
        cmsSecret,
        encryptionKey,
        rateLimitBackend,
        apiKeysHashed: true,
        webhookSecretsEncrypted: encryptionKey === 'pass',
    };
    // ── IP allowlist (config-driven; middleware enforces) ────────────────────────
    const ranges = Array.isArray(config?.admin?.ipAllowlist)
        ? config.admin.ipAllowlist
        : [];
    const currentIp = opts.currentIp ?? null;
    const ipAllowlist = {
        enabled: ranges.length > 0,
        source: (ranges.length > 0 ? 'config' : 'none'),
        ranges,
        currentIp,
        currentIpAllowed: ranges.length > 0 && currentIp ? isIpAllowed(currentIp, ranges) : null,
    };
    const sessionMaxAgeHours = 24 * 7; // login issues a fixed 7-day session today
    const status = computeSecurityStatus({
        isProduction,
        settings,
        users,
        apiKeys,
        secrets,
        ipAllowlist,
        failedLogins24h,
        activeSessions,
        csrfEnabled: true,
        uploadValidation: true,
        auditEnabled: true,
    });
    const editable = { source: 'database', editable: true };
    const sources = {
        mfaRequired: editable,
        mfaGracePeriodDays: editable,
        maxConcurrentSessions: editable,
        sessionMaxAgeHours: {
            source: 'computed',
            editable: false,
            reason: 'Sessions are issued with a fixed 7-day lifetime.',
        },
        ipAllowlist: {
            source: 'config',
            editable: false,
            reason: 'Set admin.ipAllowlist in actuate.config.ts; enforced in middleware.',
        },
        csrf: { source: 'computed', editable: false, reason: 'Always enabled for mutating requests.' },
        rateLimiting: {
            source: 'environment',
            editable: false,
            reason: 'Backend is selected from UPSTASH_REDIS_REST_URL / KV_REST_API_URL.',
        },
        uploadValidation: {
            source: 'computed',
            editable: false,
            reason: 'Always enforced server-side.',
        },
        cmsSecret: { source: 'environment', editable: false },
        encryptionKey: { source: 'environment', editable: false },
        audit: { source: 'computed', editable: false, reason: 'Always enabled.' },
    };
    return {
        settings,
        status,
        users,
        apiKeys,
        secrets,
        ipAllowlist,
        failedLogins24h,
        activeSessions,
        csrfEnabled: true,
        uploadValidation: true,
        auditEnabled: true,
        sessionMaxAgeHours,
        viewerHasMfa,
        environment: process.env.VERCEL_ENV ?? process.env.NODE_ENV ?? 'development',
        sources,
    };
}
//# sourceMappingURL=gather.js.map