917 lines
32 KiB
TypeScript
917 lines
32 KiB
TypeScript
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;
|
|
private enabledServices: string[] | null = null;
|
|
|
|
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}`);
|
|
|
|
// Load service selection from npmextra.json
|
|
await this.loadServiceConfiguration();
|
|
|
|
// Validate and update ports if needed
|
|
await this.config.validateAndUpdatePorts();
|
|
}
|
|
|
|
/**
|
|
* Load service configuration from npmextra.json
|
|
*/
|
|
private async loadServiceConfiguration(): Promise<void> {
|
|
const npmextraConfig = new plugins.npmextra.Npmextra(process.cwd());
|
|
const gitzoneConfig = npmextraConfig.dataFor<any>('gitzone', {});
|
|
|
|
// Check if services array exists
|
|
if (!gitzoneConfig.services || !Array.isArray(gitzoneConfig.services) || gitzoneConfig.services.length === 0) {
|
|
// Prompt user to select services
|
|
const smartinteract = new plugins.smartinteract.SmartInteract();
|
|
const response = await smartinteract.askQuestion({
|
|
name: 'services',
|
|
type: 'checkbox',
|
|
message: 'Which services do you want to enable for this project?',
|
|
choices: [
|
|
{ name: 'MongoDB', value: 'mongodb' },
|
|
{ name: 'MinIO (S3)', value: 'minio' },
|
|
{ name: 'Elasticsearch', value: 'elasticsearch' }
|
|
],
|
|
default: ['mongodb', 'minio', 'elasticsearch']
|
|
});
|
|
|
|
this.enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
|
|
|
// Save to npmextra.json
|
|
await this.saveServiceConfiguration(this.enabledServices);
|
|
} else {
|
|
this.enabledServices = gitzoneConfig.services;
|
|
logger.log('info', `🔧 Enabled services: ${this.enabledServices.join(', ')}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save service configuration to npmextra.json
|
|
*/
|
|
private async saveServiceConfiguration(services: string[]): Promise<void> {
|
|
const npmextraPath = plugins.path.join(process.cwd(), 'npmextra.json');
|
|
let npmextraData: any = {};
|
|
|
|
// Read existing npmextra.json if it exists
|
|
if (await plugins.smartfs.file(npmextraPath).exists()) {
|
|
const content = await plugins.smartfs.file(npmextraPath).encoding('utf8').read();
|
|
npmextraData = JSON.parse(content as string);
|
|
}
|
|
|
|
// Update gitzone.services
|
|
if (!npmextraData.gitzone) {
|
|
npmextraData.gitzone = {};
|
|
}
|
|
npmextraData.gitzone.services = services;
|
|
|
|
// Write back to npmextra.json
|
|
await plugins.smartfs
|
|
.file(npmextraPath)
|
|
.encoding('utf8')
|
|
.write(JSON.stringify(npmextraData, null, 2));
|
|
|
|
logger.log('ok', `✅ Saved service configuration to npmextra.json`);
|
|
logger.log('info', `🔧 Enabled services: ${services.join(', ')}`);
|
|
}
|
|
|
|
/**
|
|
* Check if a service is enabled
|
|
*/
|
|
private isServiceEnabled(service: string): boolean {
|
|
if (!this.enabledServices) {
|
|
return true; // If no configuration, enable all
|
|
}
|
|
return this.enabledServices.includes(service);
|
|
}
|
|
|
|
/**
|
|
* Start all enabled services
|
|
*/
|
|
public async startAll(): Promise<void> {
|
|
let first = true;
|
|
if (this.isServiceEnabled('mongodb')) {
|
|
if (!first) console.log();
|
|
await this.startMongoDB();
|
|
first = false;
|
|
}
|
|
if (this.isServiceEnabled('minio')) {
|
|
if (!first) console.log();
|
|
await this.startMinIO();
|
|
first = false;
|
|
}
|
|
if (this.isServiceEnabled('elasticsearch')) {
|
|
if (!first) console.log();
|
|
await this.startElasticsearch();
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop all enabled services
|
|
*/
|
|
public async stopAll(): Promise<void> {
|
|
let first = true;
|
|
if (this.isServiceEnabled('mongodb')) {
|
|
if (!first) console.log();
|
|
await this.stopMongoDB();
|
|
first = false;
|
|
}
|
|
if (this.isServiceEnabled('minio')) {
|
|
if (!first) console.log();
|
|
await this.stopMinIO();
|
|
first = false;
|
|
}
|
|
if (this.isServiceEnabled('elasticsearch')) {
|
|
if (!first) console.log();
|
|
await this.stopElasticsearch();
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.smartfs.directory(directories.mongo).recursive().create();
|
|
|
|
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.smartfs.directory(directories.minio).recursive().create();
|
|
|
|
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}/***)`);
|
|
}
|
|
|
|
/**
|
|
* Start Elasticsearch service
|
|
*/
|
|
public async startElasticsearch(): Promise<void> {
|
|
logger.log('note', '📦 Elasticsearch:');
|
|
|
|
const config = this.config.getConfig();
|
|
const containers = this.config.getContainerNames();
|
|
const directories = this.config.getDataDirectories();
|
|
|
|
// Ensure data directory exists
|
|
await plugins.smartfs.directory(directories.elasticsearch).recursive().create();
|
|
|
|
const status = await this.docker.getStatus(containers.elasticsearch);
|
|
|
|
switch (status) {
|
|
case 'running':
|
|
logger.log('ok', ' Already running ✓');
|
|
break;
|
|
|
|
case 'stopped':
|
|
// Check if port mapping matches config
|
|
const esPortMappings = await this.docker.getPortMappings(containers.elasticsearch);
|
|
if (esPortMappings && esPortMappings['9200'] !== config.ELASTICSEARCH_PORT) {
|
|
logger.log('note', ' Port configuration changed, recreating container...');
|
|
await this.docker.remove(containers.elasticsearch, true);
|
|
// Fall through to create new container
|
|
const success = await this.docker.run({
|
|
name: containers.elasticsearch,
|
|
image: 'elasticsearch:8.11.0',
|
|
ports: {
|
|
[`0.0.0.0:${config.ELASTICSEARCH_PORT}`]: '9200'
|
|
},
|
|
volumes: {
|
|
[directories.elasticsearch]: '/usr/share/elasticsearch/data'
|
|
},
|
|
environment: {
|
|
'discovery.type': 'single-node',
|
|
'xpack.security.enabled': 'true',
|
|
'ELASTIC_PASSWORD': config.ELASTICSEARCH_PASS,
|
|
'ES_JAVA_OPTS': '-Xms512m -Xmx512m'
|
|
},
|
|
restart: 'unless-stopped'
|
|
});
|
|
|
|
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.elasticsearch)) {
|
|
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.elasticsearch,
|
|
image: 'elasticsearch:8.11.0',
|
|
ports: {
|
|
[`0.0.0.0:${config.ELASTICSEARCH_PORT}`]: '9200'
|
|
},
|
|
volumes: {
|
|
[directories.elasticsearch]: '/usr/share/elasticsearch/data'
|
|
},
|
|
environment: {
|
|
'discovery.type': 'single-node',
|
|
'xpack.security.enabled': 'true',
|
|
'ELASTIC_PASSWORD': config.ELASTICSEARCH_PASS,
|
|
'ES_JAVA_OPTS': '-Xms512m -Xmx512m'
|
|
},
|
|
restart: 'unless-stopped'
|
|
});
|
|
|
|
if (success) {
|
|
logger.log('ok', ' Created and started ✓');
|
|
} else {
|
|
logger.log('error', ' Failed to create container');
|
|
}
|
|
break;
|
|
}
|
|
|
|
logger.log('info', ` Container: ${containers.elasticsearch}`);
|
|
logger.log('info', ` Port: ${config.ELASTICSEARCH_PORT}`);
|
|
logger.log('info', ` Connection: ${config.ELASTICSEARCH_URL}`);
|
|
logger.log('info', ` Username: ${config.ELASTICSEARCH_USER}`);
|
|
logger.log('info', ` Password: ${config.ELASTICSEARCH_PASS}`);
|
|
}
|
|
|
|
/**
|
|
* 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');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop Elasticsearch service
|
|
*/
|
|
public async stopElasticsearch(): Promise<void> {
|
|
logger.log('note', '📦 Elasticsearch:');
|
|
|
|
const containers = this.config.getContainerNames();
|
|
const status = await this.docker.getStatus(containers.elasticsearch);
|
|
|
|
if (status === 'running') {
|
|
if (await this.docker.stop(containers.elasticsearch)) {
|
|
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;
|
|
}
|
|
|
|
// Elasticsearch status
|
|
const esStatus = await this.docker.getStatus(containers.elasticsearch);
|
|
switch (esStatus) {
|
|
case 'running':
|
|
logger.log('ok', '📦 Elasticsearch: 🟢 Running');
|
|
logger.log('info', ` ├─ Container: ${containers.elasticsearch}`);
|
|
logger.log('info', ` ├─ Port: ${config.ELASTICSEARCH_PORT}`);
|
|
logger.log('info', ` ├─ Connection: ${config.ELASTICSEARCH_URL}`);
|
|
logger.log('info', ` └─ Credentials: ${config.ELASTICSEARCH_USER}/${config.ELASTICSEARCH_PASS}`);
|
|
break;
|
|
case 'stopped':
|
|
logger.log('note', '📦 Elasticsearch: 🟡 Stopped');
|
|
logger.log('info', ` ├─ Container: ${containers.elasticsearch}`);
|
|
logger.log('info', ` └─ Port: ${config.ELASTICSEARCH_PORT}`);
|
|
break;
|
|
case 'not_exists':
|
|
logger.log('info', '📦 Elasticsearch: ⚪ Not installed');
|
|
// Check port availability
|
|
const esPort = parseInt(config.ELASTICSEARCH_PORT);
|
|
const esAvailable = await helpers.isPortAvailable(esPort);
|
|
if (!esAvailable) {
|
|
logger.log('error', ` └─ ⚠️ Port ${esPort} is in use by another process`);
|
|
} else {
|
|
logger.log('info', ` └─ Port ${esPort} 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}`);
|
|
|
|
console.log();
|
|
logger.log('note', 'Elasticsearch:');
|
|
logger.log('info', ` Host: ${config.ELASTICSEARCH_HOST}:${config.ELASTICSEARCH_PORT}`);
|
|
logger.log('info', ` User: ${config.ELASTICSEARCH_USER}`);
|
|
logger.log('info', ' Password: ***');
|
|
logger.log('info', ` Container: ${this.config.getContainerNames().elasticsearch}`);
|
|
logger.log('info', ` Data: ${this.config.getDataDirectories().elasticsearch}`);
|
|
logger.log('info', ` Connection: ${config.ELASTICSEARCH_URL}`);
|
|
}
|
|
|
|
/**
|
|
* 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 'elasticsearch':
|
|
case 'es':
|
|
if (await this.docker.isRunning(containers.elasticsearch)) {
|
|
helpers.printHeader(`Elasticsearch Logs (last ${lines} lines)`);
|
|
const logs = await this.docker.logs(containers.elasticsearch, lines);
|
|
console.log(logs);
|
|
} else {
|
|
logger.log('note', 'Elasticsearch container is not running');
|
|
}
|
|
break;
|
|
|
|
case 'all':
|
|
case '':
|
|
await this.showLogs('mongo', lines);
|
|
console.log();
|
|
await this.showLogs('minio', lines);
|
|
console.log();
|
|
await this.showLogs('elasticsearch', lines);
|
|
break;
|
|
|
|
default:
|
|
logger.log('note', 'Usage: gitzone services logs [mongo|s3|elasticsearch|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 (await this.docker.exists(containers.elasticsearch)) {
|
|
if (await this.docker.remove(containers.elasticsearch, true)) {
|
|
logger.log('ok', ' Elasticsearch 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.smartfs.directory(directories.mongo).exists()) {
|
|
await plugins.smartfs.directory(directories.mongo).recursive().delete();
|
|
logger.log('ok', ' MongoDB data removed ✓');
|
|
cleaned = true;
|
|
}
|
|
|
|
if (await plugins.smartfs.directory(directories.minio).exists()) {
|
|
await plugins.smartfs.directory(directories.minio).recursive().delete();
|
|
logger.log('ok', ' S3/MinIO data removed ✓');
|
|
cleaned = true;
|
|
}
|
|
|
|
if (await plugins.smartfs.directory(directories.elasticsearch).exists()) {
|
|
await plugins.smartfs.directory(directories.elasticsearch).recursive().delete();
|
|
logger.log('ok', ' Elasticsearch data removed ✓');
|
|
cleaned = true;
|
|
}
|
|
|
|
if (!cleaned) {
|
|
logger.log('note', ' No data to clean');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configure which services are enabled
|
|
*/
|
|
public async configureServices(): Promise<void> {
|
|
logger.log('note', 'Select which services to enable for this project:');
|
|
console.log();
|
|
|
|
const currentServices = this.enabledServices || ['mongodb', 'minio', 'elasticsearch'];
|
|
|
|
const smartinteract = new plugins.smartinteract.SmartInteract();
|
|
const response = await smartinteract.askQuestion({
|
|
name: 'services',
|
|
type: 'checkbox',
|
|
message: 'Which services do you want to enable?',
|
|
choices: [
|
|
{ name: 'MongoDB', value: 'mongodb' },
|
|
{ name: 'MinIO (S3)', value: 'minio' },
|
|
{ name: 'Elasticsearch', value: 'elasticsearch' }
|
|
],
|
|
default: currentServices
|
|
});
|
|
|
|
this.enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
|
|
|
// Save to npmextra.json
|
|
await this.saveServiceConfiguration(this.enabledServices);
|
|
|
|
logger.log('ok', '✅ Service configuration updated');
|
|
}
|
|
|
|
/**
|
|
* 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 ✓');
|
|
}
|
|
|
|
if (await this.docker.exists(containers.elasticsearch)) {
|
|
await this.docker.stop(containers.elasticsearch);
|
|
logger.log('ok', ' Elasticsearch 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.startAll();
|
|
}
|
|
}
|
|
}
|