'use strict'; /** * Partner System Configuration Helper * Manages environment-based configuration for partner integrations */ const env = require('./env'); const utils = require('./utils'); const { AuthMethods } = require('./constants'); class PartnerConfig { constructor() { this.configs = { SATLOC: { apiEndpoint: env.SATLOC_API_ENDPOINT, authMethod: AuthMethods.USERNAME_PASSWORD, // Optional: Global fallback credentials (rarely used in production) defaultApiKey: env.SATLOC_API_KEY || null, defaultApiSecret: env.SATLOC_API_SECRET || null, timeout: parseInt(env.SATLOC_API_TIMEOUT) || 30000, retryAttempts: parseInt(env.SATLOC_RETRY_ATTEMPTS) || 3, retryDelay: parseInt(env.SATLOC_RETRY_DELAY) || 1000, rateLimit: { requestsPerMinute: parseInt(env.SATLOC_RATE_LIMIT) || 60, burstLimit: parseInt(env.SATLOC_BURST_LIMIT) || 10 }, features: { supportsRealTime: utils.stringToBoolean(env.SATLOC_REALTIME_ENABLED), supportsFileUpload: env.SATLOC_FILE_UPLOAD_ENABLED !== 'false', maxFileSize: parseInt(env.SATLOC_MAX_FILE_SIZE) || 10485760 // 10MB }, storage: { basePath: env.SATLOC_STORAGE_PATH || '/data/partners/satloc', tempPath: env.SATLOC_TEMP_PATH || '/tmp/satloc', logFileExtensions: ['.log', '.LOG'], maxFileAge: parseInt(env.SATLOC_MAX_FILE_AGE) || 7776000000 // 90 days in ms } }, AGIDRONEX: { apiEndpoint: env.AGIDRONEX_API_ENDPOINT || 'https://api.agidronex.com/v1', authMethod: AuthMethods.API_KEY, // Optional: Global fallback credentials (rarely used in production) defaultApiKey: env.AGIDRONEX_API_KEY || null, defaultApiSecret: env.AGIDRONEX_API_SECRET || null, defaultUsername: env.AGIDRONEX_USERNAME || null, defaultPassword: env.AGIDRONEX_PASSWORD || null, timeout: parseInt(env.AGIDRONEX_API_TIMEOUT) || 25000, retryAttempts: parseInt(env.AGIDRONEX_RETRY_ATTEMPTS) || 3, retryDelay: parseInt(env.AGIDRONEX_RETRY_DELAY) || 1500, rateLimit: { requestsPerMinute: parseInt(env.AGIDRONEX_RATE_LIMIT) || 100, burstLimit: parseInt(env.AGIDRONEX_BURST_LIMIT) || 20 }, features: { supportsRealTime: utils.stringToBoolean(env.AGIDRONEX_REALTIME_ENABLED), supportsFileUpload: env.AGIDRONEX_FILE_UPLOAD_ENABLED !== 'false', maxFileSize: parseInt(env.AGIDRONEX_MAX_FILE_SIZE) || 20971520 // 20MB }, storage: { basePath: env.AGIDRONEX_STORAGE_PATH || '/data/partners/agidronex', tempPath: env.AGIDRONEX_TEMP_PATH || '/tmp/agidronex', logFileExtensions: ['.log', '.LOG', '.txt', '.dat'], maxFileAge: parseInt(env.AGIDRONEX_MAX_FILE_AGE) || 7776000000 // 90 days in ms } } }; // Global partner system settings this.globalConfig = { syncInterval: parseInt(env.PARTNER_SYNC_INTERVAL) || 300000, // 5 minutes healthCheckInterval: parseInt(env.PARTNER_HEALTH_CHECK_INTERVAL) || 60000, // 1 minute maxConcurrentJobs: parseInt(env.PARTNER_MAX_CONCURRENT_JOBS) || 10, jobTimeout: parseInt(env.PARTNER_JOB_TIMEOUT) || 1800000, // 30 minutes enableMetrics: env.PARTNER_METRICS_ENABLED !== 'false', enableDetailedLogging: utils.stringToBoolean(env.PARTNER_DETAILED_LOGGING), encryptCredentials: env.PARTNER_ENCRYPT_CREDENTIALS !== 'false' }; } /** * Get configuration for a specific partner * @param {string} partnerCode - Partner code (e.g., 'SATLOC', 'AGIDRONEX') * @returns {object} Partner configuration */ getPartnerConfig(partnerCode) { const config = this.configs[partnerCode.toUpperCase()]; if (!config) { throw new Error(`Partner configuration not found for: ${partnerCode}`); } return { ...config }; } /** * Get global partner system configuration * @returns {object} Global configuration */ getGlobalConfig() { return { ...this.globalConfig }; } /** * Get API credentials for a partner system user * @param {object} partnerSystemUser - Partner system user document * @param {string} partnerCode - Partner code * @returns {object} API credentials and endpoint */ getApiCredentials(partnerSystemUser, partnerCode) { const partnerConfig = this.getPartnerConfig(partnerCode); const authMethod = partnerConfig.authMethod || AuthMethods.API_KEY; const credentials = { endpoint: partnerConfig.apiEndpoint, authMethod, // Additional parameters for API calls companyId: partnerSystemUser.companyId, partnerUserId: partnerSystemUser.partnerUserId, partnerUsername: partnerSystemUser.partnerUsername }; // Handle different authentication methods switch (authMethod) { case AuthMethods.USERNAME_PASSWORD: const username = partnerSystemUser.partnerUsername || partnerSystemUser.username; const password = partnerSystemUser.password; if (!username || !password) { throw new Error(`Missing username/password credentials for ${partnerCode} user: ${partnerSystemUser.partnerUserId || partnerSystemUser._id}`); } credentials.username = username; credentials.password = password; // For SatLoc specifically, sometimes userId is used instead of username credentials.userId = partnerSystemUser.partnerUserId || username; break; case AuthMethods.API_KEY: const apiKey = partnerSystemUser.apiKey || partnerConfig.defaultApiKey; const apiSecret = partnerSystemUser.apiSecret || partnerConfig.defaultApiSecret; if (!apiKey || !apiSecret) { throw new Error(`Missing API key/secret credentials for ${partnerCode} user: ${partnerSystemUser.partnerUserId || partnerSystemUser._id}`); } credentials.apiKey = apiKey; credentials.apiSecret = apiSecret; break; case AuthMethods.OAUTH: const accessToken = partnerSystemUser.accessToken; const refreshToken = partnerSystemUser.refreshToken; if (!accessToken) { throw new Error(`Missing OAuth access token for ${partnerCode} user: ${partnerSystemUser.partnerUserId || partnerSystemUser._id}`); } credentials.accessToken = accessToken; credentials.refreshToken = refreshToken; break; case AuthMethods.BEARER_TOKEN: const bearerToken = partnerSystemUser.bearerToken || partnerSystemUser.accessToken; if (!bearerToken) { throw new Error(`Missing bearer token for ${partnerCode} user: ${partnerSystemUser.partnerUserId || partnerSystemUser._id}`); } credentials.bearerToken = bearerToken; break; default: throw new Error(`Unsupported authentication method: ${authMethod} for ${partnerCode}`); } return credentials; } /** * Get request configuration for API calls * @param {string} partnerCode - Partner code * @returns {object} Request configuration */ getRequestConfig(partnerCode) { const config = this.getPartnerConfig(partnerCode); return { timeout: config.timeout, retryAttempts: config.retryAttempts, retryDelay: config.retryDelay, headers: { 'Content-Type': 'application/json', 'User-Agent': `AgMission-Integration/1.0 (${partnerCode})` } }; } /** * Check if a feature is enabled for a partner * @param {string} partnerCode - Partner code * @param {string} feature - Feature name * @returns {boolean} Whether feature is enabled */ isFeatureEnabled(partnerCode, feature) { const config = this.getPartnerConfig(partnerCode); return config.features[feature] || false; } /** * Get rate limiting configuration for a partner * @param {string} partnerCode - Partner code * @returns {object} Rate limit configuration */ getRateLimitConfig(partnerCode) { const config = this.getPartnerConfig(partnerCode); return { ...config.rateLimit }; } /** * Validate partner configuration * @param {string} partnerCode - Partner code * @returns {object} Validation result */ validateConfig(partnerCode) { try { const config = this.getPartnerConfig(partnerCode); const issues = []; const authMethod = config.authMethod || AuthMethods.API_KEY; if (!config.apiEndpoint) { issues.push(`Missing API endpoint for ${partnerCode}`); } // Validate auth method if (!Object.values(AuthMethods).includes(authMethod)) { issues.push(`Unknown authentication method: ${authMethod} for ${partnerCode}`); } // Check if default credentials are available (optional warning) switch (authMethod) { case AuthMethods.USERNAME_PASSWORD: if (!config.defaultUsername && !config.defaultPassword) { issues.push(`No default username/password configured for ${partnerCode} (optional - credentials typically come from partner system users)`); } break; case AuthMethods.API_KEY: if (!config.defaultApiKey && !config.defaultApiSecret) { issues.push(`No default API key/secret configured for ${partnerCode} (optional - credentials typically come from partner system users)`); } break; case AuthMethods.OAUTH: case AuthMethods.BEARER_TOKEN: // These typically don't have default credentials break; } if (config.timeout < 5000) { issues.push(`Timeout too low for ${partnerCode}: ${config.timeout}ms`); } return { valid: issues.length === 0, issues, config, authMethod }; } catch (error) { return { valid: false, issues: [error.message], config: null, authMethod: null }; } } /** * Get environment variable names for a partner * @param {string} partnerCode - Partner code * @returns {array} Environment variable names */ getEnvVarNames(partnerCode) { const prefix = partnerCode.toUpperCase(); return [ `${prefix}_API_ENDPOINT`, `${prefix}_API_KEY`, `${prefix}_API_SECRET`, `${prefix}_USERNAME`, `${prefix}_PASSWORD`, `${prefix}_API_TIMEOUT`, `${prefix}_RETRY_ATTEMPTS`, `${prefix}_RETRY_DELAY`, `${prefix}_RATE_LIMIT`, `${prefix}_BURST_LIMIT`, `${prefix}_REALTIME_ENABLED`, `${prefix}_FILE_UPLOAD_ENABLED`, `${prefix}_MAX_FILE_SIZE` ]; } /** * Generate sample environment file content * @returns {string} Sample .env content */ generateSampleEnv() { return ` # Partner System Configuration # Global Settings PARTNER_SYNC_INTERVAL=300000 PARTNER_HEALTH_CHECK_INTERVAL=60000 PARTNER_MAX_CONCURRENT_JOBS=10 PARTNER_JOB_TIMEOUT=1800000 PARTNER_METRICS_ENABLED=true PARTNER_DETAILED_LOGGING=false PARTNER_ENCRYPT_CREDENTIALS=true # SatLoc Configuration (Uses USERNAME_PASSWORD auth) SATLOC_API_ENDPOINT=https://www.satloccloud.com/api/Satloc # Optional: Global fallback credentials (each customer should have their own partner system user) # SATLOC_USERNAME=your_global_satloc_username # SATLOC_PASSWORD=your_global_satloc_password SATLOC_API_TIMEOUT=30000 SATLOC_RETRY_ATTEMPTS=3 SATLOC_RETRY_DELAY=1000 SATLOC_RATE_LIMIT=60 SATLOC_BURST_LIMIT=10 SATLOC_REALTIME_ENABLED=false SATLOC_FILE_UPLOAD_ENABLED=true SATLOC_MAX_FILE_SIZE=10485760 # AgIDronex Configuration (Uses API_KEY auth) AGIDRONEX_API_ENDPOINT=https://api.agidronex.com/v1 # Optional: Global fallback credentials (each customer should have their own partner system user) # AGIDRONEX_API_KEY=your_global_agidronex_api_key # AGIDRONEX_API_SECRET=your_global_agidronex_api_secret AGIDRONEX_API_TIMEOUT=25000 AGIDRONEX_RETRY_ATTEMPTS=3 AGIDRONEX_RETRY_DELAY=1500 AGIDRONEX_RATE_LIMIT=100 AGIDRONEX_BURST_LIMIT=20 AGIDRONEX_REALTIME_ENABLED=true AGIDRONEX_FILE_UPLOAD_ENABLED=true AGIDRONEX_MAX_FILE_SIZE=20971520 `.trim(); } } module.exports = new PartnerConfig();