mirror of
https://github.com/xmanrui/OpenClaw-bot-review.git
synced 2026-08-14 00:47:49 +00:00
## 備份機制 - 新增 lib/config-backup.ts:以 SHA-256 hash 偵測 openclaw.json 變更,自動備份 - Hash 持久化至 ~/.openclaw/backups/config/.last-hash,Next.js 重啟後仍能正確偵測變更 - 備份保留策略:滾動 8 個 + 昨天錨點 + 上週錨點(各取最後一個正常備份,≥ 1 KB) - 新增 API:GET /api/config-backup(列出備份)、POST /api/config-backup(還原) ## Dashboard 偵測與還原 UI(gateway-status.tsx) - Gateway 第一次失敗即觸發備份清單抓取,不等 30 秒 - 備份清單顯示大小、[建議] / [可能損毀] 標籤,並自動推薦第一個正常備份 - 還原後自動重啟 Gateway,並倒數 5 秒後自動重新整理頁面 - 提示使用者點選機器人卡片「測試」確認是否正常 - 「查看錯誤日誌」按鈕固定顯示於面板,可展開/收起最後 30 行 log - config 損毀導致頁面載入失敗時,錯誤頁仍顯示 GatewayStatus 還原入口 ## 多語系支援 - 所有新增文字支援繁中/简中/English(i18n.tsx 新增 8 個 key) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { readFileSync } from "fs";
|
|
import os from "os";
|
|
import path from "path";
|
|
|
|
const LOG_PATH = path.join(os.homedir(), ".openclaw/logs/gateway.err.log");
|
|
const TAIL_LINES = 120;
|
|
const STALL_RECENT_MS = 10 * 60 * 1000; // consider stall "active" if within last 10 min
|
|
|
|
const PATTERNS = [
|
|
{ re: /Polling stall detected/, issue: "telegram_stall" },
|
|
{ re: /sendChatAction failed: Network request/, issue: "telegram_network" },
|
|
{ re: /gateway timeout after \d+ms/, issue: "subagent_timeout" },
|
|
] as const;
|
|
|
|
export async function GET() {
|
|
try {
|
|
const content = readFileSync(LOG_PATH, "utf8");
|
|
const lines = content.split("\n").filter(Boolean).slice(-TAIL_LINES);
|
|
|
|
const issues = new Set<string>();
|
|
for (const line of lines) {
|
|
for (const { re, issue } of PATTERNS) {
|
|
if (re.test(line)) issues.add(issue);
|
|
}
|
|
}
|
|
|
|
// Find timestamp of most recent stall line
|
|
let lastStallAt: string | null = null;
|
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
if (/Polling stall detected/.test(lines[i])) {
|
|
const m = lines[i].match(/^(\d{4}-\d{2}-\d{2}T[\d:.+]+)/);
|
|
if (m) { lastStallAt = m[1]; break; }
|
|
}
|
|
}
|
|
|
|
// Only treat stall as active if it happened recently
|
|
const stallActive = lastStallAt
|
|
? Date.now() - new Date(lastStallAt).getTime() < STALL_RECENT_MS
|
|
: false;
|
|
|
|
// 回傳最後 30 行作為原始紀錄供 UI 顯示
|
|
const recentLines = lines.slice(-30);
|
|
|
|
return NextResponse.json({
|
|
ok: true,
|
|
issues: [...issues],
|
|
lastStallAt,
|
|
stallActive,
|
|
recentLines,
|
|
});
|
|
} catch {
|
|
return NextResponse.json({ ok: false, issues: [], lastStallAt: null, stallActive: false });
|
|
}
|
|
}
|