UNPKG

@git.zone/cli

Version:

A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.

584 lines (504 loc) 20.6 kB
import * as plugins from './mod.plugins.js'; import * as helpers from './helpers.js'; import { ServiceConfiguration } from './classes.serviceconfiguration.js'; import { DockerContainer } from './classes.dockercontainer.js'; import { logger } from '../gitzone.logging.js'; export class ServiceManager { private config: ServiceConfiguration; private docker: DockerContainer; constructor() { this.config = new ServiceConfiguration(); this.docker = new DockerContainer(); } /** * Initialize the service manager */ public async init(): Promise<void> { // Check Docker availability if (!(await this.docker.checkDocker())) { logger.log('error', 'Error: Docker is not installed. Please install Docker first.'); process.exit(1); } // Load or create configuration await this.config.loadOrCreate(); logger.log('info', `📋 Project: ${this.config.getConfig().PROJECT_NAME}`); // Validate and update ports if needed await this.config.validateAndUpdatePorts(); } /** * Start MongoDB service */ public async startMongoDB(): Promise<void> { logger.log('note', '📦 MongoDB:'); const config = this.config.getConfig(); const containers = this.config.getContainerNames(); const directories = this.config.getDataDirectories(); // Ensure data directory exists await plugins.smartfile.fs.ensureDir(directories.mongo); const status = await this.docker.getStatus(containers.mongo); switch (status) { case 'running': logger.log('ok', ' Already running ✓'); break; case 'stopped': // Check if port mapping matches config const mongoPortMappings = await this.docker.getPortMappings(containers.mongo); if (mongoPortMappings && mongoPortMappings['27017'] !== config.MONGODB_PORT) { logger.log('note', ' Port configuration changed, recreating container...'); await this.docker.remove(containers.mongo, true); // Fall through to create new container const success = await this.docker.run({ name: containers.mongo, image: 'mongo:7.0', ports: { [`0.0.0.0:${config.MONGODB_PORT}`]: '27017' }, volumes: { [directories.mongo]: '/data/db' }, environment: { MONGO_INITDB_ROOT_USERNAME: config.MONGODB_USER, MONGO_INITDB_ROOT_PASSWORD: config.MONGODB_PASS, MONGO_INITDB_DATABASE: config.MONGODB_NAME }, restart: 'unless-stopped', command: '--bind_ip_all' }); if (success) { logger.log('ok', ' Recreated with new port ✓'); } else { logger.log('error', ' Failed to recreate container'); } } else { // Ports match, just start the container if (await this.docker.start(containers.mongo)) { logger.log('ok', ' Started ✓'); } else { logger.log('error', ' Failed to start'); } } break; case 'not_exists': logger.log('note', ' Creating container...'); const success = await this.docker.run({ name: containers.mongo, image: 'mongo:7.0', ports: { [`0.0.0.0:${config.MONGODB_PORT}`]: '27017' }, volumes: { [directories.mongo]: '/data/db' }, environment: { MONGO_INITDB_ROOT_USERNAME: config.MONGODB_USER, MONGO_INITDB_ROOT_PASSWORD: config.MONGODB_PASS, MONGO_INITDB_DATABASE: config.MONGODB_NAME }, restart: 'unless-stopped', command: '--bind_ip_all' }); if (success) { logger.log('ok', ' Created and started ✓'); } else { logger.log('error', ' Failed to create container'); } break; } logger.log('info', ` Container: ${containers.mongo}`); logger.log('info', ` Port: ${config.MONGODB_PORT}`); logger.log('info', ` Connection: ${this.config.getMongoConnectionString()}`); // Show Compass connection string const networkIp = await helpers.getLocalNetworkIp(); const compassString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`; logger.log('ok', ` Compass: ${compassString}`); } /** * Start MinIO service */ public async startMinIO(): Promise<void> { logger.log('note', '📦 S3/MinIO:'); const config = this.config.getConfig(); const containers = this.config.getContainerNames(); const directories = this.config.getDataDirectories(); // Ensure data directory exists await plugins.smartfile.fs.ensureDir(directories.minio); const status = await this.docker.getStatus(containers.minio); switch (status) { case 'running': logger.log('ok', ' Already running ✓'); break; case 'stopped': // Check if port mapping matches config const minioPortMappings = await this.docker.getPortMappings(containers.minio); if (minioPortMappings && (minioPortMappings['9000'] !== config.S3_PORT || minioPortMappings['9001'] !== config.S3_CONSOLE_PORT)) { logger.log('note', ' Port configuration changed, recreating container...'); await this.docker.remove(containers.minio, true); // Fall through to create new container const success = await this.docker.run({ name: containers.minio, image: 'minio/minio', ports: { [config.S3_PORT]: '9000', [config.S3_CONSOLE_PORT]: '9001' }, volumes: { [directories.minio]: '/data' }, environment: { MINIO_ROOT_USER: config.S3_ACCESSKEY, MINIO_ROOT_PASSWORD: config.S3_SECRETKEY }, restart: 'unless-stopped', command: 'server /data --console-address ":9001"' }); if (success) { logger.log('ok', ' Recreated with new ports ✓'); // Wait for MinIO to be ready await plugins.smartdelay.delayFor(3000); // Create default bucket await this.docker.exec( containers.minio, `mc alias set local http://localhost:9000 ${config.S3_ACCESSKEY} ${config.S3_SECRETKEY}` ); await this.docker.exec( containers.minio, `mc mb local/${config.S3_BUCKET}` ); logger.log('ok', ` Bucket '${config.S3_BUCKET}' created ✓`); } else { logger.log('error', ' Failed to recreate container'); } } else { // Ports match, just start the container if (await this.docker.start(containers.minio)) { logger.log('ok', ' Started ✓'); } else { logger.log('error', ' Failed to start'); } } break; case 'not_exists': logger.log('note', ' Creating container...'); const success = await this.docker.run({ name: containers.minio, image: 'minio/minio', ports: { [config.S3_PORT]: '9000', [config.S3_CONSOLE_PORT]: '9001' }, volumes: { [directories.minio]: '/data' }, environment: { MINIO_ROOT_USER: config.S3_ACCESSKEY, MINIO_ROOT_PASSWORD: config.S3_SECRETKEY }, restart: 'unless-stopped', command: 'server /data --console-address ":9001"' }); if (success) { logger.log('ok', ' Created and started ✓'); // Wait for MinIO to be ready await plugins.smartdelay.delayFor(3000); // Create default bucket await this.docker.exec( containers.minio, `mc alias set local http://localhost:9000 ${config.S3_ACCESSKEY} ${config.S3_SECRETKEY}` ); await this.docker.exec( containers.minio, `mc mb local/${config.S3_BUCKET}` ); logger.log('ok', ` Bucket '${config.S3_BUCKET}' created ✓`); } else { logger.log('error', ' Failed to create container'); } break; } logger.log('info', ` Container: ${containers.minio}`); logger.log('info', ` Port: ${config.S3_PORT}`); logger.log('info', ` Bucket: ${config.S3_BUCKET}`); logger.log('info', ` API: http://${config.S3_HOST}:${config.S3_PORT}`); logger.log('info', ` Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT} (login: ${config.S3_ACCESSKEY}/***)`); } /** * Stop MongoDB service */ public async stopMongoDB(): Promise<void> { logger.log('note', '📦 MongoDB:'); const containers = this.config.getContainerNames(); const status = await this.docker.getStatus(containers.mongo); if (status === 'running') { if (await this.docker.stop(containers.mongo)) { logger.log('ok', ' Stopped ✓'); } else { logger.log('error', ' Failed to stop'); } } else { logger.log('note', ' Not running'); } } /** * Stop MinIO service */ public async stopMinIO(): Promise<void> { logger.log('note', '📦 S3/MinIO:'); const containers = this.config.getContainerNames(); const status = await this.docker.getStatus(containers.minio); if (status === 'running') { if (await this.docker.stop(containers.minio)) { logger.log('ok', ' Stopped ✓'); } else { logger.log('error', ' Failed to stop'); } } else { logger.log('note', ' Not running'); } } /** * Show service status */ public async showStatus(): Promise<void> { helpers.printHeader('Service Status'); const config = this.config.getConfig(); const containers = this.config.getContainerNames(); logger.log('info', `Project: ${config.PROJECT_NAME}`); console.log(); // MongoDB status const mongoStatus = await this.docker.getStatus(containers.mongo); switch (mongoStatus) { case 'running': logger.log('ok', '📦 MongoDB: 🟢 Running'); logger.log('info', ` ├─ Container: ${containers.mongo}`); logger.log('info', ` ├─ Port: ${config.MONGODB_PORT}`); logger.log('info', ` ├─ Connection: ${this.config.getMongoConnectionString()}`); // Show Compass connection string const networkIp = await helpers.getLocalNetworkIp(); const compassString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`; logger.log('ok', ` └─ Compass: ${compassString}`); break; case 'stopped': logger.log('note', '📦 MongoDB: 🟡 Stopped'); logger.log('info', ` ├─ Container: ${containers.mongo}`); logger.log('info', ` └─ Port: ${config.MONGODB_PORT}`); break; case 'not_exists': logger.log('info', '📦 MongoDB: ⚪ Not installed'); // Check port availability const mongoPort = parseInt(config.MONGODB_PORT); const mongoAvailable = await helpers.isPortAvailable(mongoPort); if (!mongoAvailable) { logger.log('error', ` └─ ⚠️ Port ${mongoPort} is in use by another process`); } else { logger.log('info', ` └─ Port ${mongoPort} is available`); } break; } // MinIO status const minioStatus = await this.docker.getStatus(containers.minio); switch (minioStatus) { case 'running': logger.log('ok', '📦 S3/MinIO: 🟢 Running'); logger.log('info', ` ├─ Container: ${containers.minio}`); logger.log('info', ` ├─ API: http://${config.S3_HOST}:${config.S3_PORT}`); logger.log('info', ` ├─ Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`); logger.log('info', ` └─ Bucket: ${config.S3_BUCKET}`); break; case 'stopped': logger.log('note', '📦 S3/MinIO: 🟡 Stopped'); logger.log('info', ` ├─ Container: ${containers.minio}`); logger.log('info', ` ├─ API Port: ${config.S3_PORT}`); logger.log('info', ` └─ Console Port: ${config.S3_CONSOLE_PORT}`); break; case 'not_exists': logger.log('info', '📦 S3/MinIO: ⚪ Not installed'); // Check port availability const s3Port = parseInt(config.S3_PORT); const s3ConsolePort = parseInt(config.S3_CONSOLE_PORT); const s3Available = await helpers.isPortAvailable(s3Port); const consoleAvailable = await helpers.isPortAvailable(s3ConsolePort); if (!s3Available || !consoleAvailable) { if (!s3Available) { logger.log('error', ` ├─ ⚠️ API Port ${s3Port} is in use`); } else { logger.log('info', ` ├─ API Port ${s3Port} is available`); } if (!consoleAvailable) { logger.log('error', ` └─ ⚠️ Console Port ${s3ConsolePort} is in use`); } else { logger.log('info', ` └─ Console Port ${s3ConsolePort} is available`); } } else { logger.log('info', ` ├─ API Port ${s3Port} is available`); logger.log('info', ` └─ Console Port ${s3ConsolePort} is available`); } break; } } /** * Show configuration */ public async showConfig(): Promise<void> { helpers.printHeader('Current Configuration'); const config = this.config.getConfig(); logger.log('info', `Project: ${config.PROJECT_NAME}`); console.log(); logger.log('note', 'MongoDB:'); logger.log('info', ` Host: ${config.MONGODB_HOST}:${config.MONGODB_PORT}`); logger.log('info', ` Database: ${config.MONGODB_NAME}`); logger.log('info', ` User: ${config.MONGODB_USER}`); logger.log('info', ' Password: ***'); logger.log('info', ` Container: ${this.config.getContainerNames().mongo}`); logger.log('info', ` Data: ${this.config.getDataDirectories().mongo}`); logger.log('info', ` Connection: ${this.config.getMongoConnectionString()}`); console.log(); logger.log('note', 'S3/MinIO:'); logger.log('info', ` Host: ${config.S3_HOST}`); logger.log('info', ` API Port: ${config.S3_PORT}`); logger.log('info', ` Console Port: ${config.S3_CONSOLE_PORT}`); logger.log('info', ` Access Key: ${config.S3_ACCESSKEY}`); logger.log('info', ' Secret Key: ***'); logger.log('info', ` Bucket: ${config.S3_BUCKET}`); logger.log('info', ` Use SSL: ${config.S3_USESSL}`); logger.log('info', ` Container: ${this.config.getContainerNames().minio}`); logger.log('info', ` Data: ${this.config.getDataDirectories().minio}`); logger.log('info', ` Endpoint: ${config.S3_ENDPOINT}`); logger.log('info', ` Console URL: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`); } /** * Show MongoDB Compass connection string */ public async showCompassConnection(): Promise<void> { helpers.printHeader('MongoDB Compass Connection'); const config = this.config.getConfig(); const networkIp = await helpers.getLocalNetworkIp(); const connectionString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`; logger.log('info', 'MongoDB Compass is a GUI tool for MongoDB. To connect:'); console.log(); logger.log('info', '1. Download MongoDB Compass from:'); logger.log('info', ' https://www.mongodb.com/products/compass'); console.log(); logger.log('info', '2. Open Compass and paste this connection string:'); logger.log('ok', ` ${connectionString}`); console.log(); logger.log('note', 'Connection Details:'); logger.log('info', ` Network IP: ${networkIp}`); logger.log('info', ` Port: ${config.MONGODB_PORT}`); logger.log('info', ` Database: ${config.MONGODB_NAME}`); logger.log('info', ` Username: ${config.MONGODB_USER}`); logger.log('info', ` Auth Source: admin`); } /** * Show logs for a service */ public async showLogs(service: string, lines: number = 20): Promise<void> { const containers = this.config.getContainerNames(); switch (service) { case 'mongo': case 'mongodb': if (await this.docker.isRunning(containers.mongo)) { helpers.printHeader(`MongoDB Logs (last ${lines} lines)`); const logs = await this.docker.logs(containers.mongo, lines); console.log(logs); } else { logger.log('note', 'MongoDB container is not running'); } break; case 'minio': case 's3': if (await this.docker.isRunning(containers.minio)) { helpers.printHeader(`S3/MinIO Logs (last ${lines} lines)`); const logs = await this.docker.logs(containers.minio, lines); console.log(logs); } else { logger.log('note', 'S3/MinIO container is not running'); } break; case 'all': case '': await this.showLogs('mongo', lines); console.log(); await this.showLogs('minio', lines); break; default: logger.log('note', 'Usage: gitzone services logs [mongo|s3|all] [lines]'); break; } } /** * Remove containers */ public async removeContainers(): Promise<void> { const containers = this.config.getContainerNames(); let removed = false; if (await this.docker.exists(containers.mongo)) { if (await this.docker.remove(containers.mongo, true)) { logger.log('ok', ' MongoDB container removed ✓'); removed = true; } } if (await this.docker.exists(containers.minio)) { if (await this.docker.remove(containers.minio, true)) { logger.log('ok', ' S3/MinIO container removed ✓'); removed = true; } } if (!removed) { logger.log('note', ' No containers to remove'); } } /** * Clean data directories */ public async cleanData(): Promise<void> { const directories = this.config.getDataDirectories(); let cleaned = false; if (await plugins.smartfile.fs.fileExists(directories.mongo)) { await plugins.smartfile.fs.remove(directories.mongo); logger.log('ok', ' MongoDB data removed ✓'); cleaned = true; } if (await plugins.smartfile.fs.fileExists(directories.minio)) { await plugins.smartfile.fs.remove(directories.minio); logger.log('ok', ' S3/MinIO data removed ✓'); cleaned = true; } if (!cleaned) { logger.log('note', ' No data to clean'); } } /** * Reconfigure services with new ports */ public async reconfigure(): Promise<void> { helpers.printHeader('Reconfiguring Services'); const containers = this.config.getContainerNames(); // Stop existing containers logger.log('note', '🛑 Stopping existing containers...'); if (await this.docker.exists(containers.mongo)) { await this.docker.stop(containers.mongo); logger.log('ok', ' MongoDB stopped ✓'); } if (await this.docker.exists(containers.minio)) { await this.docker.stop(containers.minio); logger.log('ok', ' S3/MinIO stopped ✓'); } // Reconfigure ports await this.config.reconfigurePorts(); // Ask if user wants to restart services const smartinteract = new plugins.smartinteract.SmartInteract(); const response = await smartinteract.askQuestion({ name: 'restart', type: 'confirm', message: 'Do you want to start services with new ports?', default: true }); if (response.value) { console.log(); await this.startMongoDB(); await this.startMinIO(); } } }