291 lines
10 KiB
JavaScript
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();
|