From d8c6a25292adcdb8c308599cc566170be633f1e8 Mon Sep 17 00:00:00 2001 From: "[._.]/ Adam Eivy" Date: Wed, 9 Sep 2026 15:17:01 +0000 Subject: [PATCH] fix: preserve complete filenames in restore previews (#6708) --- server/services/backup.js | 12 ++++++------ server/services/backup.test.js | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/server/services/backup.js b/server/services/backup.js index e9bb264a24..9cc1b8ecaf 100644 --- a/server/services/backup.js +++ b/server/services/backup.js @@ -14,6 +14,7 @@ import { hostname } from 'os'; import { join, resolve, relative, isAbsolute } from 'path'; import { PATHS, ensureDir, readJSONFile, atomicWrite, sha256File } from '../lib/fileUtils.js'; import { createFileWriteQueue } from '../lib/fileWriteQueue.js'; +import { createLineReader } from '../lib/streamLines.js'; import { getEvent } from './eventScheduler.js'; import { checkHealth, getServerMajorVersion } from '../lib/db.js'; import { resolvePgDumpBinary } from '../lib/pgTools.js'; @@ -212,14 +213,12 @@ function runRsync(srcDir, destDir, flags = []) { const changed = []; let stderr = ''; - proc.stdout.on('data', (chunk) => { - const lines = chunk.toString().split('\n').filter(Boolean); - for (const line of lines) { - if (line.startsWith('>') || line.startsWith('<')) { - changed.push(line); - } + const stdoutReader = createLineReader((line) => { + if (line.startsWith('>') || line.startsWith('<')) { + changed.push(line); } }); + proc.stdout.on('data', stdoutReader.push); proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); @@ -228,6 +227,7 @@ function runRsync(srcDir, destDir, flags = []) { proc.on('close', (code) => { // Exit code 24 = some files vanished mid-transfer (normal for active system) if (code === 0 || code === 24) { + stdoutReader.flush(); resolve(changed); } else { reject(new Error(`rsync exited with code ${code}: ${stderr.trim()}`)); diff --git a/server/services/backup.test.js b/server/services/backup.test.js index 3ba2117293..d44a8b93d9 100644 --- a/server/services/backup.test.js +++ b/server/services/backup.test.js @@ -1024,6 +1024,25 @@ describe('restoreSnapshot subdirFilter guard', () => { expect(spawn).not.toHaveBeenCalled(); }); + it('preserves complete restore preview paths across stdout byte boundaries', async () => { + const proc = fakeProc(); + spawn.mockReturnValue(proc); + const restore = restoreSnapshot('/dest', 'snap-1'); + await flush(); + + proc.stdout.emit('data', Buffer.from('>f+++++++++ example')); + const tail = Buffer.from('-settings.json\ncd+++++++++ ignored/\nf+++++++++ example-settings.json', ' { const previous = process.env.PORTOS_RSYNC; process.env.PORTOS_RSYNC = '/custom/bin/rsync';