feat(settings): Add runtime settings management, node & baremetal managers, and settings UI
This commit is contained in:
		
							
								
								
									
										73
									
								
								ts_interfaces/data/baremetal.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								ts_interfaces/data/baremetal.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
|  | ||||
| export interface IBareMetal { | ||||
|   id: string; | ||||
|   data: { | ||||
|     hostname: string; | ||||
|      | ||||
|     /** | ||||
|      * IPMI management IP address | ||||
|      */ | ||||
|     ipmiAddress?: string; | ||||
|      | ||||
|     /** | ||||
|      * Encrypted IPMI credentials | ||||
|      */ | ||||
|     ipmiCredentials?: { | ||||
|       username: string; | ||||
|       passwordEncrypted: string; | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Primary network IP address | ||||
|      */ | ||||
|     primaryIp: string; | ||||
|      | ||||
|     /** | ||||
|      * Provider of the physical server | ||||
|      */ | ||||
|     provider: 'hetzner' | 'aws' | 'digitalocean' | 'onpremise'; | ||||
|      | ||||
|     /** | ||||
|      * Data center or location | ||||
|      */ | ||||
|     location: string; | ||||
|      | ||||
|     /** | ||||
|      * Hardware specifications | ||||
|      */ | ||||
|     specs: { | ||||
|       cpuModel: string; | ||||
|       cpuCores: number; | ||||
|       memoryGB: number; | ||||
|       storageGB: number; | ||||
|       storageType: 'ssd' | 'hdd' | 'nvme'; | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Current power state | ||||
|      */ | ||||
|     powerState: 'on' | 'off' | 'unknown'; | ||||
|      | ||||
|     /** | ||||
|      * Operating system information | ||||
|      */ | ||||
|     osInfo: { | ||||
|       name: string; | ||||
|       version: string; | ||||
|       kernel?: string; | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Array of ClusterNode IDs running on this hardware | ||||
|      */ | ||||
|     assignedNodeIds: string[]; | ||||
|      | ||||
|     /** | ||||
|      * Metadata for provider-specific information | ||||
|      */ | ||||
|     providerMetadata?: { | ||||
|       [key: string]: any; | ||||
|     }; | ||||
|   }; | ||||
| } | ||||
| @@ -1,8 +1,6 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
|  | ||||
| export interface ICloudlyConfig { | ||||
|   cfToken?: string; | ||||
|   hetznerToken?: string; | ||||
|   environment?: 'production' | 'integration'; | ||||
|   letsEncryptEmail?: string; | ||||
|   letsEncryptPrivateKey?: string; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
|  | ||||
| import { type IDockerRegistryInfo } from '../data/docker.js'; | ||||
| import type { IServer } from './server.js'; | ||||
| import type { IClusterNode } from './clusternode.js'; | ||||
|  | ||||
| export interface ICluster { | ||||
|   id: string; | ||||
| @@ -24,9 +24,9 @@ export interface ICluster { | ||||
|     setupMode?: 'manual' | 'hetzner' | 'aws' | 'digitalocean'; | ||||
|  | ||||
|     /** | ||||
|      * what servers are expected to be part of the cluster | ||||
|      * Nodes that are part of the cluster | ||||
|      */ | ||||
|     servers: IServer[]; | ||||
|     nodes: IClusterNode[]; | ||||
|  | ||||
|     /** | ||||
|      * ACME info. This is used to get SSL certificates. | ||||
|   | ||||
							
								
								
									
										71
									
								
								ts_interfaces/data/clusternode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								ts_interfaces/data/clusternode.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
|  | ||||
| export interface IClusterNodeMetrics { | ||||
|   cpuUsagePercent: number; | ||||
|   memoryUsedMB: number; | ||||
|   memoryAvailableMB: number; | ||||
|   diskUsedGB: number; | ||||
|   diskAvailableGB: number; | ||||
|   containerCount: number; | ||||
|   timestamp: number; | ||||
| } | ||||
|  | ||||
| export interface IClusterNode { | ||||
|   id: string; | ||||
|   data: { | ||||
|     /** | ||||
|      * Reference to the cluster this node belongs to | ||||
|      */ | ||||
|     clusterId: string; | ||||
|      | ||||
|     /** | ||||
|      * Reference to the physical server (if applicable) | ||||
|      */ | ||||
|     baremetalId?: string; | ||||
|      | ||||
|     /** | ||||
|      * Type of node | ||||
|      */ | ||||
|     nodeType: 'baremetal' | 'vm' | 'container'; | ||||
|      | ||||
|     /** | ||||
|      * Current status of the node | ||||
|      */ | ||||
|     status: 'initializing' | 'online' | 'offline' | 'maintenance'; | ||||
|      | ||||
|     /** | ||||
|      * Role of the node in the cluster | ||||
|      */ | ||||
|     role: 'master' | 'worker'; | ||||
|      | ||||
|     /** | ||||
|      * Timestamp when node joined the cluster | ||||
|      */ | ||||
|     joinedAt: number; | ||||
|      | ||||
|     /** | ||||
|      * Last health check timestamp | ||||
|      */ | ||||
|     lastHealthCheck: number; | ||||
|      | ||||
|     /** | ||||
|      * Current metrics for the node | ||||
|      */ | ||||
|     metrics?: IClusterNodeMetrics; | ||||
|      | ||||
|     /** | ||||
|      * Docker swarm node ID if part of swarm | ||||
|      */ | ||||
|     swarmNodeId?: string; | ||||
|      | ||||
|     /** | ||||
|      * SSH keys deployed to this node | ||||
|      */ | ||||
|     sshKeys: plugins.tsclass.network.ISshKey[]; | ||||
|      | ||||
|     /** | ||||
|      * Debian packages installed on this node | ||||
|      */ | ||||
|     requiredDebianPackages: string[]; | ||||
|   }; | ||||
| } | ||||
| @@ -6,8 +6,58 @@ import * as plugins from '../plugins.js'; | ||||
|  */ | ||||
| export interface IDeployment { | ||||
|   id: string; | ||||
|   affectedServiceIds: string[]; | ||||
|    | ||||
|   /** | ||||
|    * The service being deployed (single service per deployment) | ||||
|    */ | ||||
|   serviceId: string; | ||||
|    | ||||
|   /** | ||||
|    * The node this deployment is running on | ||||
|    */ | ||||
|   nodeId: string; | ||||
|    | ||||
|   /** | ||||
|    * Docker container ID for this deployment | ||||
|    */ | ||||
|   containerId?: string; | ||||
|    | ||||
|   /** | ||||
|    * Image used for this deployment | ||||
|    */ | ||||
|   usedImageId: string; | ||||
|    | ||||
|   /** | ||||
|    * Version of the service deployed | ||||
|    */ | ||||
|   version: string; | ||||
|    | ||||
|   /** | ||||
|    * Timestamp when deployed | ||||
|    */ | ||||
|   deployedAt: number; | ||||
|    | ||||
|   /** | ||||
|    * Deployment log entries | ||||
|    */ | ||||
|   deploymentLog: string[]; | ||||
|   status: 'scheduled' | 'running' | 'deployed' | 'failed'; | ||||
|    | ||||
|   /** | ||||
|    * Current status of the deployment | ||||
|    */ | ||||
|   status: 'scheduled' | 'starting' | 'running' | 'stopping' | 'stopped' | 'failed'; | ||||
|    | ||||
|   /** | ||||
|    * Health status of the deployment | ||||
|    */ | ||||
|   healthStatus?: 'healthy' | 'unhealthy' | 'unknown'; | ||||
|    | ||||
|   /** | ||||
|    * Resource usage for this deployment | ||||
|    */ | ||||
|   resourceUsage?: { | ||||
|     cpuUsagePercent: number; | ||||
|     memoryUsedMB: number; | ||||
|     lastUpdated: number; | ||||
|   }; | ||||
| } | ||||
| @@ -7,8 +7,10 @@ export * from './event.js'; | ||||
| export * from './externalregistry.js'; | ||||
| export * from './image.js'; | ||||
| export * from './secretbundle.js'; | ||||
| export * from './secretgroup.js' | ||||
| export * from './server.js'; | ||||
| export * from './secretgroup.js'; | ||||
| export * from './baremetal.js'; | ||||
| export * from './clusternode.js'; | ||||
| export * from './settings.js'; | ||||
| export * from './service.js'; | ||||
| export * from './status.js'; | ||||
| export * from './traffic.js'; | ||||
|   | ||||
| @@ -17,6 +17,35 @@ export interface IService { | ||||
|      * and thus live past the service lifecycle | ||||
|      */ | ||||
|     additionalSecretBundleIds?: string[]; | ||||
|      | ||||
|     /** | ||||
|      * Service category determines deployment behavior | ||||
|      * - base: Core services that run on every node (coreflow, coretraffic, corelog) | ||||
|      * - distributed: Services that run on limited nodes (cores3, coremongo) | ||||
|      * - workload: User applications | ||||
|      */ | ||||
|     serviceCategory: 'base' | 'distributed' | 'workload'; | ||||
|      | ||||
|     /** | ||||
|      * Deployment strategy for the service | ||||
|      * - all-nodes: Deploy to every node in the cluster | ||||
|      * - limited-replicas: Deploy to a limited number of nodes | ||||
|      * - custom: Custom deployment logic | ||||
|      */ | ||||
|     deploymentStrategy: 'all-nodes' | 'limited-replicas' | 'custom'; | ||||
|      | ||||
|     /** | ||||
|      * Maximum number of replicas for distributed services | ||||
|      * For example, 3 for cores3 or coremongo | ||||
|      */ | ||||
|     maxReplicas?: number; | ||||
|      | ||||
|     /** | ||||
|      * Whether to enforce anti-affinity rules | ||||
|      * When true, tries to spread deployments across different BareMetal servers | ||||
|      */ | ||||
|     antiAffinity?: boolean; | ||||
|      | ||||
|     scaleFactor: number; | ||||
|     balancingStrategy: 'round-robin' | 'least-connections'; | ||||
|     ports: { | ||||
|   | ||||
							
								
								
									
										56
									
								
								ts_interfaces/data/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								ts_interfaces/data/settings.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
|  | ||||
| /** | ||||
|  * Interface for Cloudly settings stored in EasyStore | ||||
|  * These are runtime-configurable settings that can be modified via the UI | ||||
|  */ | ||||
| export interface ICloudlySettings { | ||||
|   // Cloud Provider Tokens | ||||
|   hetznerToken?: string; | ||||
|   cloudflareToken?: string; | ||||
|    | ||||
|   // AWS Credentials | ||||
|   awsAccessKey?: string; | ||||
|   awsSecretKey?: string; | ||||
|   awsRegion?: string; | ||||
|    | ||||
|   // DigitalOcean | ||||
|   digitalOceanToken?: string; | ||||
|    | ||||
|   // Azure Credentials | ||||
|   azureClientId?: string; | ||||
|   azureClientSecret?: string; | ||||
|   azureTenantId?: string; | ||||
|   azureSubscriptionId?: string; | ||||
|    | ||||
|   // Google Cloud | ||||
|   googleCloudKeyJson?: string; | ||||
|   googleCloudProjectId?: string; | ||||
|    | ||||
|   // Vultr | ||||
|   vultrApiKey?: string; | ||||
|    | ||||
|   // Linode | ||||
|   linodeToken?: string; | ||||
|    | ||||
|   // OVH | ||||
|   ovhApplicationKey?: string; | ||||
|   ovhApplicationSecret?: string; | ||||
|   ovhConsumerKey?: string; | ||||
|    | ||||
|   // Scaleway | ||||
|   scalewayAccessKey?: string; | ||||
|   scalewaySecretKey?: string; | ||||
|   scalewayOrganizationId?: string; | ||||
|    | ||||
|   // Other settings that might be added in the future | ||||
|   [key: string]: string | undefined; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Interface for masked settings (used in API responses) | ||||
|  * Shows only last 4 characters of sensitive tokens | ||||
|  */ | ||||
| export type ICloudlySettingsMasked = { | ||||
|   [K in keyof ICloudlySettings]: string | undefined; | ||||
| }; | ||||
							
								
								
									
										22
									
								
								ts_interfaces/requests/baremetal.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								ts_interfaces/requests/baremetal.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
| import type { IBareMetal } from '../data/baremetal.js'; | ||||
|  | ||||
| export interface IRequest_Any_Cloudly_GetBaremetalServers { | ||||
|   method: 'getBaremetalServers'; | ||||
|   request: {}; | ||||
|   response: { | ||||
|     baremetals: IBareMetal[]; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface IRequest_Any_Cloudly_ControlBaremetal { | ||||
|   method: 'controlBaremetal'; | ||||
|   request: { | ||||
|     baremetalId: string; | ||||
|     action: 'powerOn' | 'powerOff' | 'reset'; | ||||
|   }; | ||||
|   response: { | ||||
|     success: boolean; | ||||
|     message: string; | ||||
|   }; | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
|  | ||||
| import * as adminRequests from './admin.js'; | ||||
| import * as baremetalRequests from './baremetal.js'; | ||||
| import * as certificateRequests from './certificate.js'; | ||||
| import * as clusterRequests from './cluster.js'; | ||||
| import * as configRequests from './config.js'; | ||||
| @@ -10,16 +11,19 @@ import * as imageRequests from './image.js'; | ||||
| import * as informRequests from './inform.js'; | ||||
| import * as logRequests from './log.js'; | ||||
| import * as networkRequests from './network.js'; | ||||
| import * as nodeRequests from './node.js'; | ||||
| import * as routingRequests from './routing.js'; | ||||
| import * as secretBundleRequests from './secretbundle.js'; | ||||
| import * as secretGroupRequests from './secretgroup.js';  | ||||
| import * as serverRequests from './server.js'; | ||||
| import * as serviceRequests from './service.js'; | ||||
| import * as settingsRequests from './settings.js'; | ||||
| import * as statusRequests from './status.js'; | ||||
| import * as versionRequests from './version.js'; | ||||
|  | ||||
| export { | ||||
|   adminRequests as admin, | ||||
|   baremetalRequests as baremetal, | ||||
|   certificateRequests as certificate, | ||||
|   clusterRequests as cluster, | ||||
|   configRequests as config, | ||||
| @@ -29,11 +33,13 @@ export { | ||||
|   informRequests as inform, | ||||
|   logRequests as log, | ||||
|   networkRequests as network, | ||||
|   nodeRequests as node, | ||||
|   routingRequests as routing, | ||||
|   secretBundleRequests as secretbundle, | ||||
|   secretGroupRequests as secretgroup, | ||||
|   serverRequests as server, | ||||
|   serviceRequests as service, | ||||
|   settingsRequests as settings, | ||||
|   statusRequests as status, | ||||
|   versionRequests as version, | ||||
| }; | ||||
|   | ||||
							
								
								
									
										33
									
								
								ts_interfaces/requests/node.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ts_interfaces/requests/node.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
| import type { IClusterNode } from '../data/clusternode.js'; | ||||
| import type { IDeployment } from '../data/deployment.js'; | ||||
|  | ||||
| export interface IRequest_Any_Cloudly_GetNodeConfig { | ||||
|   method: 'getNodeConfig'; | ||||
|   request: { | ||||
|     nodeId: string; | ||||
|   }; | ||||
|   response: { | ||||
|     configData: IClusterNode; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface IRequest_Any_Cloudly_GetNodesByCluster { | ||||
|   method: 'getNodesByCluster'; | ||||
|   request: { | ||||
|     clusterId: string; | ||||
|   }; | ||||
|   response: { | ||||
|     nodes: IClusterNode[]; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export interface IRequest_Any_Cloudly_GetNodeDeployments { | ||||
|   method: 'getNodeDeployments'; | ||||
|   request: { | ||||
|     nodeId: string; | ||||
|   }; | ||||
|   response: { | ||||
|     deployments: IDeployment[]; | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										59
									
								
								ts_interfaces/requests/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								ts_interfaces/requests/settings.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import * as plugins from '../plugins.js'; | ||||
| import type { ICloudlySettings, ICloudlySettingsMasked } from '../data/settings.js'; | ||||
|  | ||||
| // Get Settings | ||||
| export interface IRequest_GetSettings extends plugins.typedrequestInterfaces.ITypedRequest { | ||||
|   method: 'getSettings'; | ||||
|   request: {}; | ||||
|   response: { | ||||
|     settings: ICloudlySettingsMasked; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| // Update Settings | ||||
| export interface IRequest_UpdateSettings extends plugins.typedrequestInterfaces.ITypedRequest { | ||||
|   method: 'updateSettings'; | ||||
|   request: { | ||||
|     updates: Partial<ICloudlySettings>; | ||||
|   }; | ||||
|   response: { | ||||
|     success: boolean; | ||||
|     message: string; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| // Clear Specific Setting | ||||
| export interface IRequest_ClearSetting extends plugins.typedrequestInterfaces.ITypedRequest { | ||||
|   method: 'clearSetting'; | ||||
|   request: { | ||||
|     key: keyof ICloudlySettings; | ||||
|   }; | ||||
|   response: { | ||||
|     success: boolean; | ||||
|     message: string; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| // Test Provider Connection | ||||
| export interface IRequest_TestProviderConnection extends plugins.typedrequestInterfaces.ITypedRequest { | ||||
|   method: 'testProviderConnection'; | ||||
|   request: { | ||||
|     provider: 'hetzner' | 'cloudflare' | 'aws' | 'digitalocean' | 'azure' | 'google' | 'vultr' | 'linode' | 'ovh' | 'scaleway'; | ||||
|   }; | ||||
|   response: { | ||||
|     success: boolean; | ||||
|     message: string; | ||||
|     connectionValid: boolean; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| // Get Single Setting (for internal use, not exposed to frontend) | ||||
| export interface IRequest_GetSetting extends plugins.typedrequestInterfaces.ITypedRequest { | ||||
|   method: 'getSetting'; | ||||
|   request: { | ||||
|     key: keyof ICloudlySettings; | ||||
|   }; | ||||
|   response: { | ||||
|     value: string | undefined; | ||||
|   }; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user