From 01dd302b97f5404096c84d6fe73eef94d0b4599c Mon Sep 17 00:00:00 2001 From: Alexander Sabino <32822107+asabino2@users.noreply.github.com> Date: Thu, 7 May 2026 12:58:05 +0100 Subject: [PATCH] =?UTF-8?q?ajusta=20l=C3=B3gica=20de=20backup=20incrementa?= =?UTF-8?q?l=20e=20melhora=20documenta=C3=A7=C3=A3o=20do=20m=C3=A9todo=20g?= =?UTF-8?q?etSelfBindSource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backupService.js | 30 ++++++++++++------------------ src/dockerService.js | 9 ++++++--- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/backupService.js b/src/backupService.js index 5b525f9..ab6119f 100644 --- a/src/backupService.js +++ b/src/backupService.js @@ -445,20 +445,11 @@ class BackupService { } else { // Fallback para containers sem GNU tar: usa helper container com GNU tar // montando os volumes diretamente — produz .snar como qualquer outro container. - if (runMode === 'incremental') { - try { - await fs.access(absoluteSnapshotPath); - tarIncrementalFlag = `--listed-incremental=${shellQuote('/backuproot/' + snapshotRelativePath)}`; - pushLog('Backup incremental via helper: snapshot anterior encontrado.', 'preparando'); - } catch { - pushLog('Aviso: snapshot anterior nao encontrado, gerando backup completo via helper.', 'preparando'); - tarIncrementalFlag = `--listed-incremental=${shellQuote('/backuproot/' + snapshotRelativePath)}`; - } - } else { - // Full: remove .snar anterior para forçar snapshot limpo. + if (runMode === 'full') { + // Full: remove .snar anterior para forçar snapshot limpo no helper. await fs.rm(absoluteSnapshotPath, { force: true }).catch(() => null); - tarIncrementalFlag = `--listed-incremental=${shellQuote('/backuproot/' + snapshotRelativePath)}`; } + // O .snar é gerenciado pelo helper usando helperSnarPath calculado abaixo. } // Containers BusyBox sem GNU tar: roda o tar num helper container que TEM GNU tar, @@ -469,28 +460,31 @@ class BackupService { // Quando rodando dentro do Docker, backupRoot é um path interno do container da app. // Precisamos do source real (host path ou volume name) para passar ao helper container. - const backupRootSource = await this.dockerService.getSelfBindSource(backupRoot); - if (!backupRootSource) { + const selfBind = await this.dockerService.getSelfBindSource(backupRoot); + if (!selfBind) { throw new Error( `Nao foi possivel determinar o source do diretorio de backup (${backupRoot}) para o helper. ` + 'Verifique se o diretorio de backup esta montado via volume ou bind no container da app.' ); } - const helperBinds = [`${backupRootSource}:/backuproot`]; + // suffix é o subpath dentro do volume (ex: /backups quando mount=/app/data e backupRoot=/app/data/backups) + const helperBackupRoot = `/backuproot_base${selfBind.suffix}`; + const helperBinds = [`${selfBind.source}:/backuproot_base`]; const helperRelPaths = []; for (const [index, mount] of activeMounts.entries()) { const src = mount.type === 'volume' ? mount.name : mount.source; helperBinds.push(`${src}:/payload/m${index}:ro`); helperRelPaths.push(`payload/m${index}`); } - const helperArchivePath = `/backuproot/${archiveRelativePath}`; - const helperSnarDir = path.posix.dirname(`/backuproot/${snapshotRelativePath}`); + const helperArchivePath = `${helperBackupRoot}/${archiveRelativePath}`; + const helperSnarPath = `${helperBackupRoot}/${snapshotRelativePath}`; + const helperSnarDir = path.posix.dirname(helperSnarPath); const helperCmd = [ 'set -u', `mkdir -p ${shellQuote(path.posix.dirname(helperArchivePath))} ${shellQuote(helperSnarDir)}`, `echo "__DBKP_TAR_BEGIN__" 1>&2`, - `tar --ignore-failed-read ${tarIncrementalFlag} -czvf ${shellQuote(helperArchivePath)} -C / ${helperRelPaths.map((p) => shellQuote(p)).join(' ')}; TAR_RC=$?; [ $TAR_RC -le 1 ] || exit $TAR_RC`, + `tar --ignore-failed-read --listed-incremental=${shellQuote(helperSnarPath)} -czvf ${shellQuote(helperArchivePath)} -C / ${helperRelPaths.map((p) => shellQuote(p)).join(' ')}; TAR_RC=$?; [ $TAR_RC -le 1 ] || exit $TAR_RC`, ].join('; '); await this.dockerService.runHelper({ diff --git a/src/dockerService.js b/src/dockerService.js index fc8c823..a558cc8 100644 --- a/src/dockerService.js +++ b/src/dockerService.js @@ -88,8 +88,9 @@ class DockerService { } // Dado um caminho absoluto dentro do container da app (ex: /app/data/backups), - // retorna o source do bind/volume que o cobre (para usar em helper binds). - // Se não encontrar mount correspondente, retorna null. + // retorna { source, suffix } onde source é o bind/volume que cobre o caminho + // e suffix é o subpath dentro desse mount (ex: source='data_vol', suffix='/backups'). + // Retorna null se não encontrar mount correspondente. async getSelfBindSource(containerPath) { const mounts = await this._getSelfMounts(); const normalized = containerPath.replace(/\/+$/, ''); @@ -100,7 +101,9 @@ class DockerService { for (const mount of sorted) { const dest = (mount.Destination || '').replace(/\/+$/, ''); if (normalized === dest || normalized.startsWith(dest + '/')) { - return mount.Type === 'volume' ? mount.Name : mount.Source; + const source = mount.Type === 'volume' ? mount.Name : mount.Source; + const suffix = normalized.slice(dest.length) || ''; + return { source, suffix }; } } return null;