Prune old upgrade job history
This commit is contained in:
@@ -60,6 +60,7 @@ const STALE_TIMEOUTS_MS: Record<string, number> = {
|
||||
running: Number(process.env.UPGRADE_STALE_RUNNING_MS || 2 * 60 * 60 * 1000),
|
||||
rolling_back: Number(process.env.UPGRADE_STALE_ROLLBACK_MS || 30 * 60 * 1000),
|
||||
};
|
||||
const HISTORY_LIMIT = Number(process.env.UPGRADE_HISTORY_LIMIT || 50);
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const authError = await requireAdmin(request);
|
||||
@@ -75,6 +76,7 @@ export async function GET(request: NextRequest) {
|
||||
latestPreflight,
|
||||
history: states,
|
||||
stateDir: getUpgradeStateRoot(),
|
||||
historyLimit: HISTORY_LIMIT,
|
||||
running: states.some(job => RUNNING_STATUSES.has(job.status)),
|
||||
runtime: getRuntimeStatus(),
|
||||
});
|
||||
@@ -239,23 +241,49 @@ async function readStates(): Promise<UpgradeJobState[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
const states = await Promise.all(
|
||||
const loadedStates = await Promise.all(
|
||||
jobNames.map(async jobName => {
|
||||
try {
|
||||
const statePath = path.join(jobsRoot, jobName, 'state.json');
|
||||
const raw = await fs.readFile(statePath, 'utf8');
|
||||
return await normalizeStaleState(JSON.parse(raw) as UpgradeJobState, statePath);
|
||||
return {
|
||||
jobName,
|
||||
state: await normalizeStaleState(JSON.parse(raw) as UpgradeJobState, statePath),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return states
|
||||
.filter((job): job is UpgradeJobState => Boolean(job))
|
||||
const entries = loadedStates
|
||||
.filter((entry): entry is { jobName: string; state: UpgradeJobState } => Boolean(entry))
|
||||
.sort((a, b) => new Date(b.state.updatedAt).getTime() - new Date(a.state.updatedAt).getTime());
|
||||
await pruneFinishedJobs(jobsRoot, entries);
|
||||
|
||||
return entries
|
||||
.map(entry => entry.state)
|
||||
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
||||
}
|
||||
|
||||
async function pruneFinishedJobs(
|
||||
jobsRoot: string,
|
||||
entries: Array<{ jobName: string; state: UpgradeJobState }>,
|
||||
): Promise<void> {
|
||||
if (!Number.isFinite(HISTORY_LIMIT) || HISTORY_LIMIT < 1) return;
|
||||
const finished = entries.filter(entry => !RUNNING_STATUSES.has(entry.state.status));
|
||||
const staleFinished = finished.slice(HISTORY_LIMIT);
|
||||
if (staleFinished.length === 0) return;
|
||||
|
||||
await Promise.all(staleFinished.map(async entry => {
|
||||
const targetDir = path.join(jobsRoot, entry.jobName);
|
||||
const resolvedRoot = path.resolve(jobsRoot);
|
||||
const resolvedTarget = path.resolve(targetDir);
|
||||
if (!resolvedTarget.startsWith(`${resolvedRoot}${path.sep}`)) return;
|
||||
await fs.rm(resolvedTarget, { recursive: true, force: true });
|
||||
}));
|
||||
}
|
||||
|
||||
async function normalizeStaleState(state: UpgradeJobState, statePath: string): Promise<UpgradeJobState> {
|
||||
if (!RUNNING_STATUSES.has(state.status)) return state;
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ type UpgradeResponse = {
|
||||
history: UpgradeJob[];
|
||||
running: boolean;
|
||||
stateDir: string;
|
||||
historyLimit?: number;
|
||||
runtime?: RuntimeStatus;
|
||||
};
|
||||
|
||||
@@ -141,6 +142,7 @@ export default function SystemUpgradeTab() {
|
||||
history: Array.isArray(data.history) ? data.history : [],
|
||||
running: data.running === true,
|
||||
stateDir: data.stateDir || '',
|
||||
historyLimit: typeof data.historyLimit === 'number' ? data.historyLimit : undefined,
|
||||
runtime: data.runtime,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -397,6 +399,11 @@ export default function SystemUpgradeTab() {
|
||||
升级历史
|
||||
</CardTitle>
|
||||
<CardDescription>保留全部升级任务,便于随时查看升级内容、执行日志、失败原因与回滚记录。</CardDescription>
|
||||
{upgradeData.historyLimit && (
|
||||
<CardDescription>
|
||||
已结束任务默认保留最近 {upgradeData.historyLimit} 个,运行中的任务不会被自动清理。
|
||||
</CardDescription>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{upgradeData.history.length === 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user