236 lines
8.5 KiB
JavaScript
236 lines
8.5 KiB
JavaScript
const exec = require('child_process').exec;
|
|
const https = require('https');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const isWin = require('os').platform().indexOf('win32') > -1;
|
|
const isLinux = require('os').platform().indexOf('linux') > -1;
|
|
const isDarwin = require('os').platform().indexOf('darwin') > -1;
|
|
const ROOT_DIR = `${__dirname}/../`;
|
|
const l = m => console.log(m);
|
|
|
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
|
|
const runCmd = (cmd, shouldOutput = true, cwd = null) => {
|
|
const startTime = new Date();
|
|
const option = cwd ? {cwd} : {};
|
|
const currentCwd = cwd || process.cwd();
|
|
l(`[${startTime.toISOString()}] 开始执行命令: ${cmd}`);
|
|
l(`执行目录: ${currentCwd}`);
|
|
|
|
return new Promise(r => {
|
|
const childProcess = exec(cmd, option);
|
|
l(`进程ID: ${childProcess.pid}`);
|
|
let result = '';
|
|
let error = '';
|
|
|
|
childProcess.stdout.on('data', function(data) {
|
|
shouldOutput && console.log(data);
|
|
result += data.toString();
|
|
});
|
|
|
|
childProcess.stderr.on('data', (data) => {
|
|
shouldOutput && console.log(data);
|
|
error += data.toString();
|
|
})
|
|
|
|
childProcess.on('exit', (code, signal) => {
|
|
const endTime = new Date();
|
|
const duration = (endTime - startTime) / 1000;
|
|
l(`[${endTime.toISOString()}] 命令执行完成,耗时: ${duration}秒`);
|
|
if (signal) {
|
|
l(`进程被信号 ${signal} 终止`);
|
|
}
|
|
l(`退出码: ${code}`);
|
|
r({code, signal, result, error})
|
|
})
|
|
});
|
|
}
|
|
|
|
const runCmdAndExitWhenFailed = async (cmd, msg, shouldOutput = true, cwd = null) => {
|
|
l('----------------------------------------');
|
|
l(`执行命令: ${cmd}`);
|
|
l(`工作目录: ${cwd || process.cwd()}`);
|
|
const ret = await runCmd(cmd, shouldOutput, cwd);
|
|
if (ret.code !== 0 || ret.code === null) {
|
|
l('命令执行失败:');
|
|
l(msg);
|
|
l(`命令: ${cmd}`);
|
|
l(`工作目录: ${cwd || process.cwd()}`);
|
|
l('退出码: ' + ret.code);
|
|
l('错误输出: ' + ret.error);
|
|
l('标准输出: ' + ret.result);
|
|
l('系统信息:');
|
|
l(`- 平台: ${process.platform}`);
|
|
l(`- 架构: ${process.arch}`);
|
|
l(`- Node版本: ${process.version}`);
|
|
l(`- 内存使用: ${JSON.stringify(process.memoryUsage())}`);
|
|
process.exit(1);
|
|
}
|
|
l('----------------------------------------');
|
|
return ret;
|
|
}
|
|
|
|
function getMediaGetBinPath() {
|
|
return path.join(ROOT_DIR, 'backend', 'bin', `media-get${isWin ? '.exe' : ''}`);
|
|
}
|
|
|
|
async function checkAndUpdateMediaGet(currentMediaGetVersion) {
|
|
const MediaGetService = require('../backend/src/service/media_fetcher/media_get');
|
|
|
|
const latestVersion = await MediaGetService.getLatestMediaGetVersion();
|
|
if (latestVersion === false) {
|
|
return;
|
|
}
|
|
if (currentMediaGetVersion.localeCompare(latestVersion, undefined, { numeric: true, sensitivity: 'base' }) >= 0) {
|
|
l('当前 media-get 版本已经是最新版本');
|
|
return;
|
|
}
|
|
l(`当前 media-get(${currentMediaGetVersion})版本不是最新版本, 开始更新到${latestVersion}`);
|
|
await MediaGetService.downloadTheLatestMediaGet(latestVersion);
|
|
}
|
|
|
|
function copyDir(src, dest) {
|
|
fs.mkdirSync(dest);
|
|
fs.readdirSync(src).forEach(file => {
|
|
const srcPath = path.join(src, file);
|
|
const destPath = path.join(dest, file);
|
|
if (fs.statSync(srcPath).isDirectory()) {
|
|
copyDir(srcPath, destPath);
|
|
} else {
|
|
fs.copyFileSync(srcPath, destPath);
|
|
}
|
|
});
|
|
}
|
|
|
|
async function getPackageManager() {
|
|
let ret = await runCmd('pnpm --version', false);
|
|
if (ret.code === 0) {
|
|
return 'pnpm';
|
|
}
|
|
ret = await runCmd('yarn --version', false);
|
|
if (ret.code === 0) {
|
|
return 'yarn';
|
|
}
|
|
|
|
return 'npm';
|
|
}
|
|
|
|
async function downloadMediaGetWithRetry(latestVersion) {
|
|
const MediaGetService = require('../backend/src/service/media_fetcher/media_get');
|
|
const maxRetries = 3;
|
|
let retryCount = 0;
|
|
|
|
while (retryCount < maxRetries) {
|
|
l(`尝试下载 media-get (第 ${retryCount + 1} 次尝试)`);
|
|
const success = await MediaGetService.downloadTheLatestMediaGet(latestVersion);
|
|
if (success) {
|
|
return true;
|
|
}
|
|
retryCount++;
|
|
if (retryCount < maxRetries) {
|
|
l(`下载失败,等待 5 秒后重试...`);
|
|
await sleep(5000);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async function run() {
|
|
try {
|
|
l('开始执行...');
|
|
|
|
// 检查 FFmpeg 安装和位置
|
|
l('检查 FFmpeg 安装状态...');
|
|
if (!process.env.CROSS_COMPILING) {
|
|
const ffmpegRet = await runCmd('which ffmpeg && ls -l $(which ffmpeg)', true);
|
|
l('FFmpeg location and permissions:');
|
|
l(ffmpegRet.result);
|
|
await runCmdAndExitWhenFailed('ffmpeg -version', '请先安装 ffmpeg', false);
|
|
} else {
|
|
l('跳过 FFmpeg 检查 (CROSS_COMPILING=1)');
|
|
}
|
|
|
|
await runCmdAndExitWhenFailed('npm version', '请先安装 npm', false);
|
|
|
|
const pm = await getPackageManager();
|
|
l(`安装 node_module via ${pm}`)
|
|
l('开始安装后端依赖...');
|
|
l(`执行命令: ${pm} install --production --verbose in ${path.join(ROOT_DIR, 'backend')}`);
|
|
const backendInstallResult = await runCmdAndExitWhenFailed(`${pm} install --production --verbose`, '安装后端 node_module 失败', true, path.join(ROOT_DIR, 'backend'));
|
|
l('后端依赖安装结果:');
|
|
l('Exit code: ' + backendInstallResult.code);
|
|
l('Output: ' + backendInstallResult.result);
|
|
if (backendInstallResult.error) {
|
|
l('Error output: ' + backendInstallResult.error);
|
|
}
|
|
|
|
l('检查 media-get');
|
|
const MediaGetService = require('../backend/src/service/media_fetcher/media_get');
|
|
|
|
const mediaGetPath = getMediaGetBinPath();
|
|
l(`检查 media-get 权限: ${mediaGetPath}`);
|
|
await runCmd(`ls -l ${mediaGetPath}`, true);
|
|
|
|
if (!fs.existsSync(mediaGetPath)) {
|
|
const latestVersion = await MediaGetService.getLatestMediaGetVersion();
|
|
if (latestVersion === false) {
|
|
l('获取 media-get 最新版本失败,无法继续安装');
|
|
return false;
|
|
}
|
|
l('开始下载核心程序 media-get');
|
|
if (await downloadMediaGetWithRetry(latestVersion) === false) {
|
|
l('下载核心程序 media-get 失败');
|
|
return false;
|
|
}
|
|
} else {
|
|
const currentMediaGetVersion = await MediaGetService.getLatestMediaGetVersion();
|
|
await checkAndUpdateMediaGet(currentMediaGetVersion);
|
|
}
|
|
|
|
l('开始安装前端依赖...');
|
|
const frontendInstallResult = await runCmdAndExitWhenFailed(`${pm} install --verbose`, '安装前端 node_module 失败', true, path.join(ROOT_DIR, 'frontend'));
|
|
l('前端依赖安装结果:');
|
|
l('Exit code: ' + frontendInstallResult.code);
|
|
l('Output: ' + frontendInstallResult.result);
|
|
if (frontendInstallResult.error) {
|
|
l('Error output: ' + frontendInstallResult.error);
|
|
}
|
|
|
|
l('开始编译前端...');
|
|
const buildResult = await runCmdAndExitWhenFailed(`${pm} run build`, '前端编译失败', true, path.join(ROOT_DIR, 'frontend'));
|
|
l('前端编译结果:');
|
|
l('Exit code: ' + buildResult.code);
|
|
l('Output: ' + buildResult.result);
|
|
if (buildResult.error) {
|
|
l('Error output: ' + buildResult.error);
|
|
}
|
|
|
|
l('删除老目录');
|
|
try {
|
|
fs.rmdirSync(path.join(ROOT_DIR, 'backend', 'public'), { recursive: true });
|
|
} catch(e) {
|
|
l('删除老目录失败,但继续执行: ' + e.message);
|
|
}
|
|
|
|
l('拷贝前端目录');
|
|
try {
|
|
copyDir(path.join(ROOT_DIR, 'frontend', 'dist'), path.join(ROOT_DIR, 'backend', 'public'));
|
|
} catch(e) {
|
|
l('拷贝前端目录失败: ' + e.message);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
l('执行过程中出现错误:');
|
|
l(error.stack || error.message || error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
run().then(isFine => {
|
|
l(isFine ? `执行完毕,执行以下命令启动服务:\r\n\r\nnpm run app` : '执行出错,请检查');
|
|
if (!isFine) {
|
|
process.exit(1);
|
|
}
|
|
}); |