auto-deploy-demo/server/services/nginxManager.js

291 lines
10 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const { execSync, exec } = require('child_process');
const config = require('../config');
class NginxManager {
constructor() {
this.configDir = path.resolve(config.nginxConfigDir);
this.templatePath = path.resolve(config.nginxTemplatePath);
this.backupFileName = 'auto-deploy.conf.backup';
this.mainConfigFile = path.join(this.configDir, 'auto-deploy.conf');
console.log('[NginxManager] Initialized with:');
console.log(` - configDir: ${this.configDir}`);
console.log(` - templatePath: ${this.templatePath}`);
console.log(` - mainConfigFile: ${this.mainConfigFile}`);
}
renderTemplate(templateName, variables) {
const templateFile = path.join(this.templatePath, templateName);
console.log(`[NginxManager] Rendering template: ${templateFile}`);
if (!fs.existsSync(templateFile)) {
console.error(`[NginxManager] Template file not found: ${templateFile}`);
throw new Error(`Template file not found: ${templateName}`);
}
let content = fs.readFileSync(templateFile, 'utf8');
Object.keys(variables).forEach(key => {
const regex = new RegExp(`{{${key}}}`, 'g');
content = content.replace(regex, variables[key] || '');
});
console.log(`[NginxManager] Template rendered successfully`);
return content;
}
generateMainConfig() {
try {
console.log('[NginxManager] Generating main config...');
const projects = this._getRunningProjects();
console.log(`[NginxManager] Found ${projects.length} running projects:`,
projects.map(p => ({ id: p.id, name: p.name, port: p.port })));
const projectLocations = projects.map(project => {
console.log(`[NginxManager] Generating location for project ${project.name} (ID: ${project.id}, Port: ${project.port})`);
return this.renderTemplate('location.conf.tpl', {
PROJECT_NAME: project.name,
PROJECT_ID: project.id,
PROJECT_PORT: project.port
});
}).join('\n');
console.log(`[NginxManager] Project locations generated: ${projectLocations.length} chars`);
const serverName = config.baseDomain || 'localhost';
const port = config.isProduction() ? 80 : config.port;
console.log(`[NginxManager] Server config: serverName=${serverName}, port=${port}`);
const mainConfig = this.renderTemplate('main.conf.tpl', {
PORT: port,
SERVER_NAME: serverName,
MANAGER_PORT: config.port,
PROJECT_LOCATIONS: projectLocations
});
console.log(`[NginxManager] Main config generated: ${mainConfig.length} chars`);
return mainConfig;
} catch (error) {
console.error(`[NginxManager] Failed to generate main config: ${error.message}`);
throw new Error(`Failed to generate main config: ${error.message}`);
}
}
addProjectLocation(project) {
console.log(`[NginxManager] addProjectLocation called for project:`, project);
try {
this._backupConfig();
console.log(`[NginxManager] Checking config directory: ${this.configDir}`);
if (!fs.existsSync(this.configDir)) {
console.log(`[NginxManager] Creating config directory: ${this.configDir}`);
fs.mkdirSync(this.configDir, { recursive: true });
}
const serverName = config.baseDomain || 'localhost';
const port = config.isProduction() ? 80 : config.port;
const projectLocation = this.renderTemplate('location.conf.tpl', {
PROJECT_NAME: project.name,
PROJECT_ID: project.id,
PROJECT_PORT: project.port
});
const mainConfig = this.renderTemplate('main.conf.tpl', {
PORT: port,
SERVER_NAME: serverName,
MANAGER_PORT: config.port,
PROJECT_LOCATIONS: projectLocation
});
console.log(`[NginxManager] Writing config to: ${this.mainConfigFile}`);
fs.writeFileSync(this.mainConfigFile, mainConfig);
console.log(`[NginxManager] Config written successfully`);
console.log(`[NginxManager] Config content preview:\n${mainConfig.substring(0, 500)}...`);
return { success: true, message: `Added location for project ${project.name}` };
} catch (error) {
console.error(`[NginxManager] addProjectLocation failed: ${error.message}`);
console.error(error.stack);
return { success: false, message: error.message };
}
}
removeProjectLocation(projectId) {
console.log(`[NginxManager] removeProjectLocation called for project: ${projectId}`);
try {
this._backupConfig();
const mainConfig = this.generateMainConfig();
if (!fs.existsSync(this.configDir)) {
fs.mkdirSync(this.configDir, { recursive: true });
}
fs.writeFileSync(this.mainConfigFile, mainConfig);
return { success: true, message: `Removed location for project ${projectId}` };
} catch (error) {
console.error(`[NginxManager] removeProjectLocation failed: ${error.message}`);
return { success: false, message: error.message };
}
}
testConfig() {
try {
const testCmd = config.nginxTestCmd || 'nginx -t';
console.log(`[NginxManager] Testing config: ${testCmd}`);
execSync(testCmd, { stdio: 'pipe' });
console.log(`[NginxManager] Config test passed`);
return { success: true, message: 'Nginx configuration is valid' };
} catch (error) {
const errorMessage = error.stderr ? error.stderr.toString() : error.message;
console.error(`[NginxManager] Config test failed: ${errorMessage}`);
return { success: false, message: `Configuration test failed: ${errorMessage}` };
}
}
reload() {
try {
const reloadCmd = config.nginxReloadCmd || 'nginx -s reload';
console.log(`[NginxManager] Reloading Nginx: ${reloadCmd}`);
execSync(reloadCmd, { stdio: 'pipe' });
console.log(`[NginxManager] Nginx reloaded successfully`);
return { success: true, message: 'Nginx reloaded successfully' };
} catch (error) {
const errorMessage = error.stderr ? error.stderr.toString() : error.message;
console.error(`[NginxManager] Nginx reload failed: ${errorMessage}`);
return { success: false, message: `Nginx reload failed: ${errorMessage}` };
}
}
rollback() {
try {
const backupFile = path.join(this.configDir, this.backupFileName);
if (!fs.existsSync(backupFile)) {
return { success: false, message: 'No backup file found to rollback' };
}
fs.copyFileSync(backupFile, this.mainConfigFile);
return { success: true, message: 'Configuration rolled back successfully' };
} catch (error) {
return { success: false, message: `Rollback failed: ${error.message}` };
}
}
checkNginxAvailable() {
try {
execSync('nginx -v', { stdio: 'pipe' });
} catch (error) {
return { available: false, reason: 'Nginx command is not available. Please install nginx first.' };
}
if (!fs.existsSync(this.configDir)) {
try {
fs.mkdirSync(this.configDir, { recursive: true });
} catch (error) {
return { available: false, reason: `Cannot create config directory: ${error.message}` };
}
}
try {
const testFile = path.join(this.configDir, '.write_test');
fs.writeFileSync(testFile, 'test');
fs.unlinkSync(testFile);
} catch (error) {
return { available: false, reason: `Config directory is not writable: ${error.message}` };
}
return { available: true, reason: 'Nginx is available and ready to use' };
}
initConfig() {
console.log('[NginxManager] initConfig called');
try {
if (!fs.existsSync(this.configDir)) {
console.log(`[NginxManager] Creating config directory: ${this.configDir}`);
fs.mkdirSync(this.configDir, { recursive: true });
}
const mainConfig = this.generateMainConfig();
console.log(`[NginxManager] Writing initial config to: ${this.mainConfigFile}`);
fs.writeFileSync(this.mainConfigFile, mainConfig);
return { success: true, message: 'Nginx configuration initialized successfully' };
} catch (error) {
console.error(`[NginxManager] initConfig failed: ${error.message}`);
return { success: false, message: `Failed to initialize config: ${error.message}` };
}
}
_backupConfig() {
if (fs.existsSync(this.mainConfigFile)) {
const backupFile = path.join(this.configDir, this.backupFileName);
fs.copyFileSync(this.mainConfigFile, backupFile);
console.log(`[NginxManager] Config backed up to: ${backupFile}`);
}
}
_getRunningProjects() {
// Use absolute path for container environment
const projectsFile = '/app/data/projects.json';
console.log(`[NginxManager] Reading projects from: ${projectsFile}`);
if (!fs.existsSync(projectsFile)) {
console.log(`[NginxManager] Projects file not found: ${projectsFile}`);
return [];
}
try {
const content = fs.readFileSync(projectsFile, 'utf8');
const projects = JSON.parse(content);
console.log(`[NginxManager] Total projects: ${projects.length}`);
const running = projects.filter(p => p.status === 'running' && p.port);
console.log(`[NginxManager] Running projects with port: ${running.length}`);
return running;
} catch (error) {
console.error('[NginxManager] Failed to read projects.json:', error.message);
return [];
}
}
async safeReload() {
console.log('[NginxManager] safeReload called');
const testResult = this.testConfig();
if (!testResult.success) {
console.error('[NginxManager] Config test failed, aborting reload');
return testResult;
}
const reloadResult = this.reload();
if (!reloadResult.success) {
console.error('[NginxManager] Reload failed, rolling back');
this.rollback();
return {
success: false,
message: `Reload failed, configuration rolled back. Error: ${reloadResult.message}`
};
}
console.log('[NginxManager] safeReload completed successfully');
return reloadResult;
}
}
module.exports = new NginxManager();