backup engine internals - streaming, timeouts, typed snapshot layout, volume keys, route guards

This commit is contained in:
jarek committed 2026-08-09 10:31:28 +02:00
1 parent c33c404259
commit 95ee76bf97
32 files changed
+1205 -352

No files matched your search

+22 -4
View File
@@ -3,6 +3,24 @@
* snapshot browser. No docker, no DB, so it is unit-testable directly.
*/
/** One restic `ls --json` line. restic emits a `snapshot` header then one entry per node.
* The node discriminator is `message_type: 'node'` in current restic; older restic used
* `struct_type: 'node'` (now deprecated). We accept EITHER so a restic bump that drops the
* old field can't silently empty the browser. */
export interface ResticLsNode {
message_type?: string;
struct_type?: string;
path?: string;
type?: string;
size?: number;
[k: string]: unknown;
}
/** True if a parsed restic ls line is a filesystem node (not the `snapshot` header). */
function isResticNode(e: ResticLsNode): boolean {
return e.message_type === 'node' || e.struct_type === 'node';
}
/** Normalize a path for self-comparison: collapse repeated slashes at the ends,
* always leading-slash, no trailing slash (root stays "/"). */
function normalizePath(p: string): string {
@@ -20,15 +38,15 @@ function normalizePath(p: string): string {
* directory (e.g. an empty `volumes/` inside `/volumes/`, `bt-vol/` inside
* `/volumes/bt-vol/`). Keep only nodes whose path is NOT the queried path.
*/
export function parseSnapshotLsEntries(stdout: string, queriedPath: string): any[] {
export function parseSnapshotLsEntries(stdout: string, queriedPath: string): ResticLsNode[] {
const queried = normalizePath(queriedPath);
const entries: any[] = [];
const entries: ResticLsNode[] = [];
for (const line of stdout.split('\n')) {
const t = line.trim();
if (!t) continue;
let e: any;
let e: ResticLsNode;
try { e = JSON.parse(t); } catch { continue; }
if (e?.struct_type !== 'node') continue;
if (!isResticNode(e)) continue;
if (normalizePath(e.path ?? '') === queried) continue; // self-node, not a child
entries.push(e);
}