mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 18:45:53 +00:00
214 lines
8.0 KiB
JavaScript
214 lines
8.0 KiB
JavaScript
const axios = require('axios');
|
|
const https = require('https');
|
|
const axiosRetry = require('axios-retry').default;
|
|
|
|
/**
|
|
* Creates a request interceptor for PVE API authentication.
|
|
* @param {Object} endpoint - The PVE endpoint configuration.
|
|
* @returns {Function} - An Axios request interceptor function.
|
|
*/
|
|
function createPveAuthInterceptor(endpoint) {
|
|
return config => {
|
|
if (endpoint.tokenId && endpoint.tokenSecret) {
|
|
config.headers.Authorization = `PVEAPIToken=${endpoint.tokenId}=${endpoint.tokenSecret}`;
|
|
} else {
|
|
// Error condition for missing credentials
|
|
console.error(`ERROR: Endpoint ${endpoint.name} is missing required API token credentials.`);
|
|
}
|
|
return config;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Logs a warning and calculates exponential delay for PVE retries.
|
|
* @param {string} endpointName - The name of the PVE endpoint.
|
|
* @param {number} retryCount - The current retry attempt number.
|
|
* @param {Error} error - The error that caused the retry.
|
|
* @returns {number} - The delay in milliseconds.
|
|
*/
|
|
function pveRetryDelayLogger(endpointName, retryCount, error) {
|
|
console.warn(`Retrying PVE API request for ${endpointName} (attempt ${retryCount}) due to error: ${error.message}`);
|
|
return axiosRetry.exponentialDelay(retryCount);
|
|
}
|
|
|
|
/**
|
|
* Checks if an error warrants retrying a PVE API call.
|
|
* @param {Error} error - The error object.
|
|
* @returns {boolean} - True if the request should be retried, false otherwise.
|
|
*/
|
|
function pveRetryConditionChecker(error) {
|
|
return (
|
|
axiosRetry.isNetworkError(error) ||
|
|
axiosRetry.isRetryableError(error) ||
|
|
error.response?.status === 596 // Specific PVE status code
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Initializes Axios clients for Proxmox VE endpoints.
|
|
* @param {Array} endpoints - Array of PVE endpoint configuration objects.
|
|
* @returns {Object} - Object containing initialized PVE API clients keyed by endpoint ID.
|
|
*/
|
|
function initializePveClients(endpoints) {
|
|
const apiClients = {};
|
|
console.log(`INFO: Initializing API clients for ${endpoints.length} Proxmox VE endpoints...`);
|
|
|
|
endpoints.forEach(endpoint => {
|
|
if (!endpoint.enabled) {
|
|
console.log(`INFO: Skipping disabled PVE endpoint: ${endpoint.name} (${endpoint.host})`);
|
|
return; // Skip disabled endpoints
|
|
}
|
|
|
|
const baseURL = endpoint.host.includes('://')
|
|
? `${endpoint.host}/api2/json`
|
|
: `https://${endpoint.host}:${endpoint.port}/api2/json`;
|
|
|
|
const authInterceptor = createPveAuthInterceptor(endpoint);
|
|
const retryConfig = {
|
|
retryDelayLogger: pveRetryDelayLogger.bind(null, endpoint.name),
|
|
retryConditionChecker: pveRetryConditionChecker,
|
|
};
|
|
|
|
const apiClient = createApiClientInstance(baseURL, endpoint.allowSelfSignedCerts, authInterceptor, retryConfig);
|
|
|
|
apiClients[endpoint.id] = { client: apiClient, config: endpoint };
|
|
console.log(`INFO: Initialized PVE API client for endpoint: ${endpoint.name} (${endpoint.host})`);
|
|
});
|
|
|
|
return apiClients;
|
|
}
|
|
|
|
// Generic function to create an Axios API client instance
|
|
function createApiClientInstance(baseURL, allowSelfSignedCerts, authInterceptor, retryConfig) {
|
|
const apiClient = axios.create({
|
|
baseURL: baseURL,
|
|
timeout: 30000, // 30 second timeout
|
|
httpsAgent: new https.Agent({
|
|
rejectUnauthorized: !allowSelfSignedCerts
|
|
}),
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (authInterceptor) {
|
|
apiClient.interceptors.request.use(authInterceptor);
|
|
}
|
|
|
|
if (retryConfig) {
|
|
axiosRetry(apiClient, {
|
|
retries: retryConfig.retries || 3,
|
|
retryDelay: retryConfig.retryDelayLogger,
|
|
retryCondition: retryConfig.retryConditionChecker,
|
|
});
|
|
}
|
|
return apiClient;
|
|
}
|
|
|
|
/**
|
|
* Logs a warning and calculates exponential delay for PBS retries.
|
|
* @param {string} configName - The name of the PBS configuration.
|
|
* @param {number} retryCount - The current retry attempt number.
|
|
* @param {Error} error - The error that caused the retry.
|
|
* @returns {number} - The delay in milliseconds.
|
|
*/
|
|
function pbsRetryDelayLogger(configName, retryCount, error) {
|
|
console.warn(`Retrying PBS API request for ${configName} (Token Auth - attempt ${retryCount}) due to error: ${error.message}`);
|
|
return axiosRetry.exponentialDelay(retryCount);
|
|
}
|
|
|
|
/**
|
|
* Checks if an error warrants retrying a PBS API call.
|
|
* @param {Error} error - The error object.
|
|
* @returns {boolean} - True if the request should be retried, false otherwise.
|
|
*/
|
|
function pbsRetryConditionChecker(error) {
|
|
return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error);
|
|
}
|
|
|
|
/**
|
|
* Creates a request interceptor for PBS API authentication (Token Auth).
|
|
* @param {Object} config - The PBS configuration object.
|
|
* @returns {Function} - An Axios request interceptor function.
|
|
*/
|
|
function createPbsAuthInterceptor(config) {
|
|
return reqConfig => {
|
|
// Assumes config.tokenId and config.tokenSecret exist (checked during config load perhaps?)
|
|
reqConfig.headers.Authorization = `PBSAPIToken=${config.tokenId}:${config.tokenSecret}`;
|
|
return reqConfig;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Initializes Axios clients for Proxmox Backup Server instances.
|
|
* @param {Array} pbsConfigs - Array of PBS configuration objects.
|
|
* @returns {Promise<Object>} - Promise resolving to an object containing initialized PBS API clients keyed by config ID.
|
|
*/
|
|
async function initializePbsClients(pbsConfigs) {
|
|
const pbsApiClients = {};
|
|
if (pbsConfigs.length === 0) {
|
|
console.log("INFO: No PBS instances configured, skipping PBS client initialization.");
|
|
return pbsApiClients;
|
|
}
|
|
|
|
console.log(`INFO: Initializing API clients for ${pbsConfigs.length} PBS instances...`);
|
|
const initPromises = pbsConfigs.map(async (config) => {
|
|
let clientData = null;
|
|
try {
|
|
if (config.authMethod === 'token') {
|
|
const pbsBaseURL = config.host.includes('://')
|
|
? `${config.host}/api2/json`
|
|
: `https://${config.host}:${config.port}/api2/json`;
|
|
|
|
const authInterceptor = createPbsAuthInterceptor(config);
|
|
const retryConfig = {
|
|
retryDelayLogger: pbsRetryDelayLogger.bind(null, config.name),
|
|
retryConditionChecker: pbsRetryConditionChecker,
|
|
};
|
|
|
|
const pbsAxiosInstance = createApiClientInstance(pbsBaseURL, config.allowSelfSignedCerts, authInterceptor, retryConfig);
|
|
|
|
clientData = { client: pbsAxiosInstance, config: config };
|
|
console.log(`INFO: [PBS Init] Successfully initialized client for instance '${config.name}' (Token Auth)`);
|
|
} else {
|
|
console.error(`ERROR: Unexpected authMethod '${config.authMethod}' found during PBS client initialization for: ${config.name}`);
|
|
}
|
|
|
|
if (clientData) {
|
|
pbsApiClients[config.id] = clientData;
|
|
}
|
|
} catch (error) {
|
|
console.error(`ERROR: Unhandled exception during PBS client initialization for ${config.name}: ${error.message}`, error.stack);
|
|
}
|
|
// We don't return clientData here, we modify pbsApiClients directly
|
|
});
|
|
|
|
await Promise.allSettled(initPromises);
|
|
console.log(`INFO: [PBS Init] Finished initialization. ${Object.keys(pbsApiClients).length} / ${pbsConfigs.length} PBS clients initialized successfully.`);
|
|
return pbsApiClients;
|
|
}
|
|
|
|
/**
|
|
* Initializes all Proxmox VE and PBS API clients.
|
|
* @param {Array} endpoints - Array of PVE endpoint configuration objects.
|
|
* @param {Array} pbsConfigs - Array of PBS configuration objects.
|
|
* @returns {Promise<Object>} - Promise resolving to an object containing { apiClients, pbsApiClients }.
|
|
*/
|
|
async function initializeApiClients(endpoints, pbsConfigs) {
|
|
const apiClients = initializePveClients(endpoints);
|
|
const pbsApiClients = await initializePbsClients(pbsConfigs); // Wait for PBS clients to initialize
|
|
return { apiClients, pbsApiClients };
|
|
}
|
|
|
|
// Export the new helper functions for potential direct testing
|
|
module.exports = {
|
|
initializeApiClients,
|
|
createPveAuthInterceptor,
|
|
createPbsAuthInterceptor,
|
|
pveRetryDelayLogger,
|
|
pveRetryConditionChecker,
|
|
pbsRetryDelayLogger,
|
|
pbsRetryConditionChecker,
|
|
createApiClientInstance,
|
|
};
|