311 lines
9.2 KiB
TypeScript
311 lines
9.2 KiB
TypeScript
/**
|
|
* Main Onebox coordinator class
|
|
*
|
|
* Coordinates all components and provides the main API
|
|
*/
|
|
|
|
import { logger } from '../logging.ts';
|
|
import { getErrorMessage } from '../utils/error.ts';
|
|
import { OneboxDatabase } from './database.ts';
|
|
import { OneboxDockerManager } from './docker.ts';
|
|
import { OneboxServicesManager } from './services.ts';
|
|
import { OneboxRegistriesManager } from './registries.ts';
|
|
import { OneboxReverseProxy } from './reverseproxy.ts';
|
|
import { OneboxDnsManager } from './dns.ts';
|
|
import { OneboxSslManager } from './ssl.ts';
|
|
import { OneboxDaemon } from './daemon.ts';
|
|
import { OneboxHttpServer } from './httpserver.ts';
|
|
import { CloudflareDomainSync } from './cloudflare-sync.ts';
|
|
import { CertRequirementManager } from './cert-requirement-manager.ts';
|
|
import { RegistryManager } from './registry.ts';
|
|
import { PlatformServicesManager } from './platform-services/index.ts';
|
|
import { CaddyLogReceiver } from './caddy-log-receiver.ts';
|
|
|
|
export class Onebox {
|
|
public database: OneboxDatabase;
|
|
public docker: OneboxDockerManager;
|
|
public services: OneboxServicesManager;
|
|
public registries: OneboxRegistriesManager;
|
|
public reverseProxy: OneboxReverseProxy;
|
|
public dns: OneboxDnsManager;
|
|
public ssl: OneboxSslManager;
|
|
public daemon: OneboxDaemon;
|
|
public httpServer: OneboxHttpServer;
|
|
public cloudflareDomainSync: CloudflareDomainSync;
|
|
public certRequirementManager: CertRequirementManager;
|
|
public registry: RegistryManager;
|
|
public platformServices: PlatformServicesManager;
|
|
public caddyLogReceiver: CaddyLogReceiver;
|
|
|
|
private initialized = false;
|
|
|
|
constructor() {
|
|
// Initialize database first
|
|
this.database = new OneboxDatabase();
|
|
|
|
// Initialize managers (passing reference to main Onebox instance)
|
|
this.docker = new OneboxDockerManager();
|
|
this.services = new OneboxServicesManager(this);
|
|
this.registries = new OneboxRegistriesManager(this);
|
|
this.reverseProxy = new OneboxReverseProxy(this);
|
|
this.dns = new OneboxDnsManager(this);
|
|
this.ssl = new OneboxSslManager(this);
|
|
this.daemon = new OneboxDaemon(this);
|
|
this.httpServer = new OneboxHttpServer(this);
|
|
this.registry = new RegistryManager({
|
|
dataDir: './.nogit/registry-data',
|
|
port: 4000,
|
|
baseUrl: 'localhost:5000',
|
|
});
|
|
|
|
// Initialize domain management
|
|
this.cloudflareDomainSync = new CloudflareDomainSync(this.database);
|
|
this.certRequirementManager = new CertRequirementManager(this.database, this.ssl);
|
|
|
|
// Initialize platform services manager
|
|
this.platformServices = new PlatformServicesManager(this);
|
|
|
|
// Initialize Caddy log receiver
|
|
this.caddyLogReceiver = new CaddyLogReceiver(9999);
|
|
}
|
|
|
|
/**
|
|
* Initialize all components
|
|
*/
|
|
async init(): Promise<void> {
|
|
try {
|
|
logger.info('Initializing Onebox...');
|
|
|
|
// Initialize database
|
|
await this.database.init();
|
|
|
|
// Ensure default admin user exists
|
|
await this.ensureDefaultUser();
|
|
|
|
// Initialize Docker
|
|
await this.docker.init();
|
|
|
|
// Start Caddy log receiver BEFORE reverse proxy (so Caddy can connect to it)
|
|
try {
|
|
await this.caddyLogReceiver.start();
|
|
} catch (error) {
|
|
logger.warn(`Failed to start Caddy log receiver: ${getErrorMessage(error)}`);
|
|
}
|
|
|
|
// Initialize Reverse Proxy
|
|
await this.reverseProxy.init();
|
|
|
|
// Load routes and certificates for reverse proxy
|
|
await this.reverseProxy.reloadRoutes();
|
|
await this.reverseProxy.reloadCertificates();
|
|
|
|
// Start HTTP reverse proxy (non-critical - don't fail init if ports are busy)
|
|
// Use 8080/8443 in dev mode to avoid permission issues
|
|
const isDev = Deno.env.get('ONEBOX_DEV') === 'true' || Deno.args.includes('--ephemeral');
|
|
const httpPort = isDev ? 8080 : 80;
|
|
const httpsPort = isDev ? 8443 : 443;
|
|
|
|
try {
|
|
await this.reverseProxy.startHttp(httpPort);
|
|
} catch (error) {
|
|
logger.warn(`Failed to start HTTP reverse proxy: ${getErrorMessage(error)}`);
|
|
}
|
|
|
|
// Start HTTPS reverse proxy if certificates are available
|
|
try {
|
|
await this.reverseProxy.startHttps(httpsPort);
|
|
} catch (error) {
|
|
logger.warn(`Failed to start HTTPS reverse proxy: ${getErrorMessage(error)}`);
|
|
}
|
|
|
|
// Initialize DNS (non-critical)
|
|
try {
|
|
await this.dns.init();
|
|
} catch (error) {
|
|
logger.warn('DNS initialization failed - DNS features will be disabled');
|
|
}
|
|
|
|
// Initialize SSL (non-critical)
|
|
try {
|
|
await this.ssl.init();
|
|
} catch (error) {
|
|
logger.warn('SSL initialization failed - SSL features will be limited');
|
|
}
|
|
|
|
// Initialize Cloudflare domain sync (non-critical)
|
|
try {
|
|
await this.cloudflareDomainSync.init();
|
|
} catch (error) {
|
|
logger.warn('Cloudflare domain sync initialization failed - domain sync will be limited');
|
|
}
|
|
|
|
// Initialize Onebox Registry (non-critical)
|
|
try {
|
|
await this.registry.init();
|
|
} catch (error) {
|
|
logger.warn('Onebox Registry initialization failed - local registry will be disabled');
|
|
logger.warn(`Error: ${getErrorMessage(error)}`);
|
|
}
|
|
|
|
// Initialize Platform Services (non-critical)
|
|
try {
|
|
await this.platformServices.init();
|
|
} catch (error) {
|
|
logger.warn('Platform services initialization failed - MongoDB/S3 features will be limited');
|
|
logger.warn(`Error: ${getErrorMessage(error)}`);
|
|
}
|
|
|
|
// Login to all registries
|
|
await this.registries.loginToAllRegistries();
|
|
|
|
// Start auto-update monitoring for registry services
|
|
this.services.startAutoUpdateMonitoring();
|
|
|
|
this.initialized = true;
|
|
logger.success('Onebox initialized successfully');
|
|
} catch (error) {
|
|
logger.error(`Failed to initialize Onebox: ${getErrorMessage(error)}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure default admin user exists
|
|
*/
|
|
private async ensureDefaultUser(): Promise<void> {
|
|
try {
|
|
const adminUser = this.database.getUserByUsername('admin');
|
|
|
|
if (!adminUser) {
|
|
logger.info('Creating default admin user...');
|
|
|
|
// Simple base64 encoding for now - should use bcrypt in production
|
|
const passwordHash = btoa('admin');
|
|
|
|
await this.database.createUser({
|
|
username: 'admin',
|
|
passwordHash,
|
|
role: 'admin',
|
|
createdAt: Date.now(),
|
|
updatedAt: Date.now(),
|
|
});
|
|
|
|
logger.warn('Default admin user created with username: admin, password: admin');
|
|
logger.warn('IMPORTANT: Change the default password immediately!');
|
|
}
|
|
} catch (error) {
|
|
logger.error(`Failed to create default user: ${getErrorMessage(error)}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if Onebox is initialized
|
|
*/
|
|
isInitialized(): boolean {
|
|
return this.initialized;
|
|
}
|
|
|
|
/**
|
|
* Get system status
|
|
*/
|
|
async getSystemStatus() {
|
|
try {
|
|
const dockerRunning = await this.docker.isDockerRunning();
|
|
const proxyStatus = this.reverseProxy.getStatus();
|
|
const dnsConfigured = this.dns.isConfigured();
|
|
const sslConfigured = this.ssl.isConfigured();
|
|
|
|
const services = this.services.listServices();
|
|
const runningServices = services.filter((s) => s.status === 'running').length;
|
|
const totalServices = services.length;
|
|
|
|
// Get platform services status
|
|
const platformServices = this.platformServices.getAllPlatformServices();
|
|
const platformServicesStatus = platformServices.map((ps) => ({
|
|
type: ps.type,
|
|
status: ps.status,
|
|
}));
|
|
|
|
return {
|
|
docker: {
|
|
running: dockerRunning,
|
|
version: dockerRunning ? await this.docker.getDockerVersion() : null,
|
|
},
|
|
reverseProxy: proxyStatus,
|
|
dns: {
|
|
configured: dnsConfigured,
|
|
},
|
|
ssl: {
|
|
configured: sslConfigured,
|
|
certificateCount: this.ssl.listCertificates().length,
|
|
},
|
|
services: {
|
|
total: totalServices,
|
|
running: runningServices,
|
|
stopped: totalServices - runningServices,
|
|
},
|
|
platformServices: platformServicesStatus,
|
|
};
|
|
} catch (error) {
|
|
logger.error(`Failed to get system status: ${getErrorMessage(error)}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start daemon mode
|
|
*/
|
|
async startDaemon(): Promise<void> {
|
|
await this.daemon.start();
|
|
}
|
|
|
|
/**
|
|
* Stop daemon mode
|
|
*/
|
|
async stopDaemon(): Promise<void> {
|
|
await this.daemon.stop();
|
|
}
|
|
|
|
/**
|
|
* Start HTTP server
|
|
*/
|
|
async startHttpServer(port?: number): Promise<void> {
|
|
await this.httpServer.start(port);
|
|
}
|
|
|
|
/**
|
|
* Stop HTTP server
|
|
*/
|
|
async stopHttpServer(): Promise<void> {
|
|
await this.httpServer.stop();
|
|
}
|
|
|
|
/**
|
|
* Shutdown Onebox gracefully
|
|
*/
|
|
async shutdown(): Promise<void> {
|
|
try {
|
|
logger.info('Shutting down Onebox...');
|
|
|
|
// Stop daemon if running
|
|
await this.daemon.stop();
|
|
|
|
// Stop HTTP server if running
|
|
await this.httpServer.stop();
|
|
|
|
// Stop reverse proxy if running
|
|
await this.reverseProxy.stop();
|
|
|
|
// Stop Caddy log receiver
|
|
await this.caddyLogReceiver.stop();
|
|
|
|
// Close database
|
|
this.database.close();
|
|
|
|
logger.success('Onebox shutdown complete');
|
|
} catch (error) {
|
|
logger.error(`Error during shutdown: ${getErrorMessage(error)}`);
|
|
}
|
|
}
|
|
}
|