UNPKG

@multicloud-io/client

Version:

Customer-facing Multicloud API client with limited field exposure

278 lines 11.9 kB
"use strict"; /** * Configuration management for Multicloud connections * * This module provides the MulticloudConfig class for loading and managing * configuration from files, environment variables, and direct parameters * with proper precedence handling. * * Supports both Node.js and browser environments (server-side only for security). */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MulticloudConfig = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const os_1 = __importDefault(require("os")); const js_yaml_1 = __importDefault(require("js-yaml")); const exceptions_1 = require("./exceptions"); class MulticloudConfig { /** * Get connection parameters with file and environment variable overrides * * Applies configuration precedence: * 1. Direct overrides (highest priority) * 2. Environment variables * 3. Configuration file * 4. Default values (lowest priority) * * @param overrides Direct parameter overrides * @param options Configuration options * @returns Connection parameters */ static getConnectionParams(overrides = {}, options = {}) { const envPrefix = options.envPrefix || 'MULTICLOUD_'; const configFile = options.configFile || this.DEFAULT_CONFIG_PATH; const isProduction = options.isProduction ?? (process.env.NODE_ENV === 'production'); // Start with default values const params = { ...this.DEFAULT_CONFIG }; // Apply configuration file overrides this.applyFileConfig(params, configFile); // Apply environment variable overrides this.applyEnvOverrides(params, envPrefix); // Apply direct overrides (highest priority) Object.assign(params, overrides); // Validate required parameters this.validateParams(params, isProduction); // Print the final serverUrl to the console console.log('[multicloud-connection-js] Final serverUrl used:', params.serverUrl); return params; } /** * Load configuration from YAML file */ static applyFileConfig(params, configFile) { const expandedPath = configFile.startsWith('~') ? path_1.default.join(os_1.default.homedir(), configFile.slice(1)) : configFile; if (!fs_1.default.existsSync(expandedPath)) { // Config file is optional, just return if it doesn't exist return; } try { const content = fs_1.default.readFileSync(expandedPath, 'utf8'); // Handle variable substitution (simple ${variable} replacement) const processedContent = this.substituteVariables(content); // Parse YAML const fileConfig = js_yaml_1.default.load(processedContent); if (fileConfig && typeof fileConfig === 'object') { // Map file config to our parameter names if (fileConfig.multicloud_url || fileConfig.server_url) { params.serverUrl = fileConfig.multicloud_url || fileConfig.server_url; } // Handle certificate paths from config if (fileConfig.certificates) { if (fileConfig.certificates['client-cert'] || fileConfig.certificates.client_cert) { params.clientCert = fileConfig.certificates['client-cert'] || fileConfig.certificates.client_cert; } if (fileConfig.certificates['client-key'] || fileConfig.certificates.client_key) { params.clientKey = fileConfig.certificates['client-key'] || fileConfig.certificates.client_key; } } // Handle other config options if (fileConfig.access_token || fileConfig.accessToken) { params.accessToken = fileConfig.access_token || fileConfig.accessToken; } if (fileConfig.verify_ssl !== undefined) { params.verifySsl = fileConfig.verify_ssl; } if (fileConfig.timeout !== undefined) { params.timeout = fileConfig.timeout * 1000; // Convert seconds to milliseconds } if (fileConfig.debug !== undefined) { params.debug = fileConfig.debug; } } } catch (error) { throw new exceptions_1.MulticloudConfigurationError(`Error loading config file ${expandedPath}: ${error}`); } } /** * Substitute variables in configuration content * Supports ${variable} syntax with simple replacements */ static substituteVariables(content) { try { // First pass: load to extract variables const rawConfig = js_yaml_1.default.load(content); const variables = {}; // Extract certificate directory for substitution if (rawConfig && typeof rawConfig === 'object' && rawConfig.certificates) { const certConfig = rawConfig.certificates; if (certConfig && typeof certConfig === 'object' && certConfig.dir) { variables['dir'] = certConfig.dir; } } // Second pass: substitute variables let processedContent = content; for (const [varName, varValue] of Object.entries(variables)) { const regex = new RegExp(`\\$\\{${varName}\\}`, 'g'); processedContent = processedContent.replace(regex, String(varValue)); } return processedContent; } catch (error) { // If variable substitution fails, log warning but continue console.warn('Warning: Variable substitution failed:', error); return content; } } /** * Apply environment variable overrides to parameters */ static applyEnvOverrides(params, envPrefix) { const envMappings = { [`${envPrefix}URL`]: 'serverUrl', [`${envPrefix}ACCESS_TOKEN`]: 'accessToken', [`${envPrefix}CLIENT_CERT`]: 'clientCert', [`${envPrefix}CLIENT_KEY`]: 'clientKey', [`${envPrefix}VERIFY_SSL`]: 'verifySsl', [`${envPrefix}TIMEOUT`]: 'timeout', [`${envPrefix}DEBUG`]: 'debug', }; for (const [envVar, configKey] of Object.entries(envMappings)) { const envValue = process.env[envVar]; if (envValue !== undefined) { switch (configKey) { case 'verifySsl': case 'debug': params[configKey] = envValue.toLowerCase() === 'true'; break; case 'timeout': const timeoutValue = parseInt(envValue, 10); if (isNaN(timeoutValue)) { throw new exceptions_1.MulticloudConfigurationError(`Invalid timeout value in ${envVar}: ${envValue}`); } params.timeout = timeoutValue; break; default: params[configKey] = envValue; } } } } /** * Validate that required parameters are present */ static validateParams(params, isProduction) { if (!params.serverUrl) { throw new exceptions_1.MulticloudConfigurationError('Server URL is required'); } // For production, require certificates if (isProduction) { if (!params.clientCert) { throw new exceptions_1.MulticloudConfigurationError('Client certificate is required in production'); } if (!params.clientKey) { throw new exceptions_1.MulticloudConfigurationError('Client key is required in production'); } } } /** * Get configuration information for debugging */ static getConfigInfo(options = {}) { const envPrefix = options.envPrefix || 'MULTICLOUD_'; const configFile = options.configFile || this.DEFAULT_CONFIG_PATH; const expandedPath = configFile.startsWith('~') ? path_1.default.join(os_1.default.homedir(), configFile.slice(1)) : configFile; const envVars = [ `${envPrefix}URL`, `${envPrefix}ACCESS_TOKEN`, `${envPrefix}CLIENT_CERT`, `${envPrefix}CLIENT_KEY`, `${envPrefix}VERIFY_SSL`, `${envPrefix}TIMEOUT`, `${envPrefix}DEBUG` ]; // Actually try to parse the configuration to get accurate status let hasServerUrl = false; let hasCertificates = false; let configParseError = null; try { // Start with defaults const testParams = { ...this.DEFAULT_CONFIG }; // Apply file config if it exists if (fs_1.default.existsSync(expandedPath)) { this.applyFileConfig(testParams, configFile); } // Apply environment overrides this.applyEnvOverrides(testParams, envPrefix); // Check what we got hasServerUrl = !!testParams.serverUrl && testParams.serverUrl !== this.DEFAULT_CONFIG.serverUrl; hasCertificates = !!(testParams.clientCert && testParams.clientKey); } catch (error) { configParseError = error instanceof Error ? error.message : String(error); } return { envVarsSet: envVars.filter(envVar => process.env[envVar] !== undefined), nodeEnv: process.env.NODE_ENV, hasServerUrl, hasCertificates, configParseError, configFile: { path: configFile, expandedPath: expandedPath, exists: fs_1.default.existsSync(expandedPath) } }; } /** * Check if configuration file exists */ static hasConfigFile(configFile) { const configPath = configFile || this.DEFAULT_CONFIG_PATH; const expandedPath = configPath.startsWith('~') ? path_1.default.join(os_1.default.homedir(), configPath.slice(1)) : configPath; return fs_1.default.existsSync(expandedPath); } /** * Check if multicloud integration is enabled * * @param options Configuration options * @returns True if multicloud is properly configured */ static isEnabled(options = {}) { const envPrefix = options.envPrefix || 'MULTICLOUD_'; const isProduction = options.isProduction ?? (process.env.NODE_ENV === 'production'); // Check if we have a config file const hasConfigFile = this.hasConfigFile(options.configFile); // In development, check if URL is configured via env vars OR config file if (!isProduction) { return !!process.env[`${envPrefix}URL`] || hasConfigFile; } // In production, require full configuration (env vars take precedence) const hasEnvConfig = !!(process.env[`${envPrefix}URL`] && process.env[`${envPrefix}CLIENT_CERT`] && process.env[`${envPrefix}CLIENT_KEY`]); return hasEnvConfig || hasConfigFile; } } exports.MulticloudConfig = MulticloudConfig; MulticloudConfig.DEFAULT_CONFIG_PATH = "~/.multicloud/config.yaml"; /** * Default configuration values */ MulticloudConfig.DEFAULT_CONFIG = { serverUrl: 'https://localhost:8443', verifySsl: true, timeout: 5000, // 5 seconds debug: false, }; //# sourceMappingURL=config.js.map