fix(backup): 手动备份按服冷却、每服保留上限与独立保留期,容量驱逐不再删除回收世界的唯一副本

This commit is contained in:
Lemon-miaow committed 2026-09-24 19:58:59 +08:00
1 parent f69cdec9d4
commit 9bda3a52fa
30 files changed
+801 -42

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@
"not_yours_title": "No permission to access backups",
"not_yours_body": "Only the owner or an admin can view and restore this server's backups.",
"latest_title": "Latest backup",
"history_note": "Historical backups can be used for restore before they expire. They are automatically cleaned up when they expire.",
"history_note": "Backups can be restored until they expire, then they are cleaned up automatically. Each server keeps only its most recent manual backups; older manual backups are removed once a new one finishes.",
"reason_inactive": "Idle archive",
"reason_manual": "Manual backup",
"reason_label": "Reason: {{reason}}",
@@ -19,6 +19,8 @@
"not_stopped": "Stop the server completely before restoring — a restore overwrites the live world volume.",
"maintenance_in_progress": "This server's world is busy with a restore, backup or file write — try again once it finishes, usually within a minute or two.",
"no_world_volume": "This server has no world volume yet — start it once so it is created, then retry.",
"backup_cooldown": "This server was backed up moments ago, and manual backups have a cooldown — try again in a few minutes.",
"backup_store_full": "The backup store is full, so manual backups are paused — ask an administrator to free space.",
"restore_unavailable": "Restore isn't available right now — try again later.",
"session_expired": "Your session expired — please sign in again.",
"forbidden": "You are not allowed to do that.",
+1 -1
View File
@@ -5,7 +5,7 @@
"not_yours_title": "无权访问备份",
"not_yours_body": "只有所有者或管理员才能查看并恢复该服务器的备份。",
"latest_title": "最新备份",
"history_note": "历史备份在过期前均可用于恢复。到期后系统会自动清理,无需手动删除或管理。",
"history_note": "历史备份在过期前均可用于恢复,到期后自动清理。手动备份每台服务器只保留最近几份,新备份完成后会自动删除更早的手动备份。",
"reason_inactive": "闲置自动回收",
"reason_manual": "手动备份",
"reason_label": "原因:{{reason}}",
@@ -19,6 +19,8 @@
"not_stopped": "回档会覆盖世界的实时存储卷,请先把服务器完全停止再回档。",
"maintenance_in_progress": "这台服务器的世界正在回档、备份或写入文件——等它完成后再试,通常不超过一两分钟。",
"no_world_volume": "这台服务器还没有世界卷——先启动一次让它创建,然后再试。",
"backup_cooldown": "这台服务器刚备份过,手动备份之间有冷却时间——请过几分钟再试。",
"backup_store_full": "备份存储已满,暂时无法手动备份——请联系管理员清理空间。",
"restore_unavailable": "回档功能当前不可用,请稍后再试。",
"session_expired": "会话已过期——请重新登录。",
"forbidden": "你无权执行此操作。",
+6
View File
@@ -390,6 +390,12 @@ describe("api access-control wire shapes", () => {
expect(humanizeError({ code: "not_running" })).toMatch(/running|wake/i);
expect(humanizeError({ code: "console_unavailable" })).toMatch(/console/i);
});
it("maps the backup rationing codes to their own copy", async () => {
const { humanizeError } = await import("./api");
expect(humanizeError({ code: "backup_cooldown" })).toMatch(/cooldown/i);
expect(humanizeError({ code: "backup_store_full" })).toMatch(/store is full/i);
});
});
describe("image whitelist and builds wire shapes", () => {
+6
View File
@@ -739,6 +739,12 @@ export function humanizeError(e: unknown): string {
return t("maintenance_in_progress");
case "no_world_volume":
return t("no_world_volume");
// On-demand backup rationing (data-durability-9): one per server per
// cooldown, none while the shared backup store is at its cap.
case "backup_cooldown":
return t("backup_cooldown");
case "backup_store_full":
return t("backup_store_full");
case "restore_unavailable":
return t("restore_unavailable");
// Server create/edit (spec §22): the portability regex + reservation list are