255 lines
9.1 KiB
TypeScript
255 lines
9.1 KiB
TypeScript
|
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<servezoneInterfaces.data.ICloudlySettings>;
|
||
|
|
||
|
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<servezoneInterfaces.data.ICloudlySettings>;
|
||
|
|
||
|
// Setup API route handlers
|
||
|
await this.setupRoutes();
|
||
|
|
||
|
this.readyDeferred.resolve();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all settings
|
||
|
*/
|
||
|
public async getSettings(): Promise<servezoneInterfaces.data.ICloudlySettings> {
|
||
|
await this.readyDeferred.promise;
|
||
|
return await this.settingsStore.readAll();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all settings with masked sensitive values (for API responses)
|
||
|
*/
|
||
|
public async getSettingsMasked(): Promise<servezoneInterfaces.data.ICloudlySettingsMasked> {
|
||
|
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<servezoneInterfaces.data.ICloudlySettings>): Promise<void> {
|
||
|
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<K extends keyof servezoneInterfaces.data.ICloudlySettings>(key: K): Promise<servezoneInterfaces.data.ICloudlySettings[K]> {
|
||
|
await this.readyDeferred.promise;
|
||
|
return await this.settingsStore.readKey(key);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a specific setting value
|
||
|
*/
|
||
|
public async setSetting<K extends keyof servezoneInterfaces.data.ICloudlySettings>(key: K, value: servezoneInterfaces.data.ICloudlySettings[K]): Promise<void> {
|
||
|
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<void> {
|
||
|
await this.readyDeferred.promise;
|
||
|
await this.settingsStore.deleteKey(key);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear all settings
|
||
|
*/
|
||
|
public async clearAllSettings(): Promise<void> {
|
||
|
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<servezoneInterfaces.requests.settings.IRequest_GetSettings>(
|
||
|
new plugins.typedrequest.TypedHandler<servezoneInterfaces.requests.settings.IRequest_GetSettings>(
|
||
|
'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<servezoneInterfaces.requests.settings.IRequest_UpdateSettings>(
|
||
|
new plugins.typedrequest.TypedHandler<servezoneInterfaces.requests.settings.IRequest_UpdateSettings>(
|
||
|
'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<servezoneInterfaces.requests.settings.IRequest_ClearSetting>(
|
||
|
new plugins.typedrequest.TypedHandler<servezoneInterfaces.requests.settings.IRequest_ClearSetting>(
|
||
|
'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<servezoneInterfaces.requests.settings.IRequest_TestProviderConnection>(
|
||
|
new plugins.typedrequest.TypedHandler<servezoneInterfaces.requests.settings.IRequest_TestProviderConnection>(
|
||
|
'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<servezoneInterfaces.requests.settings.IRequest_GetSetting>(
|
||
|
new plugins.typedrequest.TypedHandler<servezoneInterfaces.requests.settings.IRequest_GetSetting>(
|
||
|
'getSetting',
|
||
|
async (requestData) => {
|
||
|
// TODO: Add authentication check for admin users
|
||
|
const value = await this.getSetting(requestData.key);
|
||
|
return {
|
||
|
value
|
||
|
};
|
||
|
}
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
}
|