@webtypen/webframez-core 0.3.49 → 0.3.51
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
CHANGED
|
@@ -667,11 +667,11 @@ export default {
|
|
|
667
667
|
|
|
668
668
|
Built-in backup commands:
|
|
669
669
|
- `backup:list`
|
|
670
|
-
- `backup:run <key> [--dry-run] [--channel=local] [--silent]`
|
|
670
|
+
- `backup:run <key> [--dry-run] [--channel=local] [--silent] [--log-interval=1000]`
|
|
671
671
|
- `backup:queue <key> [--worker=worker_1] [--priority=10]`
|
|
672
672
|
- `backup:cleanup <key> [--channel=local] [--dry-run]`
|
|
673
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`.
|
|
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
675
|
|
|
676
676
|
Remote outputs are optional packages and must be registered by the application before the backup config is used:
|
|
677
677
|
|
|
@@ -46,6 +46,9 @@ class BackupManager {
|
|
|
46
46
|
}
|
|
47
47
|
options.log(message, payload);
|
|
48
48
|
}
|
|
49
|
+
logInterval(options) {
|
|
50
|
+
return (options === null || options === void 0 ? void 0 : options.logInterval) && options.logInterval > 0 ? options.logInterval : 1000;
|
|
51
|
+
}
|
|
49
52
|
getConfig() {
|
|
50
53
|
const config = Config_1.Config.get("backup");
|
|
51
54
|
if (!config || typeof config !== "object") {
|
|
@@ -122,7 +125,7 @@ class BackupManager {
|
|
|
122
125
|
return channels.includes(key) || channels.includes(output.driver);
|
|
123
126
|
});
|
|
124
127
|
}
|
|
125
|
-
collectFilesFromSource(source) {
|
|
128
|
+
collectFilesFromSource(source, options) {
|
|
126
129
|
const sourcePath = (0, BackupPathUtils_1.resolveProjectPath)(source.from);
|
|
127
130
|
if (!fs_1.default.existsSync(sourcePath)) {
|
|
128
131
|
if (source.optional) {
|
|
@@ -134,17 +137,28 @@ class BackupManager {
|
|
|
134
137
|
const files = [];
|
|
135
138
|
const sourceBase = sourceStats.isDirectory() ? sourcePath : path_1.default.dirname(sourcePath);
|
|
136
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;
|
|
137
143
|
const addFile = (filepath) => {
|
|
144
|
+
visited += 1;
|
|
138
145
|
const relative = (0, BackupPathUtils_1.normalizeBackupPath)(path_1.default.relative(sourceBase, filepath));
|
|
139
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
|
+
}
|
|
140
150
|
return;
|
|
141
151
|
}
|
|
152
|
+
included += 1;
|
|
142
153
|
files.push({
|
|
143
154
|
source: filepath,
|
|
144
155
|
relative: relative,
|
|
145
156
|
target: (0, BackupPathUtils_1.normalizeBackupPath)(path_1.default.join(toBase, relative)),
|
|
146
157
|
size: fs_1.default.statSync(filepath).size,
|
|
147
158
|
});
|
|
159
|
+
if (visited % interval === 0) {
|
|
160
|
+
this.log(options, `Scanned ${visited} file(s), included ${included}`);
|
|
161
|
+
}
|
|
148
162
|
};
|
|
149
163
|
const walk = (dir) => {
|
|
150
164
|
for (const entry of fs_1.default.readdirSync(dir)) {
|
|
@@ -166,11 +180,17 @@ class BackupManager {
|
|
|
166
180
|
}
|
|
167
181
|
return files;
|
|
168
182
|
}
|
|
169
|
-
copyFiles(files, contentDir) {
|
|
170
|
-
|
|
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];
|
|
171
187
|
const targetPath = path_1.default.join(contentDir, file.target);
|
|
172
188
|
fs_1.default.mkdirSync(path_1.default.dirname(targetPath), { recursive: true });
|
|
173
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
|
+
}
|
|
174
194
|
}
|
|
175
195
|
}
|
|
176
196
|
backupDatabase(source, contentDir, context) {
|
|
@@ -189,6 +209,7 @@ class BackupManager {
|
|
|
189
209
|
options: source.options || {},
|
|
190
210
|
backupKey: context.backupKey,
|
|
191
211
|
backupId: context.backupId,
|
|
212
|
+
log: context.log,
|
|
192
213
|
});
|
|
193
214
|
return {
|
|
194
215
|
connection: source.connection,
|
|
@@ -253,7 +274,7 @@ class BackupManager {
|
|
|
253
274
|
databases: [],
|
|
254
275
|
};
|
|
255
276
|
this.log(options, "Collecting file sources");
|
|
256
|
-
const fileEntries = normalizeArray(backupType.files).flatMap((source) => this.collectFilesFromSource(source));
|
|
277
|
+
const fileEntries = normalizeArray(backupType.files).flatMap((source) => this.collectFilesFromSource(source, options));
|
|
257
278
|
const fileSize = fileEntries.reduce((sum, entry) => sum + entry.size, 0);
|
|
258
279
|
this.log(options, `Collected ${fileEntries.length} file(s)`, { size: fileSize });
|
|
259
280
|
result.files = fileEntries.map((entry) => ({
|
|
@@ -280,11 +301,15 @@ class BackupManager {
|
|
|
280
301
|
fs_1.default.mkdirSync(contentDir, { recursive: true });
|
|
281
302
|
try {
|
|
282
303
|
this.log(options, `Copying ${fileEntries.length} file(s) into backup work directory`);
|
|
283
|
-
this.copyFiles(fileEntries, contentDir);
|
|
304
|
+
this.copyFiles(fileEntries, contentDir, options);
|
|
284
305
|
this.log(options, "File copy finished");
|
|
285
306
|
for (const source of normalizeArray(backupType.databases)) {
|
|
286
307
|
this.log(options, `Backing up database '${source.connection || "default"}'`);
|
|
287
|
-
result.databases.push(yield this.backupDatabase(source, contentDir, {
|
|
308
|
+
result.databases.push(yield this.backupDatabase(source, contentDir, {
|
|
309
|
+
backupKey: key,
|
|
310
|
+
backupId: id,
|
|
311
|
+
log: (message, payload) => this.log(options, message, payload),
|
|
312
|
+
}));
|
|
288
313
|
this.log(options, `Database '${source.connection || "default"}' backup finished`);
|
|
289
314
|
}
|
|
290
315
|
result.artifact = this.buildArtifact(key, backupType, workRunDir, contentDir, now, id, options);
|
|
@@ -301,6 +326,7 @@ class BackupManager {
|
|
|
301
326
|
backupKey: key,
|
|
302
327
|
backupId: id,
|
|
303
328
|
retention: retention,
|
|
329
|
+
log: (message, payload) => this.log(options, message, payload),
|
|
304
330
|
});
|
|
305
331
|
result.outputs.push(outputResult);
|
|
306
332
|
this.log(options, `Output '${output.driver}' finished with status '${outputResult.status}'`, {
|
|
@@ -309,7 +335,12 @@ class BackupManager {
|
|
|
309
335
|
});
|
|
310
336
|
if (outputResult.status === "success" && (retention === null || retention === void 0 ? void 0 : retention.runAfterBackup)) {
|
|
311
337
|
this.log(options, `Running cleanup for '${output.driver}' output`);
|
|
312
|
-
result.cleanup.push(yield driver.cleanup(output, {
|
|
338
|
+
result.cleanup.push(yield driver.cleanup(output, {
|
|
339
|
+
backupKey: key,
|
|
340
|
+
backupId: id,
|
|
341
|
+
retention: retention,
|
|
342
|
+
log: (message, payload) => this.log(options, message, payload),
|
|
343
|
+
}, { dryRun: false }));
|
|
313
344
|
this.log(options, `Cleanup for '${output.driver}' output finished`, {
|
|
314
345
|
deleted: ((_b = result.cleanup[result.cleanup.length - 1]) === null || _b === void 0 ? void 0 : _b.deleted.length) || 0,
|
|
315
346
|
});
|
|
@@ -354,6 +385,7 @@ class BackupManager {
|
|
|
354
385
|
backupKey: key,
|
|
355
386
|
backupId: (0, BackupPathUtils_1.backupTimestampId)(),
|
|
356
387
|
retention: retention,
|
|
388
|
+
log: () => null,
|
|
357
389
|
}, options));
|
|
358
390
|
}
|
|
359
391
|
return results;
|
|
@@ -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>;
|
|
@@ -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* () {
|
|
@@ -33,6 +40,7 @@ class BackupRunCommand extends ConsoleCommand_1.ConsoleCommand {
|
|
|
33
40
|
dryRun: this.getOption("dry-run") === true,
|
|
34
41
|
channels: parseChannels(this.getOption("channel")),
|
|
35
42
|
silent: this.getOption("silent") === true,
|
|
43
|
+
logInterval: parsePositiveNumber(this.getOption("log-interval")),
|
|
36
44
|
log: (message) => this.writeln(` [color=grey]${message}[/color]`),
|
|
37
45
|
});
|
|
38
46
|
if (this.getOption("silent") === true) {
|
package/package.json
CHANGED