Preserve deleted server history

Retain historical server identities, results, and evidence after deletion. Mark archived entries as Deleted and prevent old work from targeting servers that reuse the same name.

Fixes #16.
This commit is contained in:
2026-09-27 21:48:55 -04:00
parent 53b29f726c
commit 981fb27c1c
61 changed files with 929 additions and 223 deletions
+6 -4
View File
@@ -6,6 +6,7 @@
import { pollVisible } from '$ui/poll.js';
import { confirmAction } from '$ui/confirm.js';
import { serverTitle } from '$ui/server-labels.js';
import HistoricalServerIdentity from '$ui/components/HistoricalServerIdentity.svelte';
import StatusBadge from '$ui/components/primitives/StatusBadge.svelte';
import ListFooter from '$ui/components/primitives/ListFooter.svelte';
import Badge from '$ui/components/primitives/Badge.svelte';
@@ -15,6 +16,7 @@
interface ServerRow {
server: string;
server_deleted_at?: string | null;
restart_state: string;
files_changed: number;
players: number | null;
@@ -76,7 +78,7 @@
onMount(() => pollVisible(loadCampaigns, 5000));
function isActive(d: Deployment): boolean {
return d.servers.some((s) => s.restart_state === 'pending' || s.restart_state === 'failed');
return d.servers.some((s) => !s.server_deleted_at && (s.restart_state === 'pending' || s.restart_state === 'failed'));
}
function ago(iso: string): string {
const d = Date.now() - new Date(iso).getTime();
@@ -108,7 +110,7 @@
ev.preventDefault();
ev.stopPropagation();
const summary = scriptSummary(d) || 'this promote';
if (!await confirmAction({ title:'Roll back promotion?', message:`Restore the previous version of ${summary} on ${d.servers.length} servers.`, confirmLabel:'Roll back', danger:true })) return;
if (!await confirmAction({ title:'Roll back promotion?', message:`Restore the previous version of ${summary} on ${d.servers.filter(server => !server.server_deleted_at).length} servers.`, confirmLabel:'Roll back', danger:true })) return;
rollingBack = d.id;
flash = null; flashId = d.id; flashError = false;
try {
@@ -211,7 +213,7 @@
{/if}
</div>
<div class="row-head-r">
{#if d.action === 'promote' && !d.rolled_back_by}
{#if d.action === 'promote' && !d.rolled_back_by && d.servers.some(server => !server.server_deleted_at)}
<Button
variant="danger"
size="sm"
@@ -233,7 +235,7 @@
title="{s.server}: {s.restart_state}{s.error ? ' — ' + s.error : ''}"
>
<span class="dot"></span>
<a class="node-label" href="/servers/{encodeURIComponent(s.server)}?tab=diagnostics">{serverTitle(s.server)}{s.players !== null && s.players > 0 ? ` (${s.players} players at last check)` : ''}</a><StatusBadge state={s.restart_state} />
<span class="node-label"><HistoricalServerIdentity name={s.server} deletedAt={s.server_deleted_at} href={`/servers/${encodeURIComponent(s.server)}?tab=diagnostics`} />{s.players !== null && s.players > 0 ? ` (${s.players} players at last check)` : ''}</span><StatusBadge state={s.restart_state} />
</span>
{/each}
</div>