- Complete Deno-based architecture following nupst/spark patterns - SQLite database with full schema - Docker container management - Service orchestration (Docker + Nginx + DNS + SSL) - Registry authentication - Nginx reverse proxy configuration - Cloudflare DNS integration - Let's Encrypt SSL automation - Background daemon with metrics collection - HTTP API server - Comprehensive CLI - Cross-platform compilation setup - NPM distribution wrapper - Shell installer script Core features: - Deploy containers with single command - Automatic domain configuration - Automatic SSL certificates - Multi-registry support - Metrics and logging - Systemd integration Ready for Angular UI implementation and testing.
293 lines
7.4 KiB
TypeScript
293 lines
7.4 KiB
TypeScript
/**
|
|
* Daemon Manager for Onebox
|
|
*
|
|
* Handles background monitoring, metrics collection, and automatic tasks
|
|
*/
|
|
|
|
import * as plugins from './onebox.plugins.ts';
|
|
import { logger } from './onebox.logging.ts';
|
|
import { projectInfo } from './onebox.info.ts';
|
|
import type { Onebox } from './onebox.classes.onebox.ts';
|
|
|
|
export class OneboxDaemon {
|
|
private oneboxRef: Onebox;
|
|
private smartdaemon: plugins.smartdaemon.SmartDaemon;
|
|
private running = false;
|
|
private monitoringInterval: number | null = null;
|
|
private metricsInterval = 60000; // 1 minute
|
|
|
|
constructor(oneboxRef: Onebox) {
|
|
this.oneboxRef = oneboxRef;
|
|
this.smartdaemon = new plugins.smartdaemon.SmartDaemon();
|
|
|
|
// Get metrics interval from settings
|
|
const customInterval = this.oneboxRef.database.getSetting('metricsInterval');
|
|
if (customInterval) {
|
|
this.metricsInterval = parseInt(customInterval, 10);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Install systemd service
|
|
*/
|
|
async installService(): Promise<void> {
|
|
try {
|
|
logger.info('Installing Onebox daemon service...');
|
|
|
|
// Get installation directory
|
|
const execPath = Deno.execPath();
|
|
|
|
const service = await this.smartdaemon.addService({
|
|
name: 'onebox',
|
|
version: projectInfo.version,
|
|
command: `${execPath} run --allow-all ${Deno.cwd()}/mod.ts daemon start`,
|
|
description: 'Onebox - Self-hosted container platform',
|
|
workingDir: Deno.cwd(),
|
|
});
|
|
|
|
await service.save();
|
|
await service.enable();
|
|
|
|
logger.success('Onebox daemon service installed');
|
|
logger.info('Start with: sudo systemctl start smartdaemon_onebox');
|
|
} catch (error) {
|
|
logger.error(`Failed to install daemon service: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uninstall systemd service
|
|
*/
|
|
async uninstallService(): Promise<void> {
|
|
try {
|
|
logger.info('Uninstalling Onebox daemon service...');
|
|
|
|
const service = await this.smartdaemon.getService('onebox');
|
|
|
|
if (service) {
|
|
await service.stop();
|
|
await service.disable();
|
|
await service.delete();
|
|
}
|
|
|
|
logger.success('Onebox daemon service uninstalled');
|
|
} catch (error) {
|
|
logger.error(`Failed to uninstall daemon service: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start daemon mode (background monitoring)
|
|
*/
|
|
async start(): Promise<void> {
|
|
try {
|
|
if (this.running) {
|
|
logger.warn('Daemon already running');
|
|
return;
|
|
}
|
|
|
|
logger.info('Starting Onebox daemon...');
|
|
|
|
this.running = true;
|
|
|
|
// Start monitoring loop
|
|
this.startMonitoring();
|
|
|
|
// Start HTTP server
|
|
const httpPort = parseInt(this.oneboxRef.database.getSetting('httpPort') || '3000', 10);
|
|
await this.oneboxRef.httpServer.start(httpPort);
|
|
|
|
logger.success('Onebox daemon started');
|
|
logger.info(`Web UI available at http://localhost:${httpPort}`);
|
|
|
|
// Keep process alive
|
|
await this.keepAlive();
|
|
} catch (error) {
|
|
logger.error(`Failed to start daemon: ${error.message}`);
|
|
this.running = false;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop daemon mode
|
|
*/
|
|
async stop(): Promise<void> {
|
|
try {
|
|
if (!this.running) {
|
|
return;
|
|
}
|
|
|
|
logger.info('Stopping Onebox daemon...');
|
|
|
|
this.running = false;
|
|
|
|
// Stop monitoring
|
|
this.stopMonitoring();
|
|
|
|
// Stop HTTP server
|
|
await this.oneboxRef.httpServer.stop();
|
|
|
|
logger.success('Onebox daemon stopped');
|
|
} catch (error) {
|
|
logger.error(`Failed to stop daemon: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start monitoring loop
|
|
*/
|
|
private startMonitoring(): void {
|
|
logger.info('Starting monitoring loop...');
|
|
|
|
this.monitoringInterval = setInterval(async () => {
|
|
await this.monitoringTick();
|
|
}, this.metricsInterval);
|
|
|
|
// Run first tick immediately
|
|
this.monitoringTick();
|
|
}
|
|
|
|
/**
|
|
* Stop monitoring loop
|
|
*/
|
|
private stopMonitoring(): void {
|
|
if (this.monitoringInterval !== null) {
|
|
clearInterval(this.monitoringInterval);
|
|
this.monitoringInterval = null;
|
|
logger.debug('Monitoring loop stopped');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Single monitoring tick
|
|
*/
|
|
private async monitoringTick(): Promise<void> {
|
|
try {
|
|
logger.debug('Running monitoring tick...');
|
|
|
|
// Collect metrics for all services
|
|
await this.collectMetrics();
|
|
|
|
// Sync service statuses
|
|
await this.oneboxRef.services.syncAllServiceStatuses();
|
|
|
|
// Check SSL certificate expiration
|
|
await this.checkSSLExpiration();
|
|
|
|
// Check service health (TODO: implement health checks)
|
|
|
|
logger.debug('Monitoring tick complete');
|
|
} catch (error) {
|
|
logger.error(`Monitoring tick failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collect metrics for all services
|
|
*/
|
|
private async collectMetrics(): Promise<void> {
|
|
try {
|
|
const services = this.oneboxRef.services.listServices();
|
|
|
|
for (const service of services) {
|
|
if (service.status === 'running' && service.containerID) {
|
|
try {
|
|
const stats = await this.oneboxRef.docker.getContainerStats(service.containerID);
|
|
|
|
if (stats) {
|
|
this.oneboxRef.database.addMetric({
|
|
serviceId: service.id!,
|
|
timestamp: Date.now(),
|
|
cpuPercent: stats.cpuPercent,
|
|
memoryUsed: stats.memoryUsed,
|
|
memoryLimit: stats.memoryLimit,
|
|
networkRxBytes: stats.networkRx,
|
|
networkTxBytes: stats.networkTx,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
logger.debug(`Failed to collect metrics for ${service.name}: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
logger.error(`Failed to collect metrics: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check SSL certificate expiration
|
|
*/
|
|
private async checkSSLExpiration(): Promise<void> {
|
|
try {
|
|
if (!this.oneboxRef.ssl.isConfigured()) {
|
|
return;
|
|
}
|
|
|
|
await this.oneboxRef.ssl.renewExpiring();
|
|
} catch (error) {
|
|
logger.error(`Failed to check SSL expiration: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Keep process alive
|
|
*/
|
|
private async keepAlive(): Promise<void> {
|
|
// Set up signal handlers
|
|
const signalHandler = () => {
|
|
logger.info('Received shutdown signal');
|
|
this.stop().then(() => {
|
|
Deno.exit(0);
|
|
});
|
|
};
|
|
|
|
Deno.addSignalListener('SIGINT', signalHandler);
|
|
Deno.addSignalListener('SIGTERM', signalHandler);
|
|
|
|
// Keep event loop alive
|
|
while (this.running) {
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get daemon status
|
|
*/
|
|
isRunning(): boolean {
|
|
return this.running;
|
|
}
|
|
|
|
/**
|
|
* Get service status from systemd
|
|
*/
|
|
async getServiceStatus(): Promise<string> {
|
|
try {
|
|
const command = new Deno.Command('systemctl', {
|
|
args: ['status', 'smartdaemon_onebox'],
|
|
stdout: 'piped',
|
|
stderr: 'piped',
|
|
});
|
|
|
|
const { code, stdout } = await command.output();
|
|
const output = new TextDecoder().decode(stdout);
|
|
|
|
if (code === 0 || output.includes('active (running)')) {
|
|
return 'running';
|
|
} else if (output.includes('inactive') || output.includes('dead')) {
|
|
return 'stopped';
|
|
} else if (output.includes('failed')) {
|
|
return 'failed';
|
|
} else {
|
|
return 'unknown';
|
|
}
|
|
} catch (error) {
|
|
return 'not-installed';
|
|
}
|
|
}
|
|
}
|