refactor(install,uninstall,update): 统一日志输出并优化安装流程

- 重构install模块的打印逻辑,使用统一的log工具替代原生console
- 新增运行环境、前端端口等安装信息展示
- 优化uninstall模块的步骤命名和预览逻辑,新增跳过提示
- 重构update模块,复用install的日志工具,统一输出格式
- 新增健康检查直接读取后端端口配置,不再硬编码8000
This commit is contained in:
zhang1106
2026-05-12 08:57:14 +08:00
parent 58a152e155
commit 2f8e95be49
4 changed files with 261 additions and 203 deletions
+64 -33
View File
@@ -28,6 +28,16 @@ const { SCRIPT_VERSION } = require('./install/constants');
const BACKUP_DIR = path.join(__dirname, 'backup');
const LOG_DIR = path.join(__dirname, 'logs');
const UNINSTALL_STEPS = [
'停止服务',
'清理 Nginx',
'清理配置',
'数据库清理',
'日志清理',
'上传清理',
'依赖清理',
];
let logFileStream = null;
let uninstallStartTime = null;
let deletedItems = [];
@@ -53,20 +63,20 @@ function showHelp() {
${colors.bright}用法:${colors.reset} node uninstall.js [选项]
${colors.bright}选项:${colors.reset}
${ICONS.pointer} ${colors.cyan}-f, --force${colors.reset} 强制卸载(无需确认)
${ICONS.pointer} ${colors.cyan}-y, --yes${colors.reset} 强制卸载别名(同 --force
${ICONS.pointer} ${colors.cyan}-b, --backup${colors.reset} 卸载前自动备份数据库
${colors.dim}--dry-run${colors.reset} 预览模式:仅显示将要删除的内容
${colors.dim}--skip-db${colors.reset} 跳过数据库删除
${colors.dim}--skip-deps${colors.reset} 跳过依赖删除
${colors.dim}--skip-uploads${colors.reset} 跳过上传文件目录删除
${colors.dim}-h, --help${colors.reset} 显示帮助信息
${ICONS.pointer} ${colors.cyan}-f, --force${colors.reset} 强制卸载(无需确认)
${ICONS.pointer} ${colors.cyan}-y, --yes${colors.reset} 强制卸载别名(同 --force)
${ICONS.pointer} ${colors.cyan}-b, --backup${colors.reset} 卸载前自动备份数据库
${colors.dim}--dry-run${colors.reset} 预览模式:仅显示将要删除的内容
${colors.dim}--skip-db${colors.reset} 跳过数据库删除
${colors.dim}--skip-deps${colors.reset} 跳过依赖删除
${colors.dim}--skip-uploads${colors.reset} 跳过上传文件目录删除
${colors.dim}-h, --help${colors.reset} 显示帮助信息
${colors.bright}示例:${colors.reset}
${colors.cyan}node uninstall.js${colors.reset} # 交互式卸载
${colors.cyan}node uninstall.js --force${colors.reset} # 强制卸载
${colors.cyan}node uninstall.js --backup${colors.reset} # 卸载前备份
${colors.cyan}node uninstall.js --dry-run${colors.reset} # 预览将删除的内容
${colors.cyan}node uninstall.js${colors.reset} # 交互式卸载
${colors.cyan}node uninstall.js --force${colors.reset} # 强制卸载
${colors.cyan}node uninstall.js --backup${colors.reset} # 卸载前备份
${colors.cyan}node uninstall.js --dry-run${colors.reset} # 预览将删除的内容
`);
process.exit(0);
}
@@ -165,7 +175,7 @@ function isRootUser() {
}
async function stopAndDeleteServices() {
log.step('停止并删除服务');
log.step('停止服务');
if (!commandExists('pm2')) {
log.warning('未检测到 PM2,跳过服务停止步骤');
@@ -243,7 +253,7 @@ async function stopAndDeleteServices() {
}
async function cleanupNginxConfig(cmdArgs) {
log.step('清理 Nginx 配置');
log.step('清理 Nginx');
const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux';
@@ -501,7 +511,7 @@ async function cleanupNginxConfig(cmdArgs) {
}
async function cleanupConfigFiles() {
log.step('清理生成的配置文件');
log.step('清理配置');
const filesToDelete = [
{ path: path.join(__dirname, 'backend', '.env'), name: '后端环境变量 (.env)', type: '配置文件' },
@@ -687,7 +697,7 @@ async function cleanupDependencies(cmdArgs) {
}
async function cleanupLogs(cmdArgs) {
log.step('日志文件清理');
log.step('日志清理');
const logsDir = path.join(__dirname, 'backend', 'logs');
if (fs.existsSync(logsDir)) {
@@ -733,7 +743,7 @@ async function cleanupLogs(cmdArgs) {
}
async function cleanupUploads(cmdArgs) {
log.step('上传文件清理');
log.step('上传清理');
const uploadsDir = path.join(__dirname, 'backend', 'uploads');
if (fs.existsSync(uploadsDir)) {
@@ -837,7 +847,9 @@ function showDryRunPreview(cmdArgs) {
});
console.log(`\n ${ICONS.pointer} ${colors.bright}4. 数据库${colors.reset}`);
if (dbConfig.dbType === 'sqlite' && fs.existsSync(dbConfig.sqliteDbPath)) {
if (cmdArgs?.skipDb) {
console.log(` ${ICONS.pipe} ${colors.dim}[跳过] 数据库清理${colors.reset}`);
} else if (dbConfig.dbType === 'sqlite' && fs.existsSync(dbConfig.sqliteDbPath)) {
console.log(` ${ICONS.pipe} 删除 SQLite 数据库: ${path.relative(__dirname, dbConfig.sqliteDbPath)}`);
if (cmdArgs?.backup) {
console.log(` ${ICONS.pipe} 备份数据库到: backup/`);
@@ -857,23 +869,31 @@ function showDryRunPreview(cmdArgs) {
}
console.log(`\n ${ICONS.pointer} ${colors.bright}6. 上传文件${colors.reset}`);
if (fs.existsSync(path.join(__dirname, 'backend', 'uploads'))) {
if (cmdArgs?.skipUploads) {
console.log(` ${ICONS.pipe} ${colors.dim}[跳过] 上传文件目录${colors.reset}`);
} else if (fs.existsSync(path.join(__dirname, 'backend', 'uploads'))) {
console.log(` ${ICONS.pipe} 上传文件目录 (backend/uploads/)`);
} else {
console.log(` ${ICONS.pipe} ${colors.dim}未检测到上传文件目录${colors.reset}`);
}
console.log(`\n ${ICONS.pointer} ${colors.bright}7. 依赖和构建产物${colors.reset}`);
const depDirs = [
{ path: path.join(__dirname, 'backend', 'node_modules'), name: '后端 node_modules' },
{ path: path.join(__dirname, 'frontend', 'node_modules'), name: '前端 node_modules' },
{ path: path.join(__dirname, 'frontend', 'node_modules', '.vite'), name: '前端 Vite 缓存' },
{ path: path.join(__dirname, 'frontend', 'dist'), name: '前端构建产物' },
{ path: path.join(__dirname, 'backend', 'temp'), name: '后端临时文件' }
];
depDirs.forEach(d => {
if (fs.existsSync(d.path)) {
console.log(` ${ICONS.pipe} 删除: ${d.name}`);
}
});
if (cmdArgs?.skipDeps) {
console.log(` ${ICONS.pipe} ${colors.dim}[跳过] 依赖和构建产物清理${colors.reset}`);
} else {
const depDirs = [
{ path: path.join(__dirname, 'backend', 'node_modules'), name: '后端 node_modules' },
{ path: path.join(__dirname, 'frontend', 'node_modules'), name: '前端 node_modules' },
{ path: path.join(__dirname, 'frontend', 'node_modules', '.vite'), name: '前端 Vite 缓存' },
{ path: path.join(__dirname, 'frontend', 'dist'), name: '前端构建产物' },
{ path: path.join(__dirname, 'backend', 'temp'), name: '后端临时文件' }
];
depDirs.forEach(d => {
if (fs.existsSync(d.path)) {
console.log(` ${ICONS.pipe} 删除: ${d.name}`);
}
});
}
log.divider();
log.warning('移除 --dry-run 参数以实际执行卸载');
@@ -903,8 +923,19 @@ function printSummary() {
}
log.section('保留的文件');
console.log(` ${ICONS.pipe} 项目源代码 (backend/, frontend/)`);
console.log(` ${ICONS.pipe} 上传的文件 (backend/uploads/)`);
const backendExists = fs.existsSync(path.join(__dirname, 'backend'));
const frontendExists = fs.existsSync(path.join(__dirname, 'frontend'));
const uploadsExist = fs.existsSync(path.join(__dirname, 'backend', 'uploads'));
if (backendExists || frontendExists) {
console.log(` ${ICONS.pipe} 项目源代码 (backend/, frontend/)`);
}
if (uploadsExist) {
console.log(` ${ICONS.pipe} 上传的文件 (backend/uploads/)`);
}
if (!backendExists && !frontendExists && !uploadsExist) {
console.log(` ${ICONS.pipe}`);
}
log.section('重新部署');
console.log(` ${ICONS.pipe} ${colors.cyan}node install.js${colors.reset}`);