import * as plugins from './mod.plugins.js'; import * as helpers from './helpers.js'; import { ServiceManager } from './classes.servicemanager.js'; import { GlobalRegistry } from './classes.globalregistry.js'; import { logger } from '../gitzone.logging.js'; export const run = async (argvArg: any) => { const isGlobal = argvArg.g || argvArg.global; const command = argvArg._[1] || 'help'; // Handle global commands first if (isGlobal) { await handleGlobalCommand(command); return; } // Local project commands const serviceManager = new ServiceManager(); await serviceManager.init(); const service = argvArg._[2] || 'all'; switch (command) { case 'start': await handleStart(serviceManager, service); break; case 'stop': await handleStop(serviceManager, service); break; case 'restart': await handleRestart(serviceManager, service); break; case 'status': await serviceManager.showStatus(); break; case 'config': if (service === 'services' || argvArg._[2] === 'services') { await handleConfigureServices(serviceManager); } else { await serviceManager.showConfig(); } break; case 'compass': await serviceManager.showCompassConnection(); break; case 'logs': const lines = parseInt(argvArg._[3]) || 20; await serviceManager.showLogs(service, lines); break; case 'remove': await handleRemove(serviceManager); break; case 'clean': await handleClean(serviceManager); break; case 'reconfigure': await serviceManager.reconfigure(); break; case 'help': default: showHelp(); break; } }; async function handleStart(serviceManager: ServiceManager, service: string) { helpers.printHeader('Starting Services'); switch (service) { case 'mongo': case 'mongodb': await serviceManager.startMongoDB(); break; case 'minio': case 's3': await serviceManager.startMinIO(); break; case 'elasticsearch': case 'es': await serviceManager.startElasticsearch(); break; case 'all': case '': await serviceManager.startAll(); break; default: logger.log('error', `Unknown service: ${service}`); logger.log('note', 'Use: mongo, s3, elasticsearch, or all'); break; } } async function handleStop(serviceManager: ServiceManager, service: string) { helpers.printHeader('Stopping Services'); switch (service) { case 'mongo': case 'mongodb': await serviceManager.stopMongoDB(); break; case 'minio': case 's3': await serviceManager.stopMinIO(); break; case 'elasticsearch': case 'es': await serviceManager.stopElasticsearch(); break; case 'all': case '': await serviceManager.stopAll(); break; default: logger.log('error', `Unknown service: ${service}`); logger.log('note', 'Use: mongo, s3, elasticsearch, or all'); break; } } async function handleRestart(serviceManager: ServiceManager, service: string) { helpers.printHeader('Restarting Services'); switch (service) { case 'mongo': case 'mongodb': await serviceManager.stopMongoDB(); await plugins.smartdelay.delayFor(2000); await serviceManager.startMongoDB(); break; case 'minio': case 's3': await serviceManager.stopMinIO(); await plugins.smartdelay.delayFor(2000); await serviceManager.startMinIO(); break; case 'elasticsearch': case 'es': await serviceManager.stopElasticsearch(); await plugins.smartdelay.delayFor(2000); await serviceManager.startElasticsearch(); break; case 'all': case '': await serviceManager.stopAll(); await plugins.smartdelay.delayFor(2000); await serviceManager.startAll(); break; default: logger.log('error', `Unknown service: ${service}`); break; } } async function handleRemove(serviceManager: ServiceManager) { helpers.printHeader('Removing Containers'); logger.log('note', '⚠️ This will remove containers but preserve data'); const shouldContinue = await plugins.smartinteract.SmartInteract.getCliConfirmation('Continue?', false); if (shouldContinue) { await serviceManager.removeContainers(); } else { logger.log('note', 'Cancelled'); } } async function handleClean(serviceManager: ServiceManager) { helpers.printHeader('Clean All'); logger.log('error', '⚠️ WARNING: This will remove all containers and data!'); logger.log('error', 'This action cannot be undone!'); const smartinteraction = new plugins.smartinteract.SmartInteract(); const confirmAnswer = await smartinteraction.askQuestion({ name: 'confirm', type: 'input', message: 'Type "yes" to confirm:', default: 'no' }); if (confirmAnswer.value === 'yes') { await serviceManager.removeContainers(); console.log(); await serviceManager.cleanData(); logger.log('ok', 'All cleaned ✓'); } else { logger.log('note', 'Cancelled'); } } async function handleConfigureServices(serviceManager: ServiceManager) { helpers.printHeader('Configure Services'); await serviceManager.configureServices(); } function showHelp() { helpers.printHeader('GitZone Services Manager'); logger.log('ok', 'Usage: gitzone services [command] [options]'); console.log(); logger.log('note', 'Commands:'); logger.log('info', ' start [service] Start services (mongo|s3|elasticsearch|all)'); logger.log('info', ' stop [service] Stop services (mongo|s3|elasticsearch|all)'); logger.log('info', ' restart [service] Restart services (mongo|s3|elasticsearch|all)'); logger.log('info', ' status Show service status'); logger.log('info', ' config Show current configuration'); logger.log('info', ' config services Configure which services are enabled'); logger.log('info', ' compass Show MongoDB Compass connection string'); logger.log('info', ' logs [service] Show logs (mongo|s3|elasticsearch|all) [lines]'); logger.log('info', ' reconfigure Reassign ports and restart services'); logger.log('info', ' remove Remove all containers'); logger.log('info', ' clean Remove all containers and data ⚠️'); logger.log('info', ' help Show this help message'); console.log(); logger.log('note', 'Available Services:'); logger.log('info', ' • MongoDB (mongo) - Document database'); logger.log('info', ' • MinIO (s3) - S3-compatible object storage'); logger.log('info', ' • Elasticsearch (elasticsearch) - Search and analytics engine'); console.log(); logger.log('note', 'Features:'); logger.log('info', ' • Auto-creates .nogit/env.json with smart defaults'); logger.log('info', ' • Random ports (20000-30000) for MongoDB/MinIO to avoid conflicts'); logger.log('info', ' • Elasticsearch uses standard port 9200'); logger.log('info', ' • Project-specific containers for multi-project support'); logger.log('info', ' • Preserves custom configuration values'); logger.log('info', ' • MongoDB Compass connection support'); console.log(); logger.log('note', 'Examples:'); logger.log('info', ' gitzone services start # Start all services'); logger.log('info', ' gitzone services start mongo # Start only MongoDB'); logger.log('info', ' gitzone services start elasticsearch # Start only Elasticsearch'); logger.log('info', ' gitzone services stop # Stop all services'); logger.log('info', ' gitzone services status # Check service status'); logger.log('info', ' gitzone services config # Show configuration'); logger.log('info', ' gitzone services compass # Get MongoDB Compass connection'); logger.log('info', ' gitzone services logs elasticsearch # Show Elasticsearch logs'); console.log(); logger.log('note', 'Global Commands (-g/--global):'); logger.log('info', ' list -g List all registered projects'); logger.log('info', ' status -g Show status across all projects'); logger.log('info', ' stop -g Stop all containers across all projects'); logger.log('info', ' cleanup -g Remove stale registry entries'); console.log(); logger.log('note', 'Global Examples:'); logger.log('info', ' gitzone services list -g # List all registered projects'); logger.log('info', ' gitzone services status -g # Show global container status'); logger.log('info', ' gitzone services stop -g # Stop all (prompts for confirmation)'); } // ==================== Global Command Handlers ==================== async function handleGlobalCommand(command: string) { const globalRegistry = GlobalRegistry.getInstance(); switch (command) { case 'list': await handleGlobalList(globalRegistry); break; case 'status': await handleGlobalStatus(globalRegistry); break; case 'stop': await handleGlobalStop(globalRegistry); break; case 'cleanup': await handleGlobalCleanup(globalRegistry); break; case 'help': default: showHelp(); break; } } async function handleGlobalList(globalRegistry: GlobalRegistry) { helpers.printHeader('Registered Projects (Global)'); const projects = await globalRegistry.getAllProjects(); const projectPaths = Object.keys(projects); if (projectPaths.length === 0) { logger.log('note', 'No projects registered'); return; } for (const path of projectPaths) { const project = projects[path]; const lastActive = new Date(project.lastActive).toLocaleString(); console.log(); logger.log('ok', `📁 ${project.projectName}`); logger.log('info', ` Path: ${project.projectPath}`); logger.log('info', ` Services: ${project.enabledServices.join(', ')}`); logger.log('info', ` Last Active: ${lastActive}`); } } async function handleGlobalStatus(globalRegistry: GlobalRegistry) { helpers.printHeader('Global Service Status'); const statuses = await globalRegistry.getGlobalStatus(); if (statuses.length === 0) { logger.log('note', 'No projects registered'); return; } let runningCount = 0; let totalContainers = 0; for (const project of statuses) { console.log(); logger.log('ok', `📁 ${project.projectName}`); logger.log('info', ` Path: ${project.projectPath}`); if (project.containers.length === 0) { logger.log('note', ' No containers configured'); continue; } for (const container of project.containers) { totalContainers++; const statusIcon = container.status === 'running' ? '🟢' : container.status === 'exited' ? '🟡' : '⚪'; if (container.status === 'running') runningCount++; logger.log('info', ` ${statusIcon} ${container.name}: ${container.status}`); } } console.log(); logger.log('note', `Summary: ${runningCount}/${totalContainers} containers running across ${statuses.length} project(s)`); } async function handleGlobalStop(globalRegistry: GlobalRegistry) { helpers.printHeader('Stop All Containers (Global)'); const statuses = await globalRegistry.getGlobalStatus(); // Count running containers let runningCount = 0; for (const project of statuses) { for (const container of project.containers) { if (container.status === 'running') runningCount++; } } if (runningCount === 0) { logger.log('note', 'No running containers found'); return; } logger.log('note', `Found ${runningCount} running container(s) across ${statuses.length} project(s)`); console.log(); // Show what will be stopped for (const project of statuses) { const runningContainers = project.containers.filter(c => c.status === 'running'); if (runningContainers.length > 0) { logger.log('info', `${project.projectName}:`); for (const container of runningContainers) { logger.log('info', ` • ${container.name}`); } } } console.log(); const shouldContinue = await plugins.smartinteract.SmartInteract.getCliConfirmation( 'Stop all containers?', false ); if (!shouldContinue) { logger.log('note', 'Cancelled'); return; } logger.log('note', 'Stopping all containers...'); const result = await globalRegistry.stopAll(); if (result.stopped.length > 0) { logger.log('ok', `Stopped: ${result.stopped.join(', ')}`); } if (result.failed.length > 0) { logger.log('error', `Failed to stop: ${result.failed.join(', ')}`); } } async function handleGlobalCleanup(globalRegistry: GlobalRegistry) { helpers.printHeader('Cleanup Registry (Global)'); logger.log('note', 'Checking for stale registry entries...'); const removed = await globalRegistry.cleanup(); if (removed.length === 0) { logger.log('ok', 'No stale entries found'); return; } logger.log('ok', `Removed ${removed.length} stale entr${removed.length === 1 ? 'y' : 'ies'}:`); for (const path of removed) { logger.log('info', ` • ${path}`); } }