@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/Action/BackupAction.js
CHANGED
|
@@ -1,286 +1,342 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.BackupAction = void 0;
|
|
4
|
-
const AppError_1 = require("../Error/AppError");
|
|
5
7
|
const RepositoryFactory_1 = require("../Factory/RepositoryFactory");
|
|
6
8
|
const TaskFactory_1 = require("../Factory/TaskFactory");
|
|
9
|
+
const DataFormat_1 = require("../utils/DataFormat");
|
|
10
|
+
const cli_1 = require("../utils/cli");
|
|
7
11
|
const config_1 = require("../utils/datatruck/config");
|
|
12
|
+
const date_1 = require("../utils/date");
|
|
8
13
|
const fs_1 = require("../utils/fs");
|
|
14
|
+
const list_1 = require("../utils/list");
|
|
15
|
+
const progress_1 = require("../utils/progress");
|
|
16
|
+
const steps_1 = require("../utils/steps");
|
|
17
|
+
const temp_1 = require("../utils/temp");
|
|
18
|
+
const assert_1 = require("assert");
|
|
19
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
20
|
const crypto_1 = require("crypto");
|
|
21
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
10
22
|
class BackupAction {
|
|
11
23
|
config;
|
|
12
24
|
options;
|
|
13
|
-
|
|
14
|
-
repoErrors = {};
|
|
25
|
+
pm;
|
|
15
26
|
constructor(config, options = {}) {
|
|
16
27
|
this.config = config;
|
|
17
28
|
this.options = options;
|
|
29
|
+
this.pm = new progress_1.ProgressManager({
|
|
30
|
+
verbose: options.verbose,
|
|
31
|
+
tty: options.tty,
|
|
32
|
+
enabled: options.progress,
|
|
33
|
+
interval: options.progressInterval,
|
|
34
|
+
});
|
|
18
35
|
}
|
|
19
|
-
|
|
20
|
-
const
|
|
36
|
+
prepareSnapshot() {
|
|
37
|
+
const date = this.options.date ?? new Date().toISOString();
|
|
38
|
+
if (!(0, dayjs_1.default)(date, "YYYY-MM-DDTHH:mm:ss.SSS[Z]", true).isValid())
|
|
39
|
+
throw new Error(`Invalid snapshot date: ${date}`);
|
|
40
|
+
return {
|
|
21
41
|
id: (0, crypto_1.randomUUID)().replaceAll("-", ""),
|
|
22
|
-
date
|
|
42
|
+
date,
|
|
23
43
|
};
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
}
|
|
45
|
+
getPackages(snapshot) {
|
|
46
|
+
const packages = (0, config_1.filterPackages)(this.config, {
|
|
26
47
|
packageNames: this.options.packageNames,
|
|
27
48
|
packageTaskNames: this.options.packageTaskNames,
|
|
28
49
|
repositoryNames: this.options.repositoryNames,
|
|
29
50
|
repositoryTypes: this.options.repositoryTypes,
|
|
30
51
|
sourceAction: "backup",
|
|
31
52
|
});
|
|
32
|
-
|
|
53
|
+
return (0, config_1.resolvePackages)(packages, {
|
|
33
54
|
snapshotId: snapshot.id,
|
|
34
55
|
snapshotDate: snapshot.date,
|
|
35
56
|
action: "backup",
|
|
36
57
|
});
|
|
37
|
-
for (const pkg of packages) {
|
|
38
|
-
const sessionId = await session.init({
|
|
39
|
-
snapshotId: snapshot.id,
|
|
40
|
-
packageName: pkg.name,
|
|
41
|
-
tags: this.options.tags?.join(",") ?? "",
|
|
42
|
-
});
|
|
43
|
-
if (pkg.task)
|
|
44
|
-
await session.initTask({
|
|
45
|
-
sessionId: sessionId,
|
|
46
|
-
taskName: pkg.task.name,
|
|
47
|
-
});
|
|
48
|
-
for (const repositoryName of pkg.repositoryNames ?? []) {
|
|
49
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, repositoryName);
|
|
50
|
-
await session.initRepository({
|
|
51
|
-
sessionId: sessionId,
|
|
52
|
-
repositoryName: repositoryName,
|
|
53
|
-
repositoryType: repo.type,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return [snapshot, packages];
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
let error;
|
|
69
|
-
if (this.taskErrors[key]?.length) {
|
|
70
|
-
error = AppError_1.AppError.create("Previous task failed", this.taskErrors[key]);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
try {
|
|
74
|
-
await task.onBackup({
|
|
75
|
-
package: pkg,
|
|
76
|
-
options: this.options,
|
|
77
|
-
snapshot,
|
|
78
|
-
targetPath,
|
|
79
|
-
onProgress: async (progress) => {
|
|
80
|
-
await session.progressTask({
|
|
81
|
-
id: taskId,
|
|
82
|
-
progress,
|
|
83
|
-
});
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
catch (_) {
|
|
88
|
-
if (!this.taskErrors[key])
|
|
89
|
-
this.taskErrors[key] = [];
|
|
90
|
-
this.taskErrors[key].push((error = _));
|
|
91
|
-
}
|
|
59
|
+
getRepositoryNames(repositoryNames) {
|
|
60
|
+
const items = [];
|
|
61
|
+
const exclude = new Set();
|
|
62
|
+
for (const name of repositoryNames) {
|
|
63
|
+
const repo = (0, config_1.findRepositoryOrFail)(this.config, name);
|
|
64
|
+
const mirrors = (repo.mirrorRepoNames || []).filter((mirror) => !exclude.has(mirror));
|
|
65
|
+
for (const mirror of mirrors)
|
|
66
|
+
exclude.add(mirror);
|
|
67
|
+
items.push({ name, mirrors });
|
|
92
68
|
}
|
|
93
|
-
|
|
94
|
-
id: taskId,
|
|
95
|
-
error: error?.stack,
|
|
96
|
-
});
|
|
97
|
-
return {
|
|
98
|
-
error: error ? false : true,
|
|
99
|
-
tmpDirs: task.tmpDirs,
|
|
100
|
-
};
|
|
69
|
+
return items.filter((item) => !exclude.has(item.name));
|
|
101
70
|
}
|
|
102
|
-
async backup(
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
await repoInstance.onBackup({
|
|
119
|
-
package: pkg,
|
|
120
|
-
targetPath,
|
|
121
|
-
packageConfig: pkg.repositoryConfigs?.find((config) => config.type === repo.type &&
|
|
122
|
-
(!config.names || config.names.includes(repo.name)))?.config,
|
|
123
|
-
options: this.options,
|
|
124
|
-
snapshot: snapshot,
|
|
125
|
-
onProgress: async (progress) => {
|
|
126
|
-
await session.progressRepository({
|
|
127
|
-
id: repositoryId,
|
|
128
|
-
progress,
|
|
129
|
-
});
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
catch (_) {
|
|
134
|
-
if (!this.repoErrors[pkg.name])
|
|
135
|
-
this.repoErrors[pkg.name] = [];
|
|
136
|
-
this.repoErrors[pkg.name].push((error = _));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
await session.endRepository({
|
|
140
|
-
id: repositoryId,
|
|
141
|
-
error: error?.stack,
|
|
71
|
+
async backup(data) {
|
|
72
|
+
const repoConfig = (0, config_1.findRepositoryOrFail)(this.config, data.repositoryName);
|
|
73
|
+
const pkg = { ...data.pkg, path: data.snapshotPath ?? data.pkg.path };
|
|
74
|
+
(0, assert_1.ok)(pkg.path);
|
|
75
|
+
await (0, fs_1.ensureExistsDir)(pkg.path);
|
|
76
|
+
const repo = (0, RepositoryFactory_1.createRepo)(repoConfig);
|
|
77
|
+
if (this.config.minFreeDiskSpace)
|
|
78
|
+
await repo.ensureFreeDiskSpace(repoConfig.config, this.config.minFreeDiskSpace);
|
|
79
|
+
const packageConfig = pkg.repositoryConfigs?.find((config) => config.type === repoConfig.type &&
|
|
80
|
+
(!config.names || config.names.includes(repoConfig.name)))?.config;
|
|
81
|
+
await repo.backup({
|
|
82
|
+
options: this.options,
|
|
83
|
+
snapshot: data.snapshot,
|
|
84
|
+
package: pkg,
|
|
85
|
+
packageConfig,
|
|
86
|
+
onProgress: data.onProgress,
|
|
142
87
|
});
|
|
143
|
-
return {
|
|
144
|
-
error: error ? false : true,
|
|
145
|
-
tmpDirs: repoInstance?.tmpDirs ?? [],
|
|
146
|
-
};
|
|
147
88
|
}
|
|
148
|
-
async
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
89
|
+
async copy(data) {
|
|
90
|
+
const repoConfig = (0, config_1.findRepositoryOrFail)(this.config, data.repositoryName);
|
|
91
|
+
const mirrorRepoConfig = (0, config_1.findRepositoryOrFail)(this.config, data.mirrorRepositoryName);
|
|
92
|
+
const repo = (0, RepositoryFactory_1.createRepo)(repoConfig);
|
|
93
|
+
const mirrorRepo = (0, RepositoryFactory_1.createRepo)(mirrorRepoConfig);
|
|
94
|
+
if (this.config.minFreeDiskSpace)
|
|
95
|
+
await mirrorRepo.ensureFreeDiskSpace(mirrorRepoConfig.config, this.config.minFreeDiskSpace);
|
|
96
|
+
await repo.copy({
|
|
97
|
+
options: this.options,
|
|
98
|
+
package: data.pkg,
|
|
99
|
+
snapshot: data.snapshot,
|
|
100
|
+
mirrorRepositoryConfig: mirrorRepoConfig.config,
|
|
101
|
+
onProgress: data.onProgress,
|
|
155
102
|
});
|
|
156
|
-
let error;
|
|
157
|
-
let repoInstance;
|
|
158
|
-
if (this.taskErrors[pkg.name]?.length) {
|
|
159
|
-
error = AppError_1.AppError.create("Task failed", this.taskErrors[pkg.name]);
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
try {
|
|
163
|
-
repoInstance = (0, RepositoryFactory_1.RepositoryFactory)(repo);
|
|
164
|
-
await repoInstance.onCopyBackup({
|
|
165
|
-
options: this.options,
|
|
166
|
-
package: pkg,
|
|
167
|
-
snapshot,
|
|
168
|
-
mirrorRepositoryConfig: mirrorRepo.config,
|
|
169
|
-
onProgress: async (progress) => {
|
|
170
|
-
await session.progressRepository({
|
|
171
|
-
id: repositoryId,
|
|
172
|
-
progress,
|
|
173
|
-
});
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
catch (_) {
|
|
178
|
-
if (!this.repoErrors[pkg.name])
|
|
179
|
-
this.repoErrors[pkg.name] = [];
|
|
180
|
-
this.repoErrors[pkg.name].push((error = _));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
await session.endRepository({
|
|
184
|
-
id: repositoryId,
|
|
185
|
-
error: error?.stack,
|
|
186
|
-
});
|
|
187
|
-
return {
|
|
188
|
-
error: error ? false : true,
|
|
189
|
-
tmpDirs: repoInstance?.tmpDirs ?? [],
|
|
190
|
-
};
|
|
191
103
|
}
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (!errors.length)
|
|
197
|
-
return;
|
|
198
|
-
return AppError_1.AppError.create(taskErrors.length && repoErrors.length
|
|
199
|
-
? "Task and repository failed"
|
|
200
|
-
: taskErrors.length && !repoErrors.length
|
|
201
|
-
? "Task failed"
|
|
202
|
-
: "Repository failed", errors);
|
|
203
|
-
}
|
|
204
|
-
splitRepositories(repositoryNames) {
|
|
205
|
-
const mirrorRepoMap = {};
|
|
206
|
-
const allMirrorRepoNames = [];
|
|
207
|
-
const repoNames = repositoryNames ?? [];
|
|
208
|
-
for (const repoName of repoNames) {
|
|
209
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, repoName);
|
|
210
|
-
if (repo.mirrorRepoNames)
|
|
211
|
-
mirrorRepoMap[repoName] = repo.mirrorRepoNames.filter((mirrorRepoName) => {
|
|
212
|
-
allMirrorRepoNames.push(mirrorRepoName);
|
|
213
|
-
return repoNames.includes(mirrorRepoName);
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
return {
|
|
217
|
-
repoNames: repoNames.filter((v) => !allMirrorRepoNames.includes(v)),
|
|
218
|
-
mirrors: repoNames.flatMap((sourceName) => {
|
|
219
|
-
const mirrorNames = mirrorRepoMap[sourceName] || [];
|
|
220
|
-
return mirrorNames.map((name) => ({
|
|
221
|
-
sourceName,
|
|
222
|
-
name,
|
|
223
|
-
}));
|
|
224
|
-
}),
|
|
104
|
+
dataFormat(result, options = {}) {
|
|
105
|
+
const renderTitle = (item, color) => {
|
|
106
|
+
let title = item.key.slice(0, 1).toUpperCase() + item.key.slice(1);
|
|
107
|
+
return item.key === "backup" && color ? chalk_1.default.cyan(title) : title;
|
|
225
108
|
};
|
|
109
|
+
const renderData = (item, color, result = []) => {
|
|
110
|
+
const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
|
|
111
|
+
return item.key === "prune"
|
|
112
|
+
? `${item.data.pruned}/${item.data.total}`
|
|
113
|
+
: item.key === "snapshot"
|
|
114
|
+
? item.data.id
|
|
115
|
+
: item.key === "task"
|
|
116
|
+
? `${item.data.packageName} ${g(item.data.taskName)}`
|
|
117
|
+
: item.key === "backup"
|
|
118
|
+
? `${item.data.packageName} ${g(item.data.repositoryName)}`
|
|
119
|
+
: item.key === "copy"
|
|
120
|
+
? `${item.data.packageName} ${g(item.data.mirrorRepositoryName)}`
|
|
121
|
+
: item.key === "summary"
|
|
122
|
+
? (0, cli_1.renderObject)({
|
|
123
|
+
errors: item.data.errors,
|
|
124
|
+
backups: result.filter((r) => !r.error && r.key === "backup")
|
|
125
|
+
.length,
|
|
126
|
+
copies: result.filter((r) => !r.error && r.key === "copy").length,
|
|
127
|
+
})
|
|
128
|
+
: item.key === "report"
|
|
129
|
+
? item.data.type
|
|
130
|
+
: "";
|
|
131
|
+
};
|
|
132
|
+
return new DataFormat_1.DataFormat({
|
|
133
|
+
streams: options.streams,
|
|
134
|
+
json: result,
|
|
135
|
+
list: () => result
|
|
136
|
+
.filter((item) => item.key !== "cleanup")
|
|
137
|
+
.map((item) => {
|
|
138
|
+
const icon = (0, cli_1.renderResult)(item.error, false);
|
|
139
|
+
const title = renderTitle(item);
|
|
140
|
+
const data = renderData(item, false, result);
|
|
141
|
+
return `${icon} ${title}: ${data}`;
|
|
142
|
+
}),
|
|
143
|
+
table: {
|
|
144
|
+
headers: [
|
|
145
|
+
{ value: "", width: 3 },
|
|
146
|
+
{ value: "Title", width: 15 },
|
|
147
|
+
{ value: "Data", align: "left" },
|
|
148
|
+
{ value: "Duration", width: 10 },
|
|
149
|
+
{ value: "Error", width: 50 },
|
|
150
|
+
],
|
|
151
|
+
rows: () => result
|
|
152
|
+
.filter((item) => item.key !== "cleanup")
|
|
153
|
+
.map((item) => [
|
|
154
|
+
(0, cli_1.renderResult)(item.error),
|
|
155
|
+
renderTitle(item, true),
|
|
156
|
+
renderData(item, true),
|
|
157
|
+
(0, date_1.duration)(item.elapsed),
|
|
158
|
+
(0, cli_1.renderError)(item.error, options.verbose),
|
|
159
|
+
]),
|
|
160
|
+
},
|
|
161
|
+
});
|
|
226
162
|
}
|
|
227
|
-
async exec(
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
await session.start({
|
|
235
|
-
id,
|
|
236
|
-
});
|
|
237
|
-
let targetPath;
|
|
238
|
-
let taskTmpDirs = [];
|
|
239
|
-
if (pkg.task) {
|
|
240
|
-
const taskInstance = (0, TaskFactory_1.TaskFactory)(pkg.task);
|
|
241
|
-
const result = await taskInstance.onBeforeBackup({
|
|
242
|
-
options: this.options,
|
|
243
|
-
package: pkg,
|
|
244
|
-
snapshot,
|
|
245
|
-
});
|
|
246
|
-
const taskResult = await this.task(session, pkg, taskInstance, snapshot, (targetPath = result?.targetPath));
|
|
247
|
-
taskTmpDirs.push(...taskResult.tmpDirs);
|
|
248
|
-
}
|
|
249
|
-
const { repoNames, mirrors } = this.splitRepositories(pkg.repositoryNames ?? []);
|
|
250
|
-
for (const repoName of repoNames) {
|
|
251
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, repoName);
|
|
252
|
-
const { tmpDirs } = await this.backup(session, pkg, repo, snapshot, targetPath);
|
|
253
|
-
if (!this.options.verbose)
|
|
254
|
-
await (0, fs_1.rmTmpDir)(tmpDirs);
|
|
255
|
-
}
|
|
256
|
-
if (!this.options.verbose) {
|
|
257
|
-
await (0, fs_1.rmTmpDir)(taskTmpDirs);
|
|
258
|
-
if (pkg.path && (0, fs_1.isTmpDir)(pkg.path)) {
|
|
259
|
-
await (0, fs_1.rmTmpDir)(pkg.path);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
for (const mirror of mirrors) {
|
|
263
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, mirror.sourceName);
|
|
264
|
-
const mirrorRepo = (0, config_1.findRepositoryOrFail)(this.config, mirror.name);
|
|
265
|
-
const { tmpDirs } = await this.copyBackup(session, pkg, repo, mirrorRepo, snapshot);
|
|
266
|
-
if (!this.options.verbose)
|
|
267
|
-
await (0, fs_1.rmTmpDir)(tmpDirs);
|
|
268
|
-
}
|
|
269
|
-
const error = this.getError(pkg);
|
|
270
|
-
if (error)
|
|
271
|
-
errors.push(error);
|
|
272
|
-
await session.end({
|
|
273
|
-
id: id,
|
|
274
|
-
error: error?.message,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
await session.endDrivers({
|
|
278
|
-
snapshotId: snapshot.id.slice(0, 8),
|
|
163
|
+
async exec() {
|
|
164
|
+
const { options } = this;
|
|
165
|
+
const pm = new progress_1.ProgressManager({
|
|
166
|
+
verbose: options.verbose,
|
|
167
|
+
tty: options.tty,
|
|
168
|
+
enabled: options.progress,
|
|
169
|
+
interval: options.progressInterval,
|
|
279
170
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
};
|
|
171
|
+
const l = new list_1.Listr3({
|
|
172
|
+
streams: this.options.streams,
|
|
173
|
+
progressManager: pm,
|
|
174
|
+
});
|
|
175
|
+
return l
|
|
176
|
+
.add([
|
|
177
|
+
l.$task({
|
|
178
|
+
key: "snapshot",
|
|
179
|
+
data: { id: "" },
|
|
180
|
+
exitOnError: false,
|
|
181
|
+
title: {
|
|
182
|
+
initial: "Prepare snapshots",
|
|
183
|
+
started: "Preparing snapshots",
|
|
184
|
+
failed: "Snapshots prepare failed",
|
|
185
|
+
},
|
|
186
|
+
run: async (task, data) => {
|
|
187
|
+
const { minFreeDiskSpace } = this.config;
|
|
188
|
+
if (minFreeDiskSpace)
|
|
189
|
+
await (0, temp_1.ensureFreeDiskTempSpace)(minFreeDiskSpace);
|
|
190
|
+
const snapshot = this.prepareSnapshot();
|
|
191
|
+
const packages = this.getPackages(snapshot);
|
|
192
|
+
const snapshotId = (data.id = snapshot.id.slice(0, 8));
|
|
193
|
+
task.title = `Snapshots prepared: ${snapshotId} (${packages.length} packages)`;
|
|
194
|
+
return [
|
|
195
|
+
...packages.flatMap((pkg) => {
|
|
196
|
+
let taskResult = {};
|
|
197
|
+
const gc = new temp_1.GargabeCollector();
|
|
198
|
+
const repositories = this.getRepositoryNames(pkg.repositoryNames ?? []);
|
|
199
|
+
const mirrorRepositories = repositories
|
|
200
|
+
.filter((r) => r.mirrors.length)
|
|
201
|
+
.flatMap(({ name, mirrors }) => mirrors.map((mirror) => ({ name, mirror })));
|
|
202
|
+
return l.$tasks(!!pkg.task &&
|
|
203
|
+
l.$task({
|
|
204
|
+
key: "task",
|
|
205
|
+
keyIndex: pkg.name,
|
|
206
|
+
data: { taskName: pkg.task.name, packageName: pkg.name },
|
|
207
|
+
title: {
|
|
208
|
+
initial: `Execute task: ${pkg.task?.name}`,
|
|
209
|
+
started: `Executing task: ${pkg.task?.name}`,
|
|
210
|
+
failed: `Task execute failed: ${pkg.task?.name}`,
|
|
211
|
+
completed: `Task executed: ${pkg.task?.name}`,
|
|
212
|
+
},
|
|
213
|
+
exitOnError: false,
|
|
214
|
+
runWrapper: gc.cleanupIfFail.bind(gc),
|
|
215
|
+
run: async (task) => {
|
|
216
|
+
taskResult = await (0, TaskFactory_1.createTask)(pkg.task).backup({
|
|
217
|
+
options,
|
|
218
|
+
package: pkg,
|
|
219
|
+
snapshot,
|
|
220
|
+
onProgress: (p) => pm.update(p, (t) => (task.output = t)),
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
}), ...repositories.map(({ name: repositoryName }) => l.$task({
|
|
224
|
+
key: "backup",
|
|
225
|
+
keyIndex: [pkg.name, repositoryName],
|
|
226
|
+
data: {
|
|
227
|
+
packageName: pkg.name,
|
|
228
|
+
repositoryName: repositoryName,
|
|
229
|
+
},
|
|
230
|
+
title: {
|
|
231
|
+
initial: `Create backup: ${repositoryName}`,
|
|
232
|
+
started: `Creating backup: ${repositoryName}`,
|
|
233
|
+
completed: `Backup created: ${repositoryName}`,
|
|
234
|
+
failed: `Backup create failed: ${repositoryName}`,
|
|
235
|
+
},
|
|
236
|
+
exitOnError: false,
|
|
237
|
+
runWrapper: gc.cleanupOnFinish.bind(gc),
|
|
238
|
+
run: async (task) => {
|
|
239
|
+
const taskSummary = pkg.task
|
|
240
|
+
? l.result("task", pkg.name)
|
|
241
|
+
: undefined;
|
|
242
|
+
if (taskSummary?.error)
|
|
243
|
+
throw new Error(`Task failed`);
|
|
244
|
+
await this.backup({
|
|
245
|
+
pkg,
|
|
246
|
+
repositoryName,
|
|
247
|
+
snapshot,
|
|
248
|
+
snapshotPath: taskResult?.snapshotPath,
|
|
249
|
+
onProgress: (p) => this.pm.update(p, (t) => (task.output = t)),
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
})), l.$task({
|
|
253
|
+
key: "cleanup",
|
|
254
|
+
keyIndex: pkg.name,
|
|
255
|
+
data: {},
|
|
256
|
+
title: {
|
|
257
|
+
initial: "Clean task files",
|
|
258
|
+
started: "Cleaning task files",
|
|
259
|
+
completed: "Task files cleaned",
|
|
260
|
+
failed: "Task files clean failed",
|
|
261
|
+
},
|
|
262
|
+
exitOnError: false,
|
|
263
|
+
enabled: gc.pending,
|
|
264
|
+
run: () => gc.cleanup(),
|
|
265
|
+
}), ...mirrorRepositories.map(({ name, mirror }) => l.$task({
|
|
266
|
+
key: "copy",
|
|
267
|
+
keyIndex: [pkg.name, mirror],
|
|
268
|
+
data: {
|
|
269
|
+
packageName: pkg.name,
|
|
270
|
+
repositoryName: name,
|
|
271
|
+
mirrorRepositoryName: mirror,
|
|
272
|
+
},
|
|
273
|
+
title: {
|
|
274
|
+
initial: `Copy snapshot: ${mirror}`,
|
|
275
|
+
started: `Copying snapshot: ${mirror}`,
|
|
276
|
+
completed: `Snapshot copied: ${mirror}`,
|
|
277
|
+
failed: `Snapshot copy failed: ${mirror}`,
|
|
278
|
+
},
|
|
279
|
+
exitOnError: false,
|
|
280
|
+
runWrapper: gc.cleanup.bind(gc),
|
|
281
|
+
run: async (task) => {
|
|
282
|
+
const backupSummary = l.result("backup", [
|
|
283
|
+
pkg.name,
|
|
284
|
+
name,
|
|
285
|
+
]);
|
|
286
|
+
if (backupSummary.error)
|
|
287
|
+
throw new Error(`Backup failed`);
|
|
288
|
+
await this.copy({
|
|
289
|
+
repositoryName: name,
|
|
290
|
+
mirrorRepositoryName: mirror,
|
|
291
|
+
pkg,
|
|
292
|
+
snapshot,
|
|
293
|
+
onProgress: (p) => pm.update(p, (t) => (task.output = t)),
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
})));
|
|
297
|
+
}),
|
|
298
|
+
...(this.config.reports || []).map((report, index) => {
|
|
299
|
+
const reportIndex = index + 1;
|
|
300
|
+
return l.$task({
|
|
301
|
+
title: {
|
|
302
|
+
initial: `Send report ${reportIndex}`,
|
|
303
|
+
started: `Sending report ${reportIndex}`,
|
|
304
|
+
completed: `Report sent: ${reportIndex}`,
|
|
305
|
+
failed: `Report send failed: ${reportIndex}`,
|
|
306
|
+
},
|
|
307
|
+
key: "report",
|
|
308
|
+
keyIndex: index,
|
|
309
|
+
data: { type: report.run.type },
|
|
310
|
+
exitOnError: false,
|
|
311
|
+
run: async (task) => {
|
|
312
|
+
const result = l
|
|
313
|
+
.getResult()
|
|
314
|
+
.filter((r) => r.key !== "report");
|
|
315
|
+
const success = result.every((r) => !r.error);
|
|
316
|
+
const enabled = !report.when ||
|
|
317
|
+
(report.when === "success" && success) ||
|
|
318
|
+
(report.when === "error" && !success);
|
|
319
|
+
if (!enabled)
|
|
320
|
+
return task.skip(`Report send skipped: ${reportIndex}`);
|
|
321
|
+
const text = this.dataFormat(result).format(report.format ?? "list");
|
|
322
|
+
await (0, steps_1.runSteps)(report.run, {
|
|
323
|
+
verbose: this.options.verbose,
|
|
324
|
+
process: { vars: { DTT_REPORT: text } },
|
|
325
|
+
telegram: { vars: { TEXT: text } },
|
|
326
|
+
node: {
|
|
327
|
+
vars: {
|
|
328
|
+
dtt: { report: text, result },
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}),
|
|
335
|
+
];
|
|
336
|
+
},
|
|
337
|
+
}),
|
|
338
|
+
])
|
|
339
|
+
.exec();
|
|
284
340
|
}
|
|
285
341
|
}
|
|
286
342
|
exports.BackupAction = BackupAction;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { IfRequireKeys } from "../utils/ts";
|
|
2
|
-
export type
|
|
2
|
+
export type CleanCacheActionOptions = {
|
|
3
3
|
verbose?: boolean;
|
|
4
4
|
};
|
|
5
5
|
export declare class CleanCacheAction<TRequired extends boolean = true> {
|
|
6
|
-
readonly options: IfRequireKeys<TRequired,
|
|
7
|
-
constructor(options: IfRequireKeys<TRequired,
|
|
8
|
-
exec(): Promise<
|
|
6
|
+
readonly options: IfRequireKeys<TRequired, CleanCacheActionOptions>;
|
|
7
|
+
constructor(options: IfRequireKeys<TRequired, CleanCacheActionOptions>);
|
|
8
|
+
exec(): Promise<{
|
|
9
|
+
errors: never[];
|
|
10
|
+
path: string;
|
|
11
|
+
freedSize: number;
|
|
12
|
+
}>;
|
|
9
13
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CleanCacheAction = void 0;
|
|
4
4
|
const fs_1 = require("../utils/fs");
|
|
5
|
+
const temp_1 = require("../utils/temp");
|
|
5
6
|
const promises_1 = require("fs/promises");
|
|
6
7
|
class CleanCacheAction {
|
|
7
8
|
options;
|
|
@@ -9,11 +10,13 @@ class CleanCacheAction {
|
|
|
9
10
|
this.options = options;
|
|
10
11
|
}
|
|
11
12
|
async exec() {
|
|
12
|
-
const path = (0,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
13
|
+
const path = (0, temp_1.parentTmpDir)();
|
|
14
|
+
let freedSize = 0;
|
|
15
|
+
if (await (0, fs_1.existsDir)(path)) {
|
|
16
|
+
freedSize = await (0, fs_1.fastFolderSizeAsync)(path);
|
|
17
|
+
await (0, promises_1.rm)(path, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
return { errors: [], path, freedSize };
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
exports.CleanCacheAction = CleanCacheAction;
|
package/Action/ConfigAction.d.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlobalOptions } from "../Command/CommandAbstract";
|
|
2
2
|
import type { ConfigType } from "../Config/Config";
|
|
3
3
|
import { IfRequireKeys } from "../utils/ts";
|
|
4
|
-
export type
|
|
4
|
+
export type ConfigActionOptions = {
|
|
5
5
|
path: string;
|
|
6
6
|
verbose?: boolean;
|
|
7
7
|
};
|
|
8
8
|
export declare class ConfigAction<TRequired extends boolean = true> {
|
|
9
|
-
readonly options: IfRequireKeys<TRequired,
|
|
10
|
-
constructor(options: IfRequireKeys<TRequired,
|
|
9
|
+
readonly options: IfRequireKeys<TRequired, ConfigActionOptions>;
|
|
10
|
+
constructor(options: IfRequireKeys<TRequired, ConfigActionOptions>);
|
|
11
11
|
static validate(config: ConfigType): void;
|
|
12
12
|
static check(config: ConfigType): void;
|
|
13
13
|
static normalize(config: ConfigType): ConfigType;
|
|
14
|
-
static
|
|
14
|
+
static fromGlobalOptionsWithPath(globalOptions: GlobalOptions<true>): Promise<{
|
|
15
|
+
path: string;
|
|
16
|
+
data: ConfigType;
|
|
17
|
+
} | {
|
|
18
|
+
path: null;
|
|
19
|
+
data: ConfigType;
|
|
20
|
+
}>;
|
|
21
|
+
static fromGlobalOptions(globalOptions: GlobalOptions<true>): Promise<ConfigType>;
|
|
15
22
|
exec(): Promise<{
|
|
16
23
|
path: string;
|
|
17
24
|
data: ConfigType;
|