feat(services): Add comprehensive development services management (v1.17.0)
- Implemented gitzone services command for managing MongoDB and MinIO containers - Added smart port assignment (20000-30000 range) to avoid conflicts - Project-specific container names for complete isolation - Data persistence in .nogit/ directories - MongoDB Compass connection string generation with network IP detection - Auto-configuration via .nogit/env.json with secure defaults - Commands: start, stop, restart, status, config, compass, logs, remove, clean - Interactive confirmations for destructive operations - Comprehensive documentation and Task Venture Capital GmbH legal update
This commit is contained in:
@@ -131,6 +131,14 @@ export let run = async () => {
|
||||
modHelpers.run(argvArg);
|
||||
});
|
||||
|
||||
/**
|
||||
* manage development services (MongoDB, S3/MinIO)
|
||||
*/
|
||||
gitzoneSmartcli.addCommand('services').subscribe(async (argvArg) => {
|
||||
const modServices = await import('./mod_services/index.js');
|
||||
await modServices.run(argvArg);
|
||||
});
|
||||
|
||||
// start parsing of the cli
|
||||
gitzoneSmartcli.startParse();
|
||||
return await done.promise;
|
||||
|
226
ts/mod_services/classes.dockercontainer.ts
Normal file
226
ts/mod_services/classes.dockercontainer.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import * as helpers from './helpers.js';
|
||||
|
||||
export type ContainerStatus = 'running' | 'stopped' | 'not_exists';
|
||||
|
||||
export interface IDockerRunOptions {
|
||||
name: string;
|
||||
image: string;
|
||||
ports?: { [key: string]: string };
|
||||
volumes?: { [key: string]: string };
|
||||
environment?: { [key: string]: string };
|
||||
restart?: string;
|
||||
command?: string;
|
||||
}
|
||||
|
||||
export class DockerContainer {
|
||||
private smartshell: plugins.smartshell.Smartshell;
|
||||
|
||||
constructor() {
|
||||
this.smartshell = new plugins.smartshell.Smartshell({
|
||||
executor: 'bash',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Docker is installed and available
|
||||
*/
|
||||
public async checkDocker(): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.smartshell.exec('docker --version');
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container status
|
||||
*/
|
||||
public async getStatus(containerName: string): Promise<ContainerStatus> {
|
||||
try {
|
||||
// Check if running
|
||||
const runningResult = await this.smartshell.exec(
|
||||
`docker ps --format '{{.Names}}' | grep -q "^${containerName}$"`
|
||||
);
|
||||
|
||||
if (runningResult.exitCode === 0) {
|
||||
return 'running';
|
||||
}
|
||||
|
||||
// Check if exists but stopped
|
||||
const existsResult = await this.smartshell.exec(
|
||||
`docker ps -a --format '{{.Names}}' | grep -q "^${containerName}$"`
|
||||
);
|
||||
|
||||
if (existsResult.exitCode === 0) {
|
||||
return 'stopped';
|
||||
}
|
||||
|
||||
return 'not_exists';
|
||||
} catch (error) {
|
||||
return 'not_exists';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a container
|
||||
*/
|
||||
public async start(containerName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker start ${containerName}`);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a container
|
||||
*/
|
||||
public async stop(containerName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker stop ${containerName}`);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a container
|
||||
*/
|
||||
public async remove(containerName: string, force: boolean = false): Promise<boolean> {
|
||||
try {
|
||||
const forceFlag = force ? '-f' : '';
|
||||
const result = await this.smartshell.exec(`docker rm ${forceFlag} ${containerName}`);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a new container
|
||||
*/
|
||||
public async run(options: IDockerRunOptions): Promise<boolean> {
|
||||
let command = 'docker run -d';
|
||||
|
||||
// Add name
|
||||
command += ` --name ${options.name}`;
|
||||
|
||||
// Add ports
|
||||
if (options.ports) {
|
||||
for (const [hostPort, containerPort] of Object.entries(options.ports)) {
|
||||
command += ` -p ${hostPort}:${containerPort}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add volumes
|
||||
if (options.volumes) {
|
||||
for (const [hostPath, containerPath] of Object.entries(options.volumes)) {
|
||||
command += ` -v "${hostPath}:${containerPath}"`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add environment variables
|
||||
if (options.environment) {
|
||||
for (const [key, value] of Object.entries(options.environment)) {
|
||||
command += ` -e ${key}="${value}"`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add restart policy
|
||||
if (options.restart) {
|
||||
command += ` --restart ${options.restart}`;
|
||||
}
|
||||
|
||||
// Add image
|
||||
command += ` ${options.image}`;
|
||||
|
||||
// Add command if provided
|
||||
if (options.command) {
|
||||
command += ` ${options.command}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.smartshell.exec(command);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
helpers.printMessage(`Failed to run container: ${error.message}`, 'red');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command in a running container
|
||||
*/
|
||||
public async exec(containerName: string, command: string): Promise<string> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker exec ${containerName} ${command}`);
|
||||
if (result.exitCode === 0) {
|
||||
return result.stdout;
|
||||
}
|
||||
return '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container logs
|
||||
*/
|
||||
public async logs(containerName: string, lines?: number): Promise<string> {
|
||||
try {
|
||||
const tailFlag = lines ? `--tail ${lines}` : '';
|
||||
const result = await this.smartshell.exec(`docker logs ${tailFlag} ${containerName}`);
|
||||
return result.stdout;
|
||||
} catch (error) {
|
||||
return `Error getting logs: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a container exists
|
||||
*/
|
||||
public async exists(containerName: string): Promise<boolean> {
|
||||
const status = await this.getStatus(containerName);
|
||||
return status !== 'not_exists';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a container is running
|
||||
*/
|
||||
public async isRunning(containerName: string): Promise<boolean> {
|
||||
const status = await this.getStatus(containerName);
|
||||
return status === 'running';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a container to be ready
|
||||
*/
|
||||
public async waitForReady(containerName: string, maxAttempts: number = 30): Promise<boolean> {
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
if (await this.isRunning(containerName)) {
|
||||
return true;
|
||||
}
|
||||
await plugins.smartdelay.delayFor(1000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container information
|
||||
*/
|
||||
public async inspect(containerName: string): Promise<any> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker inspect ${containerName}`);
|
||||
if (result.exitCode === 0) {
|
||||
return JSON.parse(result.stdout);
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
245
ts/mod_services/classes.serviceconfiguration.ts
Normal file
245
ts/mod_services/classes.serviceconfiguration.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import * as helpers from './helpers.js';
|
||||
|
||||
export interface IServiceConfig {
|
||||
PROJECT_NAME: string;
|
||||
MONGODB_HOST: string;
|
||||
MONGODB_NAME: string;
|
||||
MONGODB_PORT: string;
|
||||
MONGODB_USER: string;
|
||||
MONGODB_PASS: string;
|
||||
S3_HOST: string;
|
||||
S3_PORT: string;
|
||||
S3_CONSOLE_PORT: string;
|
||||
S3_USER: string;
|
||||
S3_PASS: string;
|
||||
S3_BUCKET: string;
|
||||
}
|
||||
|
||||
export class ServiceConfiguration {
|
||||
private configPath: string;
|
||||
private config: IServiceConfig;
|
||||
|
||||
constructor() {
|
||||
this.configPath = plugins.path.join(process.cwd(), '.nogit', 'env.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load or create the configuration
|
||||
*/
|
||||
public async loadOrCreate(): Promise<IServiceConfig> {
|
||||
await this.ensureNogitDirectory();
|
||||
|
||||
if (await this.configExists()) {
|
||||
await this.loadConfig();
|
||||
await this.updateMissingFields();
|
||||
} else {
|
||||
await this.createDefaultConfig();
|
||||
}
|
||||
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current configuration
|
||||
*/
|
||||
public getConfig(): IServiceConfig {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the configuration to file
|
||||
*/
|
||||
public async saveConfig(): Promise<void> {
|
||||
await plugins.smartfile.memory.toFs(
|
||||
JSON.stringify(this.config, null, 2),
|
||||
this.configPath
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure .nogit directory exists
|
||||
*/
|
||||
private async ensureNogitDirectory(): Promise<void> {
|
||||
const nogitPath = plugins.path.join(process.cwd(), '.nogit');
|
||||
await plugins.smartfile.fs.ensureDir(nogitPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if configuration file exists
|
||||
*/
|
||||
private async configExists(): Promise<boolean> {
|
||||
return plugins.smartfile.fs.fileExists(this.configPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from file
|
||||
*/
|
||||
private async loadConfig(): Promise<void> {
|
||||
const configContent = await plugins.smartfile.fs.toStringSync(this.configPath);
|
||||
this.config = JSON.parse(configContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default configuration
|
||||
*/
|
||||
private async createDefaultConfig(): Promise<void> {
|
||||
const projectName = helpers.getProjectName();
|
||||
const mongoPort = await helpers.getRandomAvailablePort();
|
||||
const s3Port = await helpers.getRandomAvailablePort();
|
||||
let s3ConsolePort = s3Port + 1;
|
||||
|
||||
// Ensure console port is also available
|
||||
while (!(await helpers.isPortAvailable(s3ConsolePort))) {
|
||||
s3ConsolePort++;
|
||||
}
|
||||
|
||||
this.config = {
|
||||
PROJECT_NAME: projectName,
|
||||
MONGODB_HOST: 'localhost',
|
||||
MONGODB_NAME: projectName,
|
||||
MONGODB_PORT: mongoPort.toString(),
|
||||
MONGODB_USER: 'defaultadmin',
|
||||
MONGODB_PASS: 'defaultpass',
|
||||
S3_HOST: 'localhost',
|
||||
S3_PORT: s3Port.toString(),
|
||||
S3_CONSOLE_PORT: s3ConsolePort.toString(),
|
||||
S3_USER: 'defaultadmin',
|
||||
S3_PASS: 'defaultpass',
|
||||
S3_BUCKET: `${projectName}-documents`
|
||||
};
|
||||
|
||||
await this.saveConfig();
|
||||
|
||||
helpers.printMessage('✅ Created .nogit/env.json with project defaults', 'green');
|
||||
helpers.printMessage(`📍 MongoDB port: ${mongoPort}`, 'blue');
|
||||
helpers.printMessage(`📍 S3 API port: ${s3Port}`, 'blue');
|
||||
helpers.printMessage(`📍 S3 Console port: ${s3ConsolePort}`, 'blue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update missing fields in existing configuration
|
||||
*/
|
||||
private async updateMissingFields(): Promise<void> {
|
||||
const projectName = helpers.getProjectName();
|
||||
let updated = false;
|
||||
const fieldsAdded: string[] = [];
|
||||
|
||||
// Check and add missing fields
|
||||
if (!this.config.PROJECT_NAME) {
|
||||
this.config.PROJECT_NAME = projectName;
|
||||
fieldsAdded.push('PROJECT_NAME');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.MONGODB_HOST) {
|
||||
this.config.MONGODB_HOST = 'localhost';
|
||||
fieldsAdded.push('MONGODB_HOST');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.MONGODB_NAME) {
|
||||
this.config.MONGODB_NAME = projectName;
|
||||
fieldsAdded.push('MONGODB_NAME');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.MONGODB_PORT) {
|
||||
const port = await helpers.getRandomAvailablePort();
|
||||
this.config.MONGODB_PORT = port.toString();
|
||||
fieldsAdded.push(`MONGODB_PORT(${port})`);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.MONGODB_USER) {
|
||||
this.config.MONGODB_USER = 'defaultadmin';
|
||||
fieldsAdded.push('MONGODB_USER');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.MONGODB_PASS) {
|
||||
this.config.MONGODB_PASS = 'defaultpass';
|
||||
fieldsAdded.push('MONGODB_PASS');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.S3_HOST) {
|
||||
this.config.S3_HOST = 'localhost';
|
||||
fieldsAdded.push('S3_HOST');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.S3_PORT) {
|
||||
const port = await helpers.getRandomAvailablePort();
|
||||
this.config.S3_PORT = port.toString();
|
||||
fieldsAdded.push(`S3_PORT(${port})`);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.S3_CONSOLE_PORT) {
|
||||
const s3Port = parseInt(this.config.S3_PORT);
|
||||
let consolePort = s3Port + 1;
|
||||
|
||||
while (!(await helpers.isPortAvailable(consolePort))) {
|
||||
consolePort++;
|
||||
}
|
||||
|
||||
this.config.S3_CONSOLE_PORT = consolePort.toString();
|
||||
fieldsAdded.push(`S3_CONSOLE_PORT(${consolePort})`);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.S3_USER) {
|
||||
this.config.S3_USER = 'defaultadmin';
|
||||
fieldsAdded.push('S3_USER');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.S3_PASS) {
|
||||
this.config.S3_PASS = 'defaultpass';
|
||||
fieldsAdded.push('S3_PASS');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!this.config.S3_BUCKET) {
|
||||
this.config.S3_BUCKET = `${projectName}-documents`;
|
||||
fieldsAdded.push('S3_BUCKET');
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
await this.saveConfig();
|
||||
helpers.printMessage(`✅ Added missing fields: ${fieldsAdded.join(', ')}`, 'green');
|
||||
} else {
|
||||
helpers.printMessage('✅ Configuration complete', 'green');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MongoDB connection string
|
||||
*/
|
||||
public getMongoConnectionString(useNetworkIp: boolean = false): string {
|
||||
const host = useNetworkIp ? '${networkIp}' : this.config.MONGODB_HOST;
|
||||
return `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${host}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container names
|
||||
*/
|
||||
public getContainerNames() {
|
||||
return {
|
||||
mongo: `${this.config.PROJECT_NAME}-mongodb`,
|
||||
minio: `${this.config.PROJECT_NAME}-minio`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data directories
|
||||
*/
|
||||
public getDataDirectories() {
|
||||
return {
|
||||
mongo: plugins.path.join(process.cwd(), '.nogit', 'mongodata'),
|
||||
minio: plugins.path.join(process.cwd(), '.nogit', 'miniodata')
|
||||
};
|
||||
}
|
||||
}
|
412
ts/mod_services/classes.servicemanager.ts
Normal file
412
ts/mod_services/classes.servicemanager.ts
Normal file
@@ -0,0 +1,412 @@
|
||||
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';
|
||||
|
||||
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())) {
|
||||
helpers.printMessage('Error: Docker is not installed. Please install Docker first.', 'red');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Load or create configuration
|
||||
await this.config.loadOrCreate();
|
||||
helpers.printMessage(`📋 Project: ${this.config.getConfig().PROJECT_NAME}`, 'magenta');
|
||||
}
|
||||
|
||||
/**
|
||||
* Start MongoDB service
|
||||
*/
|
||||
public async startMongoDB(): Promise<void> {
|
||||
helpers.printMessage('📦 MongoDB:', 'yellow');
|
||||
|
||||
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':
|
||||
helpers.printMessage(' Already running ✓', 'green');
|
||||
break;
|
||||
|
||||
case 'stopped':
|
||||
if (await this.docker.start(containers.mongo)) {
|
||||
helpers.printMessage(' Started ✓', 'green');
|
||||
} else {
|
||||
helpers.printMessage(' Failed to start', 'red');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'not_exists':
|
||||
helpers.printMessage(' Creating container...', 'yellow');
|
||||
|
||||
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'
|
||||
});
|
||||
|
||||
if (success) {
|
||||
helpers.printMessage(' Created and started ✓', 'green');
|
||||
} else {
|
||||
helpers.printMessage(' Failed to create container', 'red');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
helpers.printMessage(` Container: ${containers.mongo}`, 'cyan');
|
||||
helpers.printMessage(` Port: ${config.MONGODB_PORT}`, 'cyan');
|
||||
helpers.printMessage(` Connection: ${this.config.getMongoConnectionString()}`, 'blue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Start MinIO service
|
||||
*/
|
||||
public async startMinIO(): Promise<void> {
|
||||
helpers.printMessage('📦 S3/MinIO:', 'yellow');
|
||||
|
||||
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':
|
||||
helpers.printMessage(' Already running ✓', 'green');
|
||||
break;
|
||||
|
||||
case 'stopped':
|
||||
if (await this.docker.start(containers.minio)) {
|
||||
helpers.printMessage(' Started ✓', 'green');
|
||||
} else {
|
||||
helpers.printMessage(' Failed to start', 'red');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'not_exists':
|
||||
helpers.printMessage(' Creating container...', 'yellow');
|
||||
|
||||
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_USER,
|
||||
MINIO_ROOT_PASSWORD: config.S3_PASS
|
||||
},
|
||||
restart: 'unless-stopped',
|
||||
command: 'server /data --console-address ":9001"'
|
||||
});
|
||||
|
||||
if (success) {
|
||||
helpers.printMessage(' Created and started ✓', 'green');
|
||||
|
||||
// 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_USER} ${config.S3_PASS}`
|
||||
);
|
||||
|
||||
await this.docker.exec(
|
||||
containers.minio,
|
||||
`mc mb local/${config.S3_BUCKET}`
|
||||
);
|
||||
|
||||
helpers.printMessage(` Bucket '${config.S3_BUCKET}' created ✓`, 'green');
|
||||
} else {
|
||||
helpers.printMessage(' Failed to create container', 'red');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
helpers.printMessage(` Container: ${containers.minio}`, 'cyan');
|
||||
helpers.printMessage(` Port: ${config.S3_PORT}`, 'cyan');
|
||||
helpers.printMessage(` Bucket: ${config.S3_BUCKET}`, 'cyan');
|
||||
helpers.printMessage(` API: http://${config.S3_HOST}:${config.S3_PORT}`, 'blue');
|
||||
helpers.printMessage(` Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT} (login: ${config.S3_USER}/***)`, 'blue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop MongoDB service
|
||||
*/
|
||||
public async stopMongoDB(): Promise<void> {
|
||||
helpers.printMessage('📦 MongoDB:', 'yellow');
|
||||
|
||||
const containers = this.config.getContainerNames();
|
||||
const status = await this.docker.getStatus(containers.mongo);
|
||||
|
||||
if (status === 'running') {
|
||||
if (await this.docker.stop(containers.mongo)) {
|
||||
helpers.printMessage(' Stopped ✓', 'green');
|
||||
} else {
|
||||
helpers.printMessage(' Failed to stop', 'red');
|
||||
}
|
||||
} else {
|
||||
helpers.printMessage(' Not running', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop MinIO service
|
||||
*/
|
||||
public async stopMinIO(): Promise<void> {
|
||||
helpers.printMessage('📦 S3/MinIO:', 'yellow');
|
||||
|
||||
const containers = this.config.getContainerNames();
|
||||
const status = await this.docker.getStatus(containers.minio);
|
||||
|
||||
if (status === 'running') {
|
||||
if (await this.docker.stop(containers.minio)) {
|
||||
helpers.printMessage(' Stopped ✓', 'green');
|
||||
} else {
|
||||
helpers.printMessage(' Failed to stop', 'red');
|
||||
}
|
||||
} else {
|
||||
helpers.printMessage(' Not running', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show service status
|
||||
*/
|
||||
public async showStatus(): Promise<void> {
|
||||
helpers.printHeader('Service Status');
|
||||
|
||||
const config = this.config.getConfig();
|
||||
const containers = this.config.getContainerNames();
|
||||
|
||||
helpers.printMessage(`Project: ${config.PROJECT_NAME}`, 'magenta');
|
||||
console.log();
|
||||
|
||||
// MongoDB status
|
||||
const mongoStatus = await this.docker.getStatus(containers.mongo);
|
||||
switch (mongoStatus) {
|
||||
case 'running':
|
||||
helpers.printMessage('📦 MongoDB: 🟢 Running', 'green');
|
||||
helpers.printMessage(` ├─ Container: ${containers.mongo}`, 'cyan');
|
||||
helpers.printMessage(` └─ ${this.config.getMongoConnectionString()}`, 'cyan');
|
||||
break;
|
||||
case 'stopped':
|
||||
helpers.printMessage('📦 MongoDB: 🟡 Stopped', 'yellow');
|
||||
helpers.printMessage(` └─ Container: ${containers.mongo}`, 'cyan');
|
||||
break;
|
||||
case 'not_exists':
|
||||
helpers.printMessage('📦 MongoDB: ⚪ Not installed', 'magenta');
|
||||
break;
|
||||
}
|
||||
|
||||
// MinIO status
|
||||
const minioStatus = await this.docker.getStatus(containers.minio);
|
||||
switch (minioStatus) {
|
||||
case 'running':
|
||||
helpers.printMessage('📦 S3/MinIO: 🟢 Running', 'green');
|
||||
helpers.printMessage(` ├─ Container: ${containers.minio}`, 'cyan');
|
||||
helpers.printMessage(` ├─ API: http://${config.S3_HOST}:${config.S3_PORT}`, 'cyan');
|
||||
helpers.printMessage(` ├─ Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`, 'cyan');
|
||||
helpers.printMessage(` └─ Bucket: ${config.S3_BUCKET}`, 'cyan');
|
||||
break;
|
||||
case 'stopped':
|
||||
helpers.printMessage('📦 S3/MinIO: 🟡 Stopped', 'yellow');
|
||||
helpers.printMessage(` └─ Container: ${containers.minio}`, 'cyan');
|
||||
break;
|
||||
case 'not_exists':
|
||||
helpers.printMessage('📦 S3/MinIO: ⚪ Not installed', 'magenta');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show configuration
|
||||
*/
|
||||
public async showConfig(): Promise<void> {
|
||||
helpers.printHeader('Current Configuration');
|
||||
|
||||
const config = this.config.getConfig();
|
||||
|
||||
helpers.printMessage(`Project: ${config.PROJECT_NAME}`, 'magenta');
|
||||
console.log();
|
||||
|
||||
helpers.printMessage('MongoDB:', 'yellow');
|
||||
helpers.printMessage(` Host: ${config.MONGODB_HOST}:${config.MONGODB_PORT}`, undefined);
|
||||
helpers.printMessage(` Database: ${config.MONGODB_NAME}`, undefined);
|
||||
helpers.printMessage(` User: ${config.MONGODB_USER}`, undefined);
|
||||
helpers.printMessage(' Password: ***', undefined);
|
||||
helpers.printMessage(` Container: ${this.config.getContainerNames().mongo}`, undefined);
|
||||
helpers.printMessage(` Data: ${this.config.getDataDirectories().mongo}`, undefined);
|
||||
helpers.printMessage(` Connection: ${this.config.getMongoConnectionString()}`, 'blue');
|
||||
|
||||
console.log();
|
||||
helpers.printMessage('S3/MinIO:', 'yellow');
|
||||
helpers.printMessage(` Host: ${config.S3_HOST}`, undefined);
|
||||
helpers.printMessage(` API Port: ${config.S3_PORT}`, undefined);
|
||||
helpers.printMessage(` Console Port: ${config.S3_CONSOLE_PORT}`, undefined);
|
||||
helpers.printMessage(` User: ${config.S3_USER}`, undefined);
|
||||
helpers.printMessage(' Password: ***', undefined);
|
||||
helpers.printMessage(` Bucket: ${config.S3_BUCKET}`, undefined);
|
||||
helpers.printMessage(` Container: ${this.config.getContainerNames().minio}`, undefined);
|
||||
helpers.printMessage(` Data: ${this.config.getDataDirectories().minio}`, undefined);
|
||||
helpers.printMessage(` API URL: http://${config.S3_HOST}:${config.S3_PORT}`, 'blue');
|
||||
helpers.printMessage(` Console URL: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`, 'blue');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`;
|
||||
|
||||
helpers.printMessage('MongoDB Compass is a GUI tool for MongoDB. To connect:', 'cyan');
|
||||
console.log();
|
||||
helpers.printMessage('1. Download MongoDB Compass from:', undefined);
|
||||
helpers.printMessage(' https://www.mongodb.com/products/compass', 'blue');
|
||||
console.log();
|
||||
helpers.printMessage('2. Open Compass and paste this connection string:', undefined);
|
||||
helpers.printMessage(` ${connectionString}`, 'green');
|
||||
console.log();
|
||||
helpers.printMessage('Connection Details:', 'yellow');
|
||||
helpers.printMessage(` Network IP: ${networkIp}`, undefined);
|
||||
helpers.printMessage(` Port: ${config.MONGODB_PORT}`, undefined);
|
||||
helpers.printMessage(` Database: ${config.MONGODB_NAME}`, undefined);
|
||||
helpers.printMessage(` Username: ${config.MONGODB_USER}`, undefined);
|
||||
helpers.printMessage(` Auth Source: admin`, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
helpers.printMessage('MongoDB container is not running', 'yellow');
|
||||
}
|
||||
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 {
|
||||
helpers.printMessage('S3/MinIO container is not running', 'yellow');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'all':
|
||||
case '':
|
||||
await this.showLogs('mongo', lines);
|
||||
console.log();
|
||||
await this.showLogs('minio', lines);
|
||||
break;
|
||||
|
||||
default:
|
||||
helpers.printMessage('Usage: gitzone services logs [mongo|s3|all] [lines]', 'yellow');
|
||||
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)) {
|
||||
helpers.printMessage(' MongoDB container removed ✓', 'green');
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (await this.docker.exists(containers.minio)) {
|
||||
if (await this.docker.remove(containers.minio, true)) {
|
||||
helpers.printMessage(' S3/MinIO container removed ✓', 'green');
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!removed) {
|
||||
helpers.printMessage(' No containers to remove', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
helpers.printMessage(' MongoDB data removed ✓', 'green');
|
||||
cleaned = true;
|
||||
}
|
||||
|
||||
if (await plugins.smartfile.fs.fileExists(directories.minio)) {
|
||||
await plugins.smartfile.fs.remove(directories.minio);
|
||||
helpers.printMessage(' S3/MinIO data removed ✓', 'green');
|
||||
cleaned = true;
|
||||
}
|
||||
|
||||
if (!cleaned) {
|
||||
helpers.printMessage(' No data to clean', 'yellow');
|
||||
}
|
||||
}
|
||||
}
|
148
ts/mod_services/helpers.ts
Normal file
148
ts/mod_services/helpers.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import * as net from 'net';
|
||||
|
||||
/**
|
||||
* Check if a port is available
|
||||
*/
|
||||
export const isPortAvailable = async (port: number): Promise<boolean> => {
|
||||
return new Promise((resolve) => {
|
||||
const server = net.createServer();
|
||||
|
||||
server.once('error', () => {
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
server.once('listening', () => {
|
||||
server.close();
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
server.listen(port, '0.0.0.0');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random available port between 20000 and 30000
|
||||
*/
|
||||
export const getRandomAvailablePort = async (): Promise<number> => {
|
||||
const maxAttempts = 100;
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
const port = Math.floor(Math.random() * 10001) + 20000;
|
||||
if (await isPortAvailable(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: let the system assign a port
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the project name from package.json or directory
|
||||
*/
|
||||
export const getProjectName = (): string => {
|
||||
try {
|
||||
const packageJsonPath = plugins.path.join(process.cwd(), 'package.json');
|
||||
if (plugins.smartfile.fs.fileExistsSync(packageJsonPath)) {
|
||||
const packageJson = plugins.smartfile.fs.toObjectSync(packageJsonPath);
|
||||
if (packageJson.name) {
|
||||
// Sanitize: @fin.cx/skr → fin-cx-skr
|
||||
return packageJson.name.replace(/@/g, '').replace(/[\/\.]/g, '-');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors and fall back to directory name
|
||||
}
|
||||
|
||||
return plugins.path.basename(process.cwd());
|
||||
};
|
||||
|
||||
/**
|
||||
* Print colored message to console
|
||||
*/
|
||||
export const printMessage = (message: string, color?: 'green' | 'yellow' | 'red' | 'blue' | 'magenta' | 'cyan') => {
|
||||
const logger = new plugins.smartlog.ConsoleLog();
|
||||
|
||||
switch (color) {
|
||||
case 'green':
|
||||
logger.log('ok', message);
|
||||
break;
|
||||
case 'yellow':
|
||||
logger.log('note', message);
|
||||
break;
|
||||
case 'red':
|
||||
logger.log('error', message);
|
||||
break;
|
||||
case 'blue':
|
||||
case 'magenta':
|
||||
case 'cyan':
|
||||
logger.log('info', message);
|
||||
break;
|
||||
default:
|
||||
logger.log('info', message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Print a header with decorative lines
|
||||
*/
|
||||
export const printHeader = (title: string) => {
|
||||
console.log();
|
||||
printMessage('═══════════════════════════════════════════════════════════════', 'cyan');
|
||||
printMessage(` ${title}`, 'cyan');
|
||||
printMessage('═══════════════════════════════════════════════════════════════', 'cyan');
|
||||
console.log();
|
||||
};
|
||||
|
||||
/**
|
||||
* Format bytes to human readable string
|
||||
*/
|
||||
export const formatBytes = (bytes: number): string => {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the local network IP address
|
||||
*/
|
||||
export const getLocalNetworkIp = async (): Promise<string> => {
|
||||
const smartnetworkInstance = new plugins.smartnetwork.SmartNetwork();
|
||||
const gateways = await smartnetworkInstance.getGateways();
|
||||
|
||||
// Find the best local IP from network interfaces
|
||||
for (const interfaceName of Object.keys(gateways)) {
|
||||
const interfaces = gateways[interfaceName];
|
||||
for (const iface of interfaces) {
|
||||
// Skip loopback and internal interfaces
|
||||
if (!iface.internal && iface.family === 'IPv4') {
|
||||
const address = iface.address;
|
||||
// Prefer LAN IPs
|
||||
if (address.startsWith('192.168.') || address.startsWith('10.') || address.startsWith('172.')) {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to get any non-internal IPv4
|
||||
for (const interfaceName of Object.keys(gateways)) {
|
||||
const interfaces = gateways[interfaceName];
|
||||
for (const iface of interfaces) {
|
||||
if (!iface.internal && iface.family === 'IPv4') {
|
||||
return iface.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: localhost
|
||||
return 'localhost';
|
||||
};
|
218
ts/mod_services/index.ts
Normal file
218
ts/mod_services/index.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import * as helpers from './helpers.js';
|
||||
import { ServiceManager } from './classes.servicemanager.js';
|
||||
|
||||
export const run = async (argvArg: any) => {
|
||||
const serviceManager = new ServiceManager();
|
||||
await serviceManager.init();
|
||||
|
||||
const command = argvArg._[1] || 'help';
|
||||
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':
|
||||
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 '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 'all':
|
||||
case '':
|
||||
await serviceManager.startMongoDB();
|
||||
console.log();
|
||||
await serviceManager.startMinIO();
|
||||
break;
|
||||
|
||||
default:
|
||||
helpers.printMessage(`Unknown service: ${service}`, 'red');
|
||||
helpers.printMessage('Use: mongo, s3, or all', 'yellow');
|
||||
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 'all':
|
||||
case '':
|
||||
await serviceManager.stopMongoDB();
|
||||
console.log();
|
||||
await serviceManager.stopMinIO();
|
||||
break;
|
||||
|
||||
default:
|
||||
helpers.printMessage(`Unknown service: ${service}`, 'red');
|
||||
helpers.printMessage('Use: mongo, s3, or all', 'yellow');
|
||||
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 'all':
|
||||
case '':
|
||||
await serviceManager.stopMongoDB();
|
||||
await serviceManager.stopMinIO();
|
||||
await plugins.smartdelay.delayFor(2000);
|
||||
await serviceManager.startMongoDB();
|
||||
console.log();
|
||||
await serviceManager.startMinIO();
|
||||
break;
|
||||
|
||||
default:
|
||||
helpers.printMessage(`Unknown service: ${service}`, 'red');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRemove(serviceManager: ServiceManager) {
|
||||
helpers.printHeader('Removing Containers');
|
||||
helpers.printMessage('⚠️ This will remove containers but preserve data', 'yellow');
|
||||
|
||||
const shouldContinue = await plugins.smartinteract.SmartInteract.getCliConfirmation('Continue?', false);
|
||||
|
||||
if (shouldContinue) {
|
||||
await serviceManager.removeContainers();
|
||||
} else {
|
||||
helpers.printMessage('Cancelled', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleClean(serviceManager: ServiceManager) {
|
||||
helpers.printHeader('Clean All');
|
||||
helpers.printMessage('⚠️ WARNING: This will remove all containers and data!', 'red');
|
||||
helpers.printMessage('This action cannot be undone!', 'red');
|
||||
|
||||
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();
|
||||
helpers.printMessage('All cleaned ✓', 'green');
|
||||
} else {
|
||||
helpers.printMessage('Cancelled', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
helpers.printHeader('GitZone Services Manager');
|
||||
|
||||
helpers.printMessage('Usage: gitzone services [command] [options]', 'green');
|
||||
console.log();
|
||||
|
||||
helpers.printMessage('Commands:', 'yellow');
|
||||
helpers.printMessage(' start [service] Start services (mongo|s3|all)', undefined);
|
||||
helpers.printMessage(' stop [service] Stop services (mongo|s3|all)', undefined);
|
||||
helpers.printMessage(' restart [service] Restart services (mongo|s3|all)', undefined);
|
||||
helpers.printMessage(' status Show service status', undefined);
|
||||
helpers.printMessage(' config Show current configuration', undefined);
|
||||
helpers.printMessage(' compass Show MongoDB Compass connection string', undefined);
|
||||
helpers.printMessage(' logs [service] Show logs (mongo|s3|all) [lines]', undefined);
|
||||
helpers.printMessage(' remove Remove all containers', undefined);
|
||||
helpers.printMessage(' clean Remove all containers and data ⚠️', undefined);
|
||||
helpers.printMessage(' help Show this help message', undefined);
|
||||
console.log();
|
||||
|
||||
helpers.printMessage('Features:', 'yellow');
|
||||
helpers.printMessage(' • Auto-creates .nogit/env.json with smart defaults', undefined);
|
||||
helpers.printMessage(' • Random ports (20000-30000) to avoid conflicts', undefined);
|
||||
helpers.printMessage(' • Project-specific containers for multi-project support', undefined);
|
||||
helpers.printMessage(' • Preserves custom configuration values', undefined);
|
||||
helpers.printMessage(' • MongoDB Compass connection support', undefined);
|
||||
console.log();
|
||||
|
||||
helpers.printMessage('Examples:', 'yellow');
|
||||
helpers.printMessage(' gitzone services start # Start all services', undefined);
|
||||
helpers.printMessage(' gitzone services start mongo # Start only MongoDB', undefined);
|
||||
helpers.printMessage(' gitzone services stop # Stop all services', undefined);
|
||||
helpers.printMessage(' gitzone services status # Check service status', undefined);
|
||||
helpers.printMessage(' gitzone services config # Show configuration', undefined);
|
||||
helpers.printMessage(' gitzone services compass # Get MongoDB Compass connection', undefined);
|
||||
helpers.printMessage(' gitzone services logs mongo 50 # Show last 50 lines of MongoDB logs', undefined);
|
||||
}
|
9
ts/mod_services/mod.plugins.ts
Normal file
9
ts/mod_services/mod.plugins.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export * from '../plugins.js';
|
||||
|
||||
import * as smartshell from '@push.rocks/smartshell';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartinteract from '@push.rocks/smartinteract';
|
||||
import * as smartnetwork from '@push.rocks/smartnetwork';
|
||||
import * as smartdelay from '@push.rocks/smartdelay';
|
||||
|
||||
export { smartshell, smartfile, smartinteract, smartnetwork, smartdelay };
|
@@ -7,6 +7,11 @@ import * as smartcli from '@push.rocks/smartcli';
|
||||
import * as smartpath from '@push.rocks/smartpath';
|
||||
import * as smartpromise from '@push.rocks/smartpromise';
|
||||
import * as smartupdate from '@push.rocks/smartupdate';
|
||||
import * as smartshell from '@push.rocks/smartshell';
|
||||
import * as smartnetwork from '@push.rocks/smartnetwork';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartinteract from '@push.rocks/smartinteract';
|
||||
import * as smartdelay from '@push.rocks/smartdelay';
|
||||
|
||||
export {
|
||||
smartlog,
|
||||
@@ -18,4 +23,9 @@ export {
|
||||
smartpath,
|
||||
smartpromise,
|
||||
smartupdate,
|
||||
smartshell,
|
||||
smartnetwork,
|
||||
smartfile,
|
||||
smartinteract,
|
||||
smartdelay,
|
||||
};
|
||||
|
Reference in New Issue
Block a user