const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "/api/v1"; export const AUTH_EXPIRED_EVENT = "nexavpn-admin-auth-expired"; export type User = { id: string; username: string; display_name: string; email?: string; role: string; is_active: boolean; }; export type Device = { id: string; name: string; user_id?: string; platform: string; status: string; assigned_ip?: string; }; export type DeviceProfile = { device: Device; peer: { assigned_ip: string; dns_servers: string[]; allowed_ips: string[]; gateway: { id: string; name: string; endpoint: string; public_key: string; }; profile_revision: number; }; profile: { format: string; content: string; }; resources: Array<{ type: string; value: string; label: string; }>; }; export type Policy = { id: string; name: string; description: string; priority: number; effect: string; full_tunnel: boolean; is_active: boolean; destinations?: string[]; }; export type Gateway = { id: string; name: string; endpoint: string; public_key: string; listen_port: number; vpn_cidr: string; dns_servers: string[]; is_active: boolean; }; export type AuditEvent = { id: string; event_type: string; entity_type: string; status: string; message: string; created_at: string; }; async function request(path: string, init?: RequestInit): Promise { const token = localStorage.getItem("nexavpn_admin_token"); const response = await fetch(`${API_BASE}${path}`, { ...init, headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(init?.headers ?? {}) } }); if (!response.ok) { if (response.status === 401) { localStorage.removeItem("nexavpn_admin_token"); window.dispatchEvent(new Event(AUTH_EXPIRED_EVENT)); } throw new Error(`Request failed: ${response.status}`); } return response.json() as Promise; } export const api = { users: () => request("/admin/users"), createUser: (payload: { username: string; display_name: string; email: string; password: string; role: string; }) => request("/admin/users", { method: "POST", body: JSON.stringify(payload) }), updateUser: (userId: string, payload: { display_name?: string; email?: string; role?: string; password?: string; is_active?: boolean; }) => request(`/admin/users/${userId}`, { method: "PATCH", body: JSON.stringify(payload) }), deleteUser: (userId: string) => request<{ ok: boolean }>(`/admin/users/${userId}`, { method: "DELETE" }), devices: () => request("/admin/devices"), deviceProfile: (deviceId: string) => request(`/admin/devices/${deviceId}/profile`), revokeDevice: (deviceId: string) => request<{ ok: boolean }>(`/admin/devices/${deviceId}/revoke`, { method: "POST", body: JSON.stringify({}) }), rotateDevice: (deviceId: string) => request<{ ok: boolean }>(`/admin/devices/${deviceId}/rotate`, { method: "POST", body: JSON.stringify({}) }), policies: () => request("/admin/policies"), createPolicy: (payload: { name: string; description: string; priority: number; effect: string; full_tunnel: boolean; destinations: string[]; targets: Array<{ type: string; id: string }>; }) => request("/admin/policies", { method: "POST", body: JSON.stringify(payload) }), updatePolicy: (policyId: string, payload: { name?: string; description?: string; priority?: number; effect?: string; full_tunnel?: boolean; is_active?: boolean; destinations?: string[]; targets?: Array<{ type: string; id: string }>; }) => request(`/admin/policies/${policyId}`, { method: "PATCH", body: JSON.stringify(payload) }), deletePolicy: (policyId: string) => request<{ ok: boolean }>(`/admin/policies/${policyId}`, { method: "DELETE" }), gateways: () => request("/admin/gateways"), updateGateway: (gatewayId: string, payload: { endpoint: string; public_key: string; listen_port: number; vpn_cidr: string; dns_servers: string[]; is_active: boolean; }) => request(`/admin/gateways/${gatewayId}`, { method: "PATCH", body: JSON.stringify(payload) }), gatewaySync: (gatewayId: string) => request(`/admin/gateways/${gatewayId}/sync`), audit: () => request("/admin/audit-logs") };