UNPKG

n8n-nodes-proxmox

Version:

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

172 lines (171 loc) 7.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.apiRequest = apiRequest; exports.apiRequestAllItems = apiRequestAllItems; const n8n_workflow_1 = require("n8n-workflow"); const ticketCache = {}; /** * Check if a ticket is expired */ function isTicketExpired(expiresAt) { return Date.now() > expiresAt - 60000; // Consider expired 60 seconds before actual expiration } /** * Get authentication ticket for username/password auth */ async function getAuthTicket(credentials) { var _a, _b; const cacheKey = `${credentials.host}_${credentials.port}_${credentials.username}`; // Check cache first if (ticketCache[cacheKey] && !isTicketExpired(ticketCache[cacheKey].expiresAt)) { return { ticket: ticketCache[cacheKey].ticket, csrfToken: ticketCache[cacheKey].csrfToken, }; } // Get new ticket const protocol = credentials.skipSslVerify ? 'http' : 'https'; const baseUrl = `${protocol}://${credentials.host}:${credentials.port}`; try { const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/api2/json/access/ticket`, body: { username: credentials.username, password: credentials.password, }, json: true, rejectUnauthorized: !credentials.skipSslVerify, timeout: credentials.timeout || 30000, }); if (!((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.ticket) || !((_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.CSRFPreventionToken)) { throw new Error('Invalid authentication response from ProxMox'); } // Cache ticket (ProxMox tickets typically expire after 2 hours) const expiresAt = Date.now() + 2 * 60 * 60 * 1000; // 2 hours ticketCache[cacheKey] = { ticket: response.data.ticket, csrfToken: response.data.CSRFPreventionToken, expiresAt, }; return { ticket: response.data.ticket, csrfToken: response.data.CSRFPreventionToken, }; } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `ProxMox authentication failed: ${error.message}`); } } /** * Make an API request to ProxMox VE */ async function apiRequest(method, endpoint, body = {}, qs = {}) { var _a, _b, _c; const credentials = await this.getCredentials('proxMoxApi'); if (!credentials) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials provided'); } const protocol = credentials.skipSslVerify ? 'http' : 'https'; const baseUrl = `${protocol}://${credentials.host}:${credentials.port}`; const fullUrl = `${baseUrl}/api2/json${endpoint}`; let headers = { 'Content-Type': 'application/json', }; // Handle authentication if (credentials.authMethod === 'apiToken') { // API Token authentication - ProxMox expects 'PVEAPIToken=TOKEN' format const tokenValue = credentials.tokenId; // Check if token already has PVEAPIToken prefix if (tokenValue.startsWith('PVEAPIToken=')) { headers.Authorization = tokenValue; } else { headers.Authorization = `PVEAPIToken=${tokenValue}`; } } else { // Username/Password with ticket authentication const auth = await getAuthTicket.call(this, credentials); headers.Cookie = `PVEAuthCookie=${auth.ticket}`; // Add CSRF token for write operations if (method !== 'GET') { headers.CSRFPreventionToken = auth.csrfToken; } } const requestOptions = { method, url: fullUrl, headers, body: Object.keys(body).length > 0 ? body : undefined, qs: Object.keys(qs).length > 0 ? qs : undefined, json: true, rejectUnauthorized: !credentials.skipSslVerify, timeout: credentials.timeout || 30000, resolveWithFullResponse: true, }; try { const response = await this.helpers.httpRequest(requestOptions); // ProxMox API returns data in response.data if (((_a = response.body) === null || _a === void 0 ? void 0 : _a.data) !== undefined) { return response.body.data; } return response.body || response; } catch (error) { if (error.response) { let message = 'ProxMox API error'; const errorBody = error.error || ((_b = error.response) === null || _b === void 0 ? void 0 : _b.body); if (errorBody) { if (typeof errorBody === 'object') { if (errorBody.errors) { // ProxMox validation errors const errors = Array.isArray(errorBody.errors) ? errorBody.errors.join(', ') : JSON.stringify(errorBody.errors); message = `Validation error: ${errors}`; } else if (errorBody.message) { message = errorBody.message; } else if (errorBody.reason) { message = errorBody.reason; } } else if (typeof errorBody === 'string') { message = errorBody; } } // Handle specific HTTP status codes if (error.statusCode === 401) { // Clear cached ticket if authentication fails const cacheKey = `${credentials.host}_${credentials.port}_${credentials.username}`; delete ticketCache[cacheKey]; message = 'Authentication failed. Please check your credentials.'; } else if (error.statusCode === 403) { // Permission denied - include the status text for more detail const statusText = ((_c = error.response) === null || _c === void 0 ? void 0 : _c.statusText) || 'Permission denied'; message = `Permission denied: ${statusText}`; } throw new n8n_workflow_1.NodeApiError(this.getNode(), error, { message: `${message} (Status: ${error.statusCode})`, }); } throw new n8n_workflow_1.NodeOperationError(this.getNode(), `ProxMox API request failed: ${error.message}`); } } /** * Make paginated requests to ProxMox API */ async function apiRequestAllItems(method, endpoint, body = {}, qs = {}) { // ProxMox API doesn't support standard pagination, just make a single request const response = await apiRequest.call(this, method, endpoint, body, qs); if (Array.isArray(response)) { return response; } else { // Single item response return [response]; } }