diff --git a/src/backupService.js b/src/backupService.js index a8d151b..5b525f9 100644 --- a/src/backupService.js +++ b/src/backupService.js @@ -467,7 +467,17 @@ class BackupService { pushLog('Container sem GNU tar: usando helper com GNU tar para gerar archive.', 'gerando-tar'); updateFileProgress(); - const helperBinds = [`${backupRoot}:/backuproot`]; + // 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) { + 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`]; const helperRelPaths = []; for (const [index, mount] of activeMounts.entries()) { const src = mount.type === 'volume' ? mount.name : mount.source; diff --git a/src/dockerService.js b/src/dockerService.js index c7542fe..fc8c823 100644 --- a/src/dockerService.js +++ b/src/dockerService.js @@ -63,12 +63,49 @@ class DockerService { this.docker = new Docker({ socketPath }); this.helperImage = helperImage; this.runningInContainer = detectRunningInContainer(); + this._selfMounts = null; // cache dos mounts do próprio container } isRunningInDocker() { return this.runningInContainer; } + // Retorna os mounts do container da própria aplicação. + // Lê /etc/hostname para obter o short ID, busca o container na lista e inspeciona. + async _getSelfMounts() { + if (this._selfMounts !== null) return this._selfMounts; + try { + const hostname = fs.readFileSync('/etc/hostname', 'utf8').trim(); + const all = await this.docker.listContainers({ all: true }); + const self = all.find((c) => c.Id.startsWith(hostname)); + if (!self) { this._selfMounts = []; return []; } + const info = await this.docker.getContainer(self.Id).inspect(); + this._selfMounts = info.Mounts || []; + } catch { + this._selfMounts = []; + } + return this._selfMounts; + } + + // 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. + async getSelfBindSource(containerPath) { + const mounts = await this._getSelfMounts(); + const normalized = containerPath.replace(/\/+$/, ''); + // Ordena do mais específico para o mais genérico + const sorted = [...mounts].sort( + (a, b) => (b.Destination || '').length - (a.Destination || '').length + ); + for (const mount of sorted) { + const dest = (mount.Destination || '').replace(/\/+$/, ''); + if (normalized === dest || normalized.startsWith(dest + '/')) { + return mount.Type === 'volume' ? mount.Name : mount.Source; + } + } + return null; + } + async listContainers() { const containers = await this.docker.listContainers({ all: true }); return containers