@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
 
@@ -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) {
@@ -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, { backupKey: key, backupId: id, retention: retention }, { dryRun: false }));
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;
@@ -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.50",
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",