lsh-framework
Version:
A powerful, extensible shell with advanced job management, database persistence, and modern CLI features
348 lines (347 loc) • 10.2 kB
JavaScript
/**
* 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;