mirror of
https://github.com/asabino2/dockerbackup.git
synced 2026-09-24 06:36:59 +00:00
Adiciona seleção de volumes para backup e implementa modal de escolha
This commit is contained in:
+27
-3
@@ -306,11 +306,35 @@ class BackupService {
|
||||
const archiveRelativePath = path.posix.join(safeProfileName, safeContainerName, `${stamp}-${runMode}.tar.gz`);
|
||||
const snapshotRelativePath = path.posix.join(safeProfileName, safeContainerName, 'latest.snar');
|
||||
|
||||
// Filter mounts to only those the user selected (if volumeSelections is defined for this container)
|
||||
const selectedPaths = profile.volumeSelections?.[containerId] || profile.volumeSelections?.[inspect.Id];
|
||||
const activeMounts = (backupScope === 'volumes' && selectedPaths?.length)
|
||||
? mounts.filter((m) => selectedPaths.includes(m.destination))
|
||||
: mounts;
|
||||
|
||||
if (backupScope === 'volumes' && !activeMounts.length) {
|
||||
onProgress({
|
||||
containerName,
|
||||
status: 'skipped',
|
||||
step: 'concluido',
|
||||
message: 'Nenhum volume selecionado para backup.',
|
||||
percent: 100,
|
||||
file: { current: 0, total: 0, currentFile: null, percent: 100 },
|
||||
});
|
||||
return {
|
||||
containerId: inspect.Id,
|
||||
containerName,
|
||||
status: 'skipped',
|
||||
mode: runMode,
|
||||
message: 'Nenhum volume selecionado para backup.',
|
||||
};
|
||||
}
|
||||
|
||||
const containerBackup = {
|
||||
containerId: inspect.Id,
|
||||
containerName,
|
||||
backupScope,
|
||||
backupPaths: backupScope === 'container' ? ['/'] : mounts.map((mount) => mount.destination),
|
||||
backupPaths: backupScope === 'container' ? ['/'] : activeMounts.map((mount) => mount.destination),
|
||||
mountSignature: mounts,
|
||||
archiveRelativePath,
|
||||
snapshotRelativePath,
|
||||
@@ -378,7 +402,7 @@ class BackupService {
|
||||
|
||||
const sourcePaths = backupScope === 'container'
|
||||
? ['/']
|
||||
: mounts.map((mount) => mount.destination);
|
||||
: activeMounts.map((mount) => mount.destination);
|
||||
const relSourcePaths = sourcePaths.map((item) => toContainerRelPath(item));
|
||||
|
||||
if (backupScope === 'volumes') {
|
||||
@@ -480,7 +504,7 @@ class BackupService {
|
||||
}
|
||||
|
||||
const binds = [`${backupRoot}:/backuproot`];
|
||||
for (const [index, mount] of mounts.entries()) {
|
||||
for (const [index, mount] of activeMounts.entries()) {
|
||||
binds.push(`${getMountBindingSource(mount)}:/payload/m${index}:ro`);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,24 @@ async function main() {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/containers/:containerId/mounts', async (request, response) => {
|
||||
try {
|
||||
const inspect = await dockerService.inspectContainer(request.params.containerId);
|
||||
const mounts = (inspect.Mounts || [])
|
||||
.filter((m) => m.Type === 'bind' || m.Type === 'volume')
|
||||
.map((m) => ({
|
||||
type: m.Type,
|
||||
name: m.Name || null,
|
||||
source: m.Source,
|
||||
destination: m.Destination,
|
||||
rw: m.RW,
|
||||
}));
|
||||
response.json(mounts);
|
||||
} catch (error) {
|
||||
response.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/profiles', async (_request, response) => {
|
||||
try {
|
||||
const profiles = await store.listProfiles();
|
||||
@@ -71,6 +89,7 @@ async function main() {
|
||||
containerIds: payload.containerIds,
|
||||
mode: existing?.mode || 'full',
|
||||
backupScope: payload.backupScope || existing?.backupScope || 'volumes',
|
||||
volumeSelections: payload.volumeSelections || existing?.volumeSelections || {},
|
||||
});
|
||||
|
||||
response.status(payload.id ? 200 : 201).json(profile);
|
||||
|
||||
@@ -54,6 +54,7 @@ class JsonStore {
|
||||
containerIds: profileInput.containerIds,
|
||||
mode: profileInput.mode,
|
||||
backupScope: profileInput.backupScope || 'volumes',
|
||||
volumeSelections: profileInput.volumeSelections || {},
|
||||
backupDir: profileInput.backupDir,
|
||||
updatedAt: now,
|
||||
createdAt: profileInput.createdAt || now,
|
||||
|
||||
Reference in New Issue
Block a user