docker-client-mx 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/bin/docker-client-mx.js +42 -0
- package/build/icons/icon-1024.png +0 -0
- package/build/icons/icon-128.png +0 -0
- package/build/icons/icon-16.png +0 -0
- package/build/icons/icon-256.png +0 -0
- package/build/icons/icon-32.png +0 -0
- package/build/icons/icon-48.png +0 -0
- package/build/icons/icon-512.png +0 -0
- package/build/icons/icon-64.png +0 -0
- package/build/icons/icon.ico +0 -0
- package/build/icons/icon.iconset/icon_128x128.png +0 -0
- package/build/icons/icon.iconset/icon_128x128@2x.png +0 -0
- package/build/icons/icon.iconset/icon_16x16.png +0 -0
- package/build/icons/icon.iconset/icon_16x16@2x.png +0 -0
- package/build/icons/icon.iconset/icon_256x256.png +0 -0
- package/build/icons/icon.iconset/icon_256x256@2x.png +0 -0
- package/build/icons/icon.iconset/icon_32x32.png +0 -0
- package/build/icons/icon.iconset/icon_32x32@2x.png +0 -0
- package/build/icons/icon.iconset/icon_512x512.png +0 -0
- package/build/icons/icon.iconset/icon_512x512@2x.png +0 -0
- package/build/icons/icon.png +0 -0
- package/dist/docker-desktop-mx/browser/chunk-64WWLMUP.js +39 -0
- package/dist/docker-desktop-mx/browser/chunk-N4VBBC4W.js +1 -0
- package/dist/docker-desktop-mx/browser/favicon.ico +0 -0
- package/dist/docker-desktop-mx/browser/index.html +14 -0
- package/dist/docker-desktop-mx/browser/main-CTIRTSOI.js +5 -0
- package/dist/docker-desktop-mx/browser/styles-JS3HDDI4.css +1 -0
- package/dist-electron/api.js +10 -0
- package/dist-electron/api.js.map +1 -0
- package/dist-electron/assistant-key.js +70 -0
- package/dist-electron/assistant-key.js.map +1 -0
- package/dist-electron/assistant-models.js +19 -0
- package/dist-electron/assistant-models.js.map +1 -0
- package/dist-electron/assistant-tools.js +335 -0
- package/dist-electron/assistant-tools.js.map +1 -0
- package/dist-electron/assistant.js +302 -0
- package/dist-electron/assistant.js.map +1 -0
- package/dist-electron/attention.js +75 -0
- package/dist-electron/attention.js.map +1 -0
- package/dist-electron/channels.js +113 -0
- package/dist-electron/channels.js.map +1 -0
- package/dist-electron/compose-files.js +104 -0
- package/dist-electron/compose-files.js.map +1 -0
- package/dist-electron/compose.js +271 -0
- package/dist-electron/compose.js.map +1 -0
- package/dist-electron/container-events.js +188 -0
- package/dist-electron/container-events.js.map +1 -0
- package/dist-electron/containers.js +165 -0
- package/dist-electron/containers.js.map +1 -0
- package/dist-electron/context-menu.js +56 -0
- package/dist-electron/context-menu.js.map +1 -0
- package/dist-electron/contexts.js +346 -0
- package/dist-electron/contexts.js.map +1 -0
- package/dist-electron/crash.js +129 -0
- package/dist-electron/crash.js.map +1 -0
- package/dist-electron/diagnosis.js +338 -0
- package/dist-electron/diagnosis.js.map +1 -0
- package/dist-electron/docker-client.js +284 -0
- package/dist-electron/docker-client.js.map +1 -0
- package/dist-electron/engine.js +145 -0
- package/dist-electron/engine.js.map +1 -0
- package/dist-electron/event-store.js +146 -0
- package/dist-electron/event-store.js.map +1 -0
- package/dist-electron/events.js +132 -0
- package/dist-electron/events.js.map +1 -0
- package/dist-electron/exec.js +138 -0
- package/dist-electron/exec.js.map +1 -0
- package/dist-electron/files.js +407 -0
- package/dist-electron/files.js.map +1 -0
- package/dist-electron/health.js +92 -0
- package/dist-electron/health.js.map +1 -0
- package/dist-electron/host-overview.js +50 -0
- package/dist-electron/host-overview.js.map +1 -0
- package/dist-electron/host-shell.js +196 -0
- package/dist-electron/host-shell.js.map +1 -0
- package/dist-electron/i18n.js +58 -0
- package/dist-electron/i18n.js.map +1 -0
- package/dist-electron/image-history.js +87 -0
- package/dist-electron/image-history.js.map +1 -0
- package/dist-electron/images.js +141 -0
- package/dist-electron/images.js.map +1 -0
- package/dist-electron/ipc.js +783 -0
- package/dist-electron/ipc.js.map +1 -0
- package/dist-electron/links.js +46 -0
- package/dist-electron/links.js.map +1 -0
- package/dist-electron/logs.js +153 -0
- package/dist-electron/logs.js.map +1 -0
- package/dist-electron/main.js +494 -0
- package/dist-electron/main.js.map +1 -0
- package/dist-electron/merged-logs.js +127 -0
- package/dist-electron/merged-logs.js.map +1 -0
- package/dist-electron/navigation.js +37 -0
- package/dist-electron/navigation.js.map +1 -0
- package/dist-electron/ndjson.js +30 -0
- package/dist-electron/ndjson.js.map +1 -0
- package/dist-electron/networks.js +507 -0
- package/dist-electron/networks.js.map +1 -0
- package/dist-electron/port-conflicts.js +106 -0
- package/dist-electron/port-conflicts.js.map +1 -0
- package/dist-electron/preload.js +257 -0
- package/dist-electron/preload.js.map +1 -0
- package/dist-electron/pull.js +220 -0
- package/dist-electron/pull.js.map +1 -0
- package/dist-electron/redact.js +129 -0
- package/dist-electron/redact.js.map +1 -0
- package/dist-electron/registry-auth.js +150 -0
- package/dist-electron/registry-auth.js.map +1 -0
- package/dist-electron/renew.js +162 -0
- package/dist-electron/renew.js.map +1 -0
- package/dist-electron/restart-policy.js +58 -0
- package/dist-electron/restart-policy.js.map +1 -0
- package/dist-electron/run-command.js +146 -0
- package/dist-electron/run-command.js.map +1 -0
- package/dist-electron/run.js +151 -0
- package/dist-electron/run.js.map +1 -0
- package/dist-electron/self-update.js +163 -0
- package/dist-electron/self-update.js.map +1 -0
- package/dist-electron/servers.js +108 -0
- package/dist-electron/servers.js.map +1 -0
- package/dist-electron/settings.js +146 -0
- package/dist-electron/settings.js.map +1 -0
- package/dist-electron/ssh.js +574 -0
- package/dist-electron/ssh.js.map +1 -0
- package/dist-electron/stats.js +209 -0
- package/dist-electron/stats.js.map +1 -0
- package/dist-electron/streams.js +63 -0
- package/dist-electron/streams.js.map +1 -0
- package/dist-electron/system.js +123 -0
- package/dist-electron/system.js.map +1 -0
- package/dist-electron/terminal-transcript.js +55 -0
- package/dist-electron/terminal-transcript.js.map +1 -0
- package/dist-electron/updates.js +136 -0
- package/dist-electron/updates.js.map +1 -0
- package/dist-electron/volume-backup.js +203 -0
- package/dist-electron/volume-backup.js.map +1 -0
- package/dist-electron/volume-files.js +124 -0
- package/dist-electron/volume-files.js.map +1 -0
- package/dist-electron/volumes.js +253 -0
- package/dist-electron/volumes.js.map +1 -0
- package/dist-electron/wiring-watch.js +93 -0
- package/dist-electron/wiring-watch.js.map +1 -0
- package/dist-electron/wiring.js +71 -0
- package/dist-electron/wiring.js.map +1 -0
- package/dist-electron/wsl.js +167 -0
- package/dist-electron/wsl.js.map +1 -0
- package/package.json +125 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.backupFileName = backupFileName;
|
|
37
|
+
exports.isBackupEntry = isBackupEntry;
|
|
38
|
+
exports.backupVolume = backupVolume;
|
|
39
|
+
exports.restoreVolume = restoreVolume;
|
|
40
|
+
const node_fs_1 = require("node:fs");
|
|
41
|
+
const promises_1 = require("node:fs/promises");
|
|
42
|
+
const node_stream_1 = require("node:stream");
|
|
43
|
+
const promises_2 = require("node:stream/promises");
|
|
44
|
+
const tar = __importStar(require("tar-stream"));
|
|
45
|
+
const docker_client_1 = require("./docker-client");
|
|
46
|
+
const i18n_1 = require("./i18n");
|
|
47
|
+
/**
|
|
48
|
+
* Back-up en herstel van een volume, als `.tar`.
|
|
49
|
+
*
|
|
50
|
+
* Via een hulpcontainer die nooit start: het volume hangt aan `/data`, en de Engine API leest
|
|
51
|
+
* en schrijft daar met `getArchive` en `putArchive`, net als de tab Bestanden. Het image van die
|
|
52
|
+
* container maken we ter plekke uit een lege tar (`docker import`): nul bytes, geen pull, geen
|
|
53
|
+
* netwerk. Na afloop gaan container en image weer weg, zodat ze niet in de lijsten opduiken.
|
|
54
|
+
*
|
|
55
|
+
* Over consistentie zegt dit niets: een database die tijdens de back-up schrijft, levert een
|
|
56
|
+
* back-up op die hij zelf misschien niet meer leest. De volumepagina zegt dat erbij.
|
|
57
|
+
*/
|
|
58
|
+
const HELPER_IMAGE = 'docker-client-mx-helper';
|
|
59
|
+
const HELPER_TAG = 'backup';
|
|
60
|
+
/** Waar het volume in de hulpcontainer hangt, en dus de naam van de map in de back-up. */
|
|
61
|
+
const MOUNT = '/data';
|
|
62
|
+
const ROOT = 'data/';
|
|
63
|
+
/** Een lege tar: twee blokken van nullen. Genoeg voor een image zonder bestanden. */
|
|
64
|
+
function emptyTar() {
|
|
65
|
+
return node_stream_1.Readable.from([Buffer.alloc(1024)]);
|
|
66
|
+
}
|
|
67
|
+
/** De voorgestelde bestandsnaam: volume plus datum, zodat back-ups niet over elkaar heen gaan. */
|
|
68
|
+
function backupFileName(volume, now) {
|
|
69
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
70
|
+
const stamp = `${String(now.getFullYear())}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
71
|
+
return `${volume}-${stamp}.tar`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Of een tar een back-up uit deze app is: alles staat onder `data/`. Een willekeurige tar
|
|
75
|
+
* terugzetten zou zijn inhoud in de hulpcontainer laten landen en niet in het volume, en dan
|
|
76
|
+
* lijkt het gelukt terwijl er niets veranderd is.
|
|
77
|
+
*/
|
|
78
|
+
function isBackupEntry(name) {
|
|
79
|
+
return name === ROOT || name === 'data' || name.startsWith(ROOT);
|
|
80
|
+
}
|
|
81
|
+
/** Maakt de hulpcontainer met het volume eraan, doet het werk, en ruimt alles weer op. */
|
|
82
|
+
async function withHelper(volume, readOnly, work) {
|
|
83
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
84
|
+
// Eerst kijken of het volume er is: een container die een onbekend volume koppelt, laat Docker
|
|
85
|
+
// het stilletjes aanmaken, en dan maak je een lege back-up van een volume dat nooit bestond.
|
|
86
|
+
try {
|
|
87
|
+
await docker.getVolume(volume).inspect();
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
if (err?.statusCode === 404) {
|
|
91
|
+
throw new Error((0, i18n_1.t)({
|
|
92
|
+
en: `volume ${volume} does not exist (anymore).`,
|
|
93
|
+
nl: `volume ${volume} bestaat niet (meer).`,
|
|
94
|
+
de: `Volume ${volume} existiert nicht (mehr).`,
|
|
95
|
+
fr: `le volume ${volume} n’existe pas (ou plus).`,
|
|
96
|
+
es: `el volume ${volume} no existe (ya).`,
|
|
97
|
+
}), { cause: err });
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
const image = `${HELPER_IMAGE}:${HELPER_TAG}`;
|
|
102
|
+
const imported = (await docker.importImage(emptyTar(), { repo: HELPER_IMAGE, tag: HELPER_TAG }));
|
|
103
|
+
// Het antwoord van import is een stroom met voortgang; pas als die klaar is, bestaat het image.
|
|
104
|
+
await new Promise((resolve, reject) => {
|
|
105
|
+
imported.on('end', resolve);
|
|
106
|
+
imported.on('error', reject);
|
|
107
|
+
imported.resume();
|
|
108
|
+
});
|
|
109
|
+
const container = await docker.createContainer({
|
|
110
|
+
Image: image,
|
|
111
|
+
// Wordt nooit gestart, maar zonder Cmd weigert Docker een image zonder eigen opdracht.
|
|
112
|
+
Cmd: ['niet-starten'],
|
|
113
|
+
Labels: { 'docker-desktop-mx.helper': 'volume-backup' },
|
|
114
|
+
HostConfig: { Binds: [`${volume}:${MOUNT}${readOnly ? ':ro' : ''}`] },
|
|
115
|
+
});
|
|
116
|
+
try {
|
|
117
|
+
return await work(container);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
await container.remove({ force: true }).catch(() => undefined);
|
|
121
|
+
// Kan mislukken als een tweede back-up het image nog gebruikt; dan ruimt die het op.
|
|
122
|
+
await docker.getImage(image).remove().catch(() => undefined);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Schrijft de inhoud van het volume als `.tar` naar een bestand dat de gebruiker kiest. */
|
|
126
|
+
async function backupVolume(volume, choose, now = new Date()) {
|
|
127
|
+
const target = await choose(backupFileName(volume, now));
|
|
128
|
+
if (target === null) {
|
|
129
|
+
return { ok: true, savedTo: null };
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
await withHelper(volume, true, async (container) => {
|
|
133
|
+
const archive = (await container.getArchive({ path: MOUNT }));
|
|
134
|
+
await (0, promises_2.pipeline)(archive, (0, node_fs_1.createWriteStream)(target));
|
|
135
|
+
});
|
|
136
|
+
return { ok: true, savedTo: target };
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
// Een half geschreven .tar ziet eruit als een back-up, en dat is erger dan geen bestand.
|
|
140
|
+
await (0, promises_1.rm)(target, { force: true }).catch(() => undefined);
|
|
141
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
142
|
+
return { ok: false, problem: (0, i18n_1.t)({
|
|
143
|
+
en: `Backup of ${volume} failed: ${reason}`,
|
|
144
|
+
nl: `Back-up van ${volume} mislukte: ${reason}`,
|
|
145
|
+
de: `Die Sicherung von ${volume} ist fehlgeschlagen: ${reason}`,
|
|
146
|
+
fr: `La sauvegarde de ${volume} a échoué : ${reason}`,
|
|
147
|
+
es: `La copia de ${volume} ha fallado: ${reason}`,
|
|
148
|
+
}) };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** Leest alleen de eerste entry van een tar, om te zien of het een back-up uit deze app is. */
|
|
152
|
+
async function firstEntryName(file) {
|
|
153
|
+
const extract = tar.extract();
|
|
154
|
+
const source = (0, node_fs_1.createReadStream)(file);
|
|
155
|
+
source.pipe(extract);
|
|
156
|
+
for await (const entry of extract) {
|
|
157
|
+
const name = entry.header.name;
|
|
158
|
+
source.destroy();
|
|
159
|
+
extract.destroy();
|
|
160
|
+
return name;
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Zet een back-up terug in het volume. Bestanden met dezelfde naam worden overschreven;
|
|
166
|
+
* bestanden die niet in de back-up zitten, blijven staan. Leegmaken zou een shell in een
|
|
167
|
+
* draaiende container vragen, en dat wil je bij een volume met data niet stilletjes doen.
|
|
168
|
+
*/
|
|
169
|
+
async function restoreVolume(volume, file) {
|
|
170
|
+
if (file === null) {
|
|
171
|
+
return { ok: true, restoredFrom: null };
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const first = await firstEntryName(file);
|
|
175
|
+
if (first === null || !isBackupEntry(first)) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
problem: (0, i18n_1.t)({
|
|
179
|
+
en: 'This is not a backup from this app: a volume backup has everything under data/.',
|
|
180
|
+
nl: 'Dit is geen back-up uit deze app: een volume-back-up heeft alles onder data/.',
|
|
181
|
+
de: 'Das ist keine Sicherung aus dieser App: bei einer Volume-Sicherung steht alles unter data/.',
|
|
182
|
+
fr: 'Ce n’est pas une sauvegarde de cette application : dans une sauvegarde de volume, tout se trouve sous data/.',
|
|
183
|
+
es: 'Esto no es una copia de esta aplicación: en una copia de volume todo está bajo data/.',
|
|
184
|
+
}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
await withHelper(volume, false, async (container) => {
|
|
188
|
+
await container.putArchive((0, node_fs_1.createReadStream)(file), { path: '/' });
|
|
189
|
+
});
|
|
190
|
+
return { ok: true, restoredFrom: file };
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
194
|
+
return { ok: false, problem: (0, i18n_1.t)({
|
|
195
|
+
en: `Restoring into ${volume} failed: ${reason}`,
|
|
196
|
+
nl: `Terugzetten in ${volume} mislukte: ${reason}`,
|
|
197
|
+
de: `Das Zurückspielen in ${volume} ist fehlgeschlagen: ${reason}`,
|
|
198
|
+
fr: `La restauration dans ${volume} a échoué : ${reason}`,
|
|
199
|
+
es: `La restauración en ${volume} ha fallado: ${reason}`,
|
|
200
|
+
}) };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=volume-backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volume-backup.js","sourceRoot":"","sources":["../electron/volume-backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mountedPath = mountedPath;
|
|
4
|
+
exports.volumePath = volumePath;
|
|
5
|
+
exports.listVolumeDirectory = listVolumeDirectory;
|
|
6
|
+
exports.closeVolumeBrowser = closeVolumeBrowser;
|
|
7
|
+
exports.hasVolumeBrowser = hasVolumeBrowser;
|
|
8
|
+
const docker_client_1 = require("./docker-client");
|
|
9
|
+
const files_1 = require("./files");
|
|
10
|
+
const host_shell_1 = require("./host-shell");
|
|
11
|
+
const i18n_1 = require("./i18n");
|
|
12
|
+
/**
|
|
13
|
+
* In een volume kijken, zonder de container die hem gebruikt.
|
|
14
|
+
*
|
|
15
|
+
* Een volume heeft geen eigen bestandssysteem dat de Engine API kan opsommen; je hebt er altijd
|
|
16
|
+
* een container voor nodig die hem aangekoppeld heeft. Daarom hangen we er een kortlevende
|
|
17
|
+
* hulpcontainer aan, met dezelfde `alpine:3` als de host-shell — één hulpimage is genoeg — en
|
|
18
|
+
* draaien daarin dezelfde `ls` als de tab Bestanden van een container.
|
|
19
|
+
*
|
|
20
|
+
* Alleen lezen, en dat is ook letterlijk zo: het volume gaat er read-only in. Downloaden kan al
|
|
21
|
+
* via de back-up, en schrijven in een volume buiten de container om die hem gebruikt is een
|
|
22
|
+
* goede manier om iets stuk te maken terwijl die container niets doorheeft.
|
|
23
|
+
*
|
|
24
|
+
* Eén hulpcontainer tegelijk. Open je een ander volume, dan gaat de vorige weg; sluit je het
|
|
25
|
+
* paneel, dan ook. Blijft er toch een achter — de app viel om — dan ruimt de volgende keer hem
|
|
26
|
+
* op aan zijn label, net als bij de host-shell.
|
|
27
|
+
*/
|
|
28
|
+
/** Waar het volume in de hulpcontainer hangt. Alles wat de renderer vraagt zit hieronder. */
|
|
29
|
+
const MOUNT = '/volume';
|
|
30
|
+
/** Zodat een achtergebleven hulpcontainer te herkennen is, los van die van de host-shell. */
|
|
31
|
+
const ROLE = 'volume-browser';
|
|
32
|
+
/** Blijft draaien tot wij hem weghalen; `sleep` zonder meer, want er hoeft niets te gebeuren. */
|
|
33
|
+
const IDLE_COMMAND = ['sleep', '86400'];
|
|
34
|
+
let open = null;
|
|
35
|
+
/** Het pad in de hulpcontainer voor een pad in het volume. */
|
|
36
|
+
function mountedPath(pathInVolume) {
|
|
37
|
+
const normalized = (0, files_1.normalizeContainerPath)(pathInVolume);
|
|
38
|
+
return normalized === '/' ? MOUNT : `${MOUNT}${normalized}`;
|
|
39
|
+
}
|
|
40
|
+
/** Het pad in het volume voor een pad in de hulpcontainer; de renderer ziet `/` als de wortel. */
|
|
41
|
+
function volumePath(pathInContainer) {
|
|
42
|
+
if (!pathInContainer.startsWith(MOUNT)) {
|
|
43
|
+
return '/';
|
|
44
|
+
}
|
|
45
|
+
const rest = pathInContainer.slice(MOUNT.length);
|
|
46
|
+
return rest === '' ? '/' : rest;
|
|
47
|
+
}
|
|
48
|
+
async function removeHelper(id) {
|
|
49
|
+
await (0, docker_client_1.getConnection)().docker.getContainer(id).remove({ force: true }).catch(() => undefined);
|
|
50
|
+
}
|
|
51
|
+
/** Hulpcontainers van een vorige sessie die niemand meer opruimt. */
|
|
52
|
+
async function sweepOrphans() {
|
|
53
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
54
|
+
const found = await docker.listContainers({ all: true, filters: { label: [`${host_shell_1.HELPER_LABEL}=${ROLE}`] } }).catch(() => []);
|
|
55
|
+
await Promise.all(found.filter((item) => item.Id !== open?.containerId).map((item) => removeHelper(item.Id)));
|
|
56
|
+
}
|
|
57
|
+
/** Haalt de hulpimage op als die er niet is. Dezelfde als de host-shell gebruikt. */
|
|
58
|
+
async function ensureImage() {
|
|
59
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
60
|
+
try {
|
|
61
|
+
await docker.getImage(host_shell_1.HELPER_IMAGE).inspect();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Niet aanwezig; hieronder ophalen.
|
|
66
|
+
}
|
|
67
|
+
const stream = await docker.pull(host_shell_1.HELPER_IMAGE);
|
|
68
|
+
await new Promise((resolve, reject) => {
|
|
69
|
+
docker.modem.followProgress(stream, (err) => (err === null ? resolve() : reject(err)));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** De hulpcontainer voor dit volume; maakt hem aan als hij er nog niet is. */
|
|
73
|
+
async function helperFor(volume) {
|
|
74
|
+
if (open?.volume === volume) {
|
|
75
|
+
return open.containerId;
|
|
76
|
+
}
|
|
77
|
+
await closeVolumeBrowser();
|
|
78
|
+
await sweepOrphans();
|
|
79
|
+
await ensureImage();
|
|
80
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
81
|
+
const container = await docker.createContainer({
|
|
82
|
+
name: `docker-client-mx-volume-${Date.now().toString(36)}`,
|
|
83
|
+
Image: host_shell_1.HELPER_IMAGE,
|
|
84
|
+
Cmd: IDLE_COMMAND,
|
|
85
|
+
Labels: { [host_shell_1.HELPER_LABEL]: ROLE, [host_shell_1.OWNER_LABEL]: String(process.pid) },
|
|
86
|
+
HostConfig: { Binds: [`${volume}:${MOUNT}:ro`], AutoRemove: false, NetworkMode: 'none' },
|
|
87
|
+
});
|
|
88
|
+
await container.start();
|
|
89
|
+
open = { volume, containerId: container.id };
|
|
90
|
+
return container.id;
|
|
91
|
+
}
|
|
92
|
+
/** De inhoud van een map in het volume, met `/` als de wortel van het volume zelf. */
|
|
93
|
+
async function listVolumeDirectory(volume, requested) {
|
|
94
|
+
try {
|
|
95
|
+
const containerId = await helperFor(volume);
|
|
96
|
+
const result = await (0, files_1.listDirectory)(containerId, mountedPath(requested));
|
|
97
|
+
// De renderer hoort niets van /volume te zien; voor hem begint het volume bij de wortel.
|
|
98
|
+
return result.ok
|
|
99
|
+
? { ok: true, path: volumePath(result.path), entries: result.entries }
|
|
100
|
+
: { ok: false, path: volumePath(result.path), problem: result.problem };
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return { ok: false, path: (0, files_1.normalizeContainerPath)(requested), problem: (0, i18n_1.t)({
|
|
104
|
+
en: `Could not look inside ${volume}: ${err instanceof Error ? err.message : String(err)}`,
|
|
105
|
+
nl: `Kon niet in ${volume} kijken: ${err instanceof Error ? err.message : String(err)}`,
|
|
106
|
+
de: `In ${volume} konnte nicht geschaut werden: ${err instanceof Error ? err.message : String(err)}`,
|
|
107
|
+
fr: `Impossible de regarder dans ${volume} : ${err instanceof Error ? err.message : String(err)}`,
|
|
108
|
+
es: `No se ha podido mirar dentro de ${volume}: ${err instanceof Error ? err.message : String(err)}`,
|
|
109
|
+
}) };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Ruimt de hulpcontainer op. Bij het sluiten van het paneel, en bij het afsluiten van de app. */
|
|
113
|
+
async function closeVolumeBrowser() {
|
|
114
|
+
const current = open;
|
|
115
|
+
open = null;
|
|
116
|
+
if (current !== null) {
|
|
117
|
+
await removeHelper(current.containerId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Of er nu een hulpcontainer draait; main wacht daarop bij het afsluiten. */
|
|
121
|
+
function hasVolumeBrowser() {
|
|
122
|
+
return open !== null;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=volume-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volume-files.js","sourceRoot":"","sources":["../electron/volume-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toEpochSeconds = toEpochSeconds;
|
|
4
|
+
exports.countVolumeUsage = countVolumeUsage;
|
|
5
|
+
exports.toVolumeSummary = toVolumeSummary;
|
|
6
|
+
exports.listVolumes = listVolumes;
|
|
7
|
+
exports.toSizeMap = toSizeMap;
|
|
8
|
+
exports.readVolumeSizes = readVolumeSizes;
|
|
9
|
+
exports.volumeNameProblem = volumeNameProblem;
|
|
10
|
+
exports.toDriverSpec = toDriverSpec;
|
|
11
|
+
exports.createVolume = createVolume;
|
|
12
|
+
exports.removeVolume = removeVolume;
|
|
13
|
+
exports.pruneVolumes = pruneVolumes;
|
|
14
|
+
const docker_client_1 = require("./docker-client");
|
|
15
|
+
const i18n_1 = require("./i18n");
|
|
16
|
+
const COMPOSE_PROJECT = 'com.docker.compose.project';
|
|
17
|
+
/** Epoch in seconden; de volume-API geeft een ISO-tekst waar de rest van de app getallen gebruikt. */
|
|
18
|
+
function toEpochSeconds(iso) {
|
|
19
|
+
const parsed = Date.parse(iso ?? '');
|
|
20
|
+
return Number.isNaN(parsed) ? 0 : Math.floor(parsed / 1000);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Telt per volumenaam hoeveel containers hem aankoppelen.
|
|
24
|
+
*
|
|
25
|
+
* Alleen mounts van het type `volume`: een container koppelt ook bind-mounts en tmpfs aan, en
|
|
26
|
+
* die hebben een pad in plaats van een naam. Zonder dat filter zou een bind-mount met dezelfde
|
|
27
|
+
* naam als een volume meegeteld worden.
|
|
28
|
+
*/
|
|
29
|
+
function countVolumeUsage(containers) {
|
|
30
|
+
const usage = new Map();
|
|
31
|
+
for (const container of containers) {
|
|
32
|
+
for (const mount of container.Mounts ?? []) {
|
|
33
|
+
if (mount.Type === 'volume' && typeof mount.Name === 'string') {
|
|
34
|
+
usage.set(mount.Name, (usage.get(mount.Name) ?? 0) + 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return usage;
|
|
39
|
+
}
|
|
40
|
+
/** Vertaalt één regel uit `docker volume ls` naar het contract van de renderer. */
|
|
41
|
+
function toVolumeSummary(raw, usage) {
|
|
42
|
+
return {
|
|
43
|
+
name: raw.Name,
|
|
44
|
+
driver: raw.Driver,
|
|
45
|
+
mountpoint: raw.Mountpoint,
|
|
46
|
+
scope: raw.Scope ?? 'local',
|
|
47
|
+
createdAt: toEpochSeconds(raw.CreatedAt),
|
|
48
|
+
composeProject: raw.Labels?.[COMPOSE_PROJECT] ?? null,
|
|
49
|
+
inUseBy: usage.get(raw.Name) ?? 0,
|
|
50
|
+
// De grootte komt uit /system/df en wordt apart opgehaald; tot die tijd weten we het niet.
|
|
51
|
+
sizeBytes: null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Alle volumes, zonder grootte. Die komt uit `readVolumeSizes`. */
|
|
55
|
+
async function listVolumes() {
|
|
56
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
57
|
+
const [result, containers] = await Promise.all([docker.listVolumes(), docker.listContainers({ all: true })]);
|
|
58
|
+
const usage = countVolumeUsage(containers);
|
|
59
|
+
const raw = (result.Volumes ?? []);
|
|
60
|
+
return raw.map((volume) => toVolumeSummary(volume, usage)).sort((a, b) => a.name.localeCompare(b.name));
|
|
61
|
+
}
|
|
62
|
+
/** Haalt de groottes per volumenaam uit het antwoord van `/system/df`. */
|
|
63
|
+
function toSizeMap(raw) {
|
|
64
|
+
const sizes = {};
|
|
65
|
+
for (const volume of raw.Volumes ?? []) {
|
|
66
|
+
const size = volume.UsageData?.Size;
|
|
67
|
+
// -1 betekent "niet uitgerekend"; dat is iets anders dan een leeg volume.
|
|
68
|
+
if (typeof volume.Name === 'string' && typeof size === 'number' && size >= 0) {
|
|
69
|
+
sizes[volume.Name] = size;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return sizes;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* De groottes, uit `/system/df`. Apart van de lijst omdat die aanroep de hele opslag doorloopt
|
|
76
|
+
* en bij veel images en volumes seconden kan duren; de tabel hoeft daar niet op te wachten.
|
|
77
|
+
*/
|
|
78
|
+
async function readVolumeSizes() {
|
|
79
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
80
|
+
return toSizeMap((await docker.df()));
|
|
81
|
+
}
|
|
82
|
+
// Dezelfde regel als de daemon hanteert; hier al, zodat de melding in de taal van de app is.
|
|
83
|
+
const VOLUME_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/;
|
|
84
|
+
/** `null` als de naam deugt, anders wat eraan schort. */
|
|
85
|
+
function volumeNameProblem(name) {
|
|
86
|
+
if (VOLUME_NAME.test(name)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return (0, i18n_1.t)({
|
|
90
|
+
en: 'A volume name has at least two characters: letters, digits, dots, hyphens and underscores, starting with a letter or digit.',
|
|
91
|
+
nl: 'Een volumenaam heeft minstens twee tekens: letters, cijfers, punt, streepje en underscore, en begint met een letter of cijfer.',
|
|
92
|
+
de: 'Ein Volume-Name hat mindestens zwei Zeichen: Buchstaben, Ziffern, Punkte, Bindestriche und Unterstriche, beginnend mit einem Buchstaben oder einer Ziffer.',
|
|
93
|
+
fr: 'Un nom de volume a au moins deux caractères : lettres, chiffres, points, tirets et underscores, en commençant par une lettre ou un chiffre.',
|
|
94
|
+
es: 'Un nombre de volume tiene al menos dos caracteres: letras, dígitos, puntos, guiones y guiones bajos, empezando por una letra o un dígito.',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Een hostnaam, IPv4- of IPv6-adres. Geen komma: die zou in de `o`-optie een eigen optie beginnen.
|
|
98
|
+
const NFS_SERVER = /^[\w.:-]+$/;
|
|
99
|
+
/**
|
|
100
|
+
* Zet de keuze uit de dialoog om naar een driver met opties, zoals `docker volume create
|
|
101
|
+
* --driver local --opt type=... --opt o=... --opt device=...` dat zou doen. Een map of share
|
|
102
|
+
* gaat altijd via de local-driver; een plugin kiezen hoort alleen bij de eerste keuze.
|
|
103
|
+
*/
|
|
104
|
+
function toDriverSpec(storage) {
|
|
105
|
+
if (storage.kind === 'docker') {
|
|
106
|
+
return { driver: storage.driver, options: {} };
|
|
107
|
+
}
|
|
108
|
+
const path = storage.path.trim();
|
|
109
|
+
// Docker draait op Linux, ook onder Windows (in WSL). Een pad als C:\data bestaat daar niet.
|
|
110
|
+
if (!path.startsWith('/')) {
|
|
111
|
+
return {
|
|
112
|
+
problem: (0, i18n_1.t)({
|
|
113
|
+
en: 'The path is on the machine running Docker and starts with /, for example /srv/data. On WSL, C:\\data is /mnt/c/data.',
|
|
114
|
+
nl: 'Het pad staat op de machine waar Docker draait en begint met /, bijvoorbeeld /srv/data. Onder WSL is C:\\data /mnt/c/data.',
|
|
115
|
+
de: 'Der Pfad liegt auf der Maschine, auf der Docker läuft, und beginnt mit /, zum Beispiel /srv/data. Unter WSL ist C:\\data gleich /mnt/c/data.',
|
|
116
|
+
fr: 'Le chemin est sur la machine qui fait tourner Docker et commence par /, par exemple /srv/data. Sous WSL, C:\\data correspond à /mnt/c/data.',
|
|
117
|
+
es: 'La ruta está en la máquina donde se ejecuta Docker y empieza por /, por ejemplo /srv/data. En WSL, C:\\data es /mnt/c/data.',
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (storage.kind === 'folder') {
|
|
122
|
+
return { driver: 'local', options: { type: 'none', o: 'bind', device: path } };
|
|
123
|
+
}
|
|
124
|
+
const server = storage.server.trim();
|
|
125
|
+
if (!NFS_SERVER.test(server)) {
|
|
126
|
+
return {
|
|
127
|
+
problem: (0, i18n_1.t)({
|
|
128
|
+
en: 'Enter the NFS server as a name or IP address, for example nas.local or 192.168.1.20.',
|
|
129
|
+
nl: 'Vul de NFS-server in als naam of IP-adres, bijvoorbeeld nas.local of 192.168.1.20.',
|
|
130
|
+
de: 'Geben Sie den NFS-Server als Namen oder IP-Adresse an, zum Beispiel nas.local oder 192.168.1.20.',
|
|
131
|
+
fr: 'Indiquez le serveur NFS sous forme de nom ou d’adresse IP, par exemple nas.local ou 192.168.1.20.',
|
|
132
|
+
es: 'Indica el servidor NFS como nombre o dirección IP, por ejemplo nas.local o 192.168.1.20.',
|
|
133
|
+
}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return { driver: 'local', options: { type: 'nfs', o: `addr=${server},rw`, device: `:${path}` } };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Maakt een volume met een naam aan. De daemon geeft bij een bestaande naam gewoon dat volume
|
|
140
|
+
* terug, zonder fout; dan zou "aanmaken" stilletjes niets doen. Daarom eerst kijken.
|
|
141
|
+
*
|
|
142
|
+
* Een map of share wordt pas aangekoppeld als een container het volume gebruikt. Een pad dat
|
|
143
|
+
* niet bestaat of een server die niet antwoordt, merk je dus bij het starten van die container.
|
|
144
|
+
*/
|
|
145
|
+
async function createVolume(volume) {
|
|
146
|
+
const { name } = volume;
|
|
147
|
+
const problem = volumeNameProblem(name);
|
|
148
|
+
if (problem !== null) {
|
|
149
|
+
return { ok: false, problem };
|
|
150
|
+
}
|
|
151
|
+
const spec = toDriverSpec(volume.storage);
|
|
152
|
+
if ('problem' in spec) {
|
|
153
|
+
return { ok: false, problem: spec.problem };
|
|
154
|
+
}
|
|
155
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
156
|
+
try {
|
|
157
|
+
await docker.getVolume(name).inspect();
|
|
158
|
+
return { ok: false, problem: (0, i18n_1.t)({
|
|
159
|
+
en: `There already is a volume '${name}'.`,
|
|
160
|
+
nl: `Er is al een volume '${name}'.`,
|
|
161
|
+
de: `Es gibt bereits ein Volume '${name}'.`,
|
|
162
|
+
fr: `Il existe déjà un volume '${name}'.`,
|
|
163
|
+
es: `Ya existe un volume '${name}'.`,
|
|
164
|
+
}) };
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
if (err?.statusCode !== 404) {
|
|
168
|
+
return { ok: false, problem: createProblem(err, name) };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
await docker.createVolume({ Name: name, Driver: spec.driver, DriverOpts: spec.options });
|
|
173
|
+
return { ok: true };
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
return { ok: false, problem: createProblem(err, name) };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function createProblem(err, name) {
|
|
180
|
+
return (0, i18n_1.t)({
|
|
181
|
+
en: `Creating '${name}' failed: ${describeError(err)}`,
|
|
182
|
+
nl: `Aanmaken van '${name}' mislukte: ${describeError(err)}`,
|
|
183
|
+
de: `Das Anlegen von '${name}' ist fehlgeschlagen: ${describeError(err)}`,
|
|
184
|
+
fr: `La création de '${name}' a échoué : ${describeError(err)}`,
|
|
185
|
+
es: `La creación de '${name}' ha fallado: ${describeError(err)}`,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/** Verwijdert een volume. `force` doet er alleen toe als de daemon hem nog vasthoudt. */
|
|
189
|
+
async function removeVolume(name, force) {
|
|
190
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
191
|
+
try {
|
|
192
|
+
await docker.getVolume(name).remove({ force });
|
|
193
|
+
return { ok: true };
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
return { ok: false, problem: describeVolumeProblem(err, name) };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* `all` is het verschil tussen `docker volume prune` (alleen anonieme volumes) en
|
|
201
|
+
* `docker volume prune -a` (ook de volumes met een naam). Dat tweede gooit gegevens weg die
|
|
202
|
+
* iemand bewust een naam heeft gegeven, dus het is een eigen keuze in de UI.
|
|
203
|
+
*/
|
|
204
|
+
async function pruneVolumes(all) {
|
|
205
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
206
|
+
try {
|
|
207
|
+
const raw = (await docker.pruneVolumes({
|
|
208
|
+
filters: JSON.stringify(all ? { all: { true: true } } : {}),
|
|
209
|
+
}));
|
|
210
|
+
return { ok: true, removed: raw.VolumesDeleted?.length ?? 0, reclaimedBytes: raw.SpaceReclaimed ?? 0 };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return { ok: false, problem: (0, i18n_1.t)({
|
|
214
|
+
en: `Prune failed: ${describeError(err)}`,
|
|
215
|
+
nl: `Opruimen mislukte: ${describeError(err)}`,
|
|
216
|
+
de: `Aufräumen fehlgeschlagen: ${describeError(err)}`,
|
|
217
|
+
fr: `Le nettoyage a échoué : ${describeError(err)}`,
|
|
218
|
+
es: `La limpieza ha fallado: ${describeError(err)}`,
|
|
219
|
+
}) };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function describeError(err) {
|
|
223
|
+
return err instanceof Error ? err.message : String(err);
|
|
224
|
+
}
|
|
225
|
+
function describeVolumeProblem(err, name) {
|
|
226
|
+
const status = err?.statusCode;
|
|
227
|
+
if (status === 404) {
|
|
228
|
+
return (0, i18n_1.t)({
|
|
229
|
+
en: `Volume '${name}' no longer exists.`,
|
|
230
|
+
nl: `Volume '${name}' bestaat niet meer.`,
|
|
231
|
+
de: `Volume '${name}' existiert nicht mehr.`,
|
|
232
|
+
fr: `Le volume '${name}' n’existe plus.`,
|
|
233
|
+
es: `El volume '${name}' ya no existe.`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (status === 409) {
|
|
237
|
+
return (0, i18n_1.t)({
|
|
238
|
+
en: `'${name}' is still in use by one or more containers. Remove or stop them first.`,
|
|
239
|
+
nl: `'${name}' is nog in gebruik door een of meer containers. Verwijder of stop die eerst.`,
|
|
240
|
+
de: `'${name}' wird noch von einem oder mehreren Containern benutzt. Entfernen oder stoppen Sie die zuerst.`,
|
|
241
|
+
fr: `'${name}' est encore utilisé par un ou plusieurs conteneurs. Supprimez-les ou arrêtez-les d’abord.`,
|
|
242
|
+
es: `'${name}' todavía lo usan uno o varios contenedores. Elimínalos o detenlos primero.`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return (0, i18n_1.t)({
|
|
246
|
+
en: `Removing '${name}' failed: ${describeError(err)}`,
|
|
247
|
+
nl: `Verwijderen van '${name}' mislukte: ${describeError(err)}`,
|
|
248
|
+
de: `Entfernen von '${name}' fehlgeschlagen: ${describeError(err)}`,
|
|
249
|
+
fr: `La suppression de '${name}' a échoué : ${describeError(err)}`,
|
|
250
|
+
es: `La eliminación de '${name}' ha fallado: ${describeError(err)}`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=volumes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volumes.js","sourceRoot":"","sources":["../electron/volumes.ts"],"names":[],"mappings":";;AAQA,wCAKC;AASD,4CAaC;AAYD,0CAaC;AAGD,kCAQC;AAOD,8BAcC;AAMD,0CAKC;AAMD,8CAaC;AAeD,oCAwCC;AASD,oCAwCC;AAcD,oCAWC;AAYD,oCAmBC;AAxRD,mDAAgD;AAChD,iCAA2B;AAE3B,MAAM,eAAe,GAAG,4BAA4B,CAAC;AAErD,sGAAsG;AACtG,SAAgB,cAAc,CAAC,GAAuB;IAEpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IAErC,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,gBAAgB,CAAC,UAA8C;IAE7E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IAExC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC9D,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAWD,mFAAmF;AACnF,SAAgB,eAAe,CAAC,GAAc,EAAE,KAAkC;IAEhF,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,OAAO;QAC3B,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;QACxC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,eAAe,CAAC,IAAI,IAAI;QACrD,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QACjC,2FAA2F;QAC3F,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC;AAED,oEAAoE;AAC7D,KAAK,UAAU,WAAW;IAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;IACnC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7G,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAA2B,CAAC;IAE7D,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1G,CAAC;AAMD,0EAA0E;AAC1E,SAAgB,SAAS,CAAC,GAAU;IAElC,MAAM,KAAK,GAA2B,EAAE,CAAC;IAEzC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC;QAEpC,0EAA0E;QAC1E,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YAC7E,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,eAAe;IAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;IAEnC,OAAO,SAAS,CAAC,CAAC,MAAM,MAAM,CAAC,EAAE,EAAE,CAAU,CAAC,CAAC;AACjD,CAAC;AAED,6FAA6F;AAC7F,MAAM,WAAW,GAAG,8BAA8B,CAAC;AAEnD,yDAAyD;AACzD,SAAgB,iBAAiB,CAAC,IAAY;IAE5C,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAA,QAAC,EAAC;QACP,EAAE,EAAE,6HAA6H;QACjI,EAAE,EAAE,gIAAgI;QACpI,EAAE,EAAE,4JAA4J;QAChK,EAAE,EAAE,6IAA6I;QACjJ,EAAE,EAAE,2IAA2I;KAChJ,CAAC,CAAC;AACL,CAAC;AAOD,mGAAmG;AACnG,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC;;;;GAIG;AACH,SAAgB,YAAY,CAAC,OAAsB;IAEjD,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAEjC,6FAA6F;IAC7F,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,OAAO,EAAE,IAAA,QAAC,EAAC;gBACT,EAAE,EAAE,sHAAsH;gBAC1H,EAAE,EAAE,4HAA4H;gBAChI,EAAE,EAAE,8IAA8I;gBAClJ,EAAE,EAAE,6IAA6I;gBACjJ,EAAE,EAAE,6HAA6H;aAClI,CAAC;SACH,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAErC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,IAAA,QAAC,EAAC;gBACT,EAAE,EAAE,sFAAsF;gBAC1F,EAAE,EAAE,oFAAoF;gBACxF,EAAE,EAAE,kGAAkG;gBACtG,EAAE,EAAE,mGAAmG;gBACvG,EAAE,EAAE,0FAA0F;aAC/F,CAAC;SACH,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,MAAM,KAAK,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC;AACnG,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,YAAY,CAAC,MAAiB;IAElD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;IACxB,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAExC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAChC,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE1C,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IAC9C,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QAEvC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAA,QAAC,EAAC;gBAC7B,EAAE,EAAE,8BAA8B,IAAI,IAAI;gBAC1C,EAAE,EAAE,wBAAwB,IAAI,IAAI;gBACpC,EAAE,EAAE,+BAA+B,IAAI,IAAI;gBAC3C,EAAE,EAAE,6BAA6B,IAAI,IAAI;gBACzC,EAAE,EAAE,wBAAwB,IAAI,IAAI;aACrC,CAAC,EAAE,CAAC;IACP,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAAsC,EAAE,UAAU,KAAK,GAAG,EAAE,CAAC;YAChE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAEzF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;IAC1D,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,GAAY,EAAE,IAAY;IAE/C,OAAO,IAAA,QAAC,EAAC;QACP,EAAE,EAAE,aAAa,IAAI,aAAa,aAAa,CAAC,GAAG,CAAC,EAAE;QACtD,EAAE,EAAE,iBAAiB,IAAI,eAAe,aAAa,CAAC,GAAG,CAAC,EAAE;QAC5D,EAAE,EAAE,oBAAoB,IAAI,yBAAyB,aAAa,CAAC,GAAG,CAAC,EAAE;QACzE,EAAE,EAAE,mBAAmB,IAAI,gBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE;QAC/D,EAAE,EAAE,mBAAmB,IAAI,iBAAiB,aAAa,CAAC,GAAG,CAAC,EAAE;KACjE,CAAC,CAAC;AACL,CAAC;AAED,yFAAyF;AAClF,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,KAAc;IAE7D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAE/C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,qBAAqB,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;IAClE,CAAC;AACH,CAAC;AAOD;;;;GAIG;AACI,KAAK,UAAU,YAAY,CAAC,GAAY;IAE7C,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAa,GAAE,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC;YACrC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,CAAC,CAAa,CAAC;QAEhB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,CAAC,EAAE,CAAC;IACzG,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAA,QAAC,EAAC;gBAC7B,EAAE,EAAE,iBAAiB,aAAa,CAAC,GAAG,CAAC,EAAE;gBACzC,EAAE,EAAE,sBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE;gBAC9C,EAAE,EAAE,6BAA6B,aAAa,CAAC,GAAG,CAAC,EAAE;gBACrD,EAAE,EAAE,2BAA2B,aAAa,CAAC,GAAG,CAAC,EAAE;gBACnD,EAAE,EAAE,2BAA2B,aAAa,CAAC,GAAG,CAAC,EAAE;aACpD,CAAC,EAAE,CAAC;IACP,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,GAAY;IAEjC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAY,EAAE,IAAY;IAEvD,MAAM,MAAM,GAAI,GAAsC,EAAE,UAAU,CAAC;IAEnE,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,IAAA,QAAC,EAAC;YACP,EAAE,EAAE,WAAW,IAAI,qBAAqB;YACxC,EAAE,EAAE,WAAW,IAAI,sBAAsB;YACzC,EAAE,EAAE,WAAW,IAAI,yBAAyB;YAC5C,EAAE,EAAE,cAAc,IAAI,kBAAkB;YACxC,EAAE,EAAE,cAAc,IAAI,iBAAiB;SACxC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,OAAO,IAAA,QAAC,EAAC;YACP,EAAE,EAAE,IAAI,IAAI,yEAAyE;YACrF,EAAE,EAAE,IAAI,IAAI,+EAA+E;YAC3F,EAAE,EAAE,IAAI,IAAI,gGAAgG;YAC5G,EAAE,EAAE,IAAI,IAAI,4FAA4F;YACxG,EAAE,EAAE,IAAI,IAAI,6EAA6E;SAC1F,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAA,QAAC,EAAC;QACP,EAAE,EAAE,aAAa,IAAI,aAAa,aAAa,CAAC,GAAG,CAAC,EAAE;QACtD,EAAE,EAAE,oBAAoB,IAAI,eAAe,aAAa,CAAC,GAAG,CAAC,EAAE;QAC/D,EAAE,EAAE,kBAAkB,IAAI,qBAAqB,aAAa,CAAC,GAAG,CAAC,EAAE;QACnE,EAAE,EAAE,sBAAsB,IAAI,gBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE;QAClE,EAAE,EAAE,sBAAsB,IAAI,iBAAiB,aAAa,CAAC,GAAG,CAAC,EAAE;KACpE,CAAC,CAAC;AACL,CAAC"}
|