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
This commit is contained in:
parent
49c49635a9
commit
57f53a3ab4
2 changed files with 45 additions and 1 deletions
43
backend/src/integrations/proxmox.ts
Normal file
43
backend/src/integrations/proxmox.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
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}`,
|
||||
}))
|
||||
},
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import type { IntegrationAdapter, IntegrationType } from './types.js'
|
||||
import { uptimeKuma } from './uptimeKuma.js'
|
||||
import { docker } from './docker.js'
|
||||
import { proxmox } from './proxmox.js'
|
||||
|
||||
const notImplemented: IntegrationAdapter = {
|
||||
async testConnection() {
|
||||
|
|
@ -11,7 +12,7 @@ const notImplemented: IntegrationAdapter = {
|
|||
export const adapterRegistry: Record<IntegrationType, IntegrationAdapter> = {
|
||||
uptime_kuma: uptimeKuma,
|
||||
docker,
|
||||
proxmox: notImplemented,
|
||||
proxmox,
|
||||
netbird: notImplemented,
|
||||
cloudflare: notImplemented,
|
||||
aws: notImplemented,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue