fix(files): 配置文件改为同目录临时文件 fsync 后原子改名写入,读取返回内容哈希、保存带期望哈希冲突返回 409,磁盘满返回 507,面板提示载入最新或仍然覆盖
This commit is contained in:
17 files changed
+721
-131
No files matched your search
+19
-10
@@ -404,23 +404,25 @@ export const api = {
|
||||
`/servers/${name}/files?path=${encodeURIComponent(path)}`,
|
||||
),
|
||||
|
||||
// readServerFile returns one file's bytes (base64). A file over the read
|
||||
// ceiling is a 413, never a silent truncation, because a later save of a
|
||||
// truncated body would destroy the rest of the file.
|
||||
// readServerFile returns one file's bytes (base64) and the sha256 of the file
|
||||
// as stored. A file over the read ceiling is a 413, never a silent truncation,
|
||||
// because a later save of a truncated body would destroy the rest of the file.
|
||||
readServerFile: (name: string, path: string) =>
|
||||
request<{ path: string; content: string }>(
|
||||
request<{ path: string; content: string; sha256: string }>(
|
||||
"GET",
|
||||
`/servers/${name}/file?path=${encodeURIComponent(path)}`,
|
||||
),
|
||||
|
||||
// writeServerFile replaces a file's contents (creating it if absent). Sending
|
||||
// an explicit "" is a deliberate truncate; the wire field is required, but that
|
||||
// is enforced by the caller (this method always sends one).
|
||||
writeServerFile: (name: string, path: string, content: string) =>
|
||||
request<{ path: string; status: string }>(
|
||||
// writeServerFile atomically replaces a file's contents (creating it if
|
||||
// absent). Sending an explicit "" is a deliberate truncate; the wire field is
|
||||
// required, but that is enforced by the caller (this method always sends one).
|
||||
// With expectSha256 (the hash the read returned) a file someone changed since
|
||||
// is refused with 409 file_changed; without it the write is unconditional.
|
||||
writeServerFile: (name: string, path: string, content: string, expectSha256?: string) =>
|
||||
request<{ path: string; status: string; sha256: string }>(
|
||||
"PUT",
|
||||
`/servers/${name}/file?path=${encodeURIComponent(path)}`,
|
||||
{ content },
|
||||
expectSha256 ? { content, expect_sha256: expectSha256 } : { content },
|
||||
),
|
||||
|
||||
// Account linking (spec §10). Both are POST: start reports status from the
|
||||
@@ -756,6 +758,13 @@ export function humanizeError(e: unknown): string {
|
||||
return t("maintenance_in_progress");
|
||||
case "no_world_volume":
|
||||
return t("no_world_volume");
|
||||
// File editor: the file changed after it was opened (another manager saved
|
||||
// it, or the server rewrote it), so the save was refused rather than
|
||||
// overwriting that edit.
|
||||
case "file_changed":
|
||||
return t("file_changed");
|
||||
case "volume_full":
|
||||
return t("volume_full");
|
||||
// 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":
|
||||
|
||||
Reference in new issue
Block a user