/** * Supported DNS provider types. Initially Cloudflare; the abstraction is * designed so additional providers (Route53, Gandi, DigitalOcean…) can be * added by implementing the IDnsProvider class interface in ts/dns/providers/. */ export type TDnsProviderType = 'cloudflare'; /** * Status of the last connection test against a provider. */ export type TDnsProviderStatus = 'untested' | 'ok' | 'error'; /** * Cloudflare-specific credential shape. */ export interface ICloudflareCredentials { apiToken: string; } /** * Discriminated union of all supported provider credential shapes. * Persisted opaquely on `IDnsProvider.credentials`. */ export type TDnsProviderCredentials = | ({ type: 'cloudflare' } & ICloudflareCredentials); /** * A registered DNS provider account. Holds the credentials needed to * call the provider's API and a snapshot of its last health check. */ export interface IDnsProvider { id: string; name: string; type: TDnsProviderType; /** Opaque credentials object — shape depends on `type`. */ credentials: TDnsProviderCredentials; status: TDnsProviderStatus; lastTestedAt?: number; lastError?: string; createdAt: number; updatedAt: number; createdBy: string; } /** * A redacted view of IDnsProvider safe to send to the UI / over the wire. * Strips secret fields from `credentials` while preserving the rest. */ export interface IDnsProviderPublic { id: string; name: string; type: TDnsProviderType; status: TDnsProviderStatus; lastTestedAt?: number; lastError?: string; createdAt: number; updatedAt: number; createdBy: string; /** Whether credentials are configured (true after creation). Never the secret itself. */ hasCredentials: boolean; } /** * A domain reported by a provider's API (not yet imported into dcrouter). */ export interface IProviderDomainListing { /** FQDN of the zone (e.g. 'example.com'). */ name: string; /** Provider's internal zone identifier (zone_id for Cloudflare). */ externalId: string; /** Authoritative nameservers reported by the provider. */ nameservers: string[]; } /** * Schema entry for a single credential field, used by the OpsServer UI to * render a provider's credential form dynamically. */ export interface IDnsProviderCredentialField { /** Key under which the value is stored in the credentials object. */ key: string; /** Label shown to the user. */ label: string; /** Optional inline help text. */ helpText?: string; /** Whether the field must be filled. */ required: boolean; /** True for secret fields (rendered as password input, never echoed back). */ secret: boolean; } /** * Metadata describing a DNS provider type. Drives: * - the OpsServer UI's provider type picker + credential form, * - documentation of which credentials each provider needs, * - end-to-end consistency between the type union, the discriminated * credentials union, the runtime factory, and the form rendering. * * To add a new provider, append a new entry to `dnsProviderTypeDescriptors` * below — and follow the checklist in `ts/dns/providers/factory.ts`. */ export interface IDnsProviderTypeDescriptor { type: TDnsProviderType; /** Human-readable name for the UI. */ displayName: string; /** One-line description shown next to the type picker. */ description: string; /** Schema for the credentials form. */ credentialFields: IDnsProviderCredentialField[]; } /** * Single source of truth for which DNS provider types exist and what * credentials each one needs. Used by both backend and frontend. */ export const dnsProviderTypeDescriptors: ReadonlyArray = [ { type: 'cloudflare', displayName: 'Cloudflare', description: 'Manages records via the Cloudflare API. Provider stays authoritative; dcrouter pushes record changes.', credentialFields: [ { key: 'apiToken', label: 'API Token', helpText: 'A Cloudflare API token with Zone:Read and DNS:Edit permissions for the target zones.', required: true, secret: true, }, ], }, ]; /** * Look up the descriptor for a given provider type. */ export function getDnsProviderTypeDescriptor( type: TDnsProviderType, ): IDnsProviderTypeDescriptor | undefined { return dnsProviderTypeDescriptors.find((d) => d.type === type); }