UNPKG

lsh-framework

Version:

A powerful, extensible shell with advanced job management, database persistence, and modern CLI features

348 lines (347 loc) 10.2 kB
/** * Cloud Configuration Manager * Manages shell configuration with Supabase persistence */ import DatabasePersistence from './database-persistence.js'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; export class CloudConfigManager { databasePersistence; options; localConfig = new Map(); cloudConfig = new Map(); syncTimer; constructor(options = {}) { this.options = { userId: undefined, enableCloudSync: true, localConfigPath: path.join(os.homedir(), '.lshrc'), syncInterval: 60000, // 1 minute ...options, }; this.databasePersistence = new DatabasePersistence(this.options.userId); this.loadLocalConfig(); if (this.options.enableCloudSync) { this.initializeCloudSync(); } } /** * Load local configuration file */ loadLocalConfig() { try { if (fs.existsSync(this.options.localConfigPath)) { const content = fs.readFileSync(this.options.localConfigPath, 'utf8'); const config = JSON.parse(content); Object.entries(config).forEach(([key, value]) => { this.localConfig.set(key, { key, value, type: this.getType(value), isDefault: false, }); }); } } catch (error) { console.error('Failed to load local config:', error); } } /** * Save local configuration file */ saveLocalConfig() { try { const config = {}; this.localConfig.forEach((configValue, key) => { config[key] = configValue.value; }); fs.writeFileSync(this.options.localConfigPath, JSON.stringify(config, null, 2)); } catch (error) { console.error('Failed to save local config:', error); } } /** * Get type of a value */ getType(value) { if (typeof value === 'string') return 'string'; if (typeof value === 'number') return 'number'; if (typeof value === 'boolean') return 'boolean'; if (Array.isArray(value)) return 'array'; if (typeof value === 'object' && value !== null) return 'object'; return 'string'; } /** * Initialize cloud synchronization */ async initializeCloudSync() { try { const isConnected = await this.databasePersistence.testConnection(); if (isConnected) { console.log('✅ Cloud config sync enabled'); await this.loadCloudConfig(); this.startSyncTimer(); } else { console.log('⚠️ Cloud config sync disabled - database not available'); } } catch (error) { console.error('Failed to initialize cloud config sync:', error); } } /** * Load configuration from cloud */ async loadCloudConfig() { try { const cloudConfigs = await this.databasePersistence.getConfiguration(); cloudConfigs.forEach(config => { this.cloudConfig.set(config.config_key, { key: config.config_key, value: this.parseConfigValue(config.config_value, config.config_type), type: config.config_type, description: config.description, isDefault: config.is_default, }); }); // Merge cloud config with local config this.mergeConfigurations(); } catch (error) { console.error('Failed to load cloud config:', error); } } /** * Parse configuration value based on type */ parseConfigValue(value, type) { try { switch (type) { case 'string': return value; case 'number': return parseFloat(value); case 'boolean': return value === 'true'; case 'array': case 'object': return JSON.parse(value); default: return value; } } catch (error) { console.error(`Failed to parse config value ${value} as ${type}:`, error); return value; } } /** * Serialize configuration value to string */ serializeConfigValue(value, type) { switch (type) { case 'string': case 'number': case 'boolean': return String(value); case 'array': case 'object': return JSON.stringify(value); default: return String(value); } } /** * Merge cloud and local configurations */ mergeConfigurations() { // Cloud config takes precedence for non-local overrides this.cloudConfig.forEach((cloudValue, key) => { if (!this.localConfig.has(key)) { this.localConfig.set(key, cloudValue); } }); } /** * Start periodic synchronization timer */ startSyncTimer() { this.syncTimer = setInterval(() => { this.syncToCloud(); }, this.options.syncInterval); } /** * Stop synchronization timer */ stopSyncTimer() { if (this.syncTimer) { clearInterval(this.syncTimer); this.syncTimer = undefined; } } /** * Synchronize local configuration to cloud */ async syncToCloud() { if (!this.options.enableCloudSync) { return; } try { for (const [key, configValue] of this.localConfig) { await this.databasePersistence.saveConfiguration({ config_key: key, config_value: this.serializeConfigValue(configValue.value, configValue.type), config_type: configValue.type, description: configValue.description, is_default: configValue.isDefault, }); } } catch (error) { console.error('Failed to sync config to cloud:', error); } } /** * Get configuration value */ get(key, defaultValue) { const configValue = this.localConfig.get(key); return configValue ? configValue.value : defaultValue; } /** * Set configuration value */ set(key, value, description) { const configValue = { key, value, type: this.getType(value), description, isDefault: false, }; this.localConfig.set(key, configValue); this.saveLocalConfig(); // Sync to cloud if enabled if (this.options.enableCloudSync) { this.syncToCloud().catch(error => { console.error('Failed to sync config to cloud:', error); }); } } /** * Get all configuration keys */ getKeys() { return Array.from(this.localConfig.keys()); } /** * Get all configuration entries */ getAll() { return Array.from(this.localConfig.values()); } /** * Check if configuration key exists */ has(key) { return this.localConfig.has(key); } /** * Delete configuration key */ delete(key) { this.localConfig.delete(key); this.saveLocalConfig(); // Sync to cloud if enabled if (this.options.enableCloudSync) { this.syncToCloud().catch(error => { console.error('Failed to sync config deletion to cloud:', error); }); } } /** * Reset configuration to defaults */ reset() { this.localConfig.clear(); this.saveLocalConfig(); if (this.options.enableCloudSync) { this.syncToCloud().catch(error => { console.error('Failed to sync config reset to cloud:', error); }); } } /** * Export configuration to JSON */ export() { const config = {}; this.localConfig.forEach((configValue, key) => { config[key] = configValue.value; }); return JSON.stringify(config, null, 2); } /** * Import configuration from JSON */ import(configJson) { try { const config = JSON.parse(configJson); Object.entries(config).forEach(([key, value]) => { this.set(key, value); }); } catch (error) { console.error('Failed to import configuration:', error); throw new Error('Invalid configuration JSON'); } } /** * Get configuration statistics */ getStats() { const types = {}; this.localConfig.forEach(configValue => { types[configValue.type] = (types[configValue.type] || 0) + 1; }); return { totalKeys: this.localConfig.size, localKeys: this.localConfig.size, cloudKeys: this.cloudConfig.size, types, }; } /** * Enable or disable cloud sync */ setCloudSyncEnabled(enabled) { this.options.enableCloudSync = enabled; if (enabled) { this.initializeCloudSync(); } else { this.stopSyncTimer(); } } /** * Cleanup resources */ destroy() { this.stopSyncTimer(); if (this.options.enableCloudSync) { this.syncToCloud().catch(error => { console.error('Failed to final config sync on destroy:', error); }); } } } export default CloudConfigManager;