chore: weitere Code-Aufteilung (api.ts, hooks, ldap_sync)

- src/lib/api.ts (1085 Zeilen) → 5 thematische Module unter src/lib/api/
  (core, users, ldap, tenants, mail, system) + index.ts Re-Export
- useLDAPConfig / useTenantLDAPConfig / useTenantUsers Hooks extrahiert;
  admin/page.tsx nutzt diese statt roher useState-Blöcke
- handleSyncTenantLDAP, handleAdminSyncTenantLDAP, doSyncTenantLDAP,
  buildTenantTestConfig, syncResult aus ldap_tenants.go in ldap_sync.go verschoben

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-03-20 13:05:19 +01:00
parent bc4a98de0d
commit a6a66beaa8
14 changed files with 1797 additions and 1466 deletions
+206
View File
@@ -0,0 +1,206 @@
import { API_BASE, request } from "./core";
import { User } from "./users";
import { LDAPConfig, LDAPTestResult } from "./ldap";
// ── Types ────────────────────────────────────────────────────────────────────
export interface Tenant {
id: number;
name: string;
slug: string;
active: boolean;
created_at: string;
domain_count?: number;
user_count?: number;
ldap_enabled?: boolean;
ldap_url?: string;
has_logo?: boolean;
}
export interface TenantDomain {
id: number;
tenant_id: number;
domain: string;
created_at: string;
}
export interface TenantDefaultUser {
username: string;
password: string;
role: string;
}
export interface CreateTenantResponse extends Tenant {
default_users: TenantDefaultUser[];
}
export interface TenantLDAPConfig extends LDAPConfig {
tenant_id?: number;
}
export interface LDAPSyncResult {
synced: number;
errors: string[];
}
// ── Tenant CRUD ───────────────────────────────────────────────────────────────
export async function getTenants(): Promise<Tenant[]> {
return request<Tenant[]>("/api/tenants");
}
export async function getTenantUsers(tenantId: number): Promise<User[]> {
return request<User[]>(`/api/tenants/${tenantId}/users`);
}
export async function createTenant(name: string, slug: string): Promise<CreateTenantResponse> {
return request<CreateTenantResponse>("/api/tenants", {
method: "POST",
body: JSON.stringify({ name, slug }),
});
}
export async function updateTenant(
id: number,
data: { name?: string; active?: boolean }
): Promise<Tenant> {
return request<Tenant>(`/api/tenants/${id}`, {
method: "PATCH",
body: JSON.stringify(data),
});
}
export async function deleteTenant(id: number): Promise<void> {
await request<void>(`/api/tenants/${id}`, { method: "DELETE" });
}
// ── Tenant Domains ────────────────────────────────────────────────────────────
export async function getTenantDomains(id: number): Promise<TenantDomain[]> {
return request<TenantDomain[]>(`/api/tenants/${id}/domains`);
}
export async function addTenantDomain(
tenantId: number,
domain: string
): Promise<TenantDomain> {
return request<TenantDomain>(`/api/tenants/${tenantId}/domains`, {
method: "POST",
body: JSON.stringify({ domain }),
});
}
export async function removeTenantDomain(
tenantId: number,
domainId: number
): Promise<void> {
await request<void>(`/api/tenants/${tenantId}/domains/${domainId}`, {
method: "DELETE",
});
}
// ── Tenant Logo ───────────────────────────────────────────────────────────────
export function getTenantLogoUrl(tenantId: number): string {
return `${API_BASE}/api/tenants/${tenantId}/logo`;
}
export async function uploadTenantLogo(tenantId: number, file: File): Promise<void> {
const form = new FormData();
form.append("logo", file);
const res = await fetch(`${API_BASE}/api/tenants/${tenantId}/logo`, {
method: "POST",
body: form,
credentials: "include",
});
if (!res.ok) {
const body = await res.text();
throw new Error(body || `Upload failed: ${res.status}`);
}
}
export async function deleteTenantLogo(tenantId: number): Promise<void> {
await request<void>(`/api/tenants/${tenantId}/logo`, { method: "DELETE" });
}
// domain_admin: own tenant logo
export async function uploadMyTenantLogo(file: File): Promise<void> {
const form = new FormData();
form.append("logo", file);
const res = await fetch(`${API_BASE}/api/tenant/logo`, {
method: "POST",
body: form,
credentials: "include",
});
if (!res.ok) {
const body = await res.text();
throw new Error(body || `Upload failed: ${res.status}`);
}
}
export async function deleteMyTenantLogo(): Promise<void> {
await request<void>("/api/tenant/logo", { method: "DELETE" });
}
// ── Per-Tenant LDAP (domain_admin: own tenant) ────────────────────────────────
export async function getTenantLDAPConfig(): Promise<TenantLDAPConfig | null> {
try {
return await request<TenantLDAPConfig>("/api/tenant/ldap");
} catch (e: unknown) {
if (e instanceof Error && e.message.includes("404")) return null;
if (e instanceof Error && e.message.includes("no ldap config")) return null;
throw e;
}
}
export async function saveTenantLDAPConfig(cfg: Partial<TenantLDAPConfig>): Promise<void> {
await request<void>("/api/tenant/ldap", { method: "PUT", body: JSON.stringify(cfg) });
}
export async function deleteTenantLDAPConfig(): Promise<void> {
await request<void>("/api/tenant/ldap", { method: "DELETE" });
}
export async function testTenantLDAPConfig(
payload: { use_saved: boolean } & Partial<TenantLDAPConfig>
): Promise<LDAPTestResult> {
return request<LDAPTestResult>("/api/tenant/ldap/test", {
method: "POST",
body: JSON.stringify(payload),
});
}
// ── Per-Tenant LDAP (superadmin: arbitrary tenant) ────────────────────────────
export async function getAdminTenantLDAPConfig(tenantID: number): Promise<TenantLDAPConfig | null> {
try {
return await request<TenantLDAPConfig>(`/api/admin/tenants/${tenantID}/ldap`);
} catch (e: unknown) {
if (e instanceof Error && e.message.includes("404")) return null;
if (e instanceof Error && e.message.includes("no ldap config")) return null;
throw e;
}
}
export async function saveAdminTenantLDAPConfig(tenantID: number, cfg: Partial<TenantLDAPConfig>): Promise<void> {
await request<void>(`/api/admin/tenants/${tenantID}/ldap`, { method: "PUT", body: JSON.stringify(cfg) });
}
export async function deleteAdminTenantLDAPConfig(tenantID: number): Promise<void> {
await request<void>(`/api/admin/tenants/${tenantID}/ldap`, { method: "DELETE" });
}
export async function testAdminTenantLDAPConfig(
tenantID: number,
payload: { use_saved: boolean } & Partial<TenantLDAPConfig>
): Promise<LDAPTestResult> {
return request<LDAPTestResult>(`/api/admin/tenants/${tenantID}/ldap/test`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function syncAdminTenantLDAP(tenantID: number): Promise<LDAPSyncResult> {
return request<LDAPSyncResult>(`/api/admin/tenants/${tenantID}/ldap/sync`, { method: "POST", body: "{}" });
}