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
+57 -256
View File
@@ -2,6 +2,9 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { useAuth } from "@/hooks/useAuth";
import { useLDAPConfig } from "@/hooks/useLDAPConfig";
import { useTenantLDAPConfig } from "@/hooks/useTenantLDAPConfig";
import { useTenantUsers } from "@/hooks/useTenantUsers";
import {
getUsers,
createUser,
@@ -18,24 +21,13 @@ import {
getUploadProgress,
getSecurityAudit,
fixSecurityIssue,
getLDAPConfig,
saveLDAPConfig,
deleteLDAPConfig,
testLDAPConfig,
getTenants,
getTenantUsers,
createTenant,
updateTenant,
deleteTenant,
getTenantDomains,
addTenantDomain,
removeTenantDomain,
getTenantLDAPConfig,
saveTenantLDAPConfig,
deleteTenantLDAPConfig,
testTenantLDAPConfig,
syncAdminTenantLDAP,
type LDAPSyncResult,
getAdminLabels,
createAdminLabel,
deleteAdminLabel,
@@ -55,9 +47,6 @@ import {
type SystemStats,
type UploadJob,
type SecurityAuditResult,
type LDAPConfig,
type LDAPTestResult,
type TenantLDAPConfig,
type Tenant,
type TenantDefaultUser,
type TenantDomain,
@@ -155,26 +144,23 @@ export default function AdminPage() {
const [uploadLoading, setUploadLoading] = useState(false);
const uploadPollRef = useRef<ReturnType<typeof setInterval> | null>(null);
// LDAP state
const [ldapConfig, setLdapConfig] = useState<LDAPConfig | null>(null);
const [ldapLoading, setLdapLoading] = useState(false);
const [ldapSaving, setLdapSaving] = useState(false);
const [ldapTesting, setLdapTesting] = useState(false);
const [ldapError, setLdapError] = useState("");
const [ldapTestResult, setLdapTestResult] = useState<LDAPTestResult | null>(null);
const [ldapForm, setLdapForm] = useState<LDAPConfig>({
enabled: false,
url: "ldap://",
bind_dn: "",
bind_password: "",
base_dn: "",
user_filter: "(sAMAccountName=%s)",
tls: false,
tls_skip_verify: false,
default_role: "user",
group_mappings: [],
});
const [ldapChangePassword, setLdapChangePassword] = useState(false);
// LDAP state (global, superadmin) — managed by useLDAPConfig hook
const {
ldapConfig,
ldapLoading,
ldapSaving,
ldapTesting,
ldapError,
ldapTestResult,
ldapForm,
setLdapForm,
ldapChangePassword,
setLdapChangePassword,
loadLDAP,
handleSaveLDAP,
handleTestLDAP,
handleDeleteLDAP,
} = useLDAPConfig();
// Tenants state
const [tenants, setTenants] = useState<Tenant[]>([]);
@@ -197,26 +183,29 @@ export default function AdminPage() {
const [addDomainLoading, setAddDomainLoading] = useState(false);
const [domainError, setDomainError] = useState("");
// Tenant LDAP state (domain_admin own tenant)
const [tenantLdapConfig, setTenantLdapConfig] = useState<TenantLDAPConfig | null>(null);
const [tenantLdapLoading, setTenantLdapLoading] = useState(false);
const [tenantLdapSaving, setTenantLdapSaving] = useState(false);
const [tenantLdapTesting, setTenantLdapTesting] = useState(false);
const [tenantLdapError, setTenantLdapError] = useState("");
const [tenantLdapTestResult, setTenantLdapTestResult] = useState<LDAPTestResult | null>(null);
const [tenantLdapForm, setTenantLdapForm] = useState<TenantLDAPConfig>({
enabled: false,
url: "ldap://",
bind_dn: "",
bind_password: "",
base_dn: "",
user_filter: "(sAMAccountName=%s)",
tls: false,
tls_skip_verify: false,
default_role: "user",
group_mappings: [],
});
const [tenantLdapChangePassword, setTenantLdapChangePassword] = useState(false);
// Tenant LDAP state (domain_admin own tenant) — managed by useTenantLDAPConfig hook
const {
tenantLdapConfig,
tenantLdapLoading,
tenantLdapSaving,
tenantLdapTesting,
tenantLdapError,
tenantLdapTestResult,
tenantLdapForm,
setTenantLdapForm,
tenantLdapChangePassword,
setTenantLdapChangePassword,
ownLogoPreviewUrl,
setOwnLogoPreviewUrl,
ownLogoUploading,
setOwnLogoUploading,
ownLogoError,
setOwnLogoError,
loadTenantLDAP,
handleSaveTenantLDAP,
handleTestTenantLDAP,
handleDeleteTenantLDAP,
} = useTenantLDAPConfig();
// Superadmin: tenant LDAP dialog
const [tenantLdapDialogId, setTenantLdapDialogId] = useState<number | null>(null);
@@ -227,20 +216,20 @@ export default function AdminPage() {
const [logoUploading, setLogoUploading] = useState(false);
const [logoError, setLogoError] = useState("");
// Logo for domain_admin own tenant
const [ownLogoPreviewUrl, setOwnLogoPreviewUrl] = useState<string | null>(null);
const [ownLogoUploading, setOwnLogoUploading] = useState(false);
const [ownLogoError, setOwnLogoError] = useState("");
// Tenant users dialog
const [tenantUsersDialogId, setTenantUsersDialogId] = useState<number | null>(null);
const [tenantUsersDialogName, setTenantUsersDialogName] = useState("");
const [tenantUsersDialogLdap, setTenantUsersDialogLdap] = useState(false);
const [tenantUsers, setTenantUsers] = useState<User[]>([]);
const [tenantUsersLoading, setTenantUsersLoading] = useState(false);
const [tenantUsersError, setTenantUsersError] = useState("");
const [tenantUsersSyncing, setTenantUsersSyncing] = useState(false);
const [tenantUsersSyncResult, setTenantUsersSyncResult] = useState<LDAPSyncResult | null>(null);
// Tenant users dialog — managed by useTenantUsers hook
const {
tenantUsersDialogId,
setTenantUsersDialogId,
tenantUsersDialogName,
tenantUsersDialogLdap,
tenantUsers,
tenantUsersLoading,
tenantUsersError,
tenantUsersSyncing,
tenantUsersSyncResult,
openUsersDialog,
handleSyncLDAPUsers,
} = useTenantUsers();
// Labels state
const [adminLabels, setAdminLabels] = useState<MailLabel[]>([]);
@@ -528,78 +517,6 @@ export default function AdminPage() {
}
}
// LDAP handlers
const loadLDAP = useCallback(async () => {
setLdapLoading(true);
setLdapError("");
try {
const cfg = await getLDAPConfig();
if (cfg) {
setLdapConfig(cfg);
setLdapForm({ ...cfg, bind_password: "" });
setLdapChangePassword(false);
}
} catch {
setLdapError("LDAP-Konfiguration konnte nicht geladen werden.");
} finally {
setLdapLoading(false);
}
}, []);
async function handleSaveLDAP(e: React.FormEvent) {
e.preventDefault();
setLdapSaving(true);
setLdapError("");
try {
const payload: Partial<LDAPConfig> = { ...ldapForm };
if (!ldapChangePassword) {
delete payload.bind_password;
}
await saveLDAPConfig(payload);
await loadLDAP();
} catch (err: unknown) {
setLdapError(err instanceof Error ? err.message : "Speichern fehlgeschlagen.");
} finally {
setLdapSaving(false);
}
}
async function handleTestLDAP() {
setLdapTesting(true);
setLdapError("");
setLdapTestResult(null);
try {
const payload = ldapConfig
? { use_saved: true }
: { use_saved: false, ...ldapForm };
const result = await testLDAPConfig(payload as Parameters<typeof testLDAPConfig>[0]);
setLdapTestResult(result);
} catch (err: unknown) {
setLdapError(err instanceof Error ? err.message : "Test fehlgeschlagen.");
} finally {
setLdapTesting(false);
}
}
async function handleDeleteLDAP() {
setLdapSaving(true);
setLdapError("");
try {
await deleteLDAPConfig();
setLdapConfig(null);
setLdapForm({
enabled: false, url: "ldap://", bind_dn: "", bind_password: "",
base_dn: "", user_filter: "(sAMAccountName=%s)", tls: false,
tls_skip_verify: false, default_role: "user", group_mappings: [],
});
setLdapTestResult(null);
} catch (err: unknown) {
setLdapError(err instanceof Error ? err.message : "Löschen fehlgeschlagen.");
} finally {
setLdapSaving(false);
}
}
// Tenants handlers
const loadTenants = useCallback(async () => {
setTenantsLoading(true);
@@ -747,40 +664,6 @@ export default function AdminPage() {
finally { setDomainsLoading(false); }
}
async function openUsersDialog(t: Tenant) {
setTenantUsersDialogId(t.id);
setTenantUsersDialogName(t.name);
setTenantUsersDialogLdap(t.ldap_enabled === true);
setTenantUsersLoading(true);
setTenantUsers([]);
setTenantUsersError("");
setTenantUsersSyncResult(null);
try {
const users = await getTenantUsers(t.id);
setTenantUsers(users || []);
} catch (err: unknown) {
setTenantUsersError(err instanceof Error ? err.message : "Nutzer konnten nicht geladen werden.");
} finally {
setTenantUsersLoading(false);
}
}
async function handleSyncLDAPUsers() {
if (!tenantUsersDialogId) return;
setTenantUsersSyncing(true);
setTenantUsersSyncResult(null);
try {
const result = await syncAdminTenantLDAP(tenantUsersDialogId);
setTenantUsersSyncResult(result);
const users = await getTenantUsers(tenantUsersDialogId);
setTenantUsers(users || []);
} catch (err: unknown) {
setTenantUsersSyncResult({ synced: 0, errors: [err instanceof Error ? err.message : "Sync fehlgeschlagen"] });
} finally {
setTenantUsersSyncing(false);
}
}
async function handleAddDomain() {
if (!domainDialogTenant || !newDomain) return;
setAddDomainLoading(true);
@@ -892,88 +775,6 @@ export default function AdminPage() {
}
}
// Tenant LDAP handlers (domain_admin)
const loadTenantLDAP = useCallback(async () => {
setTenantLdapLoading(true);
setTenantLdapError("");
try {
const cfg = await getTenantLDAPConfig();
if (cfg) {
setTenantLdapConfig(cfg);
setTenantLdapForm({ ...cfg, bind_password: "" });
setTenantLdapChangePassword(false);
}
} catch {
setTenantLdapError("LDAP-Konfiguration konnte nicht geladen werden.");
} finally {
setTenantLdapLoading(false);
}
// Load own tenant logo preview
try {
const res = await fetch("/api/tenant/logo", { credentials: "include" });
if (res.ok) {
const blob = await res.blob();
setOwnLogoPreviewUrl(URL.createObjectURL(blob));
}
} catch {
// no logo or not available
}
}, []);
async function handleSaveTenantLDAP(e: React.FormEvent) {
e.preventDefault();
setTenantLdapSaving(true);
setTenantLdapError("");
try {
const payload: Partial<TenantLDAPConfig> = { ...tenantLdapForm };
if (!tenantLdapChangePassword) {
delete payload.bind_password;
}
await saveTenantLDAPConfig(payload);
await loadTenantLDAP();
} catch (err: unknown) {
setTenantLdapError(err instanceof Error ? err.message : "Speichern fehlgeschlagen.");
} finally {
setTenantLdapSaving(false);
}
}
async function handleTestTenantLDAP() {
setTenantLdapTesting(true);
setTenantLdapError("");
setTenantLdapTestResult(null);
try {
const payload = tenantLdapConfig
? { use_saved: true }
: { use_saved: false, ...tenantLdapForm };
const result = await testTenantLDAPConfig(payload as Parameters<typeof testTenantLDAPConfig>[0]);
setTenantLdapTestResult(result);
} catch (err: unknown) {
setTenantLdapError(err instanceof Error ? err.message : "Test fehlgeschlagen.");
} finally {
setTenantLdapTesting(false);
}
}
async function handleDeleteTenantLDAP() {
setTenantLdapSaving(true);
setTenantLdapError("");
try {
await deleteTenantLDAPConfig();
setTenantLdapConfig(null);
setTenantLdapForm({
enabled: false, url: "ldap://", bind_dn: "", bind_password: "",
base_dn: "", user_filter: "(sAMAccountName=%s)", tls: false,
tls_skip_verify: false, default_role: "user", group_mappings: [],
});
setTenantLdapTestResult(null);
} catch (err: unknown) {
setTenantLdapError(err instanceof Error ? err.message : "Löschen fehlgeschlagen.");
} finally {
setTenantLdapSaving(false);
}
}
return (
<div className="min-h-screen">
<Navbar username={user?.username ?? ""} role={user?.role ?? ""} />