Files
cli/ts/mod_services/index.ts

433 lines
13 KiB
TypeScript

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}`);
}
}