feat(domains): Kunden, Kontakte aus AutoDNS mit Zuordnung und Domainliste

- Kunden mit Markierung eigene Firma, Kontakte live aus AutoDNS (seitenweise, 60 s Zwischenspeicher), Zuordnung je System
- Kontakt anlegen (Person/Organisation) und einem oder mehreren Kunden zuordnen, nur mit Verwalten
- Domainliste mit Kunde (ueber den Inhaber-Kontakt), Inhaber, Ablaufdatum, Status; Filter und Gruppierung nach Kunde
- Mandantenschutz-Inventar um die beiden neuen Zugriffspaare ergaenzt

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 10:50:06 +02:00
parent 1f1c984184
commit 450218fe77
26 changed files with 3943 additions and 36 deletions
+122
View File
@@ -122,3 +122,125 @@ export function testDomainsConnection(
json: { environment },
});
}
// --- Kunden, Kontakte und Domains (quick-261008-dts, Aufgabe 2) ----------------
export interface DomainsCustomer {
id: string;
name: string;
isOwnCompany: boolean;
/** Zugeordnete Kontakte im AKTIVEN System. */
contactCount: number;
}
export interface DomainsCustomerInput {
name: string;
isOwnCompany?: boolean;
}
export type DomainsContactType = 'PERSON' | 'ORG';
export interface DomainsContact {
/** AutoDNS-Kontakt-Nummer. */
id: number;
type: string | null;
displayName: string;
fname: string | null;
lname: string | null;
organization: string | null;
address: string[];
pcode: string | null;
city: string | null;
country: string | null;
email: string | null;
phone: string | null;
alias: string | null;
customerId: string | null;
customerName: string | null;
}
export interface DomainsContactList {
environment: DomainsEnvironment;
contacts: DomainsContact[];
/** Es werden nur die ersten 2000 Eintraege geliefert. */
truncated: boolean;
fetchedAt: string;
}
export interface DomainsDomain {
name: string;
/** ISO-Zeitpunkt oder null. */
expire: string | null;
/** Rohwert von AutoDNS (`registryStatus`). */
status: string | null;
cancelationPending: boolean;
ownerContactId: number | null;
ownerName: string | null;
customerId: string | null;
customerName: string | null;
}
export interface DomainsDomainList {
environment: DomainsEnvironment;
domains: DomainsDomain[];
truncated: boolean;
fetchedAt: string;
}
export interface CreateDomainsContactInput {
type: DomainsContactType;
organization?: string;
fname: string;
lname: string;
/** Anschriftzeilen (Strasse und Hausnummer, optional eine zweite Zeile). */
street: string[];
pcode: string;
city: string;
/** Laendercode nach ISO 3166-1 (zwei Grossbuchstaben). */
country: string;
email: string;
phone: string;
customerId?: string;
}
export function listCustomers(): Promise<DomainsCustomer[]> {
return request<DomainsCustomer[]>('/customers');
}
export function createCustomer(input: DomainsCustomerInput): Promise<DomainsCustomer> {
return request<DomainsCustomer>('/customers', { method: 'POST', json: input });
}
export function updateCustomer(id: string, input: DomainsCustomerInput): Promise<DomainsCustomer> {
return request<DomainsCustomer>(`/customers/${encodeURIComponent(id)}`, {
method: 'PUT',
json: input,
});
}
export function deleteCustomer(id: string): Promise<{ deleted: true }> {
return request<{ deleted: true }>(`/customers/${encodeURIComponent(id)}`, { method: 'DELETE' });
}
export function listContacts(opts: { refresh?: boolean } = {}): Promise<DomainsContactList> {
return request<DomainsContactList>(`/contacts${opts.refresh ? '?refresh=1' : ''}`);
}
export function createContact(input: CreateDomainsContactInput): Promise<DomainsContact> {
return request<DomainsContact>('/contacts', { method: 'POST', json: input });
}
/** `customerId: null` loest die Zuordnung. */
export function assignContacts(
contactIds: number[],
customerId: string | null,
): Promise<{ assigned: number; removed: number }> {
return request<{ assigned: number; removed: number }>('/contacts/assign', {
method: 'POST',
json: { contactIds, customerId },
});
}
export function listDomains(opts: { refresh?: boolean } = {}): Promise<DomainsDomainList> {
return request<DomainsDomainList>(`/domains${opts.refresh ? '?refresh=1' : ''}`);
}