import * as plugins from './mod.plugins.js'; import * as helpers from './helpers.js'; import { logger } from '../gitzone.logging.js'; import { DockerContainer } from './classes.dockercontainer.js'; export interface IServiceConfig { PROJECT_NAME: string; MONGODB_HOST: string; MONGODB_NAME: string; MONGODB_PORT: string; MONGODB_USER: string; MONGODB_PASS: string; MONGODB_URL: string; S3_HOST: string; S3_PORT: string; S3_CONSOLE_PORT: string; S3_ACCESSKEY: string; S3_SECRETKEY: string; S3_BUCKET: string; S3_ENDPOINT: string; S3_USESSL: boolean; } export class ServiceConfiguration { private configPath: string; private config: IServiceConfig; private docker: DockerContainer; constructor() { this.configPath = plugins.path.join(process.cwd(), '.nogit', 'env.json'); this.docker = new DockerContainer(); } /** * Load or create the configuration */ public async loadOrCreate(): Promise { await this.ensureNogitDirectory(); if (await this.configExists()) { await this.loadConfig(); await this.updateMissingFields(); } else { await this.createDefaultConfig(); } // Sync ports from existing Docker containers if they exist await this.syncPortsFromDocker(); return this.config; } /** * Get the current configuration */ public getConfig(): IServiceConfig { return this.config; } /** * Save the configuration to file */ public async saveConfig(): Promise { await plugins.smartfile.memory.toFs( JSON.stringify(this.config, null, 2), this.configPath ); } /** * Ensure .nogit directory exists */ private async ensureNogitDirectory(): Promise { const nogitPath = plugins.path.join(process.cwd(), '.nogit'); await plugins.smartfile.fs.ensureDir(nogitPath); } /** * Check if configuration file exists */ private async configExists(): Promise { return plugins.smartfile.fs.fileExists(this.configPath); } /** * Load configuration from file */ private async loadConfig(): Promise { const configContent = await plugins.smartfile.fs.toStringSync(this.configPath); this.config = JSON.parse(configContent); } /** * Create default configuration */ private async createDefaultConfig(): Promise { 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++; } const mongoUser = 'defaultadmin'; const mongoPass = 'defaultpass'; const mongoHost = 'localhost'; const mongoName = projectName; const mongoPortStr = mongoPort.toString(); const s3Host = 'localhost'; const s3PortStr = s3Port.toString(); this.config = { PROJECT_NAME: projectName, MONGODB_HOST: mongoHost, MONGODB_NAME: mongoName, MONGODB_PORT: mongoPortStr, MONGODB_USER: mongoUser, MONGODB_PASS: mongoPass, MONGODB_URL: `mongodb://${mongoUser}:${mongoPass}@${mongoHost}:${mongoPortStr}/${mongoName}?authSource=admin`, S3_HOST: s3Host, S3_PORT: s3PortStr, S3_CONSOLE_PORT: s3ConsolePort.toString(), S3_ACCESSKEY: 'defaultadmin', S3_SECRETKEY: 'defaultpass', S3_BUCKET: `${projectName}-documents`, S3_ENDPOINT: `http://${s3Host}:${s3PortStr}`, S3_USESSL: false }; await this.saveConfig(); logger.log('ok', '✅ Created .nogit/env.json with project defaults'); logger.log('info', `📍 MongoDB port: ${mongoPort}`); logger.log('info', `📍 S3 API port: ${s3Port}`); logger.log('info', `📍 S3 Console port: ${s3ConsolePort}`); } /** * Update missing fields in existing configuration */ private async updateMissingFields(): Promise { 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; } // Always update MONGODB_URL based on current settings const oldUrl = this.config.MONGODB_URL; this.config.MONGODB_URL = `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${this.config.MONGODB_HOST}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`; if (oldUrl !== this.config.MONGODB_URL) { fieldsAdded.push('MONGODB_URL'); 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_ACCESSKEY) { this.config.S3_ACCESSKEY = 'defaultadmin'; fieldsAdded.push('S3_ACCESSKEY'); updated = true; } if (!this.config.S3_SECRETKEY) { this.config.S3_SECRETKEY = 'defaultpass'; fieldsAdded.push('S3_SECRETKEY'); updated = true; } if (!this.config.S3_BUCKET) { this.config.S3_BUCKET = `${projectName}-documents`; fieldsAdded.push('S3_BUCKET'); updated = true; } if (!this.config.S3_USESSL) { this.config.S3_USESSL = false; fieldsAdded.push('S3_USESSL'); updated = true; } // Always update S3_ENDPOINT based on current settings const oldEndpoint = this.config.S3_ENDPOINT; const protocol = this.config.S3_USESSL ? 'https' : 'http'; this.config.S3_ENDPOINT = `${protocol}://${this.config.S3_HOST}:${this.config.S3_PORT}`; if (oldEndpoint !== this.config.S3_ENDPOINT) { fieldsAdded.push('S3_ENDPOINT'); updated = true; } if (updated) { await this.saveConfig(); logger.log('ok', `✅ Added missing fields: ${fieldsAdded.join(', ')}`); } else { logger.log('ok', '✅ Configuration complete'); } } /** * 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') }; } /** * Sync port configuration from existing Docker containers */ private async syncPortsFromDocker(): Promise { const containers = this.getContainerNames(); let updated = false; // Check MongoDB container const mongoStatus = await this.docker.getStatus(containers.mongo); if (mongoStatus !== 'not_exists') { const portMappings = await this.docker.getPortMappings(containers.mongo); if (portMappings && portMappings['27017']) { const dockerPort = portMappings['27017']; if (this.config.MONGODB_PORT !== dockerPort) { this.config.MONGODB_PORT = dockerPort; updated = true; } } } // Check MinIO container const minioStatus = await this.docker.getStatus(containers.minio); if (minioStatus !== 'not_exists') { const portMappings = await this.docker.getPortMappings(containers.minio); if (portMappings) { if (portMappings['9000']) { const dockerPort = portMappings['9000']; if (this.config.S3_PORT !== dockerPort) { this.config.S3_PORT = dockerPort; updated = true; } } if (portMappings['9001']) { const dockerPort = portMappings['9001']; if (this.config.S3_CONSOLE_PORT !== dockerPort) { this.config.S3_CONSOLE_PORT = dockerPort; updated = true; } } } } if (updated) { // Update derived fields this.config.MONGODB_URL = `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${this.config.MONGODB_HOST}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`; const protocol = this.config.S3_USESSL ? 'https' : 'http'; this.config.S3_ENDPOINT = `${protocol}://${this.config.S3_HOST}:${this.config.S3_PORT}`; await this.saveConfig(); } } /** * Validate and update ports if they're not available */ public async validateAndUpdatePorts(): Promise { let updated = false; const containers = this.getContainerNames(); // Check if containers exist - if they do, ports are fine const mongoExists = await this.docker.exists(containers.mongo); const minioExists = await this.docker.exists(containers.minio); // Only check port availability if containers don't exist if (!mongoExists) { const mongoPort = parseInt(this.config.MONGODB_PORT); if (!(await helpers.isPortAvailable(mongoPort))) { logger.log('note', `⚠️ MongoDB port ${mongoPort} is in use, finding new port...`); const newPort = await helpers.getRandomAvailablePort(); this.config.MONGODB_PORT = newPort.toString(); logger.log('ok', `✅ New MongoDB port: ${newPort}`); updated = true; } } if (!minioExists) { const s3Port = parseInt(this.config.S3_PORT); const s3ConsolePort = parseInt(this.config.S3_CONSOLE_PORT); if (!(await helpers.isPortAvailable(s3Port))) { logger.log('note', `⚠️ S3 API port ${s3Port} is in use, finding new port...`); const newPort = await helpers.getRandomAvailablePort(); this.config.S3_PORT = newPort.toString(); logger.log('ok', `✅ New S3 API port: ${newPort}`); updated = true; } if (!(await helpers.isPortAvailable(s3ConsolePort))) { logger.log('note', `⚠️ S3 Console port ${s3ConsolePort} is in use, finding new port...`); let newPort = parseInt(this.config.S3_PORT) + 1; while (!(await helpers.isPortAvailable(newPort))) { newPort++; } this.config.S3_CONSOLE_PORT = newPort.toString(); logger.log('ok', `✅ New S3 Console port: ${newPort}`); updated = true; } } if (updated) { // Update derived fields this.config.MONGODB_URL = `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${this.config.MONGODB_HOST}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`; const protocol = this.config.S3_USESSL ? 'https' : 'http'; this.config.S3_ENDPOINT = `${protocol}://${this.config.S3_HOST}:${this.config.S3_PORT}`; await this.saveConfig(); } return updated; } /** * Force reconfigure all ports with new available ones */ public async reconfigurePorts(): Promise { logger.log('note', '🔄 Finding new available ports...'); 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.MONGODB_PORT = mongoPort.toString(); this.config.S3_PORT = s3Port.toString(); this.config.S3_CONSOLE_PORT = s3ConsolePort.toString(); // Update derived fields this.config.MONGODB_URL = `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${this.config.MONGODB_HOST}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`; const protocol = this.config.S3_USESSL ? 'https' : 'http'; this.config.S3_ENDPOINT = `${protocol}://${this.config.S3_HOST}:${this.config.S3_PORT}`; await this.saveConfig(); logger.log('ok', '✅ New port configuration:'); logger.log('info', ` 📍 MongoDB: ${mongoPort}`); logger.log('info', ` 📍 S3 API: ${s3Port}`); logger.log('info', ` 📍 S3 Console: ${s3ConsolePort}`); } }