遷移修正:先移除舊控制台再搬目錄(Windows EBUSY),rename 失敗改複製,失敗時 bot 用原路徑復原
This commit is contained in:
+65
-17
@@ -3,7 +3,7 @@
|
||||
// 1) ~/.tgcodex → ~/.tgbot(設定、registry、會話全部搬過去,registry 裡的路徑一併改寫)
|
||||
// 2) 跑在舊套件路徑(telegram-codex-bot/src/index.js)上的 bot 程序重新掛到新套件
|
||||
// 3) 舊控制台程序 tgcodex-console 移除(ensureConsoleOnPm2 會掛新的 tgbot-console)
|
||||
// 每一步都是冪等的:沒有舊東西就什麼都不做。
|
||||
// 每一步都是冪等的:沒有舊東西就什麼都不做。搬家途中任何失敗都會把 bot 用原路徑拉回來,不留下停機狀態。
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pm2 = require('./pm2util');
|
||||
@@ -16,6 +16,47 @@ function rewritePrefix(p, from, to) {
|
||||
return p;
|
||||
}
|
||||
|
||||
function underDir(p, dir) {
|
||||
return path.resolve(p).toLowerCase().startsWith(path.resolve(dir).toLowerCase());
|
||||
}
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
// Windows 下剛被 pm2 刪掉的程序可能還握著檔案幾百毫秒;rename 失敗就等一下重試,
|
||||
// 真的搬不動就整份複製過去(舊目錄留著,之後可手動刪)
|
||||
async function moveDir(from, to, notes) {
|
||||
let lastErr = null;
|
||||
for (let i = 0; i < 6; i++) {
|
||||
try {
|
||||
fs.renameSync(from, to);
|
||||
return;
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
if (!/EBUSY|EPERM|EACCES/.test(err.code || '')) throw err;
|
||||
await sleep(500 * (i + 1));
|
||||
}
|
||||
}
|
||||
fs.cpSync(from, to, { recursive: true });
|
||||
try {
|
||||
fs.rmSync(from, { recursive: true, force: true });
|
||||
} catch {
|
||||
notes.push(`⚠️ 舊目錄 ${from} 被其他程序鎖住(${lastErr?.code}),已複製到新位置,舊目錄請之後手動刪除`);
|
||||
}
|
||||
}
|
||||
|
||||
async function restartBots(bots, notes, label) {
|
||||
const { loadConfig } = require('./config');
|
||||
for (const b of bots) {
|
||||
if (!b.online) { notes.push(`⏸ ${b.name} 原本是停止狀態,已從 pm2 移除,可在控制台重新啟動`); continue; }
|
||||
try {
|
||||
await pm2.startBotOnPm2(loadConfig(b.cfgPath));
|
||||
notes.push(`🔄 ${b.name} ${label}`);
|
||||
} catch (err) {
|
||||
notes.push(`⚠️ ${b.name} 重新啟動失敗:${err.message}(請在控制台手動啟動)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateLegacy() {
|
||||
const notes = [];
|
||||
let list = await pm2.jlist().catch(() => []);
|
||||
@@ -23,16 +64,33 @@ async function migrateLegacy() {
|
||||
|
||||
// 1) 家目錄搬家
|
||||
if (fs.existsSync(pm2.LEGACY_HOME) && !fs.existsSync(pm2.NEW_HOME)) {
|
||||
// 先把跑在舊家目錄上的 bot 從 pm2 拿掉(記住哪些在線上,搬完再啟)
|
||||
// 舊控制台的 cwd 與 log 都在舊目錄裡,會把目錄鎖住(Windows EBUSY),先拿掉
|
||||
if (list.some((p) => p.name === pm2.LEGACY_CONSOLE_NAME)) {
|
||||
await pm2.pm2Exec(`delete ${JSON.stringify(pm2.LEGACY_CONSOLE_NAME)}`).catch(() => {});
|
||||
notes.push(`🗑 已移除舊控制台程序 ${pm2.LEGACY_CONSOLE_NAME}`);
|
||||
}
|
||||
// 跑在舊家目錄上的 bot 也先從 pm2 拿掉(記住哪些在線上,搬完再啟)
|
||||
const affected = [];
|
||||
for (const p of list) {
|
||||
const cfgPath = pm2.botConfigPathOf(p);
|
||||
if (!cfgPath) continue;
|
||||
if (!path.resolve(cfgPath).toLowerCase().startsWith(path.resolve(pm2.LEGACY_HOME).toLowerCase())) continue;
|
||||
affected.push({ name: p.name, online: isOnline(p), cfgPath: rewritePrefix(cfgPath, pm2.LEGACY_HOME, pm2.NEW_HOME) });
|
||||
if (!cfgPath || !underDir(cfgPath, pm2.LEGACY_HOME)) continue;
|
||||
affected.push({
|
||||
name: p.name,
|
||||
online: isOnline(p),
|
||||
oldCfgPath: cfgPath,
|
||||
cfgPath: rewritePrefix(cfgPath, pm2.LEGACY_HOME, pm2.NEW_HOME),
|
||||
});
|
||||
await pm2.pm2Exec(`delete ${JSON.stringify(p.name)}`).catch(() => {});
|
||||
}
|
||||
fs.renameSync(pm2.LEGACY_HOME, pm2.NEW_HOME);
|
||||
|
||||
try {
|
||||
await moveDir(pm2.LEGACY_HOME, pm2.NEW_HOME, notes);
|
||||
} catch (err) {
|
||||
// 搬不動:bot 照原路徑拉回來,控制台由後續 ensureConsoleOnPm2 掛(會沿用舊目錄)
|
||||
notes.push(`❌ 設定目錄搬家失敗(${err.message}),維持使用 ${pm2.LEGACY_HOME}`);
|
||||
await restartBots(affected.map((b) => ({ ...b, cfgPath: b.oldCfgPath })), notes, '已用原路徑重新啟動');
|
||||
return notes;
|
||||
}
|
||||
notes.push(`📦 設定目錄已搬到 ${pm2.NEW_HOME}(原 ${pm2.LEGACY_HOME})`);
|
||||
|
||||
// registry 裡的設定檔路徑改寫
|
||||
@@ -45,17 +103,7 @@ async function migrateLegacy() {
|
||||
fs.writeFileSync(regFile, JSON.stringify(reg, null, 2) + '\n');
|
||||
} catch { /* 沒有 registry 就算了 */ }
|
||||
|
||||
// 之前在線上的 bot 用新路徑重新掛回去
|
||||
const { loadConfig } = require('./config');
|
||||
for (const b of affected) {
|
||||
if (!b.online) { notes.push(`⏸ ${b.name} 原本是停止狀態,已從 pm2 移除,可在控制台重新啟動`); continue; }
|
||||
try {
|
||||
await pm2.startBotOnPm2(loadConfig(b.cfgPath));
|
||||
notes.push(`🔄 ${b.name} 已用新路徑重新啟動`);
|
||||
} catch (err) {
|
||||
notes.push(`⚠️ ${b.name} 重新啟動失敗:${err.message}(請在控制台手動啟動)`);
|
||||
}
|
||||
}
|
||||
await restartBots(affected, notes, '已用新路徑重新啟動');
|
||||
list = await pm2.jlist().catch(() => []);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user