mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 08:09:19 +00:00
240 lines
8.2 KiB
JavaScript
240 lines
8.2 KiB
JavaScript
import { exec } from 'child_process';
|
||
import { promisify } from 'util';
|
||
import fs from 'fs/promises';
|
||
import path from 'path';
|
||
import { redis } from "../../../../utils/redis.js";
|
||
|
||
const execAsync = promisify(exec);
|
||
|
||
// Git错误处理函数
|
||
function handleGitError(error, stderr) {
|
||
if (error.message.includes('RPC failed')) {
|
||
return '网络连接失败,请检查网络后重试';
|
||
}
|
||
if (error.message.includes('early EOF')) {
|
||
return '数据传输中断,请重试';
|
||
}
|
||
if (error.message.includes('fetch-pack: invalid index-pack output')) {
|
||
return '数据包错误,请重试';
|
||
}
|
||
if (error.message.includes('Timed out')) {
|
||
return '连接超时,请检查网络后重试';
|
||
}
|
||
if (error.message.includes('Could not resolve host')) {
|
||
return '无法解析主机地址,请检查网络';
|
||
}
|
||
if (error.message.includes('Permission denied')) {
|
||
return '权限被拒绝,请检查git权限配置';
|
||
}
|
||
if (error.message.includes('be overwritten by merge')) {
|
||
return '存在冲突,请使用强制更新';
|
||
}
|
||
|
||
// 如果是其他错误,返回具体错误信息
|
||
return stderr || error.message || '未知错误';
|
||
}
|
||
|
||
async function ensureDirectory(dir) {
|
||
try {
|
||
await fs.access(dir);
|
||
} catch {
|
||
await fs.mkdir(dir, { recursive: true });
|
||
}
|
||
}
|
||
|
||
async function copyConfig(src, dest) {
|
||
try {
|
||
await ensureDirectory(path.dirname(dest));
|
||
await fs.cp(src, dest, { recursive: true });
|
||
console.log(`成功复制配置文件从 ${src} 到 ${dest}`);
|
||
return true;
|
||
} catch (error) {
|
||
console.error(`复制配置文件失败: ${error.message}`);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 清理更新状态和临时文件
|
||
async function cleanupUpdate(tempDir) {
|
||
try {
|
||
// 清理临时文件
|
||
await fs.rm(tempDir, { recursive: true, force: true });
|
||
// 清理Redis中的更新状态
|
||
await redis.del('rconsole:update:status');
|
||
await redis.del('rconsole:update:paths');
|
||
console.log('清理完成');
|
||
} catch (error) {
|
||
console.error('清理失败:', error);
|
||
}
|
||
}
|
||
|
||
export async function GET(req) {
|
||
try {
|
||
const { searchParams } = new URL(req.url);
|
||
const isCheck = searchParams.get('check') === 'true';
|
||
const isRestore = searchParams.get('restore') === 'true';
|
||
const isForce = searchParams.get('force') === 'true';
|
||
|
||
// 如果是检查请求
|
||
if (isCheck) {
|
||
const updateStatus = await redis.get('rconsole:update:status');
|
||
return new Response(JSON.stringify({
|
||
needsRestore: updateStatus === 'restoring'
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
}
|
||
|
||
// 如果是恢复请求
|
||
if (isRestore) {
|
||
const updateStatus = await redis.get('rconsole:update:status');
|
||
const paths = JSON.parse(await redis.get('rconsole:update:paths') || '{}');
|
||
|
||
if (updateStatus === 'restoring' && paths.tempDir && paths.configDir) {
|
||
try {
|
||
await copyConfig(paths.tempDir, paths.configDir);
|
||
await cleanupUpdate(paths.tempDir);
|
||
return new Response(JSON.stringify({
|
||
success: true,
|
||
message: '配置恢复完成'
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
} catch (error) {
|
||
return new Response(JSON.stringify({
|
||
success: false,
|
||
message: '配置恢复失败:' + error.message
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
}
|
||
}
|
||
|
||
return new Response(JSON.stringify({
|
||
success: true,
|
||
message: '无需恢复'
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
}
|
||
|
||
const projectRoot = path.join(process.cwd(), '..');
|
||
const tempDir = path.join(projectRoot, 'temp', 'update-tmp');
|
||
const configDir = path.join(projectRoot, 'config');
|
||
|
||
// 检查是否有未完成的更新
|
||
const updateStatus = await redis.get('rconsole:update:status');
|
||
if (updateStatus === 'restoring') {
|
||
// 如果有未完成的更新,尝试恢复
|
||
const paths = JSON.parse(await redis.get('rconsole:update:paths') || '{}');
|
||
if (paths.tempDir && paths.configDir) {
|
||
try {
|
||
await copyConfig(paths.tempDir, paths.configDir);
|
||
console.log('恢复了之前未完成的配置文件更新');
|
||
} finally {
|
||
await cleanupUpdate(paths.tempDir);
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('开始新的更新流程');
|
||
|
||
// 保存路径信息到Redis
|
||
await redis.set('rconsole:update:paths', JSON.stringify({
|
||
tempDir,
|
||
configDir
|
||
}));
|
||
await redis.set('rconsole:update:status', 'started');
|
||
|
||
// 确保临时目录存在
|
||
await ensureDirectory(tempDir);
|
||
|
||
// 备份配置文件
|
||
let configBackedUp = false;
|
||
try {
|
||
await fs.access(configDir);
|
||
await copyConfig(configDir, tempDir);
|
||
configBackedUp = true;
|
||
console.log('配置文件备份成功');
|
||
await redis.set('rconsole:update:status', 'backed_up');
|
||
} catch (error) {
|
||
console.log('无配置文件需要备份或备份失败:', error.message);
|
||
}
|
||
|
||
try {
|
||
// 执行git操作
|
||
if (isForce) {
|
||
console.log('执行强制更新...');
|
||
await execAsync(`git -C "${projectRoot}" checkout .`);
|
||
}
|
||
|
||
// 标记状态为需要恢复
|
||
await redis.set('rconsole:update:status', 'restoring');
|
||
|
||
console.log('执行git pull...');
|
||
const { stdout } = await execAsync(`git -C "${projectRoot}" pull --no-rebase`);
|
||
|
||
// 恢复配置文件
|
||
if (configBackedUp) {
|
||
console.log('开始恢复配置文件...');
|
||
await copyConfig(tempDir, configDir);
|
||
}
|
||
|
||
// 清理所有临时文件和状态
|
||
await cleanupUpdate(tempDir);
|
||
|
||
if (stdout.includes('Already up to date') || stdout.includes('已经是最新')) {
|
||
return new Response(JSON.stringify({
|
||
success: true,
|
||
message: '已经是最新版本'
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
}
|
||
|
||
return new Response(JSON.stringify({
|
||
success: true,
|
||
message: '更新成功'
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
|
||
} catch (error) {
|
||
// 如果git操作失败,也尝试恢复配置文件
|
||
if (configBackedUp) {
|
||
try {
|
||
await copyConfig(tempDir, configDir);
|
||
console.log('git操作失败,但配置文件已恢复');
|
||
} catch (restoreError) {
|
||
console.error('恢复配置文件失败:', restoreError.message);
|
||
}
|
||
}
|
||
|
||
await cleanupUpdate(tempDir);
|
||
|
||
const errorMessage = handleGitError(error, error.stderr);
|
||
return new Response(JSON.stringify({
|
||
success: false,
|
||
message: errorMessage
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('更新过程出错:', error);
|
||
|
||
// 确保清理所有临时状态
|
||
const paths = JSON.parse(await redis.get('rconsole:update:paths') || '{}');
|
||
if (paths.tempDir) {
|
||
await cleanupUpdate(paths.tempDir);
|
||
}
|
||
|
||
return new Response(JSON.stringify({
|
||
success: false,
|
||
message: '更新过程出错:' + (error.message || '未知错误')
|
||
}), {
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
}
|
||
} |