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:
co-authored by
Claude Sonnet 4.6
parent
bc4a98de0d
commit
a6a66beaa8
@@ -0,0 +1,322 @@
|
||||
import { API_BASE, request } from "./core";
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface HealthResponse {
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface SMTPStatus {
|
||||
// global daemon fields (superadmin)
|
||||
running?: boolean;
|
||||
enabled?: boolean;
|
||||
bind?: string;
|
||||
domain?: string;
|
||||
tls?: boolean;
|
||||
max_size_mb?: number;
|
||||
allowed_ips?: string[];
|
||||
received?: number;
|
||||
rejected?: number;
|
||||
last_mail_at?: string;
|
||||
// tenant-scoped fields (domain_admin)
|
||||
tenant_only?: boolean;
|
||||
domains?: string[];
|
||||
total_mails?: number;
|
||||
total_bytes?: number;
|
||||
}
|
||||
|
||||
export interface StorageStats {
|
||||
total_mails: number;
|
||||
total_bytes: number;
|
||||
}
|
||||
|
||||
export interface ServiceStatus {
|
||||
name: string;
|
||||
display_name: string;
|
||||
active: string;
|
||||
sub: string;
|
||||
enabled: string;
|
||||
description: string;
|
||||
external_blocked?: boolean;
|
||||
}
|
||||
|
||||
export interface AuditEntry {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
event_type: string;
|
||||
username: string;
|
||||
detail: string;
|
||||
}
|
||||
|
||||
export interface AuditResponse {
|
||||
total: number;
|
||||
entries: AuditEntry[];
|
||||
}
|
||||
|
||||
export interface SystemStatsCPU {
|
||||
load1: number;
|
||||
load5: number;
|
||||
load15: number;
|
||||
num_cpu: number;
|
||||
}
|
||||
|
||||
export interface SystemStatsRAM {
|
||||
total_bytes: number;
|
||||
used_bytes: number;
|
||||
free_bytes: number;
|
||||
used_pct: number;
|
||||
}
|
||||
|
||||
export interface SystemStatsDisk {
|
||||
mount: string;
|
||||
total_bytes: number;
|
||||
used_bytes: number;
|
||||
free_bytes: number;
|
||||
used_pct: number;
|
||||
fstype: string;
|
||||
}
|
||||
|
||||
export interface SystemStatsMailInfo {
|
||||
id: string;
|
||||
date: string;
|
||||
from: string;
|
||||
subject: string;
|
||||
}
|
||||
|
||||
export interface SystemStats {
|
||||
cpu: SystemStatsCPU;
|
||||
ram: SystemStatsRAM;
|
||||
disks: SystemStatsDisk[];
|
||||
archive: {
|
||||
first_mail: SystemStatsMailInfo | null;
|
||||
last_mail: SystemStatsMailInfo | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SecurityCheck {
|
||||
name: string;
|
||||
status: "ok" | "warning" | "error";
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface SecurityAuditResult {
|
||||
checks: SecurityCheck[];
|
||||
run_at: string;
|
||||
}
|
||||
|
||||
export interface MailLabel {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string;
|
||||
owner_id?: number;
|
||||
tenant_id: number;
|
||||
is_global: boolean;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface LabelRule {
|
||||
id: number;
|
||||
condition_field: "from_domain" | "source" | "subject_contains";
|
||||
condition_value: string;
|
||||
label_id: number;
|
||||
tenant_id: number;
|
||||
}
|
||||
|
||||
export interface CertInfo {
|
||||
exists: boolean;
|
||||
subject?: string;
|
||||
issuer?: string;
|
||||
not_before?: string;
|
||||
not_after?: string;
|
||||
dns_names?: string[];
|
||||
ip_addresses?: string[];
|
||||
fingerprint_sha256?: string;
|
||||
is_self_signed?: boolean;
|
||||
days_remaining?: number;
|
||||
}
|
||||
|
||||
export interface SelfSignedRequest {
|
||||
common_name: string;
|
||||
dns_names: string[];
|
||||
ip_addresses: string[];
|
||||
validity_years: number;
|
||||
}
|
||||
|
||||
export interface ACMERequest {
|
||||
domain: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
// ── Health & Stats ────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getHealth(): Promise<HealthResponse> {
|
||||
return request<HealthResponse>("/api/health");
|
||||
}
|
||||
|
||||
export async function getSMTPStatus(): Promise<SMTPStatus> {
|
||||
return request<SMTPStatus>("/api/admin/smtp/status");
|
||||
}
|
||||
|
||||
export async function getStorageStats(): Promise<StorageStats> {
|
||||
return request<StorageStats>("/api/admin/storage/stats");
|
||||
}
|
||||
|
||||
export async function getSystemStats(): Promise<SystemStats> {
|
||||
return request<SystemStats>("/api/admin/system/stats");
|
||||
}
|
||||
|
||||
// ── Services ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getServices(): Promise<ServiceStatus[]> {
|
||||
return request<ServiceStatus[]>("/api/admin/services");
|
||||
}
|
||||
|
||||
export async function serviceAction(
|
||||
name: string,
|
||||
action: "start" | "stop" | "restart" | "enable" | "disable" | "block_external" | "allow_external"
|
||||
): Promise<ServiceStatus> {
|
||||
return request<ServiceStatus>(`/api/admin/services/${encodeURIComponent(name)}/action`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ action }),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Audit ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getAuditLog(params: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
username?: string;
|
||||
event_type?: string;
|
||||
}): Promise<AuditResponse> {
|
||||
const sp = new URLSearchParams();
|
||||
if (params.page) sp.set("page", String(params.page));
|
||||
if (params.page_size) sp.set("page_size", String(params.page_size));
|
||||
if (params.username) sp.set("username", params.username);
|
||||
if (params.event_type) sp.set("event_type", params.event_type);
|
||||
return request<AuditResponse>(`/api/audit?${sp.toString()}`);
|
||||
}
|
||||
|
||||
// ── Security ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getSecurityAudit(): Promise<SecurityAuditResult> {
|
||||
return request<SecurityAuditResult>("/api/admin/security/audit");
|
||||
}
|
||||
|
||||
export async function fixSecurityIssue(action: string): Promise<{ message: string }> {
|
||||
return request<{ message: string }>("/api/admin/security/fix", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ action }),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Labels ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getLabels(): Promise<MailLabel[]> {
|
||||
return request<MailLabel[]>("/api/labels");
|
||||
}
|
||||
|
||||
export async function createLabel(name: string, color: string): Promise<MailLabel> {
|
||||
return request<MailLabel>("/api/labels", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name, color }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateLabel(id: number, name: string, color: string): Promise<void> {
|
||||
return request<void>(`/api/labels/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ name, color }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteLabel(id: number): Promise<void> {
|
||||
return request<void>(`/api/labels/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export async function assignLabel(emailId: string, labelId: number): Promise<void> {
|
||||
return request<void>(`/api/mails/${emailId}/labels`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ label_id: labelId }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeLabelFromEmail(emailId: string, labelId: number): Promise<void> {
|
||||
return request<void>(`/api/mails/${emailId}/labels/${labelId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
export async function getMailLabelIds(emailId: string): Promise<number[]> {
|
||||
return request<number[]>(`/api/mails/${emailId}/labels`);
|
||||
}
|
||||
|
||||
export async function createAdminLabel(name: string, color: string): Promise<MailLabel> {
|
||||
return request<MailLabel>("/api/admin/labels", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name, color }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAdminLabels(): Promise<MailLabel[]> {
|
||||
return request<MailLabel[]>("/api/admin/labels");
|
||||
}
|
||||
|
||||
export async function deleteAdminLabel(id: number): Promise<void> {
|
||||
return request<void>(`/api/admin/labels/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export async function getLabelRules(): Promise<LabelRule[]> {
|
||||
return request<LabelRule[]>("/api/admin/label-rules");
|
||||
}
|
||||
|
||||
export async function createLabelRule(
|
||||
condition_field: string,
|
||||
condition_value: string,
|
||||
label_id: number
|
||||
): Promise<LabelRule> {
|
||||
return request<LabelRule>("/api/admin/label-rules", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ condition_field, condition_value, label_id }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteLabelRule(id: number): Promise<void> {
|
||||
return request<void>(`/api/admin/label-rules/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
// ── Certificates ──────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getCertInfo(): Promise<CertInfo> {
|
||||
return request<CertInfo>("/api/admin/cert/info");
|
||||
}
|
||||
|
||||
export async function uploadCert(cert: File, key: File): Promise<{ ok: boolean; message: string }> {
|
||||
const form = new FormData();
|
||||
form.append("cert", cert);
|
||||
form.append("key", key);
|
||||
const res = await fetch(`${API_BASE}/api/admin/cert/upload`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: form,
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.text();
|
||||
throw new Error(body || `Upload failed: ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function generateSelfSignedCert(req: SelfSignedRequest): Promise<CertInfo & { ok: boolean }> {
|
||||
return request<CertInfo & { ok: boolean }>("/api/admin/cert/self-signed", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
}
|
||||
|
||||
export async function requestACMECert(req: ACMERequest): Promise<{ ok: boolean; output: string }> {
|
||||
return request<{ ok: boolean; output: string }>("/api/admin/cert/acme", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user