@datatruck/cli 0.26.2 → 0.28.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.
- package/Action/BackupAction.d.ts +69 -34
- package/Action/BackupAction.js +284 -244
- package/Action/CleanCacheAction.d.ts +8 -4
- package/Action/CleanCacheAction.js +8 -5
- package/Action/ConfigAction.d.ts +12 -5
- package/Action/ConfigAction.js +14 -18
- package/Action/CopyAction.d.ts +49 -0
- package/Action/CopyAction.js +144 -0
- package/Action/InitAction.d.ts +3 -3
- package/Action/InitAction.js +9 -9
- package/Action/PruneAction.d.ts +9 -9
- package/Action/PruneAction.js +39 -23
- package/Action/RestoreAction.d.ts +48 -23
- package/Action/RestoreAction.js +158 -195
- package/Action/SnapshotsAction.d.ts +8 -8
- package/Action/SnapshotsAction.js +8 -8
- package/CHANGELOG.md +495 -0
- package/Command/BackupCommand.d.ts +6 -4
- package/Command/BackupCommand.js +9 -26
- package/Command/CleanCacheCommand.d.ts +4 -4
- package/Command/CleanCacheCommand.js +26 -5
- package/Command/CommandAbstract.d.ts +10 -7
- package/Command/CommandAbstract.js +4 -1
- package/Command/ConfigCommand.d.ts +6 -9
- package/Command/ConfigCommand.js +13 -8
- package/Command/CopyCommand.d.ts +15 -0
- package/Command/CopyCommand.js +61 -0
- package/Command/InitCommand.d.ts +4 -4
- package/Command/InitCommand.js +11 -15
- package/Command/PruneCommand.d.ts +3 -3
- package/Command/PruneCommand.js +13 -12
- package/Command/RestoreCommand.js +9 -17
- package/Command/SnapshotsCommand.d.ts +4 -4
- package/Command/SnapshotsCommand.js +16 -15
- package/Command/StartServerCommand.d.ts +6 -0
- package/Command/StartServerCommand.js +24 -0
- package/Config/Config.d.ts +11 -0
- package/Config/Config.js +43 -0
- package/Config/PrunePolicyConfig.d.ts +2 -2
- package/Factory/CommandFactory.d.ts +29 -33
- package/Factory/CommandFactory.js +32 -53
- package/Factory/RepositoryFactory.d.ts +1 -1
- package/Factory/RepositoryFactory.js +3 -3
- package/Factory/TaskFactory.d.ts +1 -1
- package/Factory/TaskFactory.js +3 -3
- package/Repository/DatatruckRepository.d.ts +17 -16
- package/Repository/DatatruckRepository.js +131 -149
- package/Repository/GitRepository.d.ts +9 -8
- package/Repository/GitRepository.js +22 -25
- package/Repository/RepositoryAbstract.d.ts +39 -37
- package/Repository/RepositoryAbstract.js +4 -5
- package/Repository/ResticRepository.d.ts +9 -8
- package/Repository/ResticRepository.js +30 -28
- package/Task/GitTask.d.ts +6 -7
- package/Task/GitTask.js +24 -30
- package/Task/MariadbTask.d.ts +4 -5
- package/Task/MariadbTask.js +26 -32
- package/Task/MssqlTask.d.ts +5 -3
- package/Task/MssqlTask.js +11 -12
- package/Task/MysqlDumpTask.d.ts +10 -3
- package/Task/MysqlDumpTask.js +107 -31
- package/Task/ScriptTask.d.ts +23 -18
- package/Task/ScriptTask.js +34 -24
- package/Task/SqlDumpTaskAbstract.d.ts +8 -3
- package/Task/SqlDumpTaskAbstract.js +31 -19
- package/Task/TaskAbstract.d.ts +24 -25
- package/Task/TaskAbstract.js +6 -10
- package/cli.js +14 -5
- package/config.schema.json +124 -3
- package/package.json +4 -5
- package/utils/DataFormat.d.ts +23 -12
- package/utils/DataFormat.js +36 -14
- package/utils/cli.d.ts +2 -9
- package/utils/cli.js +9 -52
- package/utils/datatruck/client.d.ts +24 -0
- package/utils/datatruck/client.js +99 -0
- package/utils/datatruck/config.d.ts +8 -6
- package/utils/datatruck/config.js +18 -3
- package/utils/datatruck/paths.d.ts +5 -9
- package/utils/datatruck/paths.js +2 -2
- package/utils/datatruck/server.d.ts +21 -0
- package/utils/datatruck/server.js +96 -0
- package/utils/datatruck/snapshot.d.ts +2 -2
- package/utils/date.d.ts +7 -3
- package/utils/date.js +22 -14
- package/utils/fs.d.ts +27 -15
- package/utils/fs.js +110 -62
- package/utils/http.d.ts +21 -0
- package/utils/http.js +154 -0
- package/utils/list.d.ts +64 -0
- package/utils/list.js +145 -0
- package/utils/mysql.d.ts +2 -0
- package/utils/mysql.js +21 -2
- package/utils/process.d.ts +1 -0
- package/utils/process.js +24 -31
- package/utils/progress.d.ts +33 -0
- package/utils/progress.js +113 -0
- package/utils/steps.d.ts +11 -0
- package/utils/steps.js +22 -10
- package/utils/stream.d.ts +7 -0
- package/utils/stream.js +10 -0
- package/utils/string.d.ts +0 -1
- package/utils/string.js +1 -13
- package/utils/tar.d.ts +10 -3
- package/utils/tar.js +70 -44
- package/utils/temp.d.ts +26 -0
- package/utils/temp.js +133 -0
- package/utils/virtual-fs.d.ts +37 -0
- package/utils/virtual-fs.js +65 -0
- package/Action/BackupSessionsAction.d.ts +0 -13
- package/Action/BackupSessionsAction.js +0 -18
- package/Action/RestoreSessionsAction.d.ts +0 -13
- package/Action/RestoreSessionsAction.js +0 -18
- package/Command/BackupSessionsCommand.d.ts +0 -12
- package/Command/BackupSessionsCommand.js +0 -92
- package/Command/RestoreSessionsCommand.d.ts +0 -12
- package/Command/RestoreSessionsCommand.js +0 -91
- package/Decorator/EntityDecorator.d.ts +0 -11
- package/Decorator/EntityDecorator.js +0 -17
- package/Entity/BackupSessionEntity.d.ts +0 -6
- package/Entity/BackupSessionEntity.js +0 -25
- package/Entity/BackupSessionRepositoryEntity.d.ts +0 -6
- package/Entity/BackupSessionRepositoryEntity.js +0 -25
- package/Entity/BackupSessionTaskEntity.d.ts +0 -5
- package/Entity/BackupSessionTaskEntity.js +0 -24
- package/Entity/CrudEntityAbstract.d.ts +0 -5
- package/Entity/CrudEntityAbstract.js +0 -9
- package/Entity/RestoreSessionEntity.d.ts +0 -5
- package/Entity/RestoreSessionEntity.js +0 -24
- package/Entity/RestoreSessionRepositoryEntity.d.ts +0 -6
- package/Entity/RestoreSessionRepositoryEntity.js +0 -25
- package/Entity/RestoreSessionTaskEntity.d.ts +0 -5
- package/Entity/RestoreSessionTaskEntity.js +0 -24
- package/Entity/StateEntityAbstract.d.ts +0 -9
- package/Entity/StateEntityAbstract.js +0 -12
- package/Factory/EntityFactory.d.ts +0 -6
- package/Factory/EntityFactory.js +0 -40
- package/SessionDriver/ConsoleSessionDriver.d.ts +0 -42
- package/SessionDriver/ConsoleSessionDriver.js +0 -208
- package/SessionDriver/SessionDriverAbstract.d.ts +0 -77
- package/SessionDriver/SessionDriverAbstract.js +0 -28
- package/SessionDriver/SqliteSessionDriver.d.ts +0 -20
- package/SessionDriver/SqliteSessionDriver.js +0 -173
- package/SessionManager/BackupSessionManager.d.ts +0 -45
- package/SessionManager/BackupSessionManager.js +0 -218
- package/SessionManager/RestoreSessionManager.d.ts +0 -47
- package/SessionManager/RestoreSessionManager.js +0 -218
- package/SessionManager/SessionManagerAbstract.d.ts +0 -18
- package/SessionManager/SessionManagerAbstract.js +0 -36
- package/migrations/001-initial.sql +0 -98
- package/utils/entity.d.ts +0 -4
- package/utils/entity.js +0 -10
package/Task/MariadbTask.js
CHANGED
|
@@ -7,6 +7,7 @@ const fs_1 = require("../utils/fs");
|
|
|
7
7
|
const math_1 = require("../utils/math");
|
|
8
8
|
const process_1 = require("../utils/process");
|
|
9
9
|
const tar_1 = require("../utils/tar");
|
|
10
|
+
const temp_1 = require("../utils/temp");
|
|
10
11
|
const TaskAbstract_1 = require("./TaskAbstract");
|
|
11
12
|
const assert_1 = require("assert");
|
|
12
13
|
const fs_2 = require("fs");
|
|
@@ -105,19 +106,13 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
105
106
|
get command() {
|
|
106
107
|
return this.config.command ?? "mariabackup";
|
|
107
108
|
}
|
|
108
|
-
async
|
|
109
|
-
return {
|
|
110
|
-
targetPath: await this.mkTmpDir(MariadbTask.name),
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
async onBackup(data) {
|
|
109
|
+
async backup(data) {
|
|
114
110
|
this.verbose = data.options.verbose;
|
|
115
111
|
const config = this.config;
|
|
116
112
|
const command = this.command;
|
|
117
113
|
const sourcePath = data.package.path;
|
|
118
|
-
const
|
|
114
|
+
const snapshotPath = await (0, temp_1.mkTmpDir)(exports.mariadbTaskName, "task", "backup", "snapshot");
|
|
119
115
|
(0, assert_1.ok)(typeof sourcePath === "string");
|
|
120
|
-
(0, assert_1.ok)(typeof targetPath === "string");
|
|
121
116
|
const { parallel, compress } = normalizeConfig(config);
|
|
122
117
|
const args = [
|
|
123
118
|
`--backup`,
|
|
@@ -135,7 +130,7 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
135
130
|
args.push(`--stream=xbstream`);
|
|
136
131
|
}
|
|
137
132
|
else {
|
|
138
|
-
args.push(`--target-dir=${
|
|
133
|
+
args.push(`--target-dir=${snapshotPath}`);
|
|
139
134
|
}
|
|
140
135
|
if (config.includeDatabases)
|
|
141
136
|
args.push(`--databases=${config.includeDatabases.join(" ")}`);
|
|
@@ -164,7 +159,7 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
164
159
|
const matches = pathRegex.exec(text);
|
|
165
160
|
if (matches)
|
|
166
161
|
current++;
|
|
167
|
-
|
|
162
|
+
data.onProgress({
|
|
168
163
|
relative: {
|
|
169
164
|
payload: matches ? matches[1] : text,
|
|
170
165
|
},
|
|
@@ -177,9 +172,9 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
177
172
|
}
|
|
178
173
|
};
|
|
179
174
|
const stats = { xbFiles: total };
|
|
180
|
-
await (0, promises_1.writeFile)((0, path_1.join)(
|
|
175
|
+
await (0, promises_1.writeFile)((0, path_1.join)(snapshotPath, "stats.dtt.json"), JSON.stringify(stats));
|
|
181
176
|
if (compress) {
|
|
182
|
-
const p0 = (0, fs_2.createWriteStream)((0, path_1.join)(
|
|
177
|
+
const p0 = (0, fs_2.createWriteStream)((0, path_1.join)(snapshotPath, "db.xb.gz"));
|
|
183
178
|
p1 = (0, process_1.createProcess)(command, args, {
|
|
184
179
|
$log: {
|
|
185
180
|
exec: this.verbose,
|
|
@@ -210,23 +205,22 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
210
205
|
$onExitCode: (code) => `Exit code: ${code} - ${lastLineText}`,
|
|
211
206
|
});
|
|
212
207
|
await p1;
|
|
213
|
-
await (0, process_1.exec)(command, [`--prepare`, `--target-dir=${
|
|
208
|
+
await (0, process_1.exec)(command, [`--prepare`, `--target-dir=${snapshotPath}`], undefined, {
|
|
214
209
|
log: this.verbose,
|
|
215
210
|
stderr: { onData: () => { } },
|
|
216
211
|
});
|
|
217
212
|
}
|
|
213
|
+
return { snapshotPath };
|
|
218
214
|
}
|
|
219
|
-
async
|
|
215
|
+
async restore(data) {
|
|
220
216
|
this.verbose = data.options.verbose;
|
|
221
|
-
const
|
|
222
|
-
(0, assert_1.ok)(typeof restorePath === "string");
|
|
223
|
-
await (0, fs_1.mkdirIfNotExists)(restorePath);
|
|
217
|
+
const snapshotPath = data.snapshotPath;
|
|
224
218
|
const removeFiles = [];
|
|
225
219
|
let files = [];
|
|
226
220
|
const reloadFiles = async (data = {}) => {
|
|
227
221
|
if (data.removeFile)
|
|
228
222
|
removeFiles.push(data.removeFile);
|
|
229
|
-
return (files = (await (0, fs_1.readDir)(
|
|
223
|
+
return (files = (await (0, fs_1.readDir)(snapshotPath)).filter((v) => !removeFiles.includes(v)));
|
|
230
224
|
};
|
|
231
225
|
await reloadFiles();
|
|
232
226
|
const absolute = {
|
|
@@ -241,7 +235,7 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
241
235
|
const statsFile = files.find((file) => file === "stats.dtt.json");
|
|
242
236
|
let stats;
|
|
243
237
|
if (statsFile) {
|
|
244
|
-
const statsFilePath = (0, path_1.join)(
|
|
238
|
+
const statsFilePath = (0, path_1.join)(snapshotPath, statsFile);
|
|
245
239
|
const statsBuffer = await (0, promises_1.readFile)(statsFilePath);
|
|
246
240
|
stats = JSON.parse(statsBuffer.toString());
|
|
247
241
|
await reloadFiles({ removeFile: statsFile });
|
|
@@ -253,16 +247,16 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
253
247
|
if (files.length === 1 && zipFile) {
|
|
254
248
|
absolute.description = "Extracting";
|
|
255
249
|
absolute.payload = zipFile;
|
|
256
|
-
|
|
250
|
+
data.onProgress({
|
|
257
251
|
absolute,
|
|
258
252
|
});
|
|
259
253
|
await (0, tar_1.extractTar)({
|
|
260
|
-
input: (0, path_1.join)(
|
|
254
|
+
input: (0, path_1.join)(snapshotPath, zipFile),
|
|
261
255
|
decompress: true,
|
|
262
|
-
output:
|
|
256
|
+
output: snapshotPath,
|
|
263
257
|
verbose: this.verbose,
|
|
264
|
-
|
|
265
|
-
|
|
258
|
+
onEntry(item) {
|
|
259
|
+
data.onProgress({
|
|
266
260
|
absolute,
|
|
267
261
|
relative: {
|
|
268
262
|
payload: item.path,
|
|
@@ -279,23 +273,23 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
279
273
|
absolute.current++;
|
|
280
274
|
absolute.percent = (0, math_1.progressPercent)(absolute.total, absolute.current);
|
|
281
275
|
if (files.length === 1 && xbFile) {
|
|
282
|
-
const xbFilePath = (0, path_1.join)(
|
|
276
|
+
const xbFilePath = (0, path_1.join)(snapshotPath, xbFile);
|
|
283
277
|
const xbStream = (0, fs_2.createReadStream)(xbFilePath);
|
|
284
278
|
removeFiles.push(xbFile);
|
|
285
279
|
absolute.description = "Extracting stream";
|
|
286
280
|
absolute.payload = xbFile;
|
|
287
|
-
|
|
281
|
+
data.onProgress({
|
|
288
282
|
absolute,
|
|
289
283
|
});
|
|
290
284
|
let currentXbFiles = 0;
|
|
291
285
|
const { parallel } = normalizeConfig({ parallel: this.config.parallel });
|
|
292
|
-
const p1 = (0, process_1.createProcess)("mbstream", ["-x", "-C",
|
|
286
|
+
const p1 = (0, process_1.createProcess)("mbstream", ["-x", "-C", snapshotPath, "-v", "-p", parallel], {
|
|
293
287
|
$log: this.verbose,
|
|
294
288
|
$stderr: {
|
|
295
289
|
parseLines: true,
|
|
296
|
-
|
|
290
|
+
onData(line) {
|
|
297
291
|
const { text: path } = parseLine(line);
|
|
298
|
-
|
|
292
|
+
data.onProgress({
|
|
299
293
|
absolute,
|
|
300
294
|
relative: {
|
|
301
295
|
payload: path,
|
|
@@ -318,10 +312,10 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
318
312
|
absolute.percent = (0, math_1.progressPercent)(absolute.total, absolute.current);
|
|
319
313
|
if (files.length === 1 && xbFile) {
|
|
320
314
|
absolute.description = "Preparing";
|
|
321
|
-
|
|
315
|
+
data.onProgress({
|
|
322
316
|
absolute,
|
|
323
317
|
});
|
|
324
|
-
await (0, process_1.exec)(this.command, [`--prepare`, `--target-dir=${
|
|
318
|
+
await (0, process_1.exec)(this.command, [`--prepare`, `--target-dir=${snapshotPath}`], undefined, {
|
|
325
319
|
log: this.verbose,
|
|
326
320
|
stderr: { onData: () => { } },
|
|
327
321
|
});
|
|
@@ -330,7 +324,7 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
330
324
|
removeFiles.push(...files.filter((file) => file.startsWith("ib_logfile")));
|
|
331
325
|
// Remove files
|
|
332
326
|
for (const file of removeFiles) {
|
|
333
|
-
const filePath = (0, path_1.join)(
|
|
327
|
+
const filePath = (0, path_1.join)(snapshotPath, file);
|
|
334
328
|
if (this.verbose)
|
|
335
329
|
(0, cli_1.logExec)("rm", [filePath]);
|
|
336
330
|
await (0, promises_1.rm)(filePath);
|
package/Task/MssqlTask.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TaskBackupData, TaskRestoreData, TaskAbstract } from "./TaskAbstract";
|
|
2
2
|
import { JSONSchema7 } from "json-schema";
|
|
3
3
|
export type MssqlTaskConfigType = {
|
|
4
4
|
command?: string;
|
|
@@ -17,6 +17,8 @@ export declare class MssqlTask extends TaskAbstract<MssqlTaskConfigType> {
|
|
|
17
17
|
private get command();
|
|
18
18
|
exec(query: string): Promise<string[][]>;
|
|
19
19
|
fetchDatabaseNames(name?: string): Promise<string[]>;
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
backup(data: TaskBackupData): Promise<{
|
|
21
|
+
snapshotPath: string;
|
|
22
|
+
}>;
|
|
23
|
+
restore(data: TaskRestoreData): Promise<void>;
|
|
22
24
|
}
|
package/Task/MssqlTask.js
CHANGED
|
@@ -6,8 +6,8 @@ const DefinitionEnum_1 = require("../JsonSchema/DefinitionEnum");
|
|
|
6
6
|
const config_1 = require("../utils/datatruck/config");
|
|
7
7
|
const fs_1 = require("../utils/fs");
|
|
8
8
|
const process_1 = require("../utils/process");
|
|
9
|
+
const temp_1 = require("../utils/temp");
|
|
9
10
|
const TaskAbstract_1 = require("./TaskAbstract");
|
|
10
|
-
const assert_1 = require("assert");
|
|
11
11
|
const promises_1 = require("fs/promises");
|
|
12
12
|
const micromatch_1 = require("micromatch");
|
|
13
13
|
const path_1 = require("path");
|
|
@@ -67,26 +67,25 @@ class MssqlTask extends TaskAbstract_1.TaskAbstract {
|
|
|
67
67
|
.map(([database]) => database)
|
|
68
68
|
.filter((database) => !privateDatabases.includes(database));
|
|
69
69
|
}
|
|
70
|
-
async
|
|
70
|
+
async backup(data) {
|
|
71
71
|
this.verbose = data.options.verbose;
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
if (data.package.path)
|
|
73
|
+
throw new Error(`Path is not required: ${data.package.path}`);
|
|
74
|
+
const snapshotPath = await (0, temp_1.mkTmpDir)(exports.mssqlTaskName, "task", "backup", "snapshot");
|
|
74
75
|
const databaseNames = (await this.fetchDatabaseNames()).filter((databaseName) => (!this.config.includeDatabases ||
|
|
75
76
|
(0, micromatch_1.isMatch)(databaseName, this.config.includeDatabases)) &&
|
|
76
77
|
(!this.config.excludeDatabases ||
|
|
77
78
|
!(0, micromatch_1.isMatch)(databaseName, this.config.excludeDatabases)));
|
|
78
|
-
await (0, fs_1.mkdirIfNotExists)(targetPath);
|
|
79
79
|
for (const databaseName of databaseNames) {
|
|
80
|
-
const databasePath = (0, path_1.join)(
|
|
80
|
+
const databasePath = (0, path_1.join)(snapshotPath, `${databaseName}${MssqlTask.SUFFIX}`);
|
|
81
81
|
await this.exec(`BACKUP DATABASE [${databaseName}] TO DISK='${databasePath}' WITH FORMAT`);
|
|
82
82
|
}
|
|
83
|
+
return { snapshotPath };
|
|
83
84
|
}
|
|
84
|
-
async
|
|
85
|
+
async restore(data) {
|
|
85
86
|
this.verbose = data.options.verbose;
|
|
86
|
-
const
|
|
87
|
-
(0,
|
|
88
|
-
await (0, fs_1.mkdirIfNotExists)(restorePath);
|
|
89
|
-
const files = await (0, fs_1.readDir)(restorePath);
|
|
87
|
+
const snapshotPath = data.snapshotPath;
|
|
88
|
+
const files = await (0, fs_1.readDir)(snapshotPath);
|
|
90
89
|
for (const file of files) {
|
|
91
90
|
if (!file.endsWith(MssqlTask.SUFFIX))
|
|
92
91
|
continue;
|
|
@@ -99,7 +98,7 @@ class MssqlTask extends TaskAbstract_1.TaskAbstract {
|
|
|
99
98
|
snapshotId: data.options.snapshotId,
|
|
100
99
|
snapshotDate: data.snapshot.date,
|
|
101
100
|
});
|
|
102
|
-
const databasePath = (0, path_1.join)(
|
|
101
|
+
const databasePath = (0, path_1.join)(snapshotPath, file);
|
|
103
102
|
const exists = await this.fetchDatabaseNames(databaseName);
|
|
104
103
|
if (exists.length)
|
|
105
104
|
throw new AppError_1.AppError(`Target database already exists: ${databaseName}`);
|
package/Task/MysqlDumpTask.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { CompressOptions } from "../utils/tar";
|
|
1
2
|
import { SqlDumpTaskConfigType } from "./SqlDumpTaskAbstract";
|
|
2
|
-
import {
|
|
3
|
+
import { TaskBackupData, TaskPrepareRestoreData, TaskRestoreData, TaskAbstract } from "./TaskAbstract";
|
|
3
4
|
export declare const mysqlDumpTaskName = "mysql-dump";
|
|
4
5
|
export type MysqlDumpTaskConfigType = {
|
|
5
6
|
/**
|
|
@@ -11,9 +12,15 @@ export type MysqlDumpTaskConfigType = {
|
|
|
11
12
|
* @default 1
|
|
12
13
|
*/
|
|
13
14
|
concurrency?: number;
|
|
15
|
+
compress?: boolean | CompressOptions;
|
|
14
16
|
} & SqlDumpTaskConfigType;
|
|
15
17
|
export declare const mysqlDumpTaskDefinition: import("json-schema").JSONSchema7;
|
|
16
18
|
export declare class MysqlDumpTask extends TaskAbstract<MysqlDumpTaskConfigType> {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
backup(data: TaskBackupData): Promise<{
|
|
20
|
+
snapshotPath: string;
|
|
21
|
+
}>;
|
|
22
|
+
prepareRestore(data: TaskPrepareRestoreData): Promise<{
|
|
23
|
+
snapshotPath: string;
|
|
24
|
+
}>;
|
|
25
|
+
restore(data: TaskRestoreData): Promise<void>;
|
|
19
26
|
}
|
package/Task/MysqlDumpTask.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MysqlDumpTask = exports.mysqlDumpTaskDefinition = exports.mysqlDumpTaskName = void 0;
|
|
4
4
|
const AppError_1 = require("../Error/AppError");
|
|
5
|
+
const DefinitionEnum_1 = require("../JsonSchema/DefinitionEnum");
|
|
5
6
|
const async_1 = require("../utils/async");
|
|
6
7
|
const cli_1 = require("../utils/cli");
|
|
7
8
|
const config_1 = require("../utils/datatruck/config");
|
|
@@ -9,9 +10,10 @@ const fs_1 = require("../utils/fs");
|
|
|
9
10
|
const math_1 = require("../utils/math");
|
|
10
11
|
const mysql_1 = require("../utils/mysql");
|
|
11
12
|
const string_1 = require("../utils/string");
|
|
13
|
+
const tar_1 = require("../utils/tar");
|
|
14
|
+
const temp_1 = require("../utils/temp");
|
|
12
15
|
const SqlDumpTaskAbstract_1 = require("./SqlDumpTaskAbstract");
|
|
13
16
|
const TaskAbstract_1 = require("./TaskAbstract");
|
|
14
|
-
const assert_1 = require("assert");
|
|
15
17
|
const promises_1 = require("fs/promises");
|
|
16
18
|
const path_1 = require("path");
|
|
17
19
|
exports.mysqlDumpTaskName = "mysql-dump";
|
|
@@ -19,6 +21,9 @@ exports.mysqlDumpTaskDefinition = (0, SqlDumpTaskAbstract_1.sqlDumpTaskDefinitio
|
|
|
19
21
|
dataFormat: { enum: ["csv", "sql"] },
|
|
20
22
|
concurrency: { type: "integer", minimum: 1 },
|
|
21
23
|
csvSharedPath: { type: "string" },
|
|
24
|
+
compress: {
|
|
25
|
+
anyOf: [{ type: "boolean" }, (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.compressUtil)],
|
|
26
|
+
},
|
|
22
27
|
});
|
|
23
28
|
const suffix = {
|
|
24
29
|
database: ".database.sql",
|
|
@@ -28,18 +33,37 @@ const suffix = {
|
|
|
28
33
|
tableSchema: ".table-schema.sql",
|
|
29
34
|
};
|
|
30
35
|
class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
31
|
-
async
|
|
36
|
+
async backup(data) {
|
|
37
|
+
const compressAndClean = this.config.compress
|
|
38
|
+
? async (path) => {
|
|
39
|
+
data.onProgress({
|
|
40
|
+
relative: {
|
|
41
|
+
description: "Compressing",
|
|
42
|
+
payload: (0, path_1.basename)(path),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
await (0, tar_1.createTar)({
|
|
46
|
+
include: [(0, path_1.relative)(snapshotPath, path)],
|
|
47
|
+
output: `${path}.tar.gz`,
|
|
48
|
+
path: (0, path_1.dirname)(path),
|
|
49
|
+
compress: this.config.compress,
|
|
50
|
+
verbose: data.options.verbose,
|
|
51
|
+
});
|
|
52
|
+
await (0, promises_1.rm)(path);
|
|
53
|
+
}
|
|
54
|
+
: undefined;
|
|
55
|
+
const snapshotPath = data.package.path ??
|
|
56
|
+
(await (0, temp_1.mkTmpDir)(exports.mysqlDumpTaskName, "task", "backup", "snapshot"));
|
|
57
|
+
await (0, fs_1.mkdirIfNotExists)(snapshotPath);
|
|
58
|
+
await (0, fs_1.ensureEmptyDir)(snapshotPath);
|
|
32
59
|
const sql = await (0, mysql_1.createMysqlCli)({
|
|
33
60
|
...this.config,
|
|
34
61
|
database: undefined,
|
|
35
62
|
verbose: data.options.verbose,
|
|
36
63
|
});
|
|
37
64
|
const tableNames = await sql.fetchTableNames(this.config.database, this.config.includeTables, this.config.excludeTables);
|
|
38
|
-
const outputPath = data.package.path;
|
|
39
|
-
(0, assert_1.ok)(typeof outputPath === "string");
|
|
40
65
|
const concurrency = this.config.concurrency ?? 4;
|
|
41
66
|
const dataFormat = this.config.dataFormat ?? "sql";
|
|
42
|
-
await (0, promises_1.mkdir)(outputPath, { recursive: true });
|
|
43
67
|
const sharedDir = dataFormat === "csv"
|
|
44
68
|
? await sql.initSharedDir(this.config.csvSharedPath)
|
|
45
69
|
: undefined;
|
|
@@ -47,7 +71,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
47
71
|
await (0, async_1.runParallel)({
|
|
48
72
|
items: tableNames,
|
|
49
73
|
concurrency,
|
|
50
|
-
onChange:
|
|
74
|
+
onChange: ({ processed: proccesed, buffer }) => data.onProgress({
|
|
51
75
|
relative: {
|
|
52
76
|
description: buffer.size > 1 ? `Exporting (${buffer.size})` : "Exporting",
|
|
53
77
|
payload: [...buffer.keys()].join(", "),
|
|
@@ -81,15 +105,19 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
81
105
|
files.every((file) => file === schemaFile || file === dataFile);
|
|
82
106
|
if (!successCsvDump)
|
|
83
107
|
throw new AppError_1.AppError(`Invalid csv dump files: ${files.join(", ")}`);
|
|
84
|
-
|
|
85
|
-
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath,
|
|
108
|
+
const schemaPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableSchema}`);
|
|
109
|
+
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, schemaFile), schemaPath);
|
|
110
|
+
await compressAndClean?.(schemaPath);
|
|
111
|
+
const tablePath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableData}`);
|
|
112
|
+
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, dataFile), tablePath);
|
|
113
|
+
await compressAndClean?.(tablePath);
|
|
86
114
|
}
|
|
87
115
|
finally {
|
|
88
116
|
await (0, promises_1.rm)(tableSharedPath, { recursive: true });
|
|
89
117
|
}
|
|
90
118
|
}
|
|
91
119
|
else {
|
|
92
|
-
const outPath = (0, path_1.join)(
|
|
120
|
+
const outPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.table}`);
|
|
93
121
|
await sql.dump({
|
|
94
122
|
output: outPath,
|
|
95
123
|
items: [tableName],
|
|
@@ -113,16 +141,19 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
113
141
|
},
|
|
114
142
|
}),
|
|
115
143
|
});
|
|
144
|
+
await sql.assertDumpFile(outPath);
|
|
145
|
+
await compressAndClean?.(outPath);
|
|
116
146
|
}
|
|
117
147
|
},
|
|
118
148
|
});
|
|
119
149
|
}
|
|
120
150
|
else {
|
|
121
|
-
|
|
151
|
+
data.onProgress({
|
|
122
152
|
relative: { description: "Exporting" },
|
|
123
153
|
});
|
|
154
|
+
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.database}`);
|
|
124
155
|
await sql.dump({
|
|
125
|
-
output:
|
|
156
|
+
output: outPath,
|
|
126
157
|
items: tableNames,
|
|
127
158
|
database: this.config.database,
|
|
128
159
|
onProgress: (progress) => data.onProgress({
|
|
@@ -133,26 +164,39 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
133
164
|
},
|
|
134
165
|
}),
|
|
135
166
|
});
|
|
167
|
+
await sql.assertDumpFile(outPath);
|
|
168
|
+
await compressAndClean?.(outPath);
|
|
136
169
|
}
|
|
137
170
|
if (this.config.storedPrograms ?? true) {
|
|
138
|
-
|
|
171
|
+
data.onProgress({
|
|
139
172
|
relative: { description: "Exporting stored programs" },
|
|
140
173
|
});
|
|
174
|
+
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.stored}`);
|
|
141
175
|
await sql.dump({
|
|
142
176
|
database: this.config.database,
|
|
143
|
-
output:
|
|
177
|
+
output: outPath,
|
|
144
178
|
onlyStoredPrograms: true,
|
|
145
179
|
});
|
|
180
|
+
await sql.assertDumpFile(outPath);
|
|
181
|
+
await compressAndClean?.(outPath);
|
|
146
182
|
}
|
|
183
|
+
return {
|
|
184
|
+
snapshotPath,
|
|
185
|
+
};
|
|
147
186
|
}
|
|
148
|
-
async
|
|
187
|
+
async prepareRestore(data) {
|
|
188
|
+
return {
|
|
189
|
+
snapshotPath: data.package.restorePath ??
|
|
190
|
+
(await (0, temp_1.mkTmpDir)(exports.mysqlDumpTaskName, "task", "restore", "snapshot")),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async restore(data) {
|
|
149
194
|
const sql = await (0, mysql_1.createMysqlCli)({
|
|
150
195
|
...this.config,
|
|
151
196
|
database: undefined,
|
|
152
197
|
verbose: data.options.verbose,
|
|
153
198
|
});
|
|
154
|
-
const
|
|
155
|
-
(0, assert_1.ok)(typeof restorePath === "string");
|
|
199
|
+
const snapshotPath = data.snapshotPath;
|
|
156
200
|
const params = {
|
|
157
201
|
packageName: data.package.name,
|
|
158
202
|
snapshotId: data.options.snapshotId,
|
|
@@ -168,8 +212,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
168
212
|
...params,
|
|
169
213
|
database: database.name,
|
|
170
214
|
});
|
|
171
|
-
const
|
|
172
|
-
const files = (await (0, fs_1.readDir)(restorePath)).filter((f) => (0, string_1.endsWith)(f, suffixes));
|
|
215
|
+
const [files, compressed] = (0, fs_1.groupFiles)(await (0, fs_1.readDir)(snapshotPath), Object.values(suffix));
|
|
173
216
|
// Database check
|
|
174
217
|
if (files.some((f) => f.endsWith(suffix.database)) &&
|
|
175
218
|
!(await sql.isDatabaseEmpty(database.name)))
|
|
@@ -191,7 +234,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
191
234
|
: undefined;
|
|
192
235
|
await sql.createDatabase(database);
|
|
193
236
|
if (data.options.verbose)
|
|
194
|
-
(0, cli_1.logExec)("readdir", [
|
|
237
|
+
(0, cli_1.logExec)("readdir", [snapshotPath]);
|
|
195
238
|
const concurrency = this.config.concurrency ?? 1;
|
|
196
239
|
let processed = 0;
|
|
197
240
|
await (0, async_1.runParallel)({
|
|
@@ -200,7 +243,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
200
243
|
onFinished: () => {
|
|
201
244
|
processed++;
|
|
202
245
|
},
|
|
203
|
-
onChange:
|
|
246
|
+
onChange: ({ buffer }) => data.onProgress({
|
|
204
247
|
relative: {
|
|
205
248
|
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
206
249
|
payload: [...buffer.keys()].join(", "),
|
|
@@ -212,11 +255,29 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
212
255
|
},
|
|
213
256
|
}),
|
|
214
257
|
onItem: async ({ item: file, controller }) => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
258
|
+
let path = (0, path_1.join)(snapshotPath, file);
|
|
259
|
+
const tempDir = compressed[file]
|
|
260
|
+
? await (0, temp_1.useTempDir)(exports.mysqlDumpTaskName, "task", "restore", "decompress")
|
|
261
|
+
: undefined;
|
|
262
|
+
try {
|
|
263
|
+
if (tempDir) {
|
|
264
|
+
await (0, tar_1.extractTar)({
|
|
265
|
+
input: (0, path_1.join)(snapshotPath, compressed[file]),
|
|
266
|
+
output: tempDir.path,
|
|
267
|
+
decompress: true,
|
|
268
|
+
verbose: data.options.verbose,
|
|
269
|
+
});
|
|
270
|
+
path = await (0, fs_1.ensureSingleFile)(tempDir.path);
|
|
271
|
+
}
|
|
272
|
+
await sql.importFile({
|
|
273
|
+
path,
|
|
274
|
+
database: database.name,
|
|
275
|
+
onSpawn: (p) => (controller.stop = () => p.kill()),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
await tempDir?.[Symbol.asyncDispose]();
|
|
280
|
+
}
|
|
220
281
|
},
|
|
221
282
|
});
|
|
222
283
|
await (0, async_1.runParallel)({
|
|
@@ -225,7 +286,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
225
286
|
onFinished: () => {
|
|
226
287
|
processed++;
|
|
227
288
|
},
|
|
228
|
-
onChange:
|
|
289
|
+
onChange: ({ buffer }) => data.onProgress({
|
|
229
290
|
relative: {
|
|
230
291
|
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
231
292
|
payload: [...buffer.keys()].join(", "),
|
|
@@ -237,20 +298,35 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
237
298
|
},
|
|
238
299
|
}),
|
|
239
300
|
onItem: async ({ item: file, controller }) => {
|
|
240
|
-
const
|
|
301
|
+
const id = data.snapshot.id.slice(0, 8);
|
|
241
302
|
const tableName = file.slice(0, suffix.tableData.length * -1);
|
|
242
|
-
const
|
|
303
|
+
const sharedName = `tmp-dtt-restore-${id}-${tableName}.data.csv`;
|
|
304
|
+
const temp = (0, temp_1.useTempFile)((0, path_1.join)(sharedDir, sharedName));
|
|
243
305
|
try {
|
|
244
|
-
|
|
306
|
+
let csvFile = temp.path;
|
|
307
|
+
if (compressed[file]) {
|
|
308
|
+
await (0, fs_1.mkdirIfNotExists)(temp.path);
|
|
309
|
+
await (0, tar_1.extractTar)({
|
|
310
|
+
input: (0, path_1.join)(snapshotPath, compressed[file]),
|
|
311
|
+
output: temp.path,
|
|
312
|
+
decompress: true,
|
|
313
|
+
verbose: data.options.verbose,
|
|
314
|
+
});
|
|
315
|
+
csvFile = await (0, fs_1.ensureSingleFile)(temp.path);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
const sourceFile = (0, path_1.join)(snapshotPath, file);
|
|
319
|
+
await (0, fs_1.safeRename)(sourceFile, temp.path);
|
|
320
|
+
}
|
|
245
321
|
await sql.importCsvFile({
|
|
246
|
-
path:
|
|
322
|
+
path: csvFile,
|
|
247
323
|
database: database.name,
|
|
248
324
|
table: tableName,
|
|
249
325
|
onSpawn: (p) => (controller.stop = () => p.kill()),
|
|
250
326
|
});
|
|
251
327
|
}
|
|
252
328
|
finally {
|
|
253
|
-
await
|
|
329
|
+
await temp[Symbol.asyncDispose]();
|
|
254
330
|
}
|
|
255
331
|
},
|
|
256
332
|
});
|
package/Task/ScriptTask.d.ts
CHANGED
|
@@ -1,42 +1,47 @@
|
|
|
1
|
+
import { PackageConfigType } from "../Config/PackageConfig";
|
|
2
|
+
import { PreSnapshot } from "../Repository/RepositoryAbstract";
|
|
1
3
|
import { Step } from "../utils/steps";
|
|
2
|
-
import {
|
|
4
|
+
import { TaskBackupData, TaskPrepareRestoreData, TaskRestoreData, TaskAbstract } from "./TaskAbstract";
|
|
3
5
|
import { JSONSchema7 } from "json-schema";
|
|
6
|
+
type NodeVars = {
|
|
7
|
+
dtt: {
|
|
8
|
+
snapshot: PreSnapshot;
|
|
9
|
+
package: PackageConfigType;
|
|
10
|
+
snapshotPath: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
4
13
|
export type ScriptTaskConfigType = {
|
|
5
14
|
env?: Record<string, string | undefined>;
|
|
6
15
|
backupSteps: Step[];
|
|
7
16
|
restoreSteps: Step[];
|
|
8
17
|
};
|
|
18
|
+
export declare function scriptTaskCode<V extends Record<string, any>>(cb: (vars: NodeVars & V) => void): string;
|
|
9
19
|
export declare enum ScriptTaskDefinitionEnum {
|
|
10
20
|
step = "step",
|
|
11
21
|
processStepConfig = "processStepConfig",
|
|
12
|
-
nodeStepConfig = "nodeStepConfig"
|
|
22
|
+
nodeStepConfig = "nodeStepConfig",
|
|
23
|
+
telegramMessageStepConfig = "telegramMessageStepConfig"
|
|
13
24
|
}
|
|
14
25
|
export declare const scriptTaskName = "script";
|
|
15
26
|
export declare const scriptTaskDefinition: JSONSchema7;
|
|
16
27
|
export declare class ScriptTask extends TaskAbstract<ScriptTaskConfigType> {
|
|
17
28
|
protected verbose?: boolean;
|
|
18
|
-
|
|
19
|
-
targetPath: string;
|
|
20
|
-
}>;
|
|
21
|
-
protected getVars(data: BackupDataType | RestoreDataType): {
|
|
29
|
+
protected getVars(data: TaskBackupData | TaskRestoreData): {
|
|
22
30
|
process: {
|
|
23
31
|
DTT_SNAPSHOT_ID: string;
|
|
24
32
|
DTT_SNAPSHOT_DATE: string;
|
|
25
33
|
DTT_PACKAGE_NAME: string;
|
|
26
34
|
DTT_PACKAGE_PATH: string | undefined;
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
node: {
|
|
30
|
-
dtt: {
|
|
31
|
-
snapshot: import("../Repository/RepositoryAbstract").SnapshotType;
|
|
32
|
-
package: import("../Config/PackageConfig").PackageConfigType;
|
|
33
|
-
targetPath: string | undefined;
|
|
34
|
-
};
|
|
35
|
+
DTT_SNAPSHOT_PATH: string | undefined;
|
|
35
36
|
};
|
|
37
|
+
node: NodeVars;
|
|
36
38
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
backup(data: TaskBackupData): Promise<{
|
|
40
|
+
snapshotPath: string;
|
|
41
|
+
}>;
|
|
42
|
+
prepareRestore(data: TaskPrepareRestoreData): Promise<{
|
|
43
|
+
snapshotPath: string;
|
|
40
44
|
}>;
|
|
41
|
-
|
|
45
|
+
restore(data: TaskRestoreData): Promise<void>;
|
|
42
46
|
}
|
|
47
|
+
export {};
|