@tachybase/module-backup 1.0.18 → 1.1.0

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.
Files changed (40) hide show
  1. package/dist/client/AutoBackup.d.ts +1 -0
  2. package/dist/client/collections/autoBackup.d.ts +3 -0
  3. package/dist/client/components/RepeatField.d.ts +4 -0
  4. package/dist/client/components/locale/Cron.zh-CN.d.ts +34 -0
  5. package/dist/client/cron-jobs-table/AutoBackupTable.d.ts +1 -0
  6. package/dist/client/cron-jobs-table/AutoBackupTable.schema.d.ts +629 -0
  7. package/dist/client/index.js +30 -1
  8. package/dist/client/locale/index.d.ts +4 -1
  9. package/dist/constants.d.ts +1 -0
  10. package/dist/constants.js +27 -0
  11. package/dist/externalVersion.js +7 -6
  12. package/dist/locale/zh-CN.json +29 -0
  13. package/dist/node_modules/@hapi/topo/package.json +1 -1
  14. package/dist/node_modules/archiver/package.json +1 -1
  15. package/dist/node_modules/cron-parser/LICENSE +21 -0
  16. package/dist/node_modules/cron-parser/lib/date.js +252 -0
  17. package/dist/node_modules/cron-parser/lib/expression.js +1002 -0
  18. package/dist/node_modules/cron-parser/lib/field_compactor.js +70 -0
  19. package/dist/node_modules/cron-parser/lib/field_stringify.js +58 -0
  20. package/dist/node_modules/cron-parser/lib/parser.js +1 -0
  21. package/dist/node_modules/cron-parser/package.json +1 -0
  22. package/dist/node_modules/cron-parser/types/common.d.ts +131 -0
  23. package/dist/node_modules/cron-parser/types/index.d.ts +45 -0
  24. package/dist/node_modules/cron-parser/types/ts3/index.d.ts +28 -0
  25. package/dist/node_modules/mkdirp/package.json +1 -1
  26. package/dist/node_modules/semver/package.json +1 -1
  27. package/dist/node_modules/yauzl/package.json +1 -1
  28. package/dist/server/collections/autoBackup.d.ts +2 -0
  29. package/dist/server/collections/autoBackup.js +64 -0
  30. package/dist/server/commands/backup-command.d.ts +2 -0
  31. package/dist/server/commands/backup-command.js +78 -0
  32. package/dist/server/dumper.d.ts +1 -1
  33. package/dist/server/dumper.js +1 -0
  34. package/dist/server/model/AutoBackupModel.d.ts +15 -0
  35. package/dist/server/model/AutoBackupModel.js +29 -0
  36. package/dist/server/server.d.ts +9 -0
  37. package/dist/server/server.js +182 -3
  38. package/dist/server/utils/files.d.ts +6 -0
  39. package/dist/server/utils/files.js +59 -0
  40. package/package.json +14 -9
@@ -0,0 +1,2 @@
1
+ import { CollectionOptions } from '@tachybase/database';
2
+ export default function (): CollectionOptions;
@@ -0,0 +1,64 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var autoBackup_exports = {};
19
+ __export(autoBackup_exports, {
20
+ default: () => autoBackup_default
21
+ });
22
+ module.exports = __toCommonJS(autoBackup_exports);
23
+ var import_constants = require("../../constants");
24
+ function autoBackup_default() {
25
+ return {
26
+ dumpRules: "required",
27
+ name: import_constants.COLLECTION_AUTOBACKUP,
28
+ shared: true,
29
+ createdAt: true,
30
+ updatedAt: true,
31
+ createdBy: true,
32
+ updatedBy: true,
33
+ fields: [
34
+ {
35
+ type: "string",
36
+ name: "title",
37
+ required: true
38
+ },
39
+ {
40
+ type: "boolean",
41
+ name: "enabled",
42
+ defaultValue: false
43
+ },
44
+ {
45
+ type: "string",
46
+ name: "repeat"
47
+ },
48
+ {
49
+ type: "array",
50
+ name: "dumpRules"
51
+ },
52
+ {
53
+ type: "integer",
54
+ name: "maxNumber"
55
+ },
56
+ {
57
+ type: "encryption",
58
+ name: "password",
59
+ interface: "encryption",
60
+ iv: "welljzlyq2p2439v"
61
+ }
62
+ ]
63
+ };
64
+ }
@@ -0,0 +1,2 @@
1
+ import { Application } from '@tachybase/server';
2
+ export default function addBackupCommand(app: Application): void;
@@ -0,0 +1,78 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var backup_command_exports = {};
29
+ __export(backup_command_exports, {
30
+ default: () => addBackupCommand
31
+ });
32
+ module.exports = __toCommonJS(backup_command_exports);
33
+ var import_promises = __toESM(require("fs/promises"));
34
+ var import_server = require("@tachybase/server");
35
+ var import_dumper = require("../dumper");
36
+ function addBackupCommand(app) {
37
+ app.command("backup").ipc().option("-a, --app <appName>", "sub app name if you want to backup").option(
38
+ "-g, --groups <groups>",
39
+ "groups to backup",
40
+ (value, previous) => {
41
+ return previous.concat([value]);
42
+ },
43
+ []
44
+ ).action(async (options) => {
45
+ let backupApp = app;
46
+ if (options.app) {
47
+ if (!await app.db.getCollection("applications").repository.findOne({
48
+ filter: { name: options.app }
49
+ })) {
50
+ await app.db.getCollection("applications").repository.create({
51
+ values: {
52
+ name: options.app
53
+ }
54
+ });
55
+ }
56
+ const subApp = await import_server.AppSupervisor.getInstance().getApp(options.app);
57
+ if (!subApp) {
58
+ app.logger.error(`app ${options.app} not found`);
59
+ await app.stop();
60
+ return;
61
+ }
62
+ backupApp = subApp;
63
+ }
64
+ const groups = new Set(options.groups);
65
+ groups.add("required");
66
+ const dumper = new import_dumper.Dumper(app);
67
+ const appName = backupApp.name;
68
+ const backupFileName = import_dumper.Dumper.generateFileName();
69
+ const tmpFile = await dumper.writeLockFile(backupFileName, appName);
70
+ await dumper.runDumpTask({
71
+ groups,
72
+ appName,
73
+ fileName: backupFileName
74
+ });
75
+ await import_promises.default.unlink(tmpFile);
76
+ backupApp.logger.info(`${appName} backup into ${backupFileName}!`);
77
+ });
78
+ }
@@ -54,7 +54,7 @@ export declare class Dumper extends AppMigrator {
54
54
  }): Promise<string[]>;
55
55
  backUpFilePath(fileName: string, appName?: string): string;
56
56
  lockFilePath(fileName: string, appName?: string): string;
57
- writeLockFile(fileName: string, appName?: string): Promise<void>;
57
+ writeLockFile(fileName: string, appName?: string): Promise<string>;
58
58
  cleanLockFile(fileName: string, appName: string): Promise<void>;
59
59
  getLockFile(appName: string): Promise<string>;
60
60
  runDumpTask(options: DumpOptions): Promise<void>;
@@ -189,6 +189,7 @@ const _Dumper = class _Dumper extends import_app_migrator.AppMigrator {
189
189
  await (0, import_mkdirp.default)(dirname);
190
190
  const filePath = this.lockFilePath(fileName, appName);
191
191
  await import_promises.default.writeFile(filePath, "lock", "utf8");
192
+ return filePath;
192
193
  }
193
194
  async cleanLockFile(fileName, appName) {
194
195
  const filePath = this.lockFilePath(fileName, appName);
@@ -0,0 +1,15 @@
1
+ import { Model } from '@tachybase/database';
2
+ export declare class AutoBackupModel extends Model {
3
+ id: number;
4
+ title: string;
5
+ startsOn: Date;
6
+ endsOn?: Date;
7
+ repeat: string;
8
+ enabled: boolean;
9
+ createdAt: Date;
10
+ updatedAt: Date;
11
+ allExecuted: number;
12
+ password: string;
13
+ dumpRules: string;
14
+ maxNumber: number;
15
+ }
@@ -0,0 +1,29 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var AutoBackupModel_exports = {};
19
+ __export(AutoBackupModel_exports, {
20
+ AutoBackupModel: () => AutoBackupModel
21
+ });
22
+ module.exports = __toCommonJS(AutoBackupModel_exports);
23
+ var import_database = require("@tachybase/database");
24
+ class AutoBackupModel extends import_database.Model {
25
+ }
26
+ // Annotate the CommonJS export names for ESM import in node:
27
+ 0 && (module.exports = {
28
+ AutoBackupModel
29
+ });
@@ -1,7 +1,16 @@
1
1
  import { Plugin } from '@tachybase/server';
2
+ import { AutoBackupModel } from './model/AutoBackupModel';
2
3
  export default class PluginBackupRestoreServer extends Plugin {
4
+ private static readonly inspectFields;
3
5
  beforeLoad(): void;
6
+ private timers;
4
7
  load(): Promise<void>;
8
+ inspect(cronJobs: AutoBackupModel[]): void;
9
+ getNextTime(cronJob: AutoBackupModel, currentDate: Date, nextSecond?: boolean): number;
10
+ schedule(cronJob: AutoBackupModel, nextTime: number, toggle?: boolean): void;
11
+ trigger(cronJobId: number, time: number): Promise<void>;
12
+ on(cronJob: AutoBackupModel): void;
13
+ off(cronJob: AutoBackupModel): void;
5
14
  workerCreateBackUp(data: {
6
15
  dataTypes: string[];
7
16
  appName: string;
@@ -31,17 +31,194 @@ __export(server_exports, {
31
31
  });
32
32
  module.exports = __toCommonJS(server_exports);
33
33
  var import_server = require("@tachybase/server");
34
+ var import_cron_parser = __toESM(require("cron-parser"));
35
+ var import_constants = require("../constants");
34
36
  var import_dumper = require("./dumper");
35
37
  var import_backup_files = __toESM(require("./resourcers/backup-files"));
36
- class PluginBackupRestoreServer extends import_server.Plugin {
38
+ var import_files = require("./utils/files");
39
+ function parseDateWithoutMs(date) {
40
+ return Math.floor(date.getTime() / 1e3) * 1e3;
41
+ }
42
+ const MAX_SAFE_INTERVAL = 2147483647;
43
+ const _PluginBackupRestoreServer = class _PluginBackupRestoreServer extends import_server.Plugin {
44
+ constructor() {
45
+ super(...arguments);
46
+ this.timers = /* @__PURE__ */ new Map();
47
+ }
37
48
  beforeLoad() {
38
49
  this.app.acl.registerSnippet({
39
- name: `pm.${this.name}`,
50
+ name: `pm.${this.name}.files`,
40
51
  actions: ["backupFiles:*"]
41
52
  });
53
+ this.app.acl.registerSnippet({
54
+ name: `pm.${this.name}.auto`,
55
+ actions: ["autoBackup:*"]
56
+ });
42
57
  }
43
58
  async load() {
44
59
  this.app.resourcer.define(import_backup_files.default);
60
+ this.app.on("afterStart", async (app) => {
61
+ const cronJobs = await app.db.getRepository(import_constants.COLLECTION_AUTOBACKUP).find({
62
+ filter: { enabled: true }
63
+ });
64
+ this.inspect(cronJobs);
65
+ });
66
+ this.app.on("beforeStop", () => {
67
+ for (const timer of this.timers.values()) {
68
+ clearInterval(timer);
69
+ }
70
+ });
71
+ this.db.on(`${import_constants.COLLECTION_AUTOBACKUP}.beforeSave`, async (cronjob, options) => {
72
+ let changed = false;
73
+ for (const field of options.fields) {
74
+ if (_PluginBackupRestoreServer.inspectFields.includes(field)) {
75
+ changed = true;
76
+ break;
77
+ }
78
+ }
79
+ if (!changed) {
80
+ return;
81
+ }
82
+ this.off(cronjob);
83
+ });
84
+ this.db.on(`${import_constants.COLLECTION_AUTOBACKUP}.afterSave`, async (cronjob, options) => {
85
+ let changed = false;
86
+ for (const field of options.fields) {
87
+ if (_PluginBackupRestoreServer.inspectFields.includes(field)) {
88
+ changed = true;
89
+ break;
90
+ }
91
+ }
92
+ if (!changed) {
93
+ return;
94
+ }
95
+ if (cronjob.get("enabled")) {
96
+ this.on(cronjob);
97
+ }
98
+ });
99
+ this.db.on(`${import_constants.COLLECTION_AUTOBACKUP}.afterDestroy`, async (cronjob) => {
100
+ this.off(cronjob);
101
+ });
102
+ }
103
+ inspect(cronJobs) {
104
+ const now = /* @__PURE__ */ new Date();
105
+ cronJobs.forEach((cronJob) => {
106
+ const nextTime = this.getNextTime(cronJob, now);
107
+ if (nextTime) {
108
+ this.app.logger.info(
109
+ `cronJobs [${cronJob.id}] caching scheduled will run at: ${new Date(nextTime).toISOString()}`
110
+ );
111
+ } else {
112
+ this.app.logger.info(`cronJobs [${cronJob.id}] will not be scheduled`);
113
+ }
114
+ this.schedule(cronJob, nextTime, nextTime >= now.getTime());
115
+ });
116
+ }
117
+ getNextTime(cronJob, currentDate, nextSecond = false) {
118
+ currentDate.setMilliseconds(nextSecond ? 1e3 : 0);
119
+ const timestamp = currentDate.getTime();
120
+ const startTime = parseDateWithoutMs(cronJob.startsOn || /* @__PURE__ */ new Date());
121
+ if (startTime > timestamp) {
122
+ return startTime;
123
+ }
124
+ if (cronJob.repeat) {
125
+ const endTime = cronJob.endsOn ? parseDateWithoutMs(cronJob.endsOn) : null;
126
+ if (endTime && endTime < timestamp) {
127
+ return null;
128
+ }
129
+ if (cronJob.repeat && isNaN(+cronJob.repeat)) {
130
+ const interval = import_cron_parser.default.parseExpression(cronJob.repeat, { currentDate });
131
+ const next = interval.next();
132
+ return next.getTime();
133
+ } else if (!isNaN(+cronJob.repeat)) {
134
+ const repeat = +cronJob.repeat;
135
+ const next = timestamp + repeat - (timestamp - startTime) % repeat;
136
+ return next;
137
+ } else {
138
+ return null;
139
+ }
140
+ } else {
141
+ if (startTime < timestamp) {
142
+ return null;
143
+ }
144
+ return timestamp;
145
+ }
146
+ }
147
+ schedule(cronJob, nextTime, toggle = true) {
148
+ if (toggle) {
149
+ const key = `${cronJob.id}@${nextTime}`;
150
+ if (!this.timers.has(key)) {
151
+ const interval = Math.max(nextTime - Date.now(), 0);
152
+ if (interval > MAX_SAFE_INTERVAL) {
153
+ this.timers.set(
154
+ key,
155
+ setTimeout(() => {
156
+ this.timers.delete(key);
157
+ this.schedule(cronJob, nextTime);
158
+ }, MAX_SAFE_INTERVAL)
159
+ );
160
+ } else {
161
+ this.timers.set(key, setTimeout(this.trigger.bind(this, cronJob.id, nextTime), interval));
162
+ }
163
+ }
164
+ } else {
165
+ for (const [key, timer] of this.timers.entries()) {
166
+ if (key.startsWith(`${cronJob.id}@`)) {
167
+ clearTimeout(timer);
168
+ this.timers.delete(key);
169
+ }
170
+ }
171
+ }
172
+ }
173
+ async trigger(cronJobId, time) {
174
+ try {
175
+ const cronJob = await this.db.getRepository(import_constants.COLLECTION_AUTOBACKUP).findOne({ filterByTk: cronJobId });
176
+ if (!cronJob) {
177
+ this.app.logger.warn(`Scheduled cron job ${cronJobId} no longer exists`);
178
+ const eventKey2 = `${cronJobId}@${time}`;
179
+ this.timers.delete(eventKey2);
180
+ return;
181
+ }
182
+ const eventKey = `${cronJob.id}@${time}`;
183
+ this.timers.delete(eventKey);
184
+ try {
185
+ const dumper = new import_dumper.Dumper(this.app);
186
+ const filename = await dumper.getLockFile(this.app.name);
187
+ this.app.worker.callPluginMethod({
188
+ plugin: _PluginBackupRestoreServer,
189
+ method: "workerCreateBackUp",
190
+ params: {
191
+ dataTypes: cronJob.dumpRules,
192
+ appName: this.app.name,
193
+ filename
194
+ },
195
+ // 目前限制方法并发为1
196
+ concurrency: 1
197
+ }).finally(() => {
198
+ dumper.cleanLockFile(filename, this.app.name);
199
+ const dirPath = dumper.backUpStorageDir(this.app.name);
200
+ (0, import_files.cleanOldFiles)(dumper.backUpStorageDir(this.app.name), cronJob.maxNumber).then(() => {
201
+ this.app.logger.info(`clean backup ${dirPath} to count: {cronJob.maxNumber}`);
202
+ }).catch((err) => {
203
+ this.app.logger.error("clean backup error", err);
204
+ });
205
+ });
206
+ } catch (e) {
207
+ this.app.logger.error(e);
208
+ }
209
+ const nextTime = this.getNextTime(cronJob, /* @__PURE__ */ new Date(), true);
210
+ if (nextTime) {
211
+ this.schedule(cronJob, nextTime);
212
+ }
213
+ } catch (e) {
214
+ this.app.logger.error(`cronJobs [${cronJobId}] failed: ${e.message}`);
215
+ }
216
+ }
217
+ on(cronJob) {
218
+ this.inspect([cronJob]);
219
+ }
220
+ off(cronJob) {
221
+ this.schedule(cronJob, null, false);
45
222
  }
46
223
  async workerCreateBackUp(data) {
47
224
  await new import_dumper.Dumper(this.app).runDumpTask({
@@ -50,4 +227,6 @@ class PluginBackupRestoreServer extends import_server.Plugin {
50
227
  fileName: data.filename
51
228
  });
52
229
  }
53
- }
230
+ };
231
+ _PluginBackupRestoreServer.inspectFields = ["repeat", "enabled"];
232
+ let PluginBackupRestoreServer = _PluginBackupRestoreServer;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 清理指定目录中旧的 .tbdump 文件,仅保留最新的 maxNumber 个文件
3
+ * @param dirPath 目标目录路径
4
+ * @param maxNumber 要保留的最新文件数量
5
+ */
6
+ export declare function cleanOldFiles(dirPath: string, maxNumber: number): Promise<void>;
@@ -0,0 +1,59 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var files_exports = {};
29
+ __export(files_exports, {
30
+ cleanOldFiles: () => cleanOldFiles
31
+ });
32
+ module.exports = __toCommonJS(files_exports);
33
+ var import_promises = require("node:fs/promises");
34
+ var import_path = __toESM(require("path"));
35
+ async function cleanOldFiles(dirPath, maxNumber) {
36
+ const names = await (0, import_promises.readdir)(dirPath);
37
+ const files = await Promise.all(
38
+ names.map(async (name) => {
39
+ const filePath = import_path.default.join(dirPath, name);
40
+ try {
41
+ const info = await (0, import_promises.stat)(filePath);
42
+ if (info.isFile() && import_path.default.extname(name) === ".tbdump") {
43
+ return { name, mtime: info.mtime };
44
+ }
45
+ } catch (err) {
46
+ return null;
47
+ }
48
+ return null;
49
+ })
50
+ );
51
+ const validFiles = files.filter(Boolean);
52
+ validFiles.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
53
+ const toDelete = validFiles.slice(maxNumber).map((f) => f.name);
54
+ await Promise.all(toDelete.map((name) => (0, import_promises.unlink)(import_path.default.join(dirPath, name))));
55
+ }
56
+ // Annotate the CommonJS export names for ESM import in node:
57
+ 0 && (module.exports = {
58
+ cleanOldFiles
59
+ });
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@tachybase/module-backup",
3
3
  "displayName": "App backup & restore",
4
- "version": "1.0.18",
4
+ "version": "1.1.0",
5
5
  "description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.",
6
6
  "keywords": [
7
7
  "System management"
8
8
  ],
9
9
  "license": "Apache-2.0",
10
10
  "main": "./dist/server/index.js",
11
+ "dependencies": {
12
+ "cron-parser": "4.9.0",
13
+ "react-js-cron": "^3.2.0"
14
+ },
11
15
  "devDependencies": {
12
16
  "@ant-design/icons": "^5.5.2",
13
17
  "@hapi/topo": "^6.0.2",
@@ -30,16 +34,17 @@
30
34
  "semver": "^7.6.3",
31
35
  "tar": "^6.2.1",
32
36
  "yauzl": "^3.2.0",
33
- "@tachybase/components": "1.0.18",
34
- "@tachybase/module-worker-thread": "1.0.18"
37
+ "@tachybase/components": "1.1.0",
38
+ "@tachybase/module-worker-thread": "1.1.0"
35
39
  },
36
40
  "peerDependencies": {
37
- "@tachybase/actions": "1.0.18",
38
- "@tachybase/client": "1.0.18",
39
- "@tachybase/database": "1.0.18",
40
- "@tachybase/test": "1.0.18",
41
- "@tachybase/utils": "1.0.18",
42
- "@tachybase/server": "1.0.18"
41
+ "@tachybase/actions": "1.1.0",
42
+ "@tachybase/client": "1.1.0",
43
+ "@tachybase/database": "1.1.0",
44
+ "@tachybase/schema": "1.1.0",
45
+ "@tachybase/server": "1.1.0",
46
+ "@tachybase/test": "1.1.0",
47
+ "@tachybase/utils": "1.1.0"
43
48
  },
44
49
  "description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。",
45
50
  "displayName.zh-CN": "应用的备份与还原",