diff --git a/server.js b/server.js index 248459b..88184ea 100644 --- a/server.js +++ b/server.js @@ -186,7 +186,8 @@ app.put('/api/settings', requireAdmin, async (req, res) => { }); // --- Backup / Restore --- -const gunzipFile = require('util').promisify(require('zlib').gunzipFile); +const gunzipAsync = require('util').promisify(require('zlib').gunzip); +const zlib = require('zlib'); const tar = require('tar'); const os = require('os'); const uploadBackup = multer({ @@ -347,14 +348,38 @@ function cleanupUpload(req) { } catch {} } +function peekGunzip(filePath, n) { + return new Promise((resolve) => { + const gunz = zlib.createGunzip(); + const bufs = []; + let total = 0; + let done = false; + gunz.on('data', (c) => { + if (done) return; + const need = n - total; + bufs.push(c.length > need ? c.subarray(0, need) : c); + total += Math.min(c.length, need); + if (total >= n) { + done = true; + gunz.destroy(); + } + }); + gunz.on('error', () => resolve(null)); + gunz.on('close', () => resolve(Buffer.concat(bufs))); + fs.createReadStream(filePath).pipe(gunz); + }); +} + app.post('/api/restore', requireAdmin, uploadBackup.single('backup'), async (req, res) => { if (!req.file) return res.status(400).json({ error: 'backup file required' }); let data; let legacyPhotos = []; const staging = fs.mkdtempSync(path.join(os.tmpdir(), 'wido-rst-')); try { - const gunz = await gunzipFile(req.file.path).catch(() => null); - if (gunz && gunz[0] === 0x7b) { + const head = await peekGunzip(req.file.path, 8); + if (head && head[0] === 0x7b) { + const buf = await fs.promises.readFile(req.file.path); + const gunz = await gunzipAsync(buf); data = JSON.parse(gunz.toString('utf8')); legacyPhotos = data.photos || []; } else {