feat(settings): Add runtime settings management, node & baremetal managers, and settings UI

This commit is contained in:
2025-09-07 17:21:30 +00:00
parent 83abe37d8c
commit 54ef62e7af
36 changed files with 1914 additions and 301 deletions

View 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;
};
};
}

View File

@@ -1,8 +1,6 @@
import * as plugins from '../plugins.js';
export interface ICloudlyConfig {
cfToken?: string;
hetznerToken?: string;
environment?: 'production' | 'integration';
letsEncryptEmail?: string;
letsEncryptPrivateKey?: string;

View File

@@ -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.

View 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[];
};
}

View File

@@ -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;
};
}

View File

@@ -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';

View File

@@ -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: {

View 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;
};

View 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;
};
}

View File

@@ -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,
};

View 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[];
};
}

View 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;
};
}