a6a66beaa8
- 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>
207 lines
6.7 KiB
TypeScript
207 lines
6.7 KiB
TypeScript
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: "{}" });
|
|
}
|