@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
 
@@ -12,6 +12,7 @@ type ResolvedBackupTypeConfig = BackupTypeConfig & {
12
12
  };
13
13
  export declare class BackupManager {
14
14
  private log;
15
+ private logInterval;
15
16
  getConfig(): BackupConfig;
16
17
  listTypes(): {
17
18
  key: string;
@@ -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
- for (const file of files) {
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, { backupKey: key, backupId: id }));
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, { backupKey: key, backupId: id, retention: retention }, { dryRun: false }));
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;
@@ -103,6 +103,7 @@ export type BackupRunOptions = {
103
103
  dryRun?: boolean;
104
104
  channels?: string[];
105
105
  silent?: boolean;
106
+ logInterval?: number;
106
107
  log?: (message: string, payload?: any) => void;
107
108
  };
108
109
  export type BackupCleanupOptions = {
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.3.49",
3
+ "version": "0.3.51",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",