Prune old upgrade job history

This commit is contained in:
FengLee
2026-05-10 12:35:08 +08:00
parent a68c00ff93
commit ffde03bbbc
2 changed files with 39 additions and 4 deletions

View File

@@ -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;

View File

@@ -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 ? (