@datatruck/cli 0.27.0 → 0.29.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 +75 -34
- package/Action/BackupAction.js +302 -246
- 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 +51 -0
- package/Action/CopyAction.js +165 -0
- package/Action/InitAction.d.ts +3 -3
- package/Action/InitAction.js +9 -9
- package/Action/PruneAction.d.ts +10 -16
- package/Action/PruneAction.js +37 -34
- package/Action/RestoreAction.d.ts +49 -24
- package/Action/RestoreAction.js +164 -195
- package/Action/SnapshotsAction.d.ts +8 -8
- package/Action/SnapshotsAction.js +9 -9
- package/CHANGELOG.md +513 -0
- package/Command/BackupCommand.d.ts +7 -4
- package/Command/BackupCommand.js +14 -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 +16 -0
- package/Command/CopyCommand.js +66 -0
- package/Command/InitCommand.d.ts +4 -4
- package/Command/InitCommand.js +13 -17
- package/Command/PruneCommand.d.ts +4 -10
- package/Command/PruneCommand.js +13 -12
- package/Command/RestoreCommand.d.ts +1 -1
- package/Command/RestoreCommand.js +13 -21
- package/Command/SnapshotsCommand.d.ts +4 -4
- package/Command/SnapshotsCommand.js +16 -15
- package/Command/StartServerCommand.d.ts +3 -3
- package/Config/Config.d.ts +11 -0
- package/Config/Config.js +18 -0
- package/Config/PrunePolicyConfig.d.ts +2 -2
- package/Factory/CommandFactory.d.ts +27 -34
- package/Factory/CommandFactory.js +27 -54
- 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 +10 -8
- package/Repository/DatatruckRepository.js +47 -25
- 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 +102 -32
- package/Task/ScriptTask.d.ts +23 -18
- package/Task/ScriptTask.js +34 -24
- package/Task/SqlDumpTaskAbstract.d.ts +8 -3
- package/Task/SqlDumpTaskAbstract.js +32 -20
- package/Task/TaskAbstract.d.ts +24 -25
- package/Task/TaskAbstract.js +6 -10
- package/cli.js +13 -5
- package/config.schema.json +89 -1
- package/package.json +4 -5
- package/utils/DataFormat.d.ts +23 -12
- package/utils/DataFormat.js +36 -14
- package/utils/cli.d.ts +3 -9
- package/utils/cli.js +19 -55
- package/utils/crypto.d.ts +1 -0
- package/utils/crypto.js +15 -0
- package/utils/datatruck/client.d.ts +2 -0
- package/utils/datatruck/client.js +3 -0
- package/utils/datatruck/config.d.ts +2 -0
- 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/snapshot.d.ts +5 -2
- package/utils/datatruck/snapshot.js +12 -22
- package/utils/date.d.ts +21 -4
- package/utils/date.js +46 -24
- package/utils/fs.d.ts +16 -11
- package/utils/fs.js +81 -48
- 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 +73 -45
- package/utils/temp.d.ts +26 -0
- package/utils/temp.js +133 -0
- package/utils/virtual-fs.d.ts +6 -2
- package/utils/virtual-fs.js +6 -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.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 MariadbTaskConfigType = {
|
|
4
4
|
command?: string;
|
|
@@ -28,9 +28,8 @@ export declare const mariadbTaskDefinition: JSONSchema7;
|
|
|
28
28
|
export declare class MariadbTask extends TaskAbstract<MariadbTaskConfigType> {
|
|
29
29
|
protected verbose?: boolean;
|
|
30
30
|
private get command();
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
backup(data: TaskBackupData): Promise<{
|
|
32
|
+
snapshotPath: string;
|
|
33
33
|
}>;
|
|
34
|
-
|
|
35
|
-
onRestore(data: RestoreDataType): Promise<void>;
|
|
34
|
+
restore(data: TaskRestoreData): Promise<void>;
|
|
36
35
|
}
|
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,31 @@ 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
|
+
await (0, tar_1.createTar)({
|
|
40
|
+
include: [(0, path_1.relative)(snapshotPath, path)],
|
|
41
|
+
output: `${path}.tar.gz`,
|
|
42
|
+
path: (0, path_1.dirname)(path),
|
|
43
|
+
compress: this.config.compress,
|
|
44
|
+
verbose: data.options.verbose,
|
|
45
|
+
});
|
|
46
|
+
await (0, promises_1.rm)(path);
|
|
47
|
+
}
|
|
48
|
+
: undefined;
|
|
49
|
+
const snapshotPath = data.package.path ??
|
|
50
|
+
(await (0, temp_1.mkTmpDir)(exports.mysqlDumpTaskName, "task", "backup", "snapshot"));
|
|
51
|
+
await (0, fs_1.mkdirIfNotExists)(snapshotPath);
|
|
52
|
+
await (0, fs_1.ensureEmptyDir)(snapshotPath);
|
|
32
53
|
const sql = await (0, mysql_1.createMysqlCli)({
|
|
33
54
|
...this.config,
|
|
34
55
|
database: undefined,
|
|
35
56
|
verbose: data.options.verbose,
|
|
36
57
|
});
|
|
37
58
|
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
59
|
const concurrency = this.config.concurrency ?? 4;
|
|
41
60
|
const dataFormat = this.config.dataFormat ?? "sql";
|
|
42
|
-
await (0, promises_1.mkdir)(outputPath, { recursive: true });
|
|
43
61
|
const sharedDir = dataFormat === "csv"
|
|
44
62
|
? await sql.initSharedDir(this.config.csvSharedPath)
|
|
45
63
|
: undefined;
|
|
@@ -47,7 +65,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
47
65
|
await (0, async_1.runParallel)({
|
|
48
66
|
items: tableNames,
|
|
49
67
|
concurrency,
|
|
50
|
-
onChange:
|
|
68
|
+
onChange: ({ processed: proccesed, buffer }) => data.onProgress({
|
|
51
69
|
relative: {
|
|
52
70
|
description: buffer.size > 1 ? `Exporting (${buffer.size})` : "Exporting",
|
|
53
71
|
payload: [...buffer.keys()].join(", "),
|
|
@@ -81,15 +99,19 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
81
99
|
files.every((file) => file === schemaFile || file === dataFile);
|
|
82
100
|
if (!successCsvDump)
|
|
83
101
|
throw new AppError_1.AppError(`Invalid csv dump files: ${files.join(", ")}`);
|
|
84
|
-
|
|
85
|
-
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath,
|
|
102
|
+
const schemaPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableSchema}`);
|
|
103
|
+
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, schemaFile), schemaPath);
|
|
104
|
+
await compressAndClean?.(schemaPath);
|
|
105
|
+
const tablePath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.tableData}`);
|
|
106
|
+
await (0, fs_1.safeRename)((0, path_1.join)(tableSharedPath, dataFile), tablePath);
|
|
107
|
+
await compressAndClean?.(tablePath);
|
|
86
108
|
}
|
|
87
109
|
finally {
|
|
88
110
|
await (0, promises_1.rm)(tableSharedPath, { recursive: true });
|
|
89
111
|
}
|
|
90
112
|
}
|
|
91
113
|
else {
|
|
92
|
-
const outPath = (0, path_1.join)(
|
|
114
|
+
const outPath = (0, path_1.join)(snapshotPath, `${tableName}${suffix.table}`);
|
|
93
115
|
await sql.dump({
|
|
94
116
|
output: outPath,
|
|
95
117
|
items: [tableName],
|
|
@@ -113,16 +135,19 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
113
135
|
},
|
|
114
136
|
}),
|
|
115
137
|
});
|
|
138
|
+
await sql.assertDumpFile(outPath);
|
|
139
|
+
await compressAndClean?.(outPath);
|
|
116
140
|
}
|
|
117
141
|
},
|
|
118
142
|
});
|
|
119
143
|
}
|
|
120
144
|
else {
|
|
121
|
-
|
|
145
|
+
data.onProgress({
|
|
122
146
|
relative: { description: "Exporting" },
|
|
123
147
|
});
|
|
148
|
+
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.database}`);
|
|
124
149
|
await sql.dump({
|
|
125
|
-
output:
|
|
150
|
+
output: outPath,
|
|
126
151
|
items: tableNames,
|
|
127
152
|
database: this.config.database,
|
|
128
153
|
onProgress: (progress) => data.onProgress({
|
|
@@ -133,26 +158,39 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
133
158
|
},
|
|
134
159
|
}),
|
|
135
160
|
});
|
|
161
|
+
await sql.assertDumpFile(outPath);
|
|
162
|
+
await compressAndClean?.(outPath);
|
|
136
163
|
}
|
|
137
164
|
if (this.config.storedPrograms ?? true) {
|
|
138
|
-
|
|
165
|
+
data.onProgress({
|
|
139
166
|
relative: { description: "Exporting stored programs" },
|
|
140
167
|
});
|
|
168
|
+
const outPath = (0, path_1.join)(snapshotPath, `${this.config.database}${suffix.stored}`);
|
|
141
169
|
await sql.dump({
|
|
142
170
|
database: this.config.database,
|
|
143
|
-
output:
|
|
171
|
+
output: outPath,
|
|
144
172
|
onlyStoredPrograms: true,
|
|
145
173
|
});
|
|
174
|
+
await sql.assertDumpFile(outPath);
|
|
175
|
+
await compressAndClean?.(outPath);
|
|
146
176
|
}
|
|
177
|
+
return {
|
|
178
|
+
snapshotPath,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async prepareRestore(data) {
|
|
182
|
+
return {
|
|
183
|
+
snapshotPath: data.package.restorePath ??
|
|
184
|
+
(await (0, temp_1.mkTmpDir)(exports.mysqlDumpTaskName, "task", "restore", "snapshot")),
|
|
185
|
+
};
|
|
147
186
|
}
|
|
148
|
-
async
|
|
187
|
+
async restore(data) {
|
|
149
188
|
const sql = await (0, mysql_1.createMysqlCli)({
|
|
150
189
|
...this.config,
|
|
151
190
|
database: undefined,
|
|
152
191
|
verbose: data.options.verbose,
|
|
153
192
|
});
|
|
154
|
-
const
|
|
155
|
-
(0, assert_1.ok)(typeof restorePath === "string");
|
|
193
|
+
const snapshotPath = data.snapshotPath;
|
|
156
194
|
const params = {
|
|
157
195
|
packageName: data.package.name,
|
|
158
196
|
snapshotId: data.options.snapshotId,
|
|
@@ -163,13 +201,12 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
163
201
|
const database = {
|
|
164
202
|
name: (0, config_1.resolveDatabaseName)(this.config.database, params),
|
|
165
203
|
};
|
|
166
|
-
if (this.config.targetDatabase && data.options.
|
|
204
|
+
if (this.config.targetDatabase && !data.options.initial)
|
|
167
205
|
database.name = (0, config_1.resolveDatabaseName)(this.config.targetDatabase.name, {
|
|
168
206
|
...params,
|
|
169
207
|
database: database.name,
|
|
170
208
|
});
|
|
171
|
-
const
|
|
172
|
-
const files = (await (0, fs_1.readDir)(restorePath)).filter((f) => (0, string_1.endsWith)(f, suffixes));
|
|
209
|
+
const [files, compressed] = (0, fs_1.groupFiles)(await (0, fs_1.readDir)(snapshotPath), Object.values(suffix));
|
|
173
210
|
// Database check
|
|
174
211
|
if (files.some((f) => f.endsWith(suffix.database)) &&
|
|
175
212
|
!(await sql.isDatabaseEmpty(database.name)))
|
|
@@ -191,7 +228,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
191
228
|
: undefined;
|
|
192
229
|
await sql.createDatabase(database);
|
|
193
230
|
if (data.options.verbose)
|
|
194
|
-
(0, cli_1.logExec)("readdir", [
|
|
231
|
+
(0, cli_1.logExec)("readdir", [snapshotPath]);
|
|
195
232
|
const concurrency = this.config.concurrency ?? 1;
|
|
196
233
|
let processed = 0;
|
|
197
234
|
await (0, async_1.runParallel)({
|
|
@@ -200,7 +237,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
200
237
|
onFinished: () => {
|
|
201
238
|
processed++;
|
|
202
239
|
},
|
|
203
|
-
onChange:
|
|
240
|
+
onChange: ({ buffer }) => data.onProgress({
|
|
204
241
|
relative: {
|
|
205
242
|
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
206
243
|
payload: [...buffer.keys()].join(", "),
|
|
@@ -212,11 +249,29 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
212
249
|
},
|
|
213
250
|
}),
|
|
214
251
|
onItem: async ({ item: file, controller }) => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
252
|
+
let path = (0, path_1.join)(snapshotPath, file);
|
|
253
|
+
const tempDir = compressed[file]
|
|
254
|
+
? await (0, temp_1.useTempDir)(exports.mysqlDumpTaskName, "task", "restore", "decompress")
|
|
255
|
+
: undefined;
|
|
256
|
+
try {
|
|
257
|
+
if (tempDir) {
|
|
258
|
+
await (0, tar_1.extractTar)({
|
|
259
|
+
input: (0, path_1.join)(snapshotPath, compressed[file]),
|
|
260
|
+
output: tempDir.path,
|
|
261
|
+
decompress: true,
|
|
262
|
+
verbose: data.options.verbose,
|
|
263
|
+
});
|
|
264
|
+
path = await (0, fs_1.ensureSingleFile)(tempDir.path);
|
|
265
|
+
}
|
|
266
|
+
await sql.importFile({
|
|
267
|
+
path,
|
|
268
|
+
database: database.name,
|
|
269
|
+
onSpawn: (p) => (controller.stop = () => p.kill()),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
await tempDir?.[Symbol.asyncDispose]();
|
|
274
|
+
}
|
|
220
275
|
},
|
|
221
276
|
});
|
|
222
277
|
await (0, async_1.runParallel)({
|
|
@@ -225,7 +280,7 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
225
280
|
onFinished: () => {
|
|
226
281
|
processed++;
|
|
227
282
|
},
|
|
228
|
-
onChange:
|
|
283
|
+
onChange: ({ buffer }) => data.onProgress({
|
|
229
284
|
relative: {
|
|
230
285
|
description: buffer.size > 1 ? `Importing (${buffer.size})` : "Importing",
|
|
231
286
|
payload: [...buffer.keys()].join(", "),
|
|
@@ -237,20 +292,35 @@ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
237
292
|
},
|
|
238
293
|
}),
|
|
239
294
|
onItem: async ({ item: file, controller }) => {
|
|
240
|
-
const
|
|
295
|
+
const id = data.snapshot.id.slice(0, 8);
|
|
241
296
|
const tableName = file.slice(0, suffix.tableData.length * -1);
|
|
242
|
-
const
|
|
297
|
+
const sharedName = `tmp-dtt-restore-${id}-${tableName}.data.csv`;
|
|
298
|
+
const temp = (0, temp_1.useTempFile)((0, path_1.join)(sharedDir, sharedName));
|
|
243
299
|
try {
|
|
244
|
-
|
|
300
|
+
let csvFile = temp.path;
|
|
301
|
+
if (compressed[file]) {
|
|
302
|
+
await (0, fs_1.mkdirIfNotExists)(temp.path);
|
|
303
|
+
await (0, tar_1.extractTar)({
|
|
304
|
+
input: (0, path_1.join)(snapshotPath, compressed[file]),
|
|
305
|
+
output: temp.path,
|
|
306
|
+
decompress: true,
|
|
307
|
+
verbose: data.options.verbose,
|
|
308
|
+
});
|
|
309
|
+
csvFile = await (0, fs_1.ensureSingleFile)(temp.path);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
const sourceFile = (0, path_1.join)(snapshotPath, file);
|
|
313
|
+
await (0, fs_1.safeRename)(sourceFile, temp.path);
|
|
314
|
+
}
|
|
245
315
|
await sql.importCsvFile({
|
|
246
|
-
path:
|
|
316
|
+
path: csvFile,
|
|
247
317
|
database: database.name,
|
|
248
318
|
table: tableName,
|
|
249
319
|
onSpawn: (p) => (controller.stop = () => p.kill()),
|
|
250
320
|
});
|
|
251
321
|
}
|
|
252
322
|
finally {
|
|
253
|
-
await
|
|
323
|
+
await temp[Symbol.asyncDispose]();
|
|
254
324
|
}
|
|
255
325
|
},
|
|
256
326
|
});
|
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 {};
|