@datatruck/cli 0.36.1 → 0.36.3
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/lib/actions/BackupAction.js +7 -3
- package/lib/actions/CopyAction.js +7 -3
- package/lib/cli.js +2 -1
- package/lib/commands/StartServerCommand.js +6 -2
- package/lib/tasks/MysqlDumpTask.js +334 -265
- package/lib/utils/cli.d.ts +5 -0
- package/lib/utils/cli.js +45 -1
- package/lib/utils/datatruck/cron-server.js +15 -9
- package/lib/utils/datatruck/repository-server.js +14 -4
- package/lib/utils/mysql.d.ts +1 -0
- package/lib/utils/mysql.js +3 -0
- package/package.json +6 -6
|
@@ -149,7 +149,8 @@ class BackupAction {
|
|
|
149
149
|
let title = item.key.slice(0, 1).toUpperCase() + item.key.slice(1);
|
|
150
150
|
return item.key === "backup" && color ? chalk_1.default.cyan(title) : title;
|
|
151
151
|
};
|
|
152
|
-
const renderData = (item,
|
|
152
|
+
const renderData = (item, result, format) => {
|
|
153
|
+
const color = format !== "list";
|
|
153
154
|
const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
|
|
154
155
|
return (0, cli_1.renderListTaskItem)(item, color, {
|
|
155
156
|
snapshot: (data) => data.id,
|
|
@@ -176,6 +177,9 @@ class BackupAction {
|
|
|
176
177
|
result += item.data.pruned;
|
|
177
178
|
return result;
|
|
178
179
|
}, 0),
|
|
180
|
+
...(format === "list" && {
|
|
181
|
+
duration: (0, date_1.duration)(item.elapsed),
|
|
182
|
+
}),
|
|
179
183
|
}),
|
|
180
184
|
});
|
|
181
185
|
};
|
|
@@ -187,7 +191,7 @@ class BackupAction {
|
|
|
187
191
|
.map((item) => {
|
|
188
192
|
const icon = (0, cli_1.renderResult)(item.error, false);
|
|
189
193
|
const title = renderTitle(item);
|
|
190
|
-
const data = renderData(item,
|
|
194
|
+
const data = renderData(item, result, "list");
|
|
191
195
|
return `${icon} ${title}: ${data}`;
|
|
192
196
|
}),
|
|
193
197
|
table: {
|
|
@@ -203,7 +207,7 @@ class BackupAction {
|
|
|
203
207
|
.map((item) => [
|
|
204
208
|
(0, cli_1.renderResult)(item.error),
|
|
205
209
|
renderTitle(item, true),
|
|
206
|
-
renderData(item,
|
|
210
|
+
renderData(item, result, "table"),
|
|
207
211
|
(0, date_1.duration)(item.elapsed),
|
|
208
212
|
(0, cli_1.renderError)(item.error, options.verbose),
|
|
209
213
|
]),
|
|
@@ -76,7 +76,8 @@ class CopyAction {
|
|
|
76
76
|
let title = item.key.slice(0, 1).toUpperCase() + item.key.slice(1);
|
|
77
77
|
return item.key === "copy" && color ? chalk_1.default.cyan(title) : title;
|
|
78
78
|
};
|
|
79
|
-
const renderData = (item,
|
|
79
|
+
const renderData = (item, items = [], format) => {
|
|
80
|
+
const color = format !== "list";
|
|
80
81
|
const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
|
|
81
82
|
return (0, cli_1.renderListTaskItem)(item, color, {
|
|
82
83
|
snapshots: (data) => data.snapshots.length,
|
|
@@ -93,6 +94,9 @@ class CopyAction {
|
|
|
93
94
|
errors: data.errors,
|
|
94
95
|
copied: items.filter((i) => i.key === "copy" && !i.error && !i.data.skipped).length,
|
|
95
96
|
skipped: items.filter((i) => i.key === "copy" && !i.error && i.data.skipped).length,
|
|
97
|
+
...(format === "list" && {
|
|
98
|
+
duration: (0, date_1.duration)(item.elapsed),
|
|
99
|
+
}),
|
|
96
100
|
}),
|
|
97
101
|
});
|
|
98
102
|
};
|
|
@@ -102,7 +106,7 @@ class CopyAction {
|
|
|
102
106
|
list: () => result.map((item) => {
|
|
103
107
|
const icon = (0, cli_1.renderResult)(item.error, false);
|
|
104
108
|
const title = renderTitle(item);
|
|
105
|
-
const data = renderData(item,
|
|
109
|
+
const data = renderData(item, result, "list");
|
|
106
110
|
return `${icon} ${title}: ${data}`;
|
|
107
111
|
}),
|
|
108
112
|
table: {
|
|
@@ -116,7 +120,7 @@ class CopyAction {
|
|
|
116
120
|
rows: () => result.map((item) => [
|
|
117
121
|
(0, cli_1.renderResult)(item.error),
|
|
118
122
|
renderTitle(item, true),
|
|
119
|
-
renderData(item,
|
|
123
|
+
renderData(item, result, "table"),
|
|
120
124
|
(0, date_1.duration)(item.elapsed),
|
|
121
125
|
(0, cli_1.renderError)(item.error, options.verbose),
|
|
122
126
|
]),
|
package/lib/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StartServerCommand = void 0;
|
|
4
4
|
const ConfigAction_1 = require("../actions/ConfigAction");
|
|
5
|
+
const cli_1 = require("../utils/cli");
|
|
5
6
|
const cron_server_1 = require("../utils/datatruck/cron-server");
|
|
6
7
|
const repository_server_1 = require("../utils/datatruck/repository-server");
|
|
7
8
|
const error_1 = require("../utils/error");
|
|
@@ -22,7 +23,7 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
|
|
|
22
23
|
});
|
|
23
24
|
const port = repositoryOptions.listen?.port ?? 8888;
|
|
24
25
|
const address = repositoryOptions.listen?.address ?? "127.0.0.1";
|
|
25
|
-
|
|
26
|
+
(0, cli_1.logJson)("datatruck-server", `listening server on http://${address}:${port}`);
|
|
26
27
|
server.on("error", (error) => {
|
|
27
28
|
console.error(`SERVER ERROR`, error);
|
|
28
29
|
process.exit(1);
|
|
@@ -30,6 +31,7 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
|
|
|
30
31
|
server.listen(port, address);
|
|
31
32
|
}
|
|
32
33
|
const cronOptions = config.server?.cron || {};
|
|
34
|
+
const cronJobs = cronOptions.actions || [];
|
|
33
35
|
if (cronOptions.enabled ?? true) {
|
|
34
36
|
if (typeof this.configPath !== "string")
|
|
35
37
|
throw new error_1.AppError(`Config path is required by cron server`);
|
|
@@ -39,7 +41,9 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
|
|
|
39
41
|
configPath: this.configPath,
|
|
40
42
|
});
|
|
41
43
|
server.start();
|
|
42
|
-
|
|
44
|
+
(0, cli_1.logJson)("cron-server", `server started`, {
|
|
45
|
+
jobs: cronJobs.length,
|
|
46
|
+
});
|
|
43
47
|
}
|
|
44
48
|
process.on("SIGINT", () => process.exit(1));
|
|
45
49
|
process.on("SIGTERM", () => process.exit(1));
|
|
@@ -1,4 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
3
|
+
if (value !== null && value !== void 0) {
|
|
4
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
5
|
+
var dispose;
|
|
6
|
+
if (async) {
|
|
7
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
8
|
+
dispose = value[Symbol.asyncDispose];
|
|
9
|
+
}
|
|
10
|
+
if (dispose === void 0) {
|
|
11
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
12
|
+
dispose = value[Symbol.dispose];
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
16
|
+
}
|
|
17
|
+
else if (async) {
|
|
18
|
+
env.stack.push({ async: true });
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
23
|
+
return function (env) {
|
|
24
|
+
function fail(e) {
|
|
25
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
26
|
+
env.hasError = true;
|
|
27
|
+
}
|
|
28
|
+
function next() {
|
|
29
|
+
while (env.stack.length) {
|
|
30
|
+
var rec = env.stack.pop();
|
|
31
|
+
try {
|
|
32
|
+
var result = rec.dispose && rec.dispose.call(rec.value);
|
|
33
|
+
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
fail(e);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (env.hasError) throw env.error;
|
|
40
|
+
}
|
|
41
|
+
return next();
|
|
42
|
+
};
|
|
43
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
44
|
+
var e = new Error(message);
|
|
45
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
46
|
+
});
|
|
2
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
48
|
exports.MysqlDumpTask = exports.mysqlDumpTaskName = void 0;
|
|
4
49
|
const async_1 = require("../utils/async");
|
|
@@ -24,149 +69,161 @@ const suffix = {
|
|
|
24
69
|
};
|
|
25
70
|
class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
26
71
|
async backup(data) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
(0,
|
|
74
|
-
(
|
|
72
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
73
|
+
try {
|
|
74
|
+
const compressAndClean = this.config.compress
|
|
75
|
+
? async (path) => {
|
|
76
|
+
await (0, tar_1.createTar)({
|
|
77
|
+
include: [(0, path_1.relative)(snapshotPath, path)],
|
|
78
|
+
output: `${path}.tar.gz`,
|
|
79
|
+
path: (0, path_1.dirname)(path),
|
|
80
|
+
compress: this.config.compress,
|
|
81
|
+
verbose: data.options.verbose,
|
|
82
|
+
});
|
|
83
|
+
await (0, promises_1.rm)(path);
|
|
84
|
+
}
|
|
85
|
+
: undefined;
|
|
86
|
+
const snapshotPath = data.package.path ??
|
|
87
|
+
(await (0, temp_1.mkTmpDir)(exports.mysqlDumpTaskName, "task", "backup", "snapshot"));
|
|
88
|
+
await (0, fs_1.mkdirIfNotExists)(snapshotPath);
|
|
89
|
+
await (0, fs_1.ensureEmptyDir)(snapshotPath);
|
|
90
|
+
const sql = __addDisposableResource(env_1, await (0, mysql_1.createMysqlCli)({
|
|
91
|
+
...this.config,
|
|
92
|
+
database: undefined,
|
|
93
|
+
verbose: data.options.verbose,
|
|
94
|
+
}), true);
|
|
95
|
+
const tableNames = await sql.fetchTableNames(this.config.database, this.config.includeTables, this.config.excludeTables);
|
|
96
|
+
const concurrency = this.config.concurrency ?? 4;
|
|
97
|
+
const dataFormat = this.config.dataFormat ?? "sql";
|
|
98
|
+
const sharedDir = dataFormat === "csv"
|
|
99
|
+
? await sql.initSharedDir(this.config.csvSharedPath)
|
|
100
|
+
: undefined;
|
|
101
|
+
if (this.config.oneFileByTable || sharedDir) {
|
|
102
|
+
await (0, async_1.runParallel)({
|
|
103
|
+
items: tableNames,
|
|
104
|
+
concurrency,
|
|
105
|
+
onChange: ({ processed: proccesed, buffer }) => data.onProgress({
|
|
106
|
+
relative: {
|
|
107
|
+
description: buffer.size > 1 ? `Exporting (${buffer.size})` : "Exporting",
|
|
108
|
+
payload: [...buffer.keys()].join(", "),
|
|
109
|
+
},
|
|
110
|
+
absolute: {
|
|
111
|
+
total: tableNames.length,
|
|
112
|
+
current: proccesed,
|
|
113
|
+
percent: (0, math_1.progressPercent)(tableNames.length, proccesed),
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
onItem: async ({ item: tableName, index, controller }) => {
|
|
117
|
+
if (sharedDir) {
|
|
118
|
+
const tableSharedPath = (0, path_1.join)(sharedDir, `tmp-dtt-backup-${data.snapshot.id.slice(0, 8)}-${tableName}`);
|
|
119
|
+
if (data.options.verbose) {
|
|
120
|
+
(0, cli_1.logExec)("mkdir", ["-p", tableSharedPath]);
|
|
121
|
+
(0, cli_1.logExec)("chmod", ["777", tableSharedPath]);
|
|
122
|
+
}
|
|
123
|
+
await (0, promises_1.mkdir)(tableSharedPath, { recursive: true });
|
|
124
|
+
try {
|
|
125
|
+
await (0, promises_1.chmod)(tableSharedPath, 0o777);
|
|
126
|
+
await sql.csvDump({
|
|
127
|
+
sharedPath: tableSharedPath,
|
|
128
|
+
items: [tableName],
|
|
129
|
+
database: this.config.database,
|
|
130
|
+
controller,
|
|
131
|
+
});
|
|
132
|
+
const files = await (0, promises_1.readdir)(tableSharedPath);
|
|
133
|
+
const schemaFile = `${tableName}.sql`;
|
|
134
|
+
const dataFile = `${tableName}.txt`;
|
|
135
|
+
const successCsvDump = files.length === 2 &&
|
|
136
|
+
files.every((file) => file === schemaFile || file === dataFile);
|
|
137
|
+
if (!successCsvDump)
|
|
138
|
+
throw new error_1.AppError(`Invalid csv dump files: ${files.join(", ")}`);
|
|
139
|
+
const schemaPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableSchema}`);
|
|
140
|
+
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, schemaFile), schemaPath);
|
|
141
|
+
await compressAndClean?.(schemaPath);
|
|
142
|
+
const tablePath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableData}`);
|
|
143
|
+
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, dataFile), tablePath);
|
|
144
|
+
await compressAndClean?.(tablePath);
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
await (0, promises_1.rm)(tableSharedPath, { recursive: true });
|
|
148
|
+
}
|
|
75
149
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await
|
|
79
|
-
|
|
80
|
-
sharedPath: tableSharedPath,
|
|
150
|
+
else {
|
|
151
|
+
const outPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.table}`);
|
|
152
|
+
await sql.dump({
|
|
153
|
+
output: outPath,
|
|
81
154
|
items: [tableName],
|
|
82
155
|
database: this.config.database,
|
|
83
156
|
controller,
|
|
157
|
+
...(concurrency === 1 && {
|
|
158
|
+
onProgress(progress) {
|
|
159
|
+
data.onProgress({
|
|
160
|
+
relative: {
|
|
161
|
+
description: "Exporting",
|
|
162
|
+
payload: tableName,
|
|
163
|
+
current: progress.totalBytes,
|
|
164
|
+
format: "size",
|
|
165
|
+
},
|
|
166
|
+
absolute: {
|
|
167
|
+
total: tableNames.length,
|
|
168
|
+
current: index,
|
|
169
|
+
percent: (0, math_1.progressPercent)(tableNames.length, index),
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
84
174
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const dataFile = `${tableName}.txt`;
|
|
88
|
-
const successCsvDump = files.length === 2 &&
|
|
89
|
-
files.every((file) => file === schemaFile || file === dataFile);
|
|
90
|
-
if (!successCsvDump)
|
|
91
|
-
throw new error_1.AppError(`Invalid csv dump files: ${files.join(", ")}`);
|
|
92
|
-
const schemaPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableSchema}`);
|
|
93
|
-
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, schemaFile), schemaPath);
|
|
94
|
-
await compressAndClean?.(schemaPath);
|
|
95
|
-
const tablePath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableData}`);
|
|
96
|
-
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, dataFile), tablePath);
|
|
97
|
-
await compressAndClean?.(tablePath);
|
|
175
|
+
await sql.assertDumpFile(outPath);
|
|
176
|
+
await compressAndClean?.(outPath);
|
|
98
177
|
}
|
|
99
|
-
finally {
|
|
100
|
-
await (0, promises_1.rm)(tableSharedPath, { recursive: true });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
const outPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.table}`);
|
|
105
|
-
await sql.dump({
|
|
106
|
-
output: outPath,
|
|
107
|
-
items: [tableName],
|
|
108
|
-
database: this.config.database,
|
|
109
|
-
controller,
|
|
110
|
-
...(concurrency === 1 && {
|
|
111
|
-
onProgress(progress) {
|
|
112
|
-
data.onProgress({
|
|
113
|
-
relative: {
|
|
114
|
-
description: "Exporting",
|
|
115
|
-
payload: tableName,
|
|
116
|
-
current: progress.totalBytes,
|
|
117
|
-
format: "size",
|
|
118
|
-
},
|
|
119
|
-
absolute: {
|
|
120
|
-
total: tableNames.length,
|
|
121
|
-
current: index,
|
|
122
|
-
percent: (0, math_1.progressPercent)(tableNames.length, index),
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
},
|
|
126
|
-
}),
|
|
127
|
-
});
|
|
128
|
-
await sql.assertDumpFile(outPath);
|
|
129
|
-
await compressAndClean?.(outPath);
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
data.onProgress({
|
|
136
|
-
relative: { description: "Exporting" },
|
|
137
|
-
});
|
|
138
|
-
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.database}`);
|
|
139
|
-
await sql.dump({
|
|
140
|
-
output: outPath,
|
|
141
|
-
items: tableNames,
|
|
142
|
-
database: this.config.database,
|
|
143
|
-
onProgress: (progress) => data.onProgress({
|
|
144
|
-
absolute: {
|
|
145
|
-
description: "Exporting in single file",
|
|
146
|
-
current: progress.totalBytes,
|
|
147
|
-
format: "size",
|
|
148
178
|
},
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
data.onProgress({
|
|
183
|
+
relative: { description: "Exporting" },
|
|
184
|
+
});
|
|
185
|
+
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.database}`);
|
|
186
|
+
await sql.dump({
|
|
187
|
+
output: outPath,
|
|
188
|
+
items: tableNames,
|
|
189
|
+
database: this.config.database,
|
|
190
|
+
onProgress: (progress) => data.onProgress({
|
|
191
|
+
absolute: {
|
|
192
|
+
description: "Exporting in single file",
|
|
193
|
+
current: progress.totalBytes,
|
|
194
|
+
format: "size",
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
await sql.assertDumpFile(outPath);
|
|
199
|
+
await compressAndClean?.(outPath);
|
|
200
|
+
}
|
|
201
|
+
if (this.config.storedPrograms ?? true) {
|
|
202
|
+
data.onProgress({
|
|
203
|
+
relative: { description: "Exporting stored programs" },
|
|
204
|
+
});
|
|
205
|
+
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.stored}`);
|
|
206
|
+
await sql.dump({
|
|
207
|
+
database: this.config.database,
|
|
208
|
+
output: outPath,
|
|
209
|
+
onlyStoredPrograms: true,
|
|
210
|
+
});
|
|
211
|
+
await sql.assertDumpFile(outPath);
|
|
212
|
+
await compressAndClean?.(outPath);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
snapshotPath,
|
|
216
|
+
};
|
|
153
217
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
onlyStoredPrograms: true,
|
|
163
|
-
});
|
|
164
|
-
await sql.assertDumpFile(outPath);
|
|
165
|
-
await compressAndClean?.(outPath);
|
|
218
|
+
catch (e_1) {
|
|
219
|
+
env_1.error = e_1;
|
|
220
|
+
env_1.hasError = true;
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
const result_1 = __disposeResources(env_1);
|
|
224
|
+
if (result_1)
|
|
225
|
+
await result_1;
|
|
166
226
|
}
|
|
167
|
-
return {
|
|
168
|
-
snapshotPath,
|
|
169
|
-
};
|
|
170
227
|
}
|
|
171
228
|
async prepareRestore(data) {
|
|
172
229
|
return {
|
|
@@ -175,145 +232,157 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
175
232
|
};
|
|
176
233
|
}
|
|
177
234
|
async restore(data) {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
database
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
relative: {
|
|
232
|
-
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
233
|
-
payload: [...buffer.keys()].join(", "),
|
|
234
|
-
},
|
|
235
|
-
absolute: {
|
|
236
|
-
total: files.length,
|
|
237
|
-
current: processed,
|
|
238
|
-
percent: (0, math_1.progressPercent)(files.length, processed),
|
|
235
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
236
|
+
try {
|
|
237
|
+
const sql = __addDisposableResource(env_2, await (0, mysql_1.createMysqlCli)({
|
|
238
|
+
...this.config,
|
|
239
|
+
database: undefined,
|
|
240
|
+
verbose: data.options.verbose,
|
|
241
|
+
}), true);
|
|
242
|
+
const snapshotPath = data.snapshotPath;
|
|
243
|
+
const params = {
|
|
244
|
+
packageName: data.package.name,
|
|
245
|
+
snapshotId: data.options.snapshotId,
|
|
246
|
+
snapshotDate: data.snapshot.date,
|
|
247
|
+
action: "restore",
|
|
248
|
+
database: undefined,
|
|
249
|
+
};
|
|
250
|
+
const database = {
|
|
251
|
+
name: (0, config_1.resolveDatabaseName)(this.config.database, params),
|
|
252
|
+
};
|
|
253
|
+
if (this.config.targetDatabase && !data.options.initial)
|
|
254
|
+
database.name = (0, config_1.resolveDatabaseName)(this.config.targetDatabase.name, {
|
|
255
|
+
...params,
|
|
256
|
+
database: database.name,
|
|
257
|
+
});
|
|
258
|
+
const [files, compressed] = (0, fs_1.groupFiles)(await (0, fs_1.readDir)(snapshotPath), Object.values(suffix));
|
|
259
|
+
// Database check
|
|
260
|
+
if (files.some((f) => f.endsWith(suffix.database)) &&
|
|
261
|
+
!(await sql.isDatabaseEmpty(database.name)))
|
|
262
|
+
throw new error_1.AppError(`Target database is not empty: ${database.name}`);
|
|
263
|
+
// Table check
|
|
264
|
+
const restoreTables = [
|
|
265
|
+
...new Set(...files
|
|
266
|
+
.filter((f) => (0, string_1.endsWith)(f, [suffix.table, suffix.tableSchema, suffix.tableData]))
|
|
267
|
+
.map((f) => f.split(".")[0])),
|
|
268
|
+
];
|
|
269
|
+
const serverTables = await sql.fetchTableNames(database.name);
|
|
270
|
+
const errorTables = restoreTables.filter((v) => serverTables.includes(v));
|
|
271
|
+
if (errorTables.length)
|
|
272
|
+
throw new error_1.AppError(`Target table already exists: ${errorTables.join(", ")}`);
|
|
273
|
+
// Data check
|
|
274
|
+
const dataFiles = files.filter((f) => f.endsWith(suffix.tableData));
|
|
275
|
+
const sharedDir = dataFiles.length
|
|
276
|
+
? await sql.initSharedDir(this.config.csvSharedPath)
|
|
277
|
+
: undefined;
|
|
278
|
+
await sql.createDatabase(database);
|
|
279
|
+
if (data.options.verbose)
|
|
280
|
+
(0, cli_1.logExec)("readdir", [snapshotPath]);
|
|
281
|
+
const concurrency = this.config.concurrency ?? 1;
|
|
282
|
+
let processed = 0;
|
|
283
|
+
await (0, async_1.runParallel)({
|
|
284
|
+
items: files.filter((f) => !f.endsWith(suffix.tableData)),
|
|
285
|
+
concurrency,
|
|
286
|
+
onFinished: () => {
|
|
287
|
+
processed++;
|
|
239
288
|
},
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
289
|
+
onChange: ({ buffer }) => data.onProgress({
|
|
290
|
+
relative: {
|
|
291
|
+
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
292
|
+
payload: [...buffer.keys()].join(", "),
|
|
293
|
+
},
|
|
294
|
+
absolute: {
|
|
295
|
+
total: files.length,
|
|
296
|
+
current: processed,
|
|
297
|
+
percent: (0, math_1.progressPercent)(files.length, processed),
|
|
298
|
+
},
|
|
299
|
+
}),
|
|
300
|
+
onItem: async ({ item: file, controller }) => {
|
|
301
|
+
let path = (0, path_1.join)(snapshotPath, file);
|
|
302
|
+
const tempDir = compressed[file]
|
|
303
|
+
? await (0, temp_1.useTempDir)(exports.mysqlDumpTaskName, "task", "restore", "decompress")
|
|
304
|
+
: undefined;
|
|
305
|
+
try {
|
|
306
|
+
if (tempDir) {
|
|
307
|
+
await (0, tar_1.extractTar)({
|
|
308
|
+
input: (0, path_1.join)(snapshotPath, compressed[file]),
|
|
309
|
+
output: tempDir.path,
|
|
310
|
+
decompress: true,
|
|
311
|
+
verbose: data.options.verbose,
|
|
312
|
+
});
|
|
313
|
+
path = await (0, fs_1.ensureSingleFile)(tempDir.path);
|
|
314
|
+
}
|
|
315
|
+
await sql.importFile({
|
|
316
|
+
path,
|
|
317
|
+
database: database.name,
|
|
318
|
+
controller,
|
|
253
319
|
});
|
|
254
|
-
path = await (0, fs_1.ensureSingleFile)(tempDir.path);
|
|
255
320
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
controller,
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
finally {
|
|
263
|
-
await tempDir?.[Symbol.asyncDispose]();
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
await (0, async_1.runParallel)({
|
|
268
|
-
items: dataFiles,
|
|
269
|
-
concurrency,
|
|
270
|
-
onFinished: () => {
|
|
271
|
-
processed++;
|
|
272
|
-
},
|
|
273
|
-
onChange: ({ buffer }) => data.onProgress({
|
|
274
|
-
relative: {
|
|
275
|
-
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
276
|
-
payload: [...buffer.keys()].join(", "),
|
|
321
|
+
finally {
|
|
322
|
+
await tempDir?.[Symbol.asyncDispose]();
|
|
323
|
+
}
|
|
277
324
|
},
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
325
|
+
});
|
|
326
|
+
await (0, async_1.runParallel)({
|
|
327
|
+
items: dataFiles,
|
|
328
|
+
concurrency,
|
|
329
|
+
onFinished: () => {
|
|
330
|
+
processed++;
|
|
282
331
|
},
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
332
|
+
onChange: ({ buffer }) => data.onProgress({
|
|
333
|
+
relative: {
|
|
334
|
+
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
335
|
+
payload: [...buffer.keys()].join(", "),
|
|
336
|
+
},
|
|
337
|
+
absolute: {
|
|
338
|
+
total: files.length,
|
|
339
|
+
current: processed,
|
|
340
|
+
percent: (0, math_1.progressPercent)(files.length, processed),
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
onItem: async ({ item: file, controller }) => {
|
|
344
|
+
const id = data.snapshot.id.slice(0, 8);
|
|
345
|
+
const tableName = file.slice(0, suffix.tableData.length * -1);
|
|
346
|
+
const sharedName = `tmp-dtt-restore-${id}-${tableName}.data.csv`;
|
|
347
|
+
const temp = (0, temp_1.useTempFile)((0, path_1.join)(sharedDir, sharedName));
|
|
348
|
+
try {
|
|
349
|
+
let csvFile = temp.path;
|
|
350
|
+
if (compressed[file]) {
|
|
351
|
+
await (0, fs_1.mkdirIfNotExists)(temp.path);
|
|
352
|
+
await (0, tar_1.extractTar)({
|
|
353
|
+
input: (0, path_1.join)(snapshotPath, compressed[file]),
|
|
354
|
+
output: temp.path,
|
|
355
|
+
decompress: true,
|
|
356
|
+
verbose: data.options.verbose,
|
|
357
|
+
});
|
|
358
|
+
csvFile = await (0, fs_1.ensureSingleFile)(temp.path);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
const sourceFile = (0, path_1.join)(snapshotPath, file);
|
|
362
|
+
await (0, fs_1.safeRename)(sourceFile, temp.path);
|
|
363
|
+
}
|
|
364
|
+
await sql.importCsvFile({
|
|
365
|
+
path: csvFile,
|
|
366
|
+
database: database.name,
|
|
367
|
+
table: tableName,
|
|
368
|
+
controller,
|
|
298
369
|
});
|
|
299
|
-
csvFile = await (0, fs_1.ensureSingleFile)(temp.path);
|
|
300
370
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
await (0, fs_1.safeRename)(sourceFile, temp.path);
|
|
371
|
+
finally {
|
|
372
|
+
await temp[Symbol.asyncDispose]();
|
|
304
373
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
catch (e_2) {
|
|
378
|
+
env_2.error = e_2;
|
|
379
|
+
env_2.hasError = true;
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
const result_2 = __disposeResources(env_2);
|
|
383
|
+
if (result_2)
|
|
384
|
+
await result_2;
|
|
385
|
+
}
|
|
317
386
|
}
|
|
318
387
|
}
|
|
319
388
|
exports.MysqlDumpTask = MysqlDumpTask;
|
package/lib/utils/cli.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Listr3TaskResultEnd } from "./list";
|
|
3
|
+
import chalk from "chalk";
|
|
3
4
|
export declare const showCursorCommand = "\u001B[?25h";
|
|
4
5
|
export declare function renderProgressBar(progress: number, size?: number, subprogress?: number): string;
|
|
5
6
|
export declare function logExec(command: string, argv?: string[], env?: NodeJS.ProcessEnv, logToStderr?: boolean): void;
|
|
@@ -29,3 +30,7 @@ export declare function stringifyOptions<T1, T2 extends {
|
|
|
29
30
|
[K in keyof T1]: unknown;
|
|
30
31
|
}>(options: OptionsConfig<T1, T2>, object: any): string[];
|
|
31
32
|
export declare function confirm(message: string): Promise<unknown>;
|
|
33
|
+
export declare function waitForStdDrain(ms?: number): Promise<void>;
|
|
34
|
+
export declare function colorizeValue(value: unknown, color?: typeof chalk.ForegroundColor): string;
|
|
35
|
+
export declare function colorizeObject(input: Record<string, any>): string;
|
|
36
|
+
export declare function logJson(ctx: string, msg: string, data?: any): void;
|
package/lib/utils/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.confirm = exports.stringifyOptions = exports.parseOptions = exports.renderObject = exports.renderListTaskItem = exports.renderError = exports.renderResult = exports.logExec = exports.renderProgressBar = exports.showCursorCommand = void 0;
|
|
6
|
+
exports.logJson = exports.colorizeObject = exports.colorizeValue = exports.waitForStdDrain = exports.confirm = exports.stringifyOptions = exports.parseOptions = exports.renderObject = exports.renderListTaskItem = exports.renderError = exports.renderResult = exports.logExec = exports.renderProgressBar = exports.showCursorCommand = void 0;
|
|
7
7
|
const error_1 = require("./error");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const chalk_2 = require("chalk");
|
|
@@ -149,3 +149,47 @@ function confirm(message) {
|
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
151
|
exports.confirm = confirm;
|
|
152
|
+
async function waitForStdDrain(ms) {
|
|
153
|
+
await Promise.all([process.stdout, process.stderr].map((stream) => new Promise((resolve) => {
|
|
154
|
+
{
|
|
155
|
+
const finish = () => {
|
|
156
|
+
clearTimeout(timeout);
|
|
157
|
+
resolve();
|
|
158
|
+
};
|
|
159
|
+
const timeout = ms ? setTimeout(finish, ms) : undefined;
|
|
160
|
+
const drained = stream.write("", finish);
|
|
161
|
+
if (drained)
|
|
162
|
+
finish();
|
|
163
|
+
}
|
|
164
|
+
})));
|
|
165
|
+
}
|
|
166
|
+
exports.waitForStdDrain = waitForStdDrain;
|
|
167
|
+
function colorizeValue(value, color) {
|
|
168
|
+
const json = JSON.stringify(value);
|
|
169
|
+
return color ? chalk_1.default[color](json) : json;
|
|
170
|
+
}
|
|
171
|
+
exports.colorizeValue = colorizeValue;
|
|
172
|
+
function colorizeObject(input) {
|
|
173
|
+
const object = {};
|
|
174
|
+
for (const key in input) {
|
|
175
|
+
const value = input[key];
|
|
176
|
+
object[colorizeValue(key)] =
|
|
177
|
+
typeof value === "object" && !!value && !Array.isArray(value)
|
|
178
|
+
? colorizeObject(value)
|
|
179
|
+
: colorizeValue(value, "green");
|
|
180
|
+
}
|
|
181
|
+
const values = Object.entries(object)
|
|
182
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
183
|
+
.join(", ");
|
|
184
|
+
return `{ ${values} }`;
|
|
185
|
+
}
|
|
186
|
+
exports.colorizeObject = colorizeObject;
|
|
187
|
+
function logJson(ctx, msg, data) {
|
|
188
|
+
const json = colorizeObject({
|
|
189
|
+
ctx,
|
|
190
|
+
msg,
|
|
191
|
+
data,
|
|
192
|
+
});
|
|
193
|
+
console.log(json);
|
|
194
|
+
}
|
|
195
|
+
exports.logJson = logJson;
|
|
@@ -26,8 +26,7 @@ function createJobs(actions, worker) {
|
|
|
26
26
|
}
|
|
27
27
|
function createCronServer(options, config) {
|
|
28
28
|
const worker = async (action, index) => {
|
|
29
|
-
|
|
30
|
-
console.info(`> [job] ${index} - ${action.name}`);
|
|
29
|
+
let pid = 0;
|
|
31
30
|
try {
|
|
32
31
|
const Command = command_1.datatruckCommandMap[action.name];
|
|
33
32
|
const command = new Command({ config: { packages: [], repositories: [] } }, {});
|
|
@@ -35,19 +34,24 @@ function createCronServer(options, config) {
|
|
|
35
34
|
? ({ ...action.options, confirm: true })
|
|
36
35
|
: action.options);
|
|
37
36
|
const [node, bin] = process.argv;
|
|
38
|
-
|
|
37
|
+
const p = new async_process_1.AsyncProcess(node, [
|
|
39
38
|
process.env.pm_exec_path ?? bin,
|
|
40
39
|
"-c",
|
|
41
40
|
config.configPath,
|
|
42
41
|
action.name,
|
|
43
42
|
...cliOptions,
|
|
44
|
-
], { $log: config.verbose });
|
|
43
|
+
], { $log: config.verbose, $exitCode: false });
|
|
44
|
+
pid = p.child.pid || 0;
|
|
45
45
|
if (config.log)
|
|
46
|
-
|
|
46
|
+
(0, cli_1.logJson)("cron-server", `${action.name} started`, { pid });
|
|
47
|
+
const exitCode = await p.waitForClose();
|
|
48
|
+
if (config.log)
|
|
49
|
+
(0, cli_1.logJson)("cron-server", `${action.name} finished`, { pid, exitCode });
|
|
47
50
|
}
|
|
48
51
|
catch (error) {
|
|
49
52
|
if (config.log)
|
|
50
|
-
|
|
53
|
+
(0, cli_1.logJson)("cron-server", `${action.name} failed`, { pid });
|
|
54
|
+
console.error(error);
|
|
51
55
|
}
|
|
52
56
|
};
|
|
53
57
|
let jobs = createJobs(options.actions || [], worker);
|
|
@@ -55,12 +59,14 @@ function createCronServer(options, config) {
|
|
|
55
59
|
onRead: () => ConfigAction_1.ConfigAction.findAndParseFile(config.configPath),
|
|
56
60
|
onCheck: (prev, current) => (0, string_1.compareJsons)(prev, current),
|
|
57
61
|
onError: (error) => {
|
|
58
|
-
if (config.log)
|
|
59
|
-
|
|
62
|
+
if (config.log) {
|
|
63
|
+
(0, cli_1.logJson)("cron-server", "job update error");
|
|
64
|
+
console.error(error);
|
|
65
|
+
}
|
|
60
66
|
},
|
|
61
67
|
onChange: (data) => {
|
|
62
68
|
if (config.log)
|
|
63
|
-
|
|
69
|
+
(0, cli_1.logJson)("cron-server", "jobs updated");
|
|
64
70
|
handler.stop();
|
|
65
71
|
const cron = data?.server?.cron;
|
|
66
72
|
const enabled = cron?.enabled ?? true;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createDatatruckRepositoryServer = exports.headerKey = void 0;
|
|
4
4
|
const ConfigAction_1 = require("../../actions/ConfigAction");
|
|
5
|
+
const cli_1 = require("../cli");
|
|
5
6
|
const http_1 = require("../http");
|
|
6
7
|
const virtual_fs_1 = require("../virtual-fs");
|
|
7
8
|
const fs_1 = require("fs");
|
|
@@ -77,7 +78,10 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
|
|
|
77
78
|
return res.end();
|
|
78
79
|
}
|
|
79
80
|
if (config.log)
|
|
80
|
-
|
|
81
|
+
(0, cli_1.logJson)("repository-server", "request", {
|
|
82
|
+
repository,
|
|
83
|
+
url: req.url,
|
|
84
|
+
});
|
|
81
85
|
const fs = new virtual_fs_1.LocalFs({
|
|
82
86
|
backend: backend.path,
|
|
83
87
|
});
|
|
@@ -123,12 +127,18 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
|
|
|
123
127
|
res.write(JSON.stringify(json));
|
|
124
128
|
}
|
|
125
129
|
if (config.log)
|
|
126
|
-
|
|
130
|
+
(0, cli_1.logJson)("repository-server", "request finished", {
|
|
131
|
+
url: req.url,
|
|
132
|
+
});
|
|
127
133
|
res.end();
|
|
128
134
|
}
|
|
129
135
|
catch (error) {
|
|
130
|
-
if (config.log)
|
|
131
|
-
|
|
136
|
+
if (config.log) {
|
|
137
|
+
(0, cli_1.logJson)("repository-server", "request failed", {
|
|
138
|
+
url: req.url,
|
|
139
|
+
});
|
|
140
|
+
console.error(error);
|
|
141
|
+
}
|
|
132
142
|
res.statusCode = 500;
|
|
133
143
|
res.statusMessage = error.message;
|
|
134
144
|
res.end();
|
package/lib/utils/mysql.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type MysqlCliOptions = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function assertDumpFile(path: string): Promise<void>;
|
|
13
13
|
export declare function createMysqlCli(options: MysqlCliOptions): Promise<{
|
|
14
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
14
15
|
options: MysqlCliOptions;
|
|
15
16
|
initSharedDir: (sharedDir?: string) => Promise<string>;
|
|
16
17
|
args: () => Promise<string[]>;
|
package/lib/utils/mysql.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.3",
|
|
4
4
|
"description": "Tool for creating and managing backups",
|
|
5
5
|
"homepage": "https://github.com/swordev/datatruck#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
"config.schema.json"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@supercharge/promise-pool": "^3.1.
|
|
29
|
+
"@supercharge/promise-pool": "^3.1.1",
|
|
30
30
|
"ajv": "^8.12.0",
|
|
31
31
|
"async": "^3.2.5",
|
|
32
32
|
"chalk": "^4.1.2",
|
|
33
|
-
"commander": "^
|
|
34
|
-
"croner": "^8.0.
|
|
33
|
+
"commander": "^12.0.0",
|
|
34
|
+
"croner": "^8.0.1",
|
|
35
35
|
"dayjs": "^1.11.10",
|
|
36
36
|
"fast-folder-size": "^2.2.0",
|
|
37
37
|
"fast-glob": "^3.3.2",
|
|
38
|
-
"listr2": "^8.0.
|
|
38
|
+
"listr2": "^8.0.2",
|
|
39
39
|
"micromatch": "^4.0.5",
|
|
40
|
-
"mysql2": "^3.
|
|
40
|
+
"mysql2": "^3.9.1",
|
|
41
41
|
"tty-table": "^4.2.3",
|
|
42
42
|
"yaml": "^2.3.4"
|
|
43
43
|
},
|