UNPKG

n8n-nodes-proxmox

Version:

n8n community node for Proxmox Virtual Environment (VE) API integration with VM, container, storage, and cluster management capabilities

191 lines (164 loc) 5.26 kB
import { IExecuteFunctions, NodeOperationError, NodeApiError } from 'n8n-workflow'; /** * Handle ProxMox API errors with context-specific messages */ export function handleProxMoxError( this: IExecuteFunctions, error: any, operation: string, resource: string, ): never { let message = `Failed to ${operation} ${resource}`; if (error instanceof NodeApiError || error instanceof NodeOperationError) { throw error; } // Handle specific ProxMox error cases if (error.response) { const statusCode = error.statusCode || error.response.status; const errorBody = error.error || error.response.body || error.response.data; switch (statusCode) { case 400: message = `Bad request: Invalid parameters for ${operation} ${resource}`; if (errorBody?.errors) { const errors = Array.isArray(errorBody.errors) ? errorBody.errors.join(', ') : JSON.stringify(errorBody.errors); message += ` - ${errors}`; } break; case 401: message = 'Authentication failed. Please check your ProxMox credentials.'; break; case 403: message = `Permission denied. User lacks privileges to ${operation} ${resource}.`; break; case 404: message = `${resource} not found. Please check the resource ID or name.`; break; case 409: message = `Conflict: ${resource} already exists or is in an incompatible state.`; break; case 422: message = `Validation error for ${operation} ${resource}`; if (errorBody?.errors) { const errors = Array.isArray(errorBody.errors) ? errorBody.errors.join(', ') : JSON.stringify(errorBody.errors); message += ` - ${errors}`; } break; case 500: message = `ProxMox server error during ${operation} ${resource}. Please try again later.`; break; case 503: message = `ProxMox service unavailable. The node may be busy or under maintenance.`; break; default: if (errorBody) { if (typeof errorBody === 'object') { if (errorBody.message) { message = errorBody.message; } else if (errorBody.reason) { message = errorBody.reason; } else if (errorBody.errors) { const errors = Array.isArray(errorBody.errors) ? errorBody.errors.join(', ') : JSON.stringify(errorBody.errors); message = `Error ${operation} ${resource}: ${errors}`; } } else if (typeof errorBody === 'string') { message = errorBody; } } } } else if (error.message) { message = error.message; } throw new NodeOperationError(this.getNode(), message); } /** * Validate required parameters for ProxMox operations */ export function validateParameters( this: IExecuteFunctions, parameters: { [key: string]: any }, required: string[], operation: string, ): void { const missing = required.filter((param) => !parameters[param]); if (missing.length > 0) { throw new NodeOperationError( this.getNode(), `Missing required parameters for ${operation}: ${missing.join(', ')}`, ); } } /** * Validate ProxMox node name format */ export function validateNodeName(nodeName: string): boolean { // ProxMox node names should be valid hostnames const nodeNameRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/; return nodeNameRegex.test(nodeName) && nodeName.length <= 63; } /** * Validate ProxMox VM/Container ID format */ export function validateVmId(vmId: string | number): boolean { const id = typeof vmId === 'string' ? parseInt(vmId, 10) : vmId; return !isNaN(id) && id >= 100 && id <= 999999999; } /** * Validate ProxMox storage name format */ export function validateStorageName(storageName: string): boolean { // Storage names in ProxMox follow specific naming conventions const storageNameRegex = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/; return storageNameRegex.test(storageName) && storageName.length <= 64; } /** * Parse and validate ProxMox resource ID from various formats */ export function parseResourceId(resourceId: string): { node: string; type?: string; vmid?: number; storage?: string; } { // Handle different ProxMox resource ID formats: // - node/pve (node reference) // - node/qemu/vmid (VM reference) // - node/lxc/vmid (Container reference) // - node/storage/storagename (Storage reference) const parts = resourceId.split('/'); const result: { node: string; type?: string; vmid?: number; storage?: string } = { node: parts[0], }; if (parts.length >= 2) { result.type = parts[1]; if (parts.length >= 3) { if (parts[1] === 'qemu' || parts[1] === 'lxc') { const vmid = parseInt(parts[2], 10); if (isNaN(vmid)) { throw new Error(`Invalid VM/Container ID: ${parts[2]}`); } result.vmid = vmid; } else if (parts[1] === 'storage') { result.storage = parts[2]; } } } // Validate node name if (!validateNodeName(result.node)) { throw new Error(`Invalid node name: ${result.node}`); } // Validate VM ID if present if (result.vmid !== undefined && !validateVmId(result.vmid)) { throw new Error(`Invalid VM/Container ID: ${result.vmid}`); } // Validate storage name if present if (result.storage !== undefined && !validateStorageName(result.storage)) { throw new Error(`Invalid storage name: ${result.storage}`); } return result; }