import { getBackend } from './api-base'; import { redactValue } from './redact'; import { CORS_PROXY, HEADERS } from '$lib/constants'; import { MimeTypeApplication } from '$lib/enums'; import { getProtocolAdapter } from '$lib/services/protocols'; import { settingsStore } from '$lib/stores/settings/index.svelte'; import type { Backend } from '$lib/types'; /** * Get authorization headers for API requests to a backend. */ export function getAuthHeaders(backendId?: string): Record { const backend = getBackend(backendId); if (backend) return getAuthHeadersForBackend(backend); // no backends resolver yet (early startup, or a non-browser call): keep the // pre-backends behaviour and authenticate against the serving origin const apiKey = settingsStore.config.apiKey?.toString().trim(); return apiKey ? { [HEADERS.AUTHORIZATION]: `${HEADERS.BEARER}${apiKey}` } : {}; } /** * Get authorization headers for a backend object, including one that is not * registered yet (used by the connection test on the add-backend form). * The protocol adapter owns the credential scheme and any required headers. */ export function getAuthHeadersForBackend(backend: Backend): Record { return getProtocolAdapter(backend).authHeaders(backend); } /** * Get standard JSON headers with optional authorization */ export function getJsonHeaders(backendId?: string): Record { return { [HEADERS.CONTENT_TYPE]: MimeTypeApplication.JSON, ...getAuthHeaders(backendId) }; } /** * Sanitize HTTP headers by redacting sensitive values. * Known sensitive headers (from HEADERS.REDACTED) and any extra headers * specified by the caller are fully redacted. Headers listed in * `partialRedactHeaders` are partially redacted, showing only the * specified number of trailing characters. * * @param headers - Headers to sanitize * @param extraRedactedHeaders - Additional header names to fully redact * @param partialRedactHeaders - Map of header name -> number of trailing chars to keep visible * @returns Object with header names as keys and (possibly redacted) values */ export function sanitizeHeaders( headers?: HeadersInit, extraRedactedHeaders?: Iterable, partialRedactHeaders?: Map ): Record { if (!headers) { return {}; } const normalized = new Headers(headers); const sanitized: Record = {}; const redactedHeaders = new Set( Array.from(extraRedactedHeaders ?? [], (header) => header.toLowerCase()) ); for (const [key, value] of normalized.entries()) { const normalizedKey = key.toLowerCase(); const unproxiedKey = normalizedKey.startsWith(CORS_PROXY.HEADER_PREFIX) ? normalizedKey.slice(CORS_PROXY.HEADER_PREFIX.length) : normalizedKey; const partialChars = partialRedactHeaders?.get(normalizedKey) ?? partialRedactHeaders?.get(unproxiedKey); if (partialChars !== undefined) { sanitized[key] = redactValue(value, partialChars); } else if ( HEADERS.REDACTED.has(normalizedKey) || HEADERS.REDACTED.has(unproxiedKey) || redactedHeaders.has(normalizedKey) || redactedHeaders.has(unproxiedKey) ) { sanitized[key] = redactValue(value); } else { sanitized[key] = value; } } return sanitized; }