import * as plugins from '../plugins.js'; import type { Cloudly } from '../classes.cloudly.js'; import * as servezoneInterfaces from '@serve.zone/interfaces'; export class CloudlySettingsManager { public cloudlyRef: Cloudly; public readyDeferred = plugins.smartpromise.defer(); public settingsStore: plugins.smartdata.EasyStore; constructor(cloudlyRefArg: Cloudly) { this.cloudlyRef = cloudlyRefArg; } /** * Initialize the settings manager and create the EasyStore */ public async init() { this.settingsStore = await this.cloudlyRef.mongodbConnector.smartdataDb .createEasyStore('cloudly-settings') as plugins.smartdata.EasyStore; // Setup API route handlers await this.setupRoutes(); this.readyDeferred.resolve(); } /** * Get all settings */ public async getSettings(): Promise { await this.readyDeferred.promise; return await this.settingsStore.readAll(); } /** * Get all settings with masked sensitive values (for API responses) */ public async getSettingsMasked(): Promise { await this.readyDeferred.promise; const settings = await this.getSettings(); const masked: servezoneInterfaces.data.ICloudlySettingsMasked = {}; for (const [key, value] of Object.entries(settings)) { if (typeof value === 'string' && value.length > 4) { // Mask the token, showing only last 4 characters masked[key] = '****' + value.slice(-4); } else { masked[key] = value; } } return masked; } /** * Update multiple settings at once */ public async updateSettings(updates: Partial): Promise { await this.readyDeferred.promise; for (const [key, value] of Object.entries(updates)) { if (value !== undefined && value !== '') { await this.settingsStore.writeKey(key as keyof servezoneInterfaces.data.ICloudlySettings, value); } else if (value === '') { // Empty string means clear the setting await this.settingsStore.deleteKey(key as keyof servezoneInterfaces.data.ICloudlySettings); } } } /** * Get a specific setting value */ public async getSetting(key: K): Promise { await this.readyDeferred.promise; return await this.settingsStore.readKey(key); } /** * Set a specific setting value */ public async setSetting(key: K, value: servezoneInterfaces.data.ICloudlySettings[K]): Promise { await this.readyDeferred.promise; if (value !== undefined && value !== '') { await this.settingsStore.writeKey(key, value); } } /** * Clear a specific setting */ public async clearSetting(key: keyof servezoneInterfaces.data.ICloudlySettings): Promise { await this.readyDeferred.promise; await this.settingsStore.deleteKey(key); } /** * Clear all settings */ public async clearAllSettings(): Promise { await this.readyDeferred.promise; await this.settingsStore.wipe(); } /** * Test connection for a specific provider */ public async testProviderConnection(provider: string): Promise<{success: boolean; message: string}> { await this.readyDeferred.promise; try { switch (provider) { case 'hetzner': const hetznerToken = await this.getSetting('hetznerToken'); if (!hetznerToken) { return { success: false, message: 'No Hetzner token configured' }; } // TODO: Implement actual Hetzner API test return { success: true, message: 'Hetzner connection test successful' }; case 'cloudflare': const cloudflareToken = await this.getSetting('cloudflareToken'); if (!cloudflareToken) { return { success: false, message: 'No Cloudflare token configured' }; } // TODO: Implement actual Cloudflare API test return { success: true, message: 'Cloudflare connection test successful' }; case 'aws': const awsKey = await this.getSetting('awsAccessKey'); const awsSecret = await this.getSetting('awsSecretKey'); if (!awsKey || !awsSecret) { return { success: false, message: 'AWS credentials not configured' }; } // TODO: Implement actual AWS API test return { success: true, message: 'AWS connection test successful' }; case 'digitalocean': const doToken = await this.getSetting('digitalOceanToken'); if (!doToken) { return { success: false, message: 'No DigitalOcean token configured' }; } // TODO: Implement actual DigitalOcean API test return { success: true, message: 'DigitalOcean connection test successful' }; case 'azure': const azureClientId = await this.getSetting('azureClientId'); const azureClientSecret = await this.getSetting('azureClientSecret'); const azureTenantId = await this.getSetting('azureTenantId'); if (!azureClientId || !azureClientSecret || !azureTenantId) { return { success: false, message: 'Azure credentials not configured' }; } // TODO: Implement actual Azure API test return { success: true, message: 'Azure connection test successful' }; default: return { success: false, message: `Unknown provider: ${provider}` }; } } catch (error) { return { success: false, message: `Connection test failed: ${error.message}` }; } } /** * Setup API route handlers for settings management */ private async setupRoutes() { // Get Settings Handler this.cloudlyRef.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getSettings', async (requestData) => { // TODO: Add authentication check for admin users const maskedSettings = await this.getSettingsMasked(); return { settings: maskedSettings }; } ) ); // Update Settings Handler this.cloudlyRef.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updateSettings', async (requestData) => { // TODO: Add authentication check for admin users try { await this.updateSettings(requestData.updates); return { success: true, message: 'Settings updated successfully' }; } catch (error) { return { success: false, message: `Failed to update settings: ${error.message}` }; } } ) ); // Clear Setting Handler this.cloudlyRef.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'clearSetting', async (requestData) => { // TODO: Add authentication check for admin users try { await this.clearSetting(requestData.key); return { success: true, message: `Setting ${requestData.key} cleared successfully` }; } catch (error) { return { success: false, message: `Failed to clear setting: ${error.message}` }; } } ) ); // Test Provider Connection Handler this.cloudlyRef.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'testProviderConnection', async (requestData) => { // TODO: Add authentication check for admin users const testResult = await this.testProviderConnection(requestData.provider); return { success: testResult.success, message: testResult.message, connectionValid: testResult.success }; } ) ); // Get Single Setting Handler (for internal use) this.cloudlyRef.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getSetting', async (requestData) => { // TODO: Add authentication check for admin users const value = await this.getSetting(requestData.key); return { value }; } ) ); } }