3a1bfd943e
- ProxmoxServer/ProxmoxServerStatus mit RLS (tenant_isolation_policy +
system_read_policy auf ProxmoxServer fuer den kommenden Planer)
- proxmox-auth.ts (Token-Kopfzeilen PVE/PBS), proxmox-client.service.ts
(proxmoxGet, ausschliesslich lesend, Dispatcher je Aufruf aus
tlsRejectUnauthorized, nie global)
- proxmox.service.ts: Server anlegen (Geheimnis verschluesselt,
select ohne Geheimnisfelder), Serverliste, PVE-Abfrage mit
nachsichtiger Grundauswertung (Knoten/Gaeste)
- Controller/Modul/Seed nach Domaincheck-Vorbild, Kategorie
"infrastructure", @UseModule('proxmox') + @Roles auf Schreibwegen
- Modulseite (duenne Liste) + proxmox-api.ts + Registrierung in
MODULE_REGISTRY
- Zugriffsklassifikation nachgezogen (rls-access-inventory.spec.ts gruen)
Tore: api 1247/1247 (>=1240), web 693/693, type-check 4/4, lint 5/5,
Biome apps/web 53 Warnungen (unveraendert).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
/**
|
|
* Proxmox Module API client (260923-dhh). Konsumiert `/modules/proxmox/*`.
|
|
* Vorbild `dkv-api.ts`: `credentials: 'include'` fuer Cookie-Auth,
|
|
* `NEXT_PUBLIC_API_URL` als Basis.
|
|
*
|
|
* Sicherheit (T-DHH-01): keine Antwort dieses Clients enthaelt jemals ein
|
|
* Geheimnisfeld — der Server waehlt `encryptedTokenSecret`/`encryptedPassword`
|
|
* per `select` gar nicht erst aus.
|
|
*/
|
|
|
|
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
|
|
|
export type ProxmoxProductType = 'pve' | 'pbs' | 'pmg';
|
|
export type ProxmoxAuthMethod = 'token' | 'password';
|
|
export type ProxmoxErrorKind =
|
|
| 'netz'
|
|
| 'zugang'
|
|
| 'rechte'
|
|
| 'zertifikat'
|
|
| 'antwortform'
|
|
| 'server'
|
|
| 'unbekannt';
|
|
|
|
export interface ProxmoxServerStatus {
|
|
id: string;
|
|
serverId: string;
|
|
lastPolledAt: string | null;
|
|
lastOkAt: string | null;
|
|
reachable: boolean;
|
|
errorKind: ProxmoxErrorKind | null;
|
|
errorDetail: string | null;
|
|
metrics: unknown;
|
|
rawSample: unknown;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface ProxmoxServer {
|
|
id: string;
|
|
tenantId: string;
|
|
name: string;
|
|
productType: ProxmoxProductType;
|
|
baseUrl: string;
|
|
authMethod: ProxmoxAuthMethod;
|
|
tokenId: string | null;
|
|
username: string | null;
|
|
tlsRejectUnauthorized: boolean;
|
|
isActive: boolean;
|
|
pollIntervalMin: number;
|
|
position: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
status: ProxmoxServerStatus | null;
|
|
}
|
|
|
|
export interface CreateProxmoxServerPayload {
|
|
name: string;
|
|
productType: ProxmoxProductType;
|
|
baseUrl: string;
|
|
authMethod: ProxmoxAuthMethod;
|
|
tokenId?: string;
|
|
tokenSecret?: string;
|
|
username?: string;
|
|
password?: string;
|
|
tlsRejectUnauthorized?: boolean;
|
|
pollIntervalMin?: number;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export type UpdateProxmoxServerPayload = Partial<CreateProxmoxServerPayload>;
|
|
|
|
/** GET /modules/proxmox/servers — Serverliste samt Zwischenlager. */
|
|
export async function listServers(): Promise<ProxmoxServer[]> {
|
|
const res = await fetch(`${API_URL}/modules/proxmox/servers`, {
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to fetch proxmox servers');
|
|
return res.json();
|
|
}
|
|
|
|
/** POST /modules/proxmox/servers — Server anlegen (ADMIN/SUPER_ADMIN). */
|
|
export async function createServer(
|
|
payload: CreateProxmoxServerPayload,
|
|
): Promise<ProxmoxServer> {
|
|
const res = await fetch(`${API_URL}/modules/proxmox/servers`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) throw new Error('Failed to create proxmox server');
|
|
return res.json();
|
|
}
|
|
|
|
/** POST /modules/proxmox/servers/:id/poll — sofortige Abfrage (ADMIN/SUPER_ADMIN). */
|
|
export async function pollServer(id: string): Promise<unknown> {
|
|
const res = await fetch(`${API_URL}/modules/proxmox/servers/${id}/poll`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to poll proxmox server');
|
|
return res.json();
|
|
}
|