@webtypen/webframez-core 0.3.48 → 0.3.50
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/README.md +5 -1
- package/dist/Backup/BackupManager.d.ts +4 -0
- package/dist/Backup/BackupManager.js +78 -29
- package/dist/Backup/BackupTypes.d.ts +5 -0
- package/dist/Backup/Jobs/BackupRunJob.js +1 -0
- package/dist/Backup/OutputDrivers/BaseBackupOutputDriver.d.ts +1 -0
- package/dist/Backup/Utils/ZipWriter.d.ts +10 -0
- package/dist/Backup/Utils/ZipWriter.js +50 -1
- package/dist/Commands/BackupRunCommand.js +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -617,6 +617,8 @@ export default {
|
|
|
617
617
|
outputDir: "storage/backups",
|
|
618
618
|
filename: "{key}_{date}_{time}",
|
|
619
619
|
zip: true,
|
|
620
|
+
zipDriver: "auto",
|
|
621
|
+
zipCompressionLevel: 6,
|
|
620
622
|
cleanupWorkDir: true,
|
|
621
623
|
retention: {
|
|
622
624
|
keepLast: 10,
|
|
@@ -665,10 +667,12 @@ export default {
|
|
|
665
667
|
|
|
666
668
|
Built-in backup commands:
|
|
667
669
|
- `backup:list`
|
|
668
|
-
- `backup:run <key> [--dry-run] [--channel=local]`
|
|
670
|
+
- `backup:run <key> [--dry-run] [--channel=local] [--silent] [--log-interval=1000]`
|
|
669
671
|
- `backup:queue <key> [--worker=worker_1] [--priority=10]`
|
|
670
672
|
- `backup:cleanup <key> [--channel=local] [--dry-run]`
|
|
671
673
|
|
|
674
|
+
ZIP artifacts use the system `zip` binary by default when available and fall back to the built-in Node.js ZIP writer. Set `zipDriver: "system"` to require system zip, `zipDriver: "node"` to force the built-in writer, or `zipDriver: "auto"` for fallback behavior. `zipCompressionLevel` accepts `0` through `9`. Backup runs log file-scan and copy progress every 1000 files by default; change this with `--log-interval=250` or suppress progress with `--silent`.
|
|
675
|
+
|
|
672
676
|
Remote outputs are optional packages and must be registered by the application before the backup config is used:
|
|
673
677
|
|
|
674
678
|
```ts
|
|
@@ -4,11 +4,15 @@ type ResolvedBackupTypeConfig = BackupTypeConfig & {
|
|
|
4
4
|
outputDir: string;
|
|
5
5
|
filename: string;
|
|
6
6
|
zip: boolean;
|
|
7
|
+
zipDriver: "auto" | "system" | "node";
|
|
8
|
+
zipCompressionLevel?: number;
|
|
7
9
|
cleanupWorkDir: boolean;
|
|
8
10
|
retention?: BackupRetentionConfig;
|
|
9
11
|
outputs: BackupOutputConfig[];
|
|
10
12
|
};
|
|
11
13
|
export declare class BackupManager {
|
|
14
|
+
private log;
|
|
15
|
+
private logInterval;
|
|
12
16
|
getConfig(): BackupConfig;
|
|
13
17
|
listTypes(): {
|
|
14
18
|
key: string;
|
|
@@ -40,6 +40,15 @@ function normalizeArray(value) {
|
|
|
40
40
|
return value && Array.isArray(value) ? value : [];
|
|
41
41
|
}
|
|
42
42
|
class BackupManager {
|
|
43
|
+
log(options, message, payload) {
|
|
44
|
+
if ((options === null || options === void 0 ? void 0 : options.silent) || !(options === null || options === void 0 ? void 0 : options.log)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
options.log(message, payload);
|
|
48
|
+
}
|
|
49
|
+
logInterval(options) {
|
|
50
|
+
return (options === null || options === void 0 ? void 0 : options.logInterval) && options.logInterval > 0 ? options.logInterval : 1000;
|
|
51
|
+
}
|
|
43
52
|
getConfig() {
|
|
44
53
|
const config = Config_1.Config.get("backup");
|
|
45
54
|
if (!config || typeof config !== "object") {
|
|
@@ -62,7 +71,7 @@ class BackupManager {
|
|
|
62
71
|
}
|
|
63
72
|
const defaults = config.defaults || {};
|
|
64
73
|
const outputDir = backupType.outputDir || defaults.outputDir || "storage/backups";
|
|
65
|
-
return Object.assign(Object.assign(Object.assign({}, defaults), backupType), { workDir: backupType.workDir || defaults.workDir || "storage/backups/.work", outputDir: outputDir, filename: backupType.filename || defaults.filename || "{key}_{date}_{time}", zip: backupType.zip !== undefined ? backupType.zip : defaults.zip !== undefined ? defaults.zip : true, cleanupWorkDir: backupType.cleanupWorkDir !== undefined
|
|
74
|
+
return Object.assign(Object.assign(Object.assign({}, defaults), backupType), { workDir: backupType.workDir || defaults.workDir || "storage/backups/.work", outputDir: outputDir, filename: backupType.filename || defaults.filename || "{key}_{date}_{time}", zip: backupType.zip !== undefined ? backupType.zip : defaults.zip !== undefined ? defaults.zip : true, zipDriver: backupType.zipDriver || defaults.zipDriver || "auto", zipCompressionLevel: backupType.zipCompressionLevel !== undefined ? backupType.zipCompressionLevel : defaults.zipCompressionLevel, cleanupWorkDir: backupType.cleanupWorkDir !== undefined
|
|
66
75
|
? backupType.cleanupWorkDir
|
|
67
76
|
: defaults.cleanupWorkDir !== undefined
|
|
68
77
|
? defaults.cleanupWorkDir
|
|
@@ -116,7 +125,7 @@ class BackupManager {
|
|
|
116
125
|
return channels.includes(key) || channels.includes(output.driver);
|
|
117
126
|
});
|
|
118
127
|
}
|
|
119
|
-
collectFilesFromSource(source) {
|
|
128
|
+
collectFilesFromSource(source, options) {
|
|
120
129
|
const sourcePath = (0, BackupPathUtils_1.resolveProjectPath)(source.from);
|
|
121
130
|
if (!fs_1.default.existsSync(sourcePath)) {
|
|
122
131
|
if (source.optional) {
|
|
@@ -128,17 +137,28 @@ class BackupManager {
|
|
|
128
137
|
const files = [];
|
|
129
138
|
const sourceBase = sourceStats.isDirectory() ? sourcePath : path_1.default.dirname(sourcePath);
|
|
130
139
|
const toBase = (0, BackupPathUtils_1.normalizeBackupPath)(source.to !== undefined ? source.to : sourceStats.isDirectory() ? path_1.default.basename(source.from) : "");
|
|
140
|
+
const interval = this.logInterval(options);
|
|
141
|
+
let visited = 0;
|
|
142
|
+
let included = 0;
|
|
131
143
|
const addFile = (filepath) => {
|
|
144
|
+
visited += 1;
|
|
132
145
|
const relative = (0, BackupPathUtils_1.normalizeBackupPath)(path_1.default.relative(sourceBase, filepath));
|
|
133
146
|
if (!(0, GlobMatcher_1.matchesBackupGlobs)(relative, source.include, source.exclude)) {
|
|
147
|
+
if (visited % interval === 0) {
|
|
148
|
+
this.log(options, `Scanned ${visited} file(s), included ${included}`);
|
|
149
|
+
}
|
|
134
150
|
return;
|
|
135
151
|
}
|
|
152
|
+
included += 1;
|
|
136
153
|
files.push({
|
|
137
154
|
source: filepath,
|
|
138
155
|
relative: relative,
|
|
139
156
|
target: (0, BackupPathUtils_1.normalizeBackupPath)(path_1.default.join(toBase, relative)),
|
|
140
157
|
size: fs_1.default.statSync(filepath).size,
|
|
141
158
|
});
|
|
159
|
+
if (visited % interval === 0) {
|
|
160
|
+
this.log(options, `Scanned ${visited} file(s), included ${included}`);
|
|
161
|
+
}
|
|
142
162
|
};
|
|
143
163
|
const walk = (dir) => {
|
|
144
164
|
for (const entry of fs_1.default.readdirSync(dir)) {
|
|
@@ -160,11 +180,17 @@ class BackupManager {
|
|
|
160
180
|
}
|
|
161
181
|
return files;
|
|
162
182
|
}
|
|
163
|
-
copyFiles(files, contentDir) {
|
|
164
|
-
|
|
183
|
+
copyFiles(files, contentDir, options) {
|
|
184
|
+
const interval = this.logInterval(options);
|
|
185
|
+
for (let index = 0; index < files.length; index += 1) {
|
|
186
|
+
const file = files[index];
|
|
165
187
|
const targetPath = path_1.default.join(contentDir, file.target);
|
|
166
188
|
fs_1.default.mkdirSync(path_1.default.dirname(targetPath), { recursive: true });
|
|
167
189
|
fs_1.default.copyFileSync(file.source, targetPath);
|
|
190
|
+
const count = index + 1;
|
|
191
|
+
if (count % interval === 0 || count === files.length) {
|
|
192
|
+
this.log(options, `Copied ${count}/${files.length} file(s)`);
|
|
193
|
+
}
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
196
|
backupDatabase(source, contentDir, context) {
|
|
@@ -191,28 +217,21 @@ class BackupManager {
|
|
|
191
217
|
};
|
|
192
218
|
});
|
|
193
219
|
}
|
|
194
|
-
buildArtifact(key, backupType, workRunDir, contentDir, date, id) {
|
|
220
|
+
buildArtifact(key, backupType, workRunDir, contentDir, date, id, options) {
|
|
195
221
|
const filename = (0, BackupPathUtils_1.formatBackupFilename)(backupType.filename, { key, date, id });
|
|
196
222
|
if (backupType.zip) {
|
|
197
223
|
const zipPath = path_1.default.join(workRunDir, `${filename}.zip`);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
name: (0, BackupPathUtils_1.normalizeBackupPath)(path_1.default.relative(contentDir, filepath)),
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
walk(contentDir);
|
|
215
|
-
(0, ZipWriter_1.createZipFile)(zipPath, files);
|
|
224
|
+
this.log(options, `Creating ZIP artifact '${path_1.default.basename(zipPath)}'`, {
|
|
225
|
+
driver: backupType.zipDriver,
|
|
226
|
+
compressionLevel: backupType.zipCompressionLevel,
|
|
227
|
+
});
|
|
228
|
+
const zipResult = (0, ZipWriter_1.createZipFileFromDirectory)(zipPath, contentDir, {
|
|
229
|
+
driver: backupType.zipDriver,
|
|
230
|
+
compressionLevel: backupType.zipCompressionLevel,
|
|
231
|
+
});
|
|
232
|
+
this.log(options, `ZIP artifact created using ${zipResult.driver} zip`, {
|
|
233
|
+
path: zipPath,
|
|
234
|
+
});
|
|
216
235
|
return {
|
|
217
236
|
path: zipPath,
|
|
218
237
|
filename: path_1.default.basename(zipPath),
|
|
@@ -228,10 +247,11 @@ class BackupManager {
|
|
|
228
247
|
};
|
|
229
248
|
}
|
|
230
249
|
run(key, options) {
|
|
231
|
-
var _a, _b;
|
|
250
|
+
var _a, _b, _c;
|
|
232
251
|
return __awaiter(this, void 0, void 0, function* () {
|
|
233
252
|
const backupType = this.resolveType(key);
|
|
234
253
|
this.ensureActive(key, backupType);
|
|
254
|
+
this.log(options, `Starting backup '${key}'`);
|
|
235
255
|
const now = new Date();
|
|
236
256
|
const id = (0, BackupPathUtils_1.backupTimestampId)(now);
|
|
237
257
|
const workRoot = (0, BackupPathUtils_1.resolveProjectPath)(backupType.workDir);
|
|
@@ -252,13 +272,17 @@ class BackupManager {
|
|
|
252
272
|
files: [],
|
|
253
273
|
databases: [],
|
|
254
274
|
};
|
|
255
|
-
|
|
275
|
+
this.log(options, "Collecting file sources");
|
|
276
|
+
const fileEntries = normalizeArray(backupType.files).flatMap((source) => this.collectFilesFromSource(source, options));
|
|
277
|
+
const fileSize = fileEntries.reduce((sum, entry) => sum + entry.size, 0);
|
|
278
|
+
this.log(options, `Collected ${fileEntries.length} file(s)`, { size: fileSize });
|
|
256
279
|
result.files = fileEntries.map((entry) => ({
|
|
257
280
|
from: entry.source,
|
|
258
281
|
to: entry.target,
|
|
259
282
|
size: entry.size,
|
|
260
283
|
}));
|
|
261
284
|
if (options === null || options === void 0 ? void 0 : options.dryRun) {
|
|
285
|
+
this.log(options, "Dry run enabled; skipping artifact and output writes");
|
|
262
286
|
result.outputs = outputs.map((output) => ({
|
|
263
287
|
driver: output.driver,
|
|
264
288
|
status: "skipped",
|
|
@@ -275,28 +299,50 @@ class BackupManager {
|
|
|
275
299
|
fs_1.default.rmSync(workRunDir, { recursive: true, force: true });
|
|
276
300
|
fs_1.default.mkdirSync(contentDir, { recursive: true });
|
|
277
301
|
try {
|
|
278
|
-
this.
|
|
302
|
+
this.log(options, `Copying ${fileEntries.length} file(s) into backup work directory`);
|
|
303
|
+
this.copyFiles(fileEntries, contentDir, options);
|
|
304
|
+
this.log(options, "File copy finished");
|
|
279
305
|
for (const source of normalizeArray(backupType.databases)) {
|
|
306
|
+
this.log(options, `Backing up database '${source.connection || "default"}'`);
|
|
280
307
|
result.databases.push(yield this.backupDatabase(source, contentDir, { backupKey: key, backupId: id }));
|
|
308
|
+
this.log(options, `Database '${source.connection || "default"}' backup finished`);
|
|
281
309
|
}
|
|
282
|
-
result.artifact = this.buildArtifact(key, backupType, workRunDir, contentDir, now, id);
|
|
310
|
+
result.artifact = this.buildArtifact(key, backupType, workRunDir, contentDir, now, id, options);
|
|
283
311
|
const manifestPath = path_1.default.join(workRunDir, `${result.artifact.filename}.run-manifest.json`);
|
|
284
312
|
result.manifestPath = manifestPath;
|
|
285
313
|
for (const output of outputs) {
|
|
286
314
|
const retention = mergeRetention((_a = this.getConfig().defaults) === null || _a === void 0 ? void 0 : _a.retention, backupType.retention, output.retention);
|
|
287
315
|
const driver = BackupOutputDrivers_1.BackupOutputDrivers.get(output.driver);
|
|
316
|
+
this.log(options, `Writing artifact to '${output.driver}' output`, {
|
|
317
|
+
path: output.path,
|
|
318
|
+
folderPath: output.folderPath,
|
|
319
|
+
});
|
|
288
320
|
const outputResult = yield driver.write(result.artifact, output, {
|
|
289
321
|
backupKey: key,
|
|
290
322
|
backupId: id,
|
|
291
323
|
retention: retention,
|
|
324
|
+
log: (message, payload) => this.log(options, message, payload),
|
|
292
325
|
});
|
|
293
326
|
result.outputs.push(outputResult);
|
|
327
|
+
this.log(options, `Output '${output.driver}' finished with status '${outputResult.status}'`, {
|
|
328
|
+
path: outputResult.path,
|
|
329
|
+
error: outputResult.error,
|
|
330
|
+
});
|
|
294
331
|
if (outputResult.status === "success" && (retention === null || retention === void 0 ? void 0 : retention.runAfterBackup)) {
|
|
295
|
-
|
|
332
|
+
this.log(options, `Running cleanup for '${output.driver}' output`);
|
|
333
|
+
result.cleanup.push(yield driver.cleanup(output, {
|
|
334
|
+
backupKey: key,
|
|
335
|
+
backupId: id,
|
|
336
|
+
retention: retention,
|
|
337
|
+
log: (message, payload) => this.log(options, message, payload),
|
|
338
|
+
}, { dryRun: false }));
|
|
339
|
+
this.log(options, `Cleanup for '${output.driver}' output finished`, {
|
|
340
|
+
deleted: ((_b = result.cleanup[result.cleanup.length - 1]) === null || _b === void 0 ? void 0 : _b.deleted.length) || 0,
|
|
341
|
+
});
|
|
296
342
|
}
|
|
297
343
|
}
|
|
298
344
|
result.endedAt = new Date().toISOString();
|
|
299
|
-
const outputManifestPath = (
|
|
345
|
+
const outputManifestPath = (_c = result.outputs.find((output) => output.payload && output.payload.manifestPath)) === null || _c === void 0 ? void 0 : _c.payload.manifestPath;
|
|
300
346
|
if (outputManifestPath) {
|
|
301
347
|
result.manifestPath = outputManifestPath;
|
|
302
348
|
}
|
|
@@ -307,10 +353,12 @@ class BackupManager {
|
|
|
307
353
|
fs_1.default.writeFileSync(output.payload.manifestPath, manifestJson, "utf-8");
|
|
308
354
|
}
|
|
309
355
|
}
|
|
356
|
+
this.log(options, `Backup '${key}' finished`);
|
|
310
357
|
return result;
|
|
311
358
|
}
|
|
312
359
|
finally {
|
|
313
360
|
if (backupType.cleanupWorkDir) {
|
|
361
|
+
this.log(options, "Cleaning backup work directory");
|
|
314
362
|
fs_1.default.rmSync(workRunDir, { recursive: true, force: true });
|
|
315
363
|
}
|
|
316
364
|
}
|
|
@@ -332,6 +380,7 @@ class BackupManager {
|
|
|
332
380
|
backupKey: key,
|
|
333
381
|
backupId: (0, BackupPathUtils_1.backupTimestampId)(),
|
|
334
382
|
retention: retention,
|
|
383
|
+
log: () => null,
|
|
335
384
|
}, options));
|
|
336
385
|
}
|
|
337
386
|
return results;
|
|
@@ -32,6 +32,8 @@ export type BackupDefaultsConfig = {
|
|
|
32
32
|
outputDir?: string;
|
|
33
33
|
filename?: string;
|
|
34
34
|
zip?: boolean;
|
|
35
|
+
zipDriver?: "auto" | "system" | "node";
|
|
36
|
+
zipCompressionLevel?: number;
|
|
35
37
|
cleanupWorkDir?: boolean;
|
|
36
38
|
retention?: BackupRetentionConfig;
|
|
37
39
|
};
|
|
@@ -100,6 +102,9 @@ export type BackupRunResult = {
|
|
|
100
102
|
export type BackupRunOptions = {
|
|
101
103
|
dryRun?: boolean;
|
|
102
104
|
channels?: string[];
|
|
105
|
+
silent?: boolean;
|
|
106
|
+
logInterval?: number;
|
|
107
|
+
log?: (message: string, payload?: any) => void;
|
|
103
108
|
};
|
|
104
109
|
export type BackupCleanupOptions = {
|
|
105
110
|
dryRun?: boolean;
|
|
@@ -22,6 +22,7 @@ class BackupRunJob extends BaseQueueJob_1.BaseQueueJob {
|
|
|
22
22
|
this.log(`Starting backup '${backupKey}'`);
|
|
23
23
|
const result = yield new BackupManager_1.BackupManager().run(backupKey, {
|
|
24
24
|
channels: job.payload.channels,
|
|
25
|
+
log: (message, payload) => this.log(message, payload),
|
|
25
26
|
});
|
|
26
27
|
this.log(`Backup '${backupKey}' finished`, {
|
|
27
28
|
id: result.id,
|
|
@@ -3,6 +3,7 @@ export type BackupOutputDriverContext = {
|
|
|
3
3
|
backupKey: string;
|
|
4
4
|
backupId: string;
|
|
5
5
|
retention?: BackupRetentionConfig;
|
|
6
|
+
log?: (message: string, payload?: any) => void;
|
|
6
7
|
};
|
|
7
8
|
export interface BaseBackupOutputDriver {
|
|
8
9
|
write(artifact: BackupArtifact, output: BackupOutputConfig, context: BackupOutputDriverContext): Promise<BackupOutputResult>;
|
|
@@ -2,3 +2,13 @@ export declare function createZipFile(zipPath: string, files: Array<{
|
|
|
2
2
|
source: string;
|
|
3
3
|
name: string;
|
|
4
4
|
}>): string;
|
|
5
|
+
export declare function createZipFileFromDirectory(zipPath: string, sourceDir: string, options?: {
|
|
6
|
+
driver?: "auto" | "system" | "node";
|
|
7
|
+
compressionLevel?: number;
|
|
8
|
+
}): {
|
|
9
|
+
path: string;
|
|
10
|
+
driver: "system";
|
|
11
|
+
} | {
|
|
12
|
+
path: string;
|
|
13
|
+
driver: "node";
|
|
14
|
+
};
|
|
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createZipFile = void 0;
|
|
6
|
+
exports.createZipFileFromDirectory = exports.createZipFile = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const zlib_1 = __importDefault(require("zlib"));
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
10
11
|
const BackupPathUtils_1 = require("./BackupPathUtils");
|
|
11
12
|
const crcTable = (() => {
|
|
12
13
|
const table = [];
|
|
@@ -118,3 +119,51 @@ function createZipFile(zipPath, files) {
|
|
|
118
119
|
return zipPath;
|
|
119
120
|
}
|
|
120
121
|
exports.createZipFile = createZipFile;
|
|
122
|
+
function collectZipFiles(sourceDir) {
|
|
123
|
+
const files = [];
|
|
124
|
+
const walk = (dir) => {
|
|
125
|
+
for (const entry of fs_1.default.readdirSync(dir)) {
|
|
126
|
+
const filepath = path_1.default.join(dir, entry);
|
|
127
|
+
const stats = fs_1.default.statSync(filepath);
|
|
128
|
+
if (stats.isDirectory()) {
|
|
129
|
+
walk(filepath);
|
|
130
|
+
}
|
|
131
|
+
else if (stats.isFile()) {
|
|
132
|
+
files.push({
|
|
133
|
+
source: filepath,
|
|
134
|
+
name: (0, BackupPathUtils_1.normalizeBackupPath)(path_1.default.relative(sourceDir, filepath)),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
walk(sourceDir);
|
|
140
|
+
return files;
|
|
141
|
+
}
|
|
142
|
+
function compressionArg(level) {
|
|
143
|
+
if (level === undefined || level === null || Number.isNaN(level)) {
|
|
144
|
+
return "-6";
|
|
145
|
+
}
|
|
146
|
+
const normalized = Math.max(0, Math.min(9, Math.round(level)));
|
|
147
|
+
return `-${normalized}`;
|
|
148
|
+
}
|
|
149
|
+
function createZipFileFromDirectory(zipPath, sourceDir, options) {
|
|
150
|
+
var _a;
|
|
151
|
+
fs_1.default.mkdirSync(path_1.default.dirname(zipPath), { recursive: true });
|
|
152
|
+
if ((options === null || options === void 0 ? void 0 : options.driver) !== "node") {
|
|
153
|
+
const args = ["-q", compressionArg(options === null || options === void 0 ? void 0 : options.compressionLevel), "-r", zipPath, "."];
|
|
154
|
+
const result = (0, child_process_1.spawnSync)("zip", args, {
|
|
155
|
+
cwd: sourceDir,
|
|
156
|
+
encoding: "utf-8",
|
|
157
|
+
});
|
|
158
|
+
if (result.status === 0 && fs_1.default.existsSync(zipPath)) {
|
|
159
|
+
return { path: zipPath, driver: "system" };
|
|
160
|
+
}
|
|
161
|
+
if ((options === null || options === void 0 ? void 0 : options.driver) === "system") {
|
|
162
|
+
const detail = result.stderr || result.stdout || ((_a = result.error) === null || _a === void 0 ? void 0 : _a.message) || "unknown error";
|
|
163
|
+
throw new Error(`System zip failed: ${detail}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
createZipFile(zipPath, collectZipFiles(sourceDir));
|
|
167
|
+
return { path: zipPath, driver: "node" };
|
|
168
|
+
}
|
|
169
|
+
exports.createZipFileFromDirectory = createZipFileFromDirectory;
|
|
@@ -21,6 +21,13 @@ function parseChannels(value) {
|
|
|
21
21
|
.map((entry) => entry.trim())
|
|
22
22
|
.filter((entry) => entry !== "");
|
|
23
23
|
}
|
|
24
|
+
function parsePositiveNumber(value) {
|
|
25
|
+
if (!value || typeof value !== "string") {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const parsed = parseInt(value, 10);
|
|
29
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
30
|
+
}
|
|
24
31
|
class BackupRunCommand extends ConsoleCommand_1.ConsoleCommand {
|
|
25
32
|
handle() {
|
|
26
33
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -32,7 +39,13 @@ class BackupRunCommand extends ConsoleCommand_1.ConsoleCommand {
|
|
|
32
39
|
const result = yield new BackupManager_1.BackupManager().run(key, {
|
|
33
40
|
dryRun: this.getOption("dry-run") === true,
|
|
34
41
|
channels: parseChannels(this.getOption("channel")),
|
|
42
|
+
silent: this.getOption("silent") === true,
|
|
43
|
+
logInterval: parsePositiveNumber(this.getOption("log-interval")),
|
|
44
|
+
log: (message) => this.writeln(` [color=grey]${message}[/color]`),
|
|
35
45
|
});
|
|
46
|
+
if (this.getOption("silent") === true) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
36
49
|
if (result.dryRun) {
|
|
37
50
|
this.info(`Dry run for backup '${key}' finished.`);
|
|
38
51
|
}
|
package/package.json
CHANGED