/**
 * API key generation and scope validation.
 *
 * Keys are formatted `act_sk_<64 hex chars>`. We store only a SHA-256 hash of
 * the key in the database; the raw key is shown to the user exactly once at
 * creation time. The first 15 chars (`act_sk_` + 8 hex) are stored as
 * `keyPrefix` so the admin UI can display a human-recognizable token without
 * leaking the secret half.
 */
/** Generate a new API key with scoped permissions. */
export async function generateApiKey(config) {
    const rawBytes = crypto.getRandomValues(new Uint8Array(32));
    const rawKey = Array.from(rawBytes)
        .map((b) => b.toString(16).padStart(2, '0'))
        .join('');
    const key = `${config.prefix}_${rawKey}`;
    // First 8 hex chars after the underscore are safe to display (the remaining
    // 56 hex chars provide >224 bits of entropy).
    const keyPrefix = key.slice(0, config.prefix.length + 9);
    const keyHash = await hashApiKey(key);
    return { key, keyHash, keyPrefix };
}
/** SHA-256 hash a raw API key for lookup. */
export async function hashApiKey(rawKey) {
    const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(rawKey));
    return Array.from(new Uint8Array(hashBuffer))
        .map((b) => b.toString(16).padStart(2, '0'))
        .join('');
}
/** True when the request's bearer token looks like an API key (vs a session JWT). */
export function looksLikeApiKey(token) {
    return /^act_sk_[a-f0-9]{32,}$/i.test(token);
}
/**
 * Validate an API key's scopes against a collection action. Returns true when
 * the key is permitted to perform `action` on `collection`. Admin keys always
 * pass.
 */
export function validateApiKeyScope(scopes, collection, action) {
    if (scopes.admin)
        return true;
    if (!scopes.collections || scopes.collections.length === 0)
        return false;
    if (!scopes.actions || !scopes.actions.includes(action))
        return false;
    if (scopes.collections.includes('*'))
        return true;
    return scopes.collections.includes(collection);
}
/** Validate scope for a global action. */
export function validateApiKeyGlobalScope(scopes, slug) {
    if (scopes.admin)
        return true;
    if (!scopes.globals || scopes.globals.length === 0)
        return false;
    if (scopes.globals.includes('*'))
        return true;
    return scopes.globals.includes(slug);
}
/** Validate scope for media uploads/management. */
export function validateApiKeyMediaScope(scopes) {
    return scopes.admin === true || scopes.media === true;
}
/** Validate scope for page-builder/AI endpoints. */
export function validateApiKeyPageBuilderScope(scopes) {
    return scopes.admin === true || scopes.pageBuilder === true;
}
/** Validate IP restrictions against the request's client IP. */
export function validateApiKeyIp(restrictions, ip) {
    if (!restrictions || restrictions.length === 0)
        return true;
    if (!ip || ip === 'unknown')
        return false;
    return restrictions.includes(ip);
}
//# sourceMappingURL=api-key-enhanced.js.map