feat(domains): Domain registrieren mit Schutz vor Doppelbestellung, Aufträge, Changelog und Anleitung

- Verfügbarkeitsprüfung über DomainStudio, Entwurf mit Kontakten und Nameservern, Zusammenfassung mit System und Preis
- Verbindliches Abschicken: atomarer Anspruch DRAFT -> SUBMITTING vor dem Netzaufruf, genau ein POST /domain ohne Wiederholung, unklarer Ausgang als UNKNOWN, Bindung an das System
- Aufträge: Stand aus dem AutoDNS-Auftrag, Abgleich unklarer Aufträge, Verwerfen erst nach Abgleich
- Reiter Registrieren und Aufträge, Changelog, Anwender- und Administrationshandbuch, Mandantenschutz-Inventar

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 11:10:03 +02:00
parent 450218fe77
commit d332246bde
28 changed files with 4330 additions and 15 deletions
+96
View File
@@ -244,3 +244,99 @@ export function assignContacts(
export function listDomains(opts: { refresh?: boolean } = {}): Promise<DomainsDomainList> {
return request<DomainsDomainList>(`/domains${opts.refresh ? '?refresh=1' : ''}`);
}
// --- Verfuegbarkeit, Registrierung und Auftraege (quick-261008-dts, Aufgabe 3) ----
export type DomainOrderStatus =
| 'DRAFT'
| 'SUBMITTING'
| 'SUBMITTED'
| 'SUCCESS'
| 'FAILED'
| 'UNKNOWN'
| 'CANCELED';
export interface OrderPrice {
amount: number;
currency: string;
}
export interface AvailabilityResult {
environment: DomainsEnvironment;
/** ASCII-Schreibweise (Punycode) — so geht sie an AutoDNS. */
domain: string;
domainUnicode: string;
/** Nur `FREE` ist bestellbar. */
available: boolean;
whoisStatus: string;
/** Preis fuer ein Jahr; `null`, wenn AutoDNS keinen nennt. */
price: OrderPrice | null;
}
export interface DomainsOrder {
id: string;
environment: DomainsEnvironment;
domainName: string;
domainNameUnicode: string;
status: DomainOrderStatus;
jobStatus: string | null;
errorText: string | null;
confirmedByUsername: string | null;
confirmedAt: string | null;
createdAt: string;
lastCheckedAt: string | null;
price: OrderPrice | null;
}
export interface OrderContact {
id: number;
displayName: string;
}
/** Zusammenfassung eines Entwurfs — das, was der Benutzer verbindlich bestaetigt. */
export interface OrderSummary {
order: DomainsOrder;
contacts: {
owner: OrderContact;
admin: OrderContact;
tech: OrderContact;
zone: OrderContact;
};
nameServers: string[];
periodYears: 1;
}
export interface CreateOrderInput {
domain: string;
ownerContactId: number;
adminContactId: number;
techContactId: number;
zoneContactId: number;
nameServers: string[];
}
export function checkAvailability(domain: string): Promise<AvailabilityResult> {
return request<AvailabilityResult>('/availability', { method: 'POST', json: { domain } });
}
export function createOrder(input: CreateOrderInput): Promise<OrderSummary> {
return request<OrderSummary>('/orders', { method: 'POST', json: input });
}
/** Verbindlich: schickt die Registrierung an AutoDNS (kostet im Live-System Geld). */
export function submitOrder(id: string): Promise<DomainsOrder> {
return request<DomainsOrder>(`/orders/${encodeURIComponent(id)}/submit`, { method: 'POST' });
}
export function cancelOrder(id: string): Promise<DomainsOrder> {
return request<DomainsOrder>(`/orders/${encodeURIComponent(id)}/cancel`, { method: 'POST' });
}
export function listOrders(): Promise<DomainsOrder[]> {
return request<DomainsOrder[]>('/orders');
}
/** Gleicht offene Auftraege mit AutoDNS ab und liefert die aktuelle Liste. */
export function refreshOrders(): Promise<DomainsOrder[]> {
return request<DomainsOrder[]>('/orders/refresh', { method: 'POST' });
}