dev_arc_aws/backend/src/integrations/proxmox.ts
Claude 57f53a3ab4
Add Proxmox integration adapter with real resource listing
Implements testConnection (via /api2/json/version) and listResources
(via /api2/json/cluster/resources) using Proxmox's API token auth header,
following the same pattern as the Docker adapter. Verified end-to-end:
graceful failure against an unreachable host, correct event logging, and
exclusion from the resources endpoint when not connected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF
2026-06-18 20:12:40 +00:00

43 lines
1.6 KiB
TypeScript

import type { IntegrationAdapter, Resource } from './types.js'
interface ProxmoxResourceEntry {
type: string
name?: string
status?: string
vmid?: number
node?: string
}
function authHeader(apiKey: string): Record<string, string> {
return { Authorization: `PVEAPIToken=${apiKey}` }
}
export const proxmox: IntegrationAdapter = {
async testConnection(config, secrets) {
const baseUrl = config.baseUrl?.replace(/\/$/, '')
const apiKey = secrets.apiKey
if (!baseUrl) return { ok: false, message: 'Missing baseUrl' }
if (!apiKey) return { ok: false, message: 'Missing API token' }
try {
const res = await fetch(`${baseUrl}/api2/json/version`, { headers: authHeader(apiKey) })
if (!res.ok) return { ok: false, message: `HTTP ${res.status}` }
return { ok: true, message: 'Connected' }
} catch (err) {
return { ok: false, message: err instanceof Error ? err.message : 'Connection failed' }
}
},
async listResources(config, secrets): Promise<Resource[]> {
const baseUrl = config.baseUrl?.replace(/\/$/, '')
const apiKey = secrets.apiKey
if (!baseUrl || !apiKey) return []
const res = await fetch(`${baseUrl}/api2/json/cluster/resources?type=vm`, { headers: authHeader(apiKey) })
if (!res.ok) return []
const body = (await res.json()) as { data: ProxmoxResourceEntry[] }
return body.data.map((entry) => ({
name: entry.name ?? `vm-${entry.vmid}`,
status: entry.status === 'running' ? 'healthy' : entry.status === 'stopped' ? 'unknown' : 'warning',
detail: `${entry.type} on ${entry.node}${entry.status}`,
}))
},
}