Files
technitium-console/src/api/hub-zones.ts
T
DenozordecandClaude Opus 5.5 392b644faa Hub stage 7: compare and align zones, settings and blocking
Zones gets "All servers" in hub mode: one table of every zone on every
server with its type and serial, marking zones that are on one server
only or whose serials or types differ. A zone can be copied to another
server (export, create, import; Overwrite must be ticked for a zone
that is already there) or made a Secondary there (the source's zone
transfer and notify lists gain the target, then the target zone is
created).

Settings and Blocking can be compared across servers against a baseline
kept in the hub. Host fields stay local, secrets are compared inside
the hub only and never shown or logged, and an apply sends only the
changed shared fields after saving each server's settings to history.
Blocked and Allowed lists are compared as sets; extras are removed only
when asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 01:20:05 +07:00

81 lines
2.7 KiB
TypeScript

import { apiRequest, type ApiOutcome } from './client'
import type { RunOutcome } from './apps'
/*
Zones across the servers, behind a hub (hub/zones-sync.ts): the merged list, copying a zone to
other servers, and making a zone a Secondary of another server's Primary.
*/
export interface ZoneCell {
state: 'present' | 'absent' | 'unknown'
type?: string
serial?: number | null
disabled?: boolean
dnssec?: string
lastModified?: string
}
export type RowStatus = 'same' | 'serial-differs' | 'type-differs' | 'only-on' | 'missing-on' | 'unknown'
export interface MatrixZone {
name: string
cells: Record<string, ZoneCell>
status: RowStatus
onlyOn?: string
missingOn?: string[]
note?: 'primary-secondary' | 'secondary-behind'
}
export interface ZonesMatrixData {
servers: { id: string; ok: boolean; writable: boolean; host: string; error?: string }[]
zones: MatrixZone[]
}
export const getZonesMatrix = (token: string | null): Promise<ApiOutcome<{ response: ZonesMatrixData }>> => apiRequest('hub/zones/matrix', { token })
export interface CopyTargetPreview {
server: string
ok: boolean
error?: string
writable: boolean
exists: boolean
type?: string
serial?: number | null
needsOverwrite: boolean
}
export interface CopyPreview {
zone: string
source: { server: string; type: string; serial: number | null; records: number; bytes: number }
warnings: string[]
targets: CopyTargetPreview[]
}
export const previewZoneCopy = (token: string | null, source: string, zone: string, targets: string[]): Promise<ApiOutcome<{ response: CopyPreview }>> =>
apiRequest('hub/zones/copy/preview', { token, method: 'POST', body: { source, zone, targets: targets.join(',') } })
export const copyZone = (token: string | null, source: string, zone: string, targets: string[], overwrite: string[]): Promise<RunOutcome> =>
apiRequest('hub/zones/copy', { token, method: 'POST', body: { source, zone, targets: targets.join(','), overwrite: overwrite.join(',') } })
export interface SecondaryInput {
source: string
target: string
zone: string
primaryAddress: string
targetAddress: string
tsigKeyName: string
}
export interface SecondaryPreview {
zone: string
source: string
target: string
warnings: string[]
steps: { server: string; role: 'source' | 'target'; summary: string; changes: boolean }[]
}
export const previewSecondary = (token: string | null, input: SecondaryInput): Promise<ApiOutcome<{ response: SecondaryPreview }>> =>
apiRequest('hub/zones/secondary/preview', { token, method: 'POST', body: { ...input } })
export const makeSecondary = (token: string | null, input: SecondaryInput): Promise<RunOutcome> =>
apiRequest('hub/zones/secondary', { token, method: 'POST', body: { ...input } })