@webtypen/webframez-core 0.3.49 → 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
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) {
|
|
@@ -253,7 +273,7 @@ class BackupManager {
|
|
|
253
273
|
databases: [],
|
|
254
274
|
};
|
|
255
275
|
this.log(options, "Collecting file sources");
|
|
256
|
-
const fileEntries = normalizeArray(backupType.files).flatMap((source) => this.collectFilesFromSource(source));
|
|
276
|
+
const fileEntries = normalizeArray(backupType.files).flatMap((source) => this.collectFilesFromSource(source, options));
|
|
257
277
|
const fileSize = fileEntries.reduce((sum, entry) => sum + entry.size, 0);
|
|
258
278
|
this.log(options, `Collected ${fileEntries.length} file(s)`, { size: fileSize });
|
|
259
279
|
result.files = fileEntries.map((entry) => ({
|
|
@@ -280,7 +300,7 @@ class BackupManager {
|
|
|
280
300
|
fs_1.default.mkdirSync(contentDir, { recursive: true });
|
|
281
301
|
try {
|
|
282
302
|
this.log(options, `Copying ${fileEntries.length} file(s) into backup work directory`);
|
|
283
|
-
this.copyFiles(fileEntries, contentDir);
|
|
303
|
+
this.copyFiles(fileEntries, contentDir, options);
|
|
284
304
|
this.log(options, "File copy finished");
|
|
285
305
|
for (const source of normalizeArray(backupType.databases)) {
|
|
286
306
|
this.log(options, `Backing up database '${source.connection || "default"}'`);
|
|
@@ -301,6 +321,7 @@ class BackupManager {
|
|
|
301
321
|
backupKey: key,
|
|
302
322
|
backupId: id,
|
|
303
323
|
retention: retention,
|
|
324
|
+
log: (message, payload) => this.log(options, message, payload),
|
|
304
325
|
});
|
|
305
326
|
result.outputs.push(outputResult);
|
|
306
327
|
this.log(options, `Output '${output.driver}' finished with status '${outputResult.status}'`, {
|
|
@@ -309,7 +330,12 @@ class BackupManager {
|
|
|
309
330
|
});
|
|
310
331
|
if (outputResult.status === "success" && (retention === null || retention === void 0 ? void 0 : retention.runAfterBackup)) {
|
|
311
332
|
this.log(options, `Running cleanup for '${output.driver}' output`);
|
|
312
|
-
result.cleanup.push(yield driver.cleanup(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 }));
|
|
313
339
|
this.log(options, `Cleanup for '${output.driver}' output finished`, {
|
|
314
340
|
deleted: ((_b = result.cleanup[result.cleanup.length - 1]) === null || _b === void 0 ? void 0 : _b.deleted.length) || 0,
|
|
315
341
|
});
|
|
@@ -354,6 +380,7 @@ class BackupManager {
|
|
|
354
380
|
backupKey: key,
|
|
355
381
|
backupId: (0, BackupPathUtils_1.backupTimestampId)(),
|
|
356
382
|
retention: retention,
|
|
383
|
+
log: () => null,
|
|
357
384
|
}, options));
|
|
358
385
|
}
|
|
359
386
|
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