Files
ProxMenux/AppImage/lib/host-backup-messages.ts
T

142 lines
11 KiB
TypeScript

// Legacy HostBackup API messages are an English wire contract shared with CLI callers.
// Match complete, source-verified messages only; never derive a translation key from input.
// This presentation-only adapter keeps unknown tool diagnostics and parameter text intact.
type Translate = (key: string, params?: Record<string, string | number>) => string
const fixed = new Map<string, string>([
["path must be absolute", "backup.apiErrors.absolutePath"],
["this path is already configured", "backup.apiErrors.pathConfigured"],
["path is required", "backup.apiErrors.pathRequired"],
["path query parameter is required", "backup.apiErrors.pathQuery"],
["path not found", "backup.apiErrors.pathNotFound"],
["invalid job id", "backup.apiErrors.invalidJobId"],
["job not found", "backup.apiErrors.jobNotFound"],
["runner script not installed", "backup.apiErrors.runnerMissing"],
["no restore state to dismiss", "backup.apiErrors.noRestore"],
["invalid file name", "backup.apiErrors.invalidFile"],
["not found", "backup.apiErrors.notFound"],
["log path outside allowed directory", "backup.apiErrors.logOutside"],
["targets must be a list", "backup.apiErrors.targetsList"],
["empty expression", "backup.apiErrors.emptyExpression"],
["invalid id (use letters, digits, _ or -)", "backup.apiErrors.invalidId"],
["backend must be pbs, local or borg", "backup.apiErrors.backend"],
["backend must be pbs or borg", "backup.apiErrors.remoteBackend"],
["backend must be 'pbs' or 'borg'", "backup.apiErrors.remoteBackendQuoted"],
["profile_mode must be default or custom", "backup.apiErrors.profileMode"],
["borg cannot be attached (PVE vzdump only writes to pbs/local storages)", "backup.apiErrors.borgAttached"],
["borg is not a valid backend for attached jobs (PVE vzdump only writes to pbs/local storages)", "backup.apiErrors.borgAttachedLong"],
["pve_parent_job_id and pve_storage are required for attached", "backup.apiErrors.parentRequired"],
["paths must be a list of absolute paths", "backup.apiErrors.absolutePaths"],
["custom profile needs at least one path", "backup.apiErrors.customPath"],
["could not resolve default profile paths", "backup.apiErrors.defaultPaths"],
["retention must be an object", "backup.apiErrors.retentionObject"],
["pbs_repository is required", "backup.apiErrors.pbsRepository"],
["pbs_encrypt_mode must be 'none', 'new' or 'existing'", "backup.apiErrors.pbsEncryption"],
["failed to create PBS encryption keyfile", "backup.apiErrors.pbsKeyFailed"],
["failed to create PBS encryption keyfile (is proxmox-backup-client installed?)", "backup.apiErrors.pbsClientMissing"],
["borg_repo is required", "backup.apiErrors.borgRepository"],
["borg_encrypt_mode must be one of: none, repokey, keyfile, authenticated", "backup.apiErrors.borgEncryption"],
["encrypt_mode must be one of: none, repokey, keyfile, authenticated", "backup.apiErrors.encryption"],
["borg_passphrase is required when encryption is enabled (save it on the destination or pass one in the request)", "backup.apiErrors.borgPassphrase"],
["passphrase is required when encryption is enabled", "backup.apiErrors.passphrase"],
["password is required", "backup.apiErrors.password"],
["confirm_device must match device exactly (typo / safety check)", "backup.apiErrors.deviceConfirmation"],
["invalid device path", "backup.apiErrors.invalidDevice"],
["absolute mountpoint required", "backup.apiErrors.absoluteMountpoint"],
["archive layout not recognized — no rootfs/ found", "backup.apiErrors.archiveLayout"],
["archive not found", "backup.apiErrors.archiveMissing"],
["archive path is not a regular file", "backup.apiErrors.archiveNotFile"],
["invalid name", "backup.apiErrors.invalidName"],
["invalid staging_path", "backup.apiErrors.invalidStaging"],
["job name cannot be changed (delete + recreate to rename)", "backup.apiErrors.jobRename"],
["key_path must be absolute", "backup.apiErrors.absoluteKey"],
["mode must be \"local\" or \"ssh\"", "backup.apiErrors.mode"],
["name is required (letters, digits, _ or -)", "backup.apiErrors.nameRequired"],
["no log available for this job yet", "backup.apiErrors.noLog"],
["no manual PBS configs file", "backup.apiErrors.noPbsConfig"],
["output file missing", "backup.apiErrors.outputMissing"],
["parser output was not valid JSON", "backup.apiErrors.parserJson"],
["run_restore output was not valid JSON", "backup.apiErrors.restoreJson"],
["repo is required", "backup.apiErrors.repoRequired"],
["repo_name and snapshot are required", "backup.apiErrors.repoSnapshot"],
["repo_name and snapshot are required for borg", "backup.apiErrors.borgRepoSnapshot"],
["repo_name and snapshot are required for pbs", "backup.apiErrors.pbsRepoSnapshot"],
["repo_name is required", "backup.apiErrors.repoName"],
["restore tooling not installed on this host", "backup.apiErrors.restoreMissing"],
["server and datastore are required", "backup.apiErrors.serverDatastore"],
["snapshot is required", "backup.apiErrors.snapshotRequired"],
["source must be pbs, borg, or local", "backup.apiErrors.source"],
["ssh_port must be an integer between 1 and 65535", "backup.apiErrors.sshPort"],
["ssh_user, ssh_host and ssh_remote_path are required for ssh mode", "backup.apiErrors.sshRequired"],
["target has no repository path", "backup.apiErrors.repositoryPath"],
["task not found or expired", "backup.apiErrors.taskExpired"],
["invalid expression", "backup.apiErrors.invalidExpression"],
["no report emitted", "backup.apiErrors.noReport"],
["parse_manifest exited non-zero", "backup.apiErrors.parseManifestFailed"],
])
const parameterized: ReadonlyArray<readonly [RegExp, string]> = [
[new RegExp("^job already exists: ([\\s\\S]+)$"), "backup.apiErrors.jobExists"],
[new RegExp("^path does not exist on this host: ([\\s\\S]+)$"), "backup.apiErrors.pathMissing"],
[new RegExp("^device does not exist: ([\\s\\S]+)$"), "backup.apiErrors.deviceMissing"],
[new RegExp("^not a mountpoint: ([\\s\\S]+)$"), "backup.apiErrors.mountpoint"],
[new RegExp("^local archive not found: ([\\s\\S]+)$"), "backup.apiErrors.localArchiveMissing"],
[new RegExp("^Borg destination '([\\s\\S]+)' not found$"), "backup.apiErrors.borgDestination"],
[new RegExp("^PBS destination '([\\s\\S]+)' not found$"), "backup.apiErrors.pbsDestination"],
[new RegExp("^no password available for PBS repo '([\\s\\S]+)'$"), "backup.apiErrors.pbsPassword"],
[new RegExp("^Borg repo \"([\\s\\S]+)\" not configured$"), "backup.apiErrors.borgRepoMissing"],
[new RegExp("^PBS repo \"([\\s\\S]+)\" not configured$"), "backup.apiErrors.pbsRepoMissing"],
[new RegExp("^Borg target not found: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetMissing"],
[new RegExp("^PBS repo not found: ([\\s\\S]+)$"), "backup.apiErrors.pbsTargetMissing"],
[new RegExp("^PVE vzdump job not found: ([\\s\\S]+)$"), "backup.apiErrors.pveJobMissing"],
[new RegExp("^cannot persist state: ([\\s\\S]+)$"), "backup.apiErrors.statePersist"],
[new RegExp("^cannot prepare ssh dir: ([\\s\\S]+)$"), "backup.apiErrors.sshDirectory"],
[new RegExp("^cannot read log: ([\\s\\S]+)$"), "backup.apiErrors.logRead"],
[new RegExp("^could not update \\.env: ([\\s\\S]+)$"), "backup.apiErrors.envUpdate"],
[new RegExp("^failed to persist PBS config: ([\\s\\S]+)$"), "backup.apiErrors.pbsConfigWrite"],
[new RegExp("^failed to persist: ([\\s\\S]+)$"), "backup.apiErrors.persist"],
[new RegExp("^failed to remove archive: ([\\s\\S]+)$"), "backup.apiErrors.archiveRemove"],
[new RegExp("^failed to start: ([\\s\\S]+)$"), "backup.apiErrors.start"],
[new RegExp("^failed to update borg-targets\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetsUpdate"],
[new RegExp("^failed to update pbs-manual-configs\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.pbsTargetsUpdate"],
[new RegExp("^failed to update systemd units: ([\\s\\S]+)$"), "backup.apiErrors.unitsUpdate"],
[new RegExp("^failed to write borg-targets\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetsWrite"],
[new RegExp("^failed to write job files: ([\\s\\S]+)$"), "backup.apiErrors.jobWrite"],
[new RegExp("^failed to write local-target\\.conf: ([\\s\\S]+)$"), "backup.apiErrors.localTargetWrite"],
[new RegExp("^failed to write systemd units: ([\\s\\S]+)$"), "backup.apiErrors.unitsWrite"],
[new RegExp("^format failed: ([\\s\\S]+)$"), "backup.apiErrors.format"],
[new RegExp("^invalid on_calendar: ([\\s\\S]+)$"), "backup.apiErrors.calendar"],
[new RegExp("^mount failed: ([\\s\\S]+)$"), "backup.apiErrors.mount"],
[new RegExp("^parser invocation failed: ([\\s\\S]+)$"), "backup.apiErrors.parserInvocation"],
[new RegExp("^preflight invocation failed: ([\\s\\S]+)$"), "backup.apiErrors.preflightInvocation"],
[new RegExp("^public key not found after generation: ([\\s\\S]+)$"), "backup.apiErrors.publicKeyMissing"],
[new RegExp("^retention\\.([\\s\\S]+) must be an integer$"), "backup.apiErrors.retentionInteger"],
[new RegExp("^retention\\.([\\s\\S]+) must be non-negative$"), "backup.apiErrors.retentionNegative"],
[new RegExp("^ssh-keygen failed: ([\\s\\S]+)$"), "backup.apiErrors.keygen"],
[new RegExp("^systemctl failed: ([\\s\\S]+)$"), "backup.apiErrors.systemctl"],
[new RegExp("^umount failed: ([\\s\\S]+)$"), "backup.apiErrors.unmount"],
[new RegExp("^task is in state \"([\\s\\S]+)\", not completed$"), "backup.apiErrors.taskIncomplete"],
[new RegExp("^unknown kind: ([\\s\\S]+)$"), "backup.apiErrors.unknownKind"],
[new RegExp("^unknown mode \"([\\s\\S]+)\"$"), "backup.apiErrors.unknownMode"],
[new RegExp("^([\\s\\S]+) is the built-in default and cannot be removed$"), "backup.apiErrors.defaultRemove"],
[new RegExp("^([\\s\\S]+) is the built-in default; no need to add it$"), "backup.apiErrors.defaultAdd"],
[new RegExp("^pbs_encrypt_mode='existing' but no keyfile is installed at ([\\s\\S]+) — import one first with POST /api/host-backups/pbs-encryption/import$"), "backup.apiErrors.keyImport"],
[new RegExp("^PVE job storage mismatch \\(job uses ([\\s\\S]+), payload says ([\\s\\S]+)\\)$"), "backup.apiErrors.storageMismatch"],
]
export function hostBackupErrorText(problem: unknown, t: Translate): string {
const message = typeof problem === "string" ? problem
: problem && typeof problem === "object" && "message" in problem && typeof problem.message === "string"
? problem.message : ""
if (!message) return t("backup.apiErrors.requestFailed")
const key = fixed.get(message)
if (key) return t(key)
// Keep unusually large tool output verbatim, without running pattern matching over it.
if (message.length > 16384) return message
for (const [pattern, patternKey] of parameterized) {
const match = pattern.exec(message)
if (match) return t(patternKey, { value: match[1], other: match[2] ?? "" })
}
return message
}