@nocobase/plugin-backup-restore 0.19.0-alpha.1

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 (158) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +118 -0
  3. package/README.zh-CN.md +118 -0
  4. package/client.d.ts +2 -0
  5. package/client.js +1 -0
  6. package/dist/client/Configuration.d.ts +2 -0
  7. package/dist/client/DuplicatorProvider.d.ts +2 -0
  8. package/dist/client/index.d.ts +5 -0
  9. package/dist/client/index.js +1 -0
  10. package/dist/client/locale/index.d.ts +4 -0
  11. package/dist/externalVersion.js +14 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +39 -0
  14. package/dist/locale/en-US.json +44 -0
  15. package/dist/locale/ja-JP.d.ts +25 -0
  16. package/dist/locale/ja-JP.js +46 -0
  17. package/dist/locale/pt-BR.d.ts +26 -0
  18. package/dist/locale/pt-BR.js +48 -0
  19. package/dist/locale/zh-CN.json +50 -0
  20. package/dist/node_modules/@hapi/topo/lib/index.d.ts +60 -0
  21. package/dist/node_modules/@hapi/topo/lib/index.js +1 -0
  22. package/dist/node_modules/@hapi/topo/package.json +1 -0
  23. package/dist/node_modules/archiver/LICENSE +22 -0
  24. package/dist/node_modules/archiver/index.js +68 -0
  25. package/dist/node_modules/archiver/lib/core.js +974 -0
  26. package/dist/node_modules/archiver/lib/error.js +40 -0
  27. package/dist/node_modules/archiver/lib/plugins/json.js +110 -0
  28. package/dist/node_modules/archiver/lib/plugins/tar.js +167 -0
  29. package/dist/node_modules/archiver/lib/plugins/zip.js +120 -0
  30. package/dist/node_modules/archiver/node_modules/readable-stream/errors-browser.js +127 -0
  31. package/dist/node_modules/archiver/node_modules/readable-stream/errors.js +116 -0
  32. package/dist/node_modules/archiver/node_modules/readable-stream/experimentalWarning.js +17 -0
  33. package/dist/node_modules/archiver/node_modules/readable-stream/lib/_stream_duplex.js +126 -0
  34. package/dist/node_modules/archiver/node_modules/readable-stream/lib/_stream_passthrough.js +37 -0
  35. package/dist/node_modules/archiver/node_modules/readable-stream/lib/_stream_readable.js +1027 -0
  36. package/dist/node_modules/archiver/node_modules/readable-stream/lib/_stream_transform.js +190 -0
  37. package/dist/node_modules/archiver/node_modules/readable-stream/lib/_stream_writable.js +641 -0
  38. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/async_iterator.js +180 -0
  39. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/buffer_list.js +183 -0
  40. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/destroy.js +96 -0
  41. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +86 -0
  42. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/from-browser.js +3 -0
  43. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/from.js +52 -0
  44. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/pipeline.js +86 -0
  45. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/state.js +22 -0
  46. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
  47. package/dist/node_modules/archiver/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
  48. package/dist/node_modules/archiver/node_modules/readable-stream/package.json +68 -0
  49. package/dist/node_modules/archiver/node_modules/readable-stream/readable-browser.js +9 -0
  50. package/dist/node_modules/archiver/node_modules/readable-stream/readable.js +16 -0
  51. package/dist/node_modules/archiver/node_modules/string_decoder/lib/string_decoder.js +296 -0
  52. package/dist/node_modules/archiver/node_modules/string_decoder/package.json +34 -0
  53. package/dist/node_modules/archiver/node_modules/tar-stream/extract.js +257 -0
  54. package/dist/node_modules/archiver/node_modules/tar-stream/headers.js +295 -0
  55. package/dist/node_modules/archiver/node_modules/tar-stream/index.js +2 -0
  56. package/dist/node_modules/archiver/node_modules/tar-stream/pack.js +255 -0
  57. package/dist/node_modules/archiver/node_modules/tar-stream/package.json +58 -0
  58. package/dist/node_modules/archiver/node_modules/tar-stream/sandbox.js +11 -0
  59. package/dist/node_modules/archiver/package.json +1 -0
  60. package/dist/node_modules/decompress/index.js +16 -0
  61. package/dist/node_modules/decompress/license +9 -0
  62. package/dist/node_modules/decompress/node_modules/make-dir/index.js +85 -0
  63. package/dist/node_modules/decompress/node_modules/make-dir/license +9 -0
  64. package/dist/node_modules/decompress/node_modules/make-dir/node_modules/pify/index.js +84 -0
  65. package/dist/node_modules/decompress/node_modules/make-dir/node_modules/pify/license +9 -0
  66. package/dist/node_modules/decompress/node_modules/make-dir/node_modules/pify/package.json +51 -0
  67. package/dist/node_modules/decompress/node_modules/make-dir/package.json +54 -0
  68. package/dist/node_modules/decompress/package.json +1 -0
  69. package/dist/node_modules/mkdirp/LICENSE +21 -0
  70. package/dist/node_modules/mkdirp/bin/cmd.js +68 -0
  71. package/dist/node_modules/mkdirp/index.js +1 -0
  72. package/dist/node_modules/mkdirp/lib/find-made.js +29 -0
  73. package/dist/node_modules/mkdirp/lib/mkdirp-manual.js +64 -0
  74. package/dist/node_modules/mkdirp/lib/mkdirp-native.js +39 -0
  75. package/dist/node_modules/mkdirp/lib/opts-arg.js +23 -0
  76. package/dist/node_modules/mkdirp/lib/path-arg.js +29 -0
  77. package/dist/node_modules/mkdirp/lib/use-native.js +10 -0
  78. package/dist/node_modules/mkdirp/package.json +1 -0
  79. package/dist/node_modules/mkdirp/readme.markdown +266 -0
  80. package/dist/node_modules/semver/LICENSE +15 -0
  81. package/dist/node_modules/semver/bin/semver.js +197 -0
  82. package/dist/node_modules/semver/classes/comparator.js +141 -0
  83. package/dist/node_modules/semver/classes/index.js +5 -0
  84. package/dist/node_modules/semver/classes/range.js +539 -0
  85. package/dist/node_modules/semver/classes/semver.js +302 -0
  86. package/dist/node_modules/semver/functions/clean.js +6 -0
  87. package/dist/node_modules/semver/functions/cmp.js +52 -0
  88. package/dist/node_modules/semver/functions/coerce.js +52 -0
  89. package/dist/node_modules/semver/functions/compare-build.js +7 -0
  90. package/dist/node_modules/semver/functions/compare-loose.js +3 -0
  91. package/dist/node_modules/semver/functions/compare.js +5 -0
  92. package/dist/node_modules/semver/functions/diff.js +65 -0
  93. package/dist/node_modules/semver/functions/eq.js +3 -0
  94. package/dist/node_modules/semver/functions/gt.js +3 -0
  95. package/dist/node_modules/semver/functions/gte.js +3 -0
  96. package/dist/node_modules/semver/functions/inc.js +19 -0
  97. package/dist/node_modules/semver/functions/lt.js +3 -0
  98. package/dist/node_modules/semver/functions/lte.js +3 -0
  99. package/dist/node_modules/semver/functions/major.js +3 -0
  100. package/dist/node_modules/semver/functions/minor.js +3 -0
  101. package/dist/node_modules/semver/functions/neq.js +3 -0
  102. package/dist/node_modules/semver/functions/parse.js +16 -0
  103. package/dist/node_modules/semver/functions/patch.js +3 -0
  104. package/dist/node_modules/semver/functions/prerelease.js +6 -0
  105. package/dist/node_modules/semver/functions/rcompare.js +3 -0
  106. package/dist/node_modules/semver/functions/rsort.js +3 -0
  107. package/dist/node_modules/semver/functions/satisfies.js +10 -0
  108. package/dist/node_modules/semver/functions/sort.js +3 -0
  109. package/dist/node_modules/semver/functions/valid.js +6 -0
  110. package/dist/node_modules/semver/index.js +1 -0
  111. package/dist/node_modules/semver/internal/constants.js +35 -0
  112. package/dist/node_modules/semver/internal/debug.js +9 -0
  113. package/dist/node_modules/semver/internal/identifiers.js +23 -0
  114. package/dist/node_modules/semver/internal/parse-options.js +15 -0
  115. package/dist/node_modules/semver/internal/re.js +212 -0
  116. package/dist/node_modules/semver/package.json +1 -0
  117. package/dist/node_modules/semver/preload.js +2 -0
  118. package/dist/node_modules/semver/range.bnf +16 -0
  119. package/dist/node_modules/semver/ranges/gtr.js +4 -0
  120. package/dist/node_modules/semver/ranges/intersects.js +7 -0
  121. package/dist/node_modules/semver/ranges/ltr.js +4 -0
  122. package/dist/node_modules/semver/ranges/max-satisfying.js +25 -0
  123. package/dist/node_modules/semver/ranges/min-satisfying.js +24 -0
  124. package/dist/node_modules/semver/ranges/min-version.js +61 -0
  125. package/dist/node_modules/semver/ranges/outside.js +80 -0
  126. package/dist/node_modules/semver/ranges/simplify.js +47 -0
  127. package/dist/node_modules/semver/ranges/subset.js +247 -0
  128. package/dist/node_modules/semver/ranges/to-comparators.js +8 -0
  129. package/dist/node_modules/semver/ranges/valid.js +11 -0
  130. package/dist/server/app-migrator.d.ts +17 -0
  131. package/dist/server/app-migrator.js +61 -0
  132. package/dist/server/collection-group-manager.d.ts +4 -0
  133. package/dist/server/collection-group-manager.js +29 -0
  134. package/dist/server/commands/restore-command.d.ts +2 -0
  135. package/dist/server/commands/restore-command.js +67 -0
  136. package/dist/server/dumper.d.ts +71 -0
  137. package/dist/server/dumper.js +406 -0
  138. package/dist/server/errors/restore-check-error.d.ts +3 -0
  139. package/dist/server/errors/restore-check-error.js +32 -0
  140. package/dist/server/field-value-writer.d.ts +9 -0
  141. package/dist/server/field-value-writer.js +101 -0
  142. package/dist/server/index.d.ts +1 -0
  143. package/dist/server/index.js +33 -0
  144. package/dist/server/locale/zh-CN.d.ts +9 -0
  145. package/dist/server/locale/zh-CN.js +30 -0
  146. package/dist/server/resourcers/backup-files.d.ts +25 -0
  147. package/dist/server/resourcers/backup-files.js +193 -0
  148. package/dist/server/restorer.d.ts +36 -0
  149. package/dist/server/restorer.js +326 -0
  150. package/dist/server/server.d.ts +5 -0
  151. package/dist/server/server.js +41 -0
  152. package/dist/server/utils.d.ts +5 -0
  153. package/dist/server/utils.js +78 -0
  154. package/dist/swagger/index.d.ts +392 -0
  155. package/dist/swagger/index.js +447 -0
  156. package/package.json +40 -0
  157. package/server.d.ts +2 -0
  158. package/server.js +1 -0
@@ -0,0 +1,193 @@
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_files_exports = {};
29
+ __export(backup_files_exports, {
30
+ default: () => backup_files_default
31
+ });
32
+ module.exports = __toCommonJS(backup_files_exports);
33
+ var import_dumper = require("../dumper");
34
+ var import_fs = __toESM(require("fs"));
35
+ var import_utils = require("@nocobase/utils");
36
+ var import_os = __toESM(require("os"));
37
+ var import_path = __toESM(require("path"));
38
+ var import_promises = __toESM(require("fs/promises"));
39
+ var import_restorer = require("../restorer");
40
+ var import_actions = require("@nocobase/actions");
41
+ var backup_files_default = {
42
+ name: "backupFiles",
43
+ middleware: async (ctx, next) => {
44
+ if (ctx.action.actionName !== "upload") {
45
+ return next();
46
+ }
47
+ const storage = import_utils.koaMulter.diskStorage({
48
+ destination: import_os.default.tmpdir(),
49
+ filename: function(req, file, cb) {
50
+ const randomName = Date.now().toString() + Math.random().toString().slice(2);
51
+ cb(null, randomName);
52
+ }
53
+ });
54
+ const upload = (0, import_utils.koaMulter)({ storage }).single("file");
55
+ return upload(ctx, next);
56
+ },
57
+ actions: {
58
+ async list(ctx, next) {
59
+ const { page = import_actions.DEFAULT_PAGE, pageSize = import_actions.DEFAULT_PER_PAGE } = ctx.action.params;
60
+ const dumper = new import_dumper.Dumper(ctx.app);
61
+ const backupFiles = await dumper.allBackUpFilePaths({
62
+ includeInProgress: true
63
+ });
64
+ const count = backupFiles.length;
65
+ const rows = await Promise.all(
66
+ backupFiles.slice((page - 1) * pageSize, page * pageSize).map(async (file) => {
67
+ return await import_dumper.Dumper.getFileStatus(file.endsWith(".lock") ? file.replace(".lock", "") : file);
68
+ })
69
+ );
70
+ ctx.body = {
71
+ count,
72
+ rows,
73
+ page: Number(page),
74
+ pageSize: Number(pageSize),
75
+ totalPage: Math.ceil(count / pageSize)
76
+ };
77
+ await next();
78
+ },
79
+ async get(ctx, next) {
80
+ const { filterByTk } = ctx.action.params;
81
+ const dumper = new import_dumper.Dumper(ctx.app);
82
+ const filePath = dumper.backUpFilePath(filterByTk);
83
+ async function sendError(message, status = 404) {
84
+ ctx.body = { status: "error", message };
85
+ ctx.status = status;
86
+ }
87
+ try {
88
+ const fileState = await import_dumper.Dumper.getFileStatus(filePath);
89
+ if (fileState.status !== "ok") {
90
+ await sendError(`Backup file ${filterByTk} not found`);
91
+ } else {
92
+ const restorer = new import_restorer.Restorer(ctx.app, {
93
+ backUpFilePath: filePath
94
+ });
95
+ const restoreMeta = await restorer.parseBackupFile();
96
+ ctx.body = {
97
+ ...fileState,
98
+ meta: restoreMeta
99
+ };
100
+ }
101
+ } catch (e) {
102
+ if (e.code === "ENOENT") {
103
+ await sendError(`Backup file ${filterByTk} not found`);
104
+ }
105
+ }
106
+ await next();
107
+ },
108
+ /**
109
+ * create dump task
110
+ * @param ctx
111
+ * @param next
112
+ */
113
+ async create(ctx, next) {
114
+ const data = ctx.request.body;
115
+ const dumper = new import_dumper.Dumper(ctx.app);
116
+ const taskId = await dumper.runDumpTask({
117
+ groups: new Set(data.dataTypes)
118
+ });
119
+ ctx.body = {
120
+ key: taskId
121
+ };
122
+ await next();
123
+ },
124
+ /**
125
+ * download backup file
126
+ * @param ctx
127
+ * @param next
128
+ */
129
+ async download(ctx, next) {
130
+ const { filterByTk } = ctx.action.params;
131
+ const dumper = new import_dumper.Dumper(ctx.app);
132
+ const filePath = dumper.backUpFilePath(filterByTk);
133
+ const fileState = await import_dumper.Dumper.getFileStatus(filePath);
134
+ if (fileState.status !== "ok") {
135
+ throw new Error(`Backup file ${filterByTk} not found`);
136
+ }
137
+ ctx.attachment(filePath);
138
+ ctx.body = import_fs.default.createReadStream(filePath);
139
+ await next();
140
+ },
141
+ async restore(ctx, next) {
142
+ const { dataTypes, filterByTk, key } = ctx.action.params.values;
143
+ const filePath = (() => {
144
+ if (key) {
145
+ const tmpDir = import_os.default.tmpdir();
146
+ return import_path.default.resolve(tmpDir, key);
147
+ }
148
+ if (filterByTk) {
149
+ const dumper = new import_dumper.Dumper(ctx.app);
150
+ return dumper.backUpFilePath(filterByTk);
151
+ }
152
+ })();
153
+ if (!filePath) {
154
+ throw new Error(`Backup file ${filterByTk} not found`);
155
+ }
156
+ const args = ["restore", "-f", filePath];
157
+ for (const dataType of dataTypes) {
158
+ args.push("-g", dataType);
159
+ }
160
+ await ctx.app.runCommand(...args);
161
+ await next();
162
+ },
163
+ async destroy(ctx, next) {
164
+ const { filterByTk } = ctx.action.params;
165
+ const dumper = new import_dumper.Dumper(ctx.app);
166
+ const filePath = dumper.backUpFilePath(filterByTk);
167
+ await import_promises.default.unlink(filePath);
168
+ ctx.body = {
169
+ status: "ok"
170
+ };
171
+ await next();
172
+ },
173
+ async upload(ctx, next) {
174
+ const file = ctx.file;
175
+ const fileName = file.filename;
176
+ const restorer = new import_restorer.Restorer(ctx.app, {
177
+ backUpFilePath: file.path
178
+ });
179
+ const restoreMeta = await restorer.parseBackupFile();
180
+ ctx.body = {
181
+ key: fileName,
182
+ meta: restoreMeta
183
+ };
184
+ await next();
185
+ },
186
+ async dumpableCollections(ctx, next) {
187
+ ctx.withoutDataWrapping = true;
188
+ const dumper = new import_dumper.Dumper(ctx.app);
189
+ ctx.body = await dumper.dumpableCollectionsGroupByGroup();
190
+ await next();
191
+ }
192
+ }
193
+ };
@@ -0,0 +1,36 @@
1
+ import { AppMigrator, AppMigratorOptions } from './app-migrator';
2
+ import { Application } from '@nocobase/server';
3
+ import { DumpRulesGroupType } from '@nocobase/database';
4
+ type RestoreOptions = {
5
+ groups: Set<DumpRulesGroupType>;
6
+ };
7
+ export declare class Restorer extends AppMigrator {
8
+ direction: "restore";
9
+ backUpFilePath: string;
10
+ decompressed: boolean;
11
+ importedCollections: string[];
12
+ constructor(app: Application, options: AppMigratorOptions & {
13
+ backUpFilePath?: string;
14
+ });
15
+ static sortCollectionsByInherits(collections: Array<{
16
+ name: string;
17
+ inherits: string[];
18
+ }>): any;
19
+ setBackUpFilePath(backUpFilePath: string): void;
20
+ parseBackupFile(): Promise<any>;
21
+ restore(options: RestoreOptions): Promise<void>;
22
+ getImportMeta(): Promise<any>;
23
+ checkMeta(): Promise<void>;
24
+ importCollections(options: RestoreOptions): Promise<void>;
25
+ decompressBackup(backupFilePath: string): Promise<void>;
26
+ readCollectionMeta(collectionName: string): Promise<any>;
27
+ importCollection(options: {
28
+ name: string;
29
+ insert?: boolean;
30
+ clear?: boolean;
31
+ rowCondition?: (row: any) => boolean;
32
+ }): Promise<any>;
33
+ importDb(options: RestoreOptions): Promise<void>;
34
+ upgradeApp(): Promise<void>;
35
+ }
36
+ export {};
@@ -0,0 +1,326 @@
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 restorer_exports = {};
29
+ __export(restorer_exports, {
30
+ Restorer: () => Restorer
31
+ });
32
+ module.exports = __toCommonJS(restorer_exports);
33
+ var import_decompress = __toESM(require("decompress"));
34
+ var import_fs = __toESM(require("fs"));
35
+ var import_promises = __toESM(require("fs/promises"));
36
+ var import_path = __toESM(require("path"));
37
+ var import_app_migrator = require("./app-migrator");
38
+ var import_utils = require("./utils");
39
+ var import_database = require("@nocobase/database");
40
+ var import_lodash = __toESM(require("lodash"));
41
+ var import_field_value_writer = require("./field-value-writer");
42
+ var Topo = __toESM(require("@hapi/topo"));
43
+ var import_restore_check_error = require("./errors/restore-check-error");
44
+ var import_semver = __toESM(require("semver"));
45
+ class Restorer extends import_app_migrator.AppMigrator {
46
+ direction = "restore";
47
+ backUpFilePath;
48
+ decompressed = false;
49
+ importedCollections = [];
50
+ constructor(app, options) {
51
+ super(app, options);
52
+ const { backUpFilePath } = options;
53
+ if (backUpFilePath) {
54
+ this.setBackUpFilePath(backUpFilePath);
55
+ }
56
+ }
57
+ static sortCollectionsByInherits(collections) {
58
+ var _a;
59
+ const sorter = new Topo.Sorter();
60
+ for (const collection of collections) {
61
+ const options = {
62
+ group: collection.name
63
+ };
64
+ if ((_a = collection.inherits) == null ? void 0 : _a.length) {
65
+ options.after = collection.inherits;
66
+ }
67
+ sorter.add(collection, options);
68
+ }
69
+ return sorter.sort();
70
+ }
71
+ setBackUpFilePath(backUpFilePath) {
72
+ if (import_path.default.isAbsolute(backUpFilePath)) {
73
+ this.backUpFilePath = backUpFilePath;
74
+ } else if (import_path.default.basename(backUpFilePath) === backUpFilePath) {
75
+ const dirname = import_path.default.resolve(process.cwd(), "storage", "duplicator");
76
+ this.backUpFilePath = import_path.default.resolve(dirname, backUpFilePath);
77
+ } else {
78
+ this.backUpFilePath = import_path.default.resolve(process.cwd(), backUpFilePath);
79
+ }
80
+ }
81
+ async parseBackupFile() {
82
+ await this.decompressBackup(this.backUpFilePath);
83
+ return await this.getImportMeta();
84
+ }
85
+ async restore(options) {
86
+ await this.decompressBackup(this.backUpFilePath);
87
+ await this.checkMeta();
88
+ await this.importCollections(options);
89
+ await this.importDb(options);
90
+ await this.upgradeApp();
91
+ await this.clearWorkDir();
92
+ }
93
+ async getImportMeta() {
94
+ const metaFile = import_path.default.resolve(this.workDir, "meta");
95
+ return JSON.parse(await import_promises.default.readFile(metaFile, "utf8"));
96
+ }
97
+ async checkMeta() {
98
+ const meta = await this.getImportMeta();
99
+ if (meta["dialectOnly"] && !this.app.db.inDialect(meta["dialect"])) {
100
+ throw new import_restore_check_error.RestoreCheckError(`this backup file can only be imported in database ${meta["dialect"]}`);
101
+ }
102
+ const checkEnv = (envName) => {
103
+ const valueInPackage = meta[envName] || "";
104
+ const valueInEnv = process.env[envName] || "";
105
+ if (valueInPackage && valueInEnv !== valueInPackage) {
106
+ throw new import_restore_check_error.RestoreCheckError(`for use this backup file, please set ${envName}=${valueInPackage}`);
107
+ }
108
+ };
109
+ for (const envName of ["DB_UNDERSCORED", "DB_SCHEMA", "COLLECTION_MANAGER_SCHEMA", "DB_TABLE_PREFIX"]) {
110
+ checkEnv(envName);
111
+ }
112
+ const version = meta["version"];
113
+ if (import_semver.default.lt(version, "0.18.0-alpha.2")) {
114
+ throw new import_restore_check_error.RestoreCheckError(`this backup file can only be imported in nocobase ${version}`);
115
+ }
116
+ }
117
+ async importCollections(options) {
118
+ const importCollection = async (collectionName) => {
119
+ await this.importCollection({
120
+ name: collectionName
121
+ });
122
+ };
123
+ const { dumpableCollectionsGroupByGroup, delayCollections } = await this.parseBackupFile();
124
+ await importCollection("applicationPlugins");
125
+ await this.app.reload();
126
+ const metaCollections = dumpableCollectionsGroupByGroup.required;
127
+ for (const collection of metaCollections) {
128
+ if (collection.name === "applicationPlugins") {
129
+ continue;
130
+ }
131
+ if (delayCollections.includes(collection.name)) {
132
+ continue;
133
+ }
134
+ await importCollection(collection.name);
135
+ }
136
+ options.groups.delete("required");
137
+ const importGroups = [...options.groups];
138
+ for (const group of importGroups) {
139
+ const collections = dumpableCollectionsGroupByGroup[group];
140
+ if (!collections) {
141
+ this.app.log.warn(`group ${group} not found`);
142
+ continue;
143
+ }
144
+ for (const collection of Restorer.sortCollectionsByInherits(collections)) {
145
+ await importCollection(collection.name);
146
+ }
147
+ }
148
+ await this.app.reload();
149
+ await this.app.db.getRepository("collections").load();
150
+ await this.app.db.sync();
151
+ for (const collectionName of delayCollections) {
152
+ const delayRestore = this.app.db.getCollection(collectionName).options.dumpRules["delayRestore"];
153
+ await delayRestore(this);
154
+ }
155
+ await this.emitAsync("restoreCollectionsFinished");
156
+ }
157
+ async decompressBackup(backupFilePath) {
158
+ if (!this.decompressed)
159
+ await (0, import_decompress.default)(backupFilePath, this.workDir);
160
+ }
161
+ async readCollectionMeta(collectionName) {
162
+ const dir = this.workDir;
163
+ const collectionMetaPath = import_path.default.resolve(dir, "collections", collectionName, "meta");
164
+ const metaContent = await import_promises.default.readFile(collectionMetaPath, "utf8");
165
+ return JSON.parse(metaContent);
166
+ }
167
+ async importCollection(options) {
168
+ const app = this.app;
169
+ const db = app.db;
170
+ const collectionName = options.name;
171
+ if (!collectionName) {
172
+ throw new Error("collection name is required");
173
+ }
174
+ const dir = this.workDir;
175
+ const collectionDataPath = import_path.default.resolve(dir, "collections", collectionName, "data");
176
+ const collectionMetaPath = import_path.default.resolve(dir, "collections", collectionName, "meta");
177
+ try {
178
+ await import_promises.default.stat(collectionMetaPath);
179
+ } catch (e) {
180
+ app.logger.info(`${collectionName} has no meta`);
181
+ return;
182
+ }
183
+ const metaContent = await import_promises.default.readFile(collectionMetaPath, "utf8");
184
+ const meta = JSON.parse(metaContent);
185
+ let addSchemaTableName = meta.tableName;
186
+ if (!this.app.db.inDialect("postgres") && (0, import_lodash.isPlainObject)(addSchemaTableName)) {
187
+ addSchemaTableName = addSchemaTableName.tableName;
188
+ }
189
+ const columns = meta["columns"];
190
+ if (columns.length == 0) {
191
+ app.logger.info(`${collectionName} has no columns`);
192
+ return;
193
+ }
194
+ const fieldAttributes = import_lodash.default.mapValues(meta.attributes, (attr) => {
195
+ if (attr.isCollectionField) {
196
+ const fieldClass = db.fieldTypes.get(attr.type);
197
+ if (!fieldClass)
198
+ throw new Error(`field type ${attr.type} not found`);
199
+ return new fieldClass(attr.typeOptions, {
200
+ database: db
201
+ });
202
+ }
203
+ return void 0;
204
+ });
205
+ const rawAttributes = import_lodash.default.mapValues(meta.attributes, (attr, key) => {
206
+ if (attr.isCollectionField) {
207
+ const field = fieldAttributes[key];
208
+ return {
209
+ ...field.toSequelize(),
210
+ field: attr.field
211
+ };
212
+ }
213
+ const DataTypeClass = import_database.DataTypes[db.options.dialect][attr.type] || import_database.DataTypes[attr.type];
214
+ const obj = {
215
+ ...attr,
216
+ type: new DataTypeClass()
217
+ };
218
+ if (attr.defaultValue && ["JSON", "JSONB", "JSONTYPE"].includes(attr.type)) {
219
+ obj.defaultValue = JSON.stringify(attr.defaultValue);
220
+ }
221
+ return obj;
222
+ });
223
+ if (options.clear !== false) {
224
+ await db.sequelize.getQueryInterface().dropTable(addSchemaTableName, {
225
+ cascade: true
226
+ });
227
+ await db.sequelize.getQueryInterface().createTable(addSchemaTableName, rawAttributes);
228
+ if (meta.inherits) {
229
+ for (const inherit of import_lodash.default.uniq(meta.inherits)) {
230
+ const parentMeta = await this.readCollectionMeta(inherit);
231
+ const sql2 = `ALTER TABLE ${app.db.utils.quoteTable(addSchemaTableName)} INHERIT ${app.db.utils.quoteTable(
232
+ parentMeta.tableName
233
+ )};`;
234
+ await db.sequelize.query(sql2);
235
+ }
236
+ }
237
+ }
238
+ const rows = await (0, import_utils.readLines)(collectionDataPath);
239
+ if (rows.length == 0) {
240
+ app.logger.info(`${collectionName} has no data to import`);
241
+ this.importedCollections.push(collectionName);
242
+ return;
243
+ }
244
+ const rowsWithMeta = rows.map(
245
+ (row) => JSON.parse(row).map((val, index) => [columns[index], val]).reduce((carry, [column, val]) => {
246
+ const field = fieldAttributes[column];
247
+ carry[column] = field ? import_field_value_writer.FieldValueWriter.write(field, val) : val;
248
+ return carry;
249
+ }, {})
250
+ ).filter((row) => {
251
+ if (options.rowCondition) {
252
+ return options.rowCondition(row);
253
+ }
254
+ return true;
255
+ });
256
+ if (rowsWithMeta.length == 0) {
257
+ app.logger.info(`${collectionName} has no data to import`);
258
+ this.importedCollections.push(collectionName);
259
+ return;
260
+ }
261
+ const insertGeneratorAttributes = import_lodash.default.mapKeys(rawAttributes, (value, key) => {
262
+ return value.field;
263
+ });
264
+ const sql = db.sequelize.queryInterface.queryGenerator.bulkInsertQuery(
265
+ addSchemaTableName,
266
+ rowsWithMeta,
267
+ {},
268
+ insertGeneratorAttributes
269
+ );
270
+ if (options.insert === false) {
271
+ return sql;
272
+ }
273
+ await app.db.sequelize.query(sql, {
274
+ type: "INSERT"
275
+ });
276
+ app.logger.info(`${collectionName} imported with ${rowsWithMeta.length} rows`);
277
+ if (meta.autoIncrement) {
278
+ const queryInterface = app.db.queryInterface;
279
+ await queryInterface.setAutoIncrementVal({
280
+ tableInfo: (0, import_lodash.isPlainObject)(meta.tableName) ? meta.tableName : {
281
+ schema: "public",
282
+ tableName: meta.tableName
283
+ },
284
+ columnName: meta.autoIncrement.fieldName,
285
+ seqName: meta.autoIncrement.seqName,
286
+ currentVal: meta.autoIncrement.currentVal
287
+ });
288
+ }
289
+ this.importedCollections.push(collectionName);
290
+ }
291
+ async importDb(options) {
292
+ const sqlContentPath = import_path.default.resolve(this.workDir, "sql-content.json");
293
+ if (!import_fs.default.existsSync(sqlContentPath)) {
294
+ return;
295
+ }
296
+ const sqlData = JSON.parse(await import_promises.default.readFile(sqlContentPath, "utf8"));
297
+ const sqlContent = Object.keys(sqlData).filter((key) => options.groups.has(sqlData[key].group)).reduce((acc, key) => {
298
+ acc[key] = sqlData[key];
299
+ return acc;
300
+ }, {});
301
+ const queries = Object.values(
302
+ sqlContent
303
+ );
304
+ for (const sqlData2 of queries) {
305
+ try {
306
+ this.app.log.info(`import sql: ${sqlData2.sql}`);
307
+ for (const sql of import_lodash.default.castArray(sqlData2.sql)) {
308
+ await this.app.db.sequelize.query(sql);
309
+ }
310
+ } catch (e) {
311
+ if (e.name === "SequelizeDatabaseError") {
312
+ this.app.logger.error(e.message);
313
+ } else {
314
+ throw e;
315
+ }
316
+ }
317
+ }
318
+ }
319
+ async upgradeApp() {
320
+ await this.app.runCommand("upgrade");
321
+ }
322
+ }
323
+ // Annotate the CommonJS export names for ESM import in node:
324
+ 0 && (module.exports = {
325
+ Restorer
326
+ });
@@ -0,0 +1,5 @@
1
+ import { Plugin } from '@nocobase/server';
2
+ export default class Duplicator extends Plugin {
3
+ beforeLoad(): void;
4
+ load(): Promise<void>;
5
+ }
@@ -0,0 +1,41 @@
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 server_exports = {};
29
+ __export(server_exports, {
30
+ default: () => Duplicator
31
+ });
32
+ module.exports = __toCommonJS(server_exports);
33
+ var import_server = require("@nocobase/server");
34
+ var import_backup_files = __toESM(require("./resourcers/backup-files"));
35
+ class Duplicator extends import_server.Plugin {
36
+ beforeLoad() {
37
+ }
38
+ async load() {
39
+ this.app.resourcer.define(import_backup_files.default);
40
+ }
41
+ }
@@ -0,0 +1,5 @@
1
+ import { Database } from '@nocobase/database';
2
+ export declare const DUMPED_EXTENSION = "nbdump";
3
+ export declare function sqlAdapter(database: Database, sql: string): string;
4
+ export declare function readLines(filePath: string): Promise<any[]>;
5
+ export declare function humanFileSize(bytes: any, si?: boolean, dp?: number): string;
@@ -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 utils_exports = {};
29
+ __export(utils_exports, {
30
+ DUMPED_EXTENSION: () => DUMPED_EXTENSION,
31
+ humanFileSize: () => humanFileSize,
32
+ readLines: () => readLines,
33
+ sqlAdapter: () => sqlAdapter
34
+ });
35
+ module.exports = __toCommonJS(utils_exports);
36
+ var import_lodash = __toESM(require("lodash"));
37
+ var import_fs = __toESM(require("fs"));
38
+ var import_readline = __toESM(require("readline"));
39
+ const DUMPED_EXTENSION = "nbdump";
40
+ function sqlAdapter(database, sql) {
41
+ if (database.isMySQLCompatibleDialect()) {
42
+ return import_lodash.default.replace(sql, /"/g, "`");
43
+ }
44
+ return sql;
45
+ }
46
+ async function readLines(filePath) {
47
+ const results = [];
48
+ const fileStream = import_fs.default.createReadStream(filePath);
49
+ const rl = import_readline.default.createInterface({
50
+ input: fileStream,
51
+ crlfDelay: Infinity
52
+ });
53
+ for await (const line of rl) {
54
+ results.push(line);
55
+ }
56
+ return results;
57
+ }
58
+ function humanFileSize(bytes, si = false, dp = 1) {
59
+ const thresh = si ? 1e3 : 1024;
60
+ if (Math.abs(bytes) < thresh) {
61
+ return bytes + " B";
62
+ }
63
+ const units = si ? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] : ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
64
+ let u = -1;
65
+ const r = 10 ** dp;
66
+ do {
67
+ bytes /= thresh;
68
+ ++u;
69
+ } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
70
+ return bytes.toFixed(dp) + " " + units[u];
71
+ }
72
+ // Annotate the CommonJS export names for ESM import in node:
73
+ 0 && (module.exports = {
74
+ DUMPED_EXTENSION,
75
+ humanFileSize,
76
+ readLines,
77
+ sqlAdapter
78
+ });