Files
yunrui_asset/install/nginx.js
T
yi e9bff9beb3
Build and Push Docker Images / Build Backend Image (push) Has been cancelled
Build and Push Docker Images / Build Frontend Image (push) Has been cancelled
Build and Push Docker Images / Notify Build Complete (push) Has been cancelled
Update 2026-06-23 10:40:10
2026-06-23 10:40:10 +08:00

435 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { colors, ICONS, log } = require('./logger');
const { ask, select } = require('./ui');
const { config } = require('./config');
const { runCommand } = require('./utils');
const { isNginxInstalled, detectLinuxDistro, isRootUser } = require('./env-check');
function showNginxInstallGuideLinux() {
const distro = detectLinuxDistro();
const root = isRootUser();
const sudoPrefix = root ? '' : 'sudo ';
log.section('Nginx 安装命令');
if (root) {
console.log(` ${ICONS.pipe} ${colors.gray}(当前以 root 用户运行,无需 sudo)${colors.reset}`);
}
switch (distro) {
case 'ubuntu':
case 'debian':
console.log(` ${colors.cyan}${sudoPrefix}apt update${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}apt install -y nginx${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}systemctl start nginx${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}systemctl enable nginx${colors.reset}`);
break;
case 'centos':
case 'rhel':
case 'fedora':
console.log(` ${colors.cyan}${sudoPrefix}yum install -y epel-release${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}yum install -y nginx${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}systemctl start nginx${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}systemctl enable nginx${colors.reset}`);
break;
case 'arch':
console.log(` ${colors.cyan}${sudoPrefix}pacman -S --noconfirm nginx${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}systemctl start nginx${colors.reset}`);
console.log(` ${colors.cyan}${sudoPrefix}systemctl enable nginx${colors.reset}`);
break;
default:
console.log(` ${colors.cyan}# Ubuntu/Debian${colors.reset}`);
console.log(` ${sudoPrefix}apt update && ${sudoPrefix}apt install -y nginx`);
console.log(`\n ${colors.cyan}# CentOS/RHEL/Fedora${colors.reset}`);
console.log(` ${sudoPrefix}yum install -y epel-release && ${sudoPrefix}yum install -y nginx`);
console.log(`\n ${colors.cyan}# Arch Linux${colors.reset}`);
console.log(` ${sudoPrefix}pacman -S nginx`);
}
console.log('\n' + colors.gray + '安装完成后,请重新运行此部署脚本。' + colors.reset);
}
async function autoInstallNginxLinux() {
const distro = detectLinuxDistro();
const root = isRootUser();
const sudoPrefix = root ? '' : 'sudo ';
log.step('自动安装 Nginx');
log.info(`检测到 Linux 发行版: ${distro}`);
if (root) {
log.info('当前以 root 用户运行,无需 sudo');
}
try {
let installCommand = '';
let startCommand = `${sudoPrefix}systemctl start nginx`;
let enableCommand = `${sudoPrefix}systemctl enable nginx`;
switch (distro) {
case 'ubuntu':
case 'debian':
log.info('使用 apt 安装 Nginx...');
log.info(`执行: ${sudoPrefix}apt update`);
const updateResult = runCommand(`${sudoPrefix}apt update`);
if (!updateResult.success) {
log.warning('apt update 失败,尝试继续安装...');
}
log.info(`执行: ${sudoPrefix}apt install -y nginx`);
installCommand = `${sudoPrefix}apt install -y nginx`;
break;
case 'centos':
case 'rhel':
case 'fedora':
log.info('使用 yum 安装 Nginx...');
log.info(`执行: ${sudoPrefix}yum install -y epel-release`);
const epelResult = runCommand(`${sudoPrefix}yum install -y epel-release`);
if (!epelResult.success) {
log.warning('epel-release 安装失败,尝试继续...');
}
log.info(`执行: ${sudoPrefix}yum install -y nginx`);
installCommand = `${sudoPrefix}yum install -y nginx`;
break;
case 'arch':
log.info('使用 pacman 安装 Nginx...');
log.info(`执行: ${sudoPrefix}pacman -S --noconfirm nginx`);
installCommand = `${sudoPrefix}pacman -S --noconfirm nginx`;
break;
default:
log.error('未知的 Linux 发行版,无法自动安装');
return false;
}
const installResult = runCommand(installCommand);
if (!installResult.success) {
log.error('Nginx 安装失败');
return false;
}
log.success('Nginx 安装完成');
log.info('启动 Nginx 服务...');
const startResult = runCommand(startCommand);
if (!startResult.success) {
log.warning('Nginx 启动失败,可能需要手动启动');
} else {
log.success('Nginx 服务已启动');
}
log.info('设置开机自启...');
runCommand(enableCommand, { silent: true });
const checkResult = runCommand('nginx -v', { silent: true });
if (checkResult.success) {
log.success(`Nginx 安装成功: ${checkResult.output.trim()}`);
return true;
} else {
log.error('Nginx 安装验证失败');
return false;
}
} catch (error) {
log.error(`自动安装失败: ${error.message}`);
return false;
}
}
async function autoInstallNginx() {
log.step('自动安装 Nginx');
log.info('正在下载 Nginx...');
const https = require('https');
const nginxUrl = 'https://nginx.org/download/nginx-1.24.0.zip';
const downloadPath = path.join(__dirname, '..', 'nginx-1.24.0.zip');
const installPath = 'C:\\nginx';
try {
await new Promise((resolve, reject) => {
const file = fs.createWriteStream(downloadPath);
https.get(nginxUrl, (response) => {
response.pipe(file);
file.on('finish', () => {
file.close();
resolve();
});
}).on('error', reject);
});
log.success('Nginx 下载完成');
log.info('正在解压...');
execSync(`powershell -Command "Expand-Archive -Path '${downloadPath}' -DestinationPath 'C:\\\\' -Force"`, { stdio: 'inherit' });
if (fs.existsSync('C:\\nginx-1.24.0')) {
if (fs.existsSync(installPath)) {
fs.rmSync(installPath, { recursive: true });
}
fs.renameSync('C:\\nginx-1.24.0', installPath);
}
fs.unlinkSync(downloadPath);
log.success(`Nginx 已安装到 ${installPath}`);
log.info('安装后操作:');
console.log(` 1. 将配置文件复制到: ${colors.cyan}C:/nginx/conf/conf.d/idc.conf${colors.reset}`);
console.log(` 2. 修改主配置: 在 C:/nginx/conf/nginx.conf 的 http 段添加: ${colors.cyan}include conf.d/*.conf;${colors.reset}`);
console.log(` 3. 启动 Nginx: ${colors.cyan}C:/nginx/nginx.exe${colors.reset}`);
} catch (error) {
log.error('Nginx 自动安装失败: ' + error.message);
log.info('请手动下载安装: https://nginx.org/en/download.html');
throw error;
}
}
async function autoConfigureNginx() {
log.step('配置 Nginx');
if (!isNginxInstalled()) {
log.warning('Nginx 未安装,尝试自动安装...');
if (process.platform === 'linux') {
const installed = await autoInstallNginxLinux();
if (!installed) {
log.error('Nginx 自动安装失败');
log.info('请手动安装 Nginx 后执行:');
console.log(` ${colors.cyan}sudo cp deploy/nginx-yunrui.conf /etc/nginx/sites-available/yunrui_asset${colors.reset}`);
console.log(` ${colors.cyan}sudo ln -sf /etc/nginx/sites-available/yunrui_asset /etc/nginx/sites-enabled/yunrui_asset${colors.reset}`);
console.log(` ${colors.cyan}sudo nginx -t && sudo systemctl restart nginx${colors.reset}`);
return;
}
} else {
log.error('当前平台不支持自动安装 Nginx,请手动安装');
return;
}
}
const root = isRootUser();
const sudoPrefix = root ? '' : 'sudo ';
const isBtPanel = fs.existsSync('/www/server/nginx/sbin/nginx') ||
fs.existsSync('/www/server/nginx/conf/nginx.conf');
let wwwDir;
if (isBtPanel) {
wwwDir = '/www/wwwroot/idc';
log.info('检测到宝塔面板 Nginx 环境');
} else {
wwwDir = '/var/www/yunrui_asset';
}
const distDir = path.join(__dirname, '..', 'frontend', 'dist');
if (fs.existsSync(distDir)) {
log.info(`复制前端文件到 ${wwwDir}...`);
runCommand(`${sudoPrefix}mkdir -p ${wwwDir}`, { silent: true });
const copyResult = runCommand(`${sudoPrefix}cp -r ${distDir}/* ${wwwDir}/`, { silent: true });
if (copyResult.success) {
runCommand(`${sudoPrefix}chmod -R 755 ${wwwDir}`, { silent: true });
log.success('前端文件部署完成');
} else {
log.warning('前端文件复制失败,Nginx 可能无法访问前端页面');
}
} else {
log.warning(`前端构建产物不存在: ${distDir}`);
log.info('请先完成前端构建后再配置 Nginx');
}
config.nginxRoot = wwwDir;
const configSource = path.join(__dirname, '..', 'deploy', 'nginx-yunrui.conf');
if (!fs.existsSync(configSource)) {
log.error('Nginx 配置文件不存在,请先运行安装脚本');
return;
}
try {
let configDir = '';
let useSitesAvailable = false;
let isBtNginx = false;
if (isBtPanel && fs.existsSync('/www/server/nginx/conf/vhost')) {
configDir = '/www/server/nginx/conf/vhost';
isBtNginx = true;
log.info('使用宝塔面板 Nginx 虚拟主机目录');
} else if (fs.existsSync('/etc/nginx/sites-available')) {
configDir = '/etc/nginx/sites-available';
useSitesAvailable = true;
} else if (fs.existsSync('/etc/nginx/conf.d')) {
configDir = '/etc/nginx/conf.d';
useSitesAvailable = false;
} else {
log.warning('未找到标准 Nginx 配置目录,请手动配置');
return;
}
const configDest = path.join(configDir, 'idc.conf');
log.info(`复制配置到 ${configDest}...`);
const copyResult = runCommand(`${sudoPrefix}cp "${configSource}" "${configDest}"`);
if (!copyResult.success) {
log.error('配置文件复制失败');
return;
}
if (useSitesAvailable) {
log.info('创建站点软链接...');
runCommand(`${sudoPrefix}ln -sf "${configDest}" /etc/nginx/sites-enabled/yunrui_asset.conf`);
if (fs.existsSync('/etc/nginx/sites-enabled/default')) {
log.info('删除默认站点配置...');
runCommand(`${sudoPrefix}rm -f /etc/nginx/sites-enabled/default`, { silent: true });
}
}
if (isBtNginx) {
const btNginxConf = '/www/server/nginx/conf/nginx.conf';
try {
const mainConf = fs.readFileSync(btNginxConf, 'utf8');
if (!mainConf.includes('vhost')) {
log.warning('宝塔 Nginx 主配置未包含 vhost 目录,请手动确认');
log.info(`确保 ${btNginxConf} 的 http 段包含: include vhost/*.conf;`);
}
} catch {}
}
log.info('测试 Nginx 配置...');
const testResult = runCommand(`${sudoPrefix}nginx -t`);
if (!testResult.success) {
log.error('Nginx 配置测试失败');
log.info('请检查配置文件: ' + configDest);
return;
}
log.info('重启 Nginx 服务...');
const restartResult = runCommand(`${sudoPrefix}systemctl restart nginx`);
if (restartResult.success) {
log.success('Nginx 配置完成并已生效');
} else {
log.warning('systemctl 重启失败,尝试直接重载...');
const reloadResult = runCommand(`${sudoPrefix}nginx -s reload`);
if (reloadResult.success) {
log.success('Nginx 配置完成并已生效');
} else {
log.warning('Nginx 重载失败,请手动重启: sudo systemctl restart nginx');
}
}
} catch (error) {
log.error(`自动配置失败: ${error.message}`);
log.info('请手动配置 Nginx');
}
}
async function configureServices(cmdArgs) {
log.step('服务配置');
if (cmdArgs?.nonInteractive && cmdArgs?.backendPort) {
config.backendPort = cmdArgs.backendPort;
log.info(`使用命令行参数: 后端端口 = ${config.backendPort}`);
} else if (cmdArgs?.nonInteractive) {
config.backendPort = '8000';
log.info('使用默认配置: 后端端口 = 8000');
} else {
config.backendPort = await ask('后端服务端口', config.backendPort || '8000');
}
if (cmdArgs?.nonInteractive) {
config.nodeEnv = 'production';
log.info('使用默认配置: 运行环境 = production');
} else {
const defaultEnvIndex = (config.nodeEnv || 'production') === 'development' ? 2 : 1;
config.nodeEnv = await select('选择运行环境:', [
{ label: 'production(生产模式,性能优化,推荐正式部署)', value: 'production' },
{ label: 'development(开发模式,详细日志,便于调试)', value: 'development' }
], defaultEnvIndex);
}
if (cmdArgs?.nonInteractive || cmdArgs?.skipNginx) {
config.frontendDeploy = 'pm2';
config.frontendPort = '3000';
log.info('使用默认配置: 前端部署 = PM2 serve (端口 3000)');
} else {
const defaultDeployIndex = (config.frontendDeploy || 'nginx') === 'pm2' ? 2 : 1;
config.frontendDeploy = await select('前端部署方式:', [
{ label: 'Nginx(性能最优,推荐生产环境)', value: 'nginx' },
{ label: 'PM2 serve(简单快捷,无需额外安装)', value: 'pm2' }
], defaultDeployIndex);
}
if (config.frontendDeploy === 'nginx') {
const nginxInstalled = isNginxInstalled();
if (!nginxInstalled) {
log.warning('未检测到 Nginx 安装');
if (process.platform === 'win32') {
if (!cmdArgs?.nonInteractive) {
const autoInstall = await ask('是否自动下载并安装 Nginx? (Y/n)', 'Y');
if (autoInstall.toLowerCase() === 'y') {
await autoInstallNginx();
} else {
log.info('已跳过自动安装,请手动安装 Nginx 后启动服务');
console.log(` 下载地址: ${colors.cyan}https://nginx.org/en/download.html${colors.reset}`);
console.log(` 安装路径建议: ${colors.cyan}C:/nginx${colors.reset}`);
}
}
} else if (process.platform === 'linux') {
if (!cmdArgs?.nonInteractive) {
const autoInstall = await ask('是否自动安装 Nginx? (Y/n)', 'Y');
if (autoInstall.toLowerCase() === 'y') {
const installed = await autoInstallNginxLinux();
if (!installed) {
log.error('Nginx 自动安装失败');
log.info('请手动安装后重新运行脚本');
showNginxInstallGuideLinux();
process.exit(1);
}
} else {
log.info('已跳过自动安装');
showNginxInstallGuideLinux();
const continueDeploy = await ask('是否继续部署(后端将启动,前端需手动配置 Nginx? (Y/n)', 'Y');
if (continueDeploy.toLowerCase() !== 'y') {
log.info('已取消部署,请安装 Nginx 后重新运行');
process.exit(0);
}
}
}
} else {
log.info('请使用 Homebrew 安装 Nginx');
console.log(` ${colors.cyan}brew install nginx${colors.reset}`);
}
}
if (!cmdArgs?.nonInteractive) {
config.frontendPort = await ask('Nginx 监听端口', config.frontendPort || '80');
config.domain = await ask('域名(没有则填localhost', config.domain || 'localhost');
} else {
config.frontendPort = config.frontendPort || '80';
config.domain = config.domain || 'localhost';
}
} else {
if (!cmdArgs?.nonInteractive) {
config.frontendPort = await ask('前端服务端口', config.frontendPort || '3000');
}
}
log.divider();
}
module.exports = {
showNginxInstallGuideLinux,
autoInstallNginxLinux,
autoInstallNginx,
autoConfigureNginx,
configureServices,
};