@datatruck/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Action/BackupAction.d.ts +32 -0
- package/lib/Action/BackupAction.js +201 -0
- package/lib/Action/BackupSessionsAction.d.ts +13 -0
- package/lib/Action/BackupSessionsAction.js +16 -0
- package/lib/Action/CleanCacheAction.d.ts +9 -0
- package/lib/Action/CleanCacheAction.js +18 -0
- package/lib/Action/ConfigAction.d.ts +14 -0
- package/lib/Action/ConfigAction.js +64 -0
- package/lib/Action/InitAction.d.ts +18 -0
- package/lib/Action/InitAction.js +39 -0
- package/lib/Action/PruneAction.d.ts +37 -0
- package/lib/Action/PruneAction.js +80 -0
- package/lib/Action/RestoreAction.d.ts +36 -0
- package/lib/Action/RestoreAction.js +246 -0
- package/lib/Action/RestoreSessionsAction.d.ts +13 -0
- package/lib/Action/RestoreSessionsAction.js +16 -0
- package/lib/Action/SnapshotsAction.d.ts +30 -0
- package/lib/Action/SnapshotsAction.js +35 -0
- package/lib/Command/BackupCommand.d.ts +15 -0
- package/lib/Command/BackupCommand.js +83 -0
- package/lib/Command/BackupSessionsCommand.d.ts +12 -0
- package/lib/Command/BackupSessionsCommand.js +88 -0
- package/lib/Command/CleanCacheCommand.d.ts +6 -0
- package/lib/Command/CleanCacheCommand.js +20 -0
- package/lib/Command/CommandAbstract.d.ts +19 -0
- package/lib/Command/CommandAbstract.js +14 -0
- package/lib/Command/ConfigCommand.d.ts +17 -0
- package/lib/Command/ConfigCommand.js +61 -0
- package/lib/Command/InitCommand.d.ts +13 -0
- package/lib/Command/InitCommand.js +67 -0
- package/lib/Command/PruneCommand.d.ts +27 -0
- package/lib/Command/PruneCommand.js +160 -0
- package/lib/Command/RestoreCommand.d.ts +14 -0
- package/lib/Command/RestoreCommand.js +71 -0
- package/lib/Command/RestoreSessionsCommand.d.ts +12 -0
- package/lib/Command/RestoreSessionsCommand.js +87 -0
- package/lib/Command/SnapshotsCommand.d.ts +25 -0
- package/lib/Command/SnapshotsCommand.js +128 -0
- package/lib/Config/Config.d.ts +8 -0
- package/lib/Config/Config.js +19 -0
- package/lib/Config/PackageConfig.d.ts +27 -0
- package/lib/Config/PackageConfig.js +69 -0
- package/lib/Config/PackageRepositoryConfig.d.ts +17 -0
- package/lib/Config/PackageRepositoryConfig.js +37 -0
- package/lib/Config/PrunePolicyConfig.d.ts +4 -0
- package/lib/Config/PrunePolicyConfig.js +28 -0
- package/lib/Config/RepositoryConfig.d.ts +18 -0
- package/lib/Config/RepositoryConfig.js +37 -0
- package/lib/Config/TaskConfig.d.ts +23 -0
- package/lib/Config/TaskConfig.js +39 -0
- package/lib/Decorator/EntityDecorator.d.ts +11 -0
- package/lib/Decorator/EntityDecorator.js +17 -0
- package/lib/Entity/BackupSessionEntity.d.ts +6 -0
- package/lib/Entity/BackupSessionEntity.js +22 -0
- package/lib/Entity/BackupSessionRepositoryEntity.d.ts +6 -0
- package/lib/Entity/BackupSessionRepositoryEntity.js +22 -0
- package/lib/Entity/BackupSessionTaskEntity.d.ts +5 -0
- package/lib/Entity/BackupSessionTaskEntity.js +22 -0
- package/lib/Entity/CrudEntityAbstract.d.ts +5 -0
- package/lib/Entity/CrudEntityAbstract.js +6 -0
- package/lib/Entity/RestoreSessionEntity.d.ts +5 -0
- package/lib/Entity/RestoreSessionEntity.js +22 -0
- package/lib/Entity/RestoreSessionRepositoryEntity.d.ts +6 -0
- package/lib/Entity/RestoreSessionRepositoryEntity.js +22 -0
- package/lib/Entity/RestoreSessionTaskEntity.d.ts +5 -0
- package/lib/Entity/RestoreSessionTaskEntity.js +22 -0
- package/lib/Entity/StateEntityAbstract.d.ts +12 -0
- package/lib/Entity/StateEntityAbstract.js +7 -0
- package/lib/Error/AppError.d.ts +2 -0
- package/lib/Error/AppError.js +6 -0
- package/lib/Factory/CommandFactory.d.ts +42 -0
- package/lib/Factory/CommandFactory.js +79 -0
- package/lib/Factory/EntityFactory.d.ts +6 -0
- package/lib/Factory/EntityFactory.js +40 -0
- package/lib/Factory/RepositoryFactory.d.ts +3 -0
- package/lib/Factory/RepositoryFactory.js +23 -0
- package/lib/Factory/TaskFactory.d.ts +3 -0
- package/lib/Factory/TaskFactory.js +30 -0
- package/lib/JsonSchema/DefinitionEnum.d.ts +25 -0
- package/lib/JsonSchema/DefinitionEnum.js +32 -0
- package/lib/JsonSchema/JsonSchema.d.ts +4 -0
- package/lib/JsonSchema/JsonSchema.js +50 -0
- package/lib/Repository/GitRepository.d.ts +29 -0
- package/lib/Repository/GitRepository.js +237 -0
- package/lib/Repository/LocalRepository.d.ts +51 -0
- package/lib/Repository/LocalRepository.js +358 -0
- package/lib/Repository/RepositoryAbstract.d.ts +77 -0
- package/lib/Repository/RepositoryAbstract.js +19 -0
- package/lib/Repository/ResticRepository.d.ts +36 -0
- package/lib/Repository/ResticRepository.js +230 -0
- package/lib/SessionDriver/ConsoleSessionDriver.d.ts +37 -0
- package/lib/SessionDriver/ConsoleSessionDriver.js +178 -0
- package/lib/SessionDriver/SessionDriverAbstract.d.ts +78 -0
- package/lib/SessionDriver/SessionDriverAbstract.js +27 -0
- package/lib/SessionDriver/SqliteSessionDriver.d.ts +20 -0
- package/lib/SessionDriver/SqliteSessionDriver.js +169 -0
- package/lib/SessionManager/BackupSessionManager.d.ts +44 -0
- package/lib/SessionManager/BackupSessionManager.js +206 -0
- package/lib/SessionManager/RestoreSessionManager.d.ts +44 -0
- package/lib/SessionManager/RestoreSessionManager.js +206 -0
- package/lib/Task/GitTask.d.ts +35 -0
- package/lib/Task/GitTask.js +248 -0
- package/lib/Task/MariadbTask.d.ts +25 -0
- package/lib/Task/MariadbTask.js +139 -0
- package/lib/Task/MssqlTask.d.ts +22 -0
- package/lib/Task/MssqlTask.js +109 -0
- package/lib/Task/MysqlDumpTask.d.ts +14 -0
- package/lib/Task/MysqlDumpTask.js +129 -0
- package/lib/Task/PostgresqlDumpTask.d.ts +14 -0
- package/lib/Task/PostgresqlDumpTask.js +101 -0
- package/lib/Task/SqlDumpTaskAbstract.d.ts +36 -0
- package/lib/Task/SqlDumpTaskAbstract.js +146 -0
- package/lib/Task/TaskAbstract.d.ts +37 -0
- package/lib/Task/TaskAbstract.js +17 -0
- package/lib/bin.d.ts +2 -0
- package/lib/bin.js +5 -0
- package/lib/cli.d.ts +4 -0
- package/lib/cli.js +110 -0
- package/lib/index.d.ts +0 -0
- package/lib/index.js +1 -0
- package/lib/util/DataFormat.d.ts +24 -0
- package/lib/util/DataFormat.js +50 -0
- package/lib/util/GitUtil.d.ts +38 -0
- package/lib/util/GitUtil.js +105 -0
- package/lib/util/ObjectVault.d.ts +13 -0
- package/lib/util/ObjectVault.js +31 -0
- package/lib/util/ResticUtil.d.ts +92 -0
- package/lib/util/ResticUtil.js +144 -0
- package/lib/util/cli-util.d.ts +27 -0
- package/lib/util/cli-util.js +118 -0
- package/lib/util/datatruck/config-util.d.ts +55 -0
- package/lib/util/datatruck/config-util.js +93 -0
- package/lib/util/datatruck/paths-util.d.ts +5 -0
- package/lib/util/datatruck/paths-util.js +22 -0
- package/lib/util/datatruck/snapshot-util.d.ts +4 -0
- package/lib/util/datatruck/snapshot-util.js +31 -0
- package/lib/util/date-util.d.ts +12 -0
- package/lib/util/date-util.js +74 -0
- package/lib/util/entity-util.d.ts +4 -0
- package/lib/util/entity-util.js +10 -0
- package/lib/util/fs-util.d.ts +43 -0
- package/lib/util/fs-util.js +278 -0
- package/lib/util/math-util.d.ts +1 -0
- package/lib/util/math-util.js +7 -0
- package/lib/util/object-util.d.ts +7 -0
- package/lib/util/object-util.js +58 -0
- package/lib/util/process-util.d.ts +50 -0
- package/lib/util/process-util.js +181 -0
- package/lib/util/string-util.d.ts +17 -0
- package/lib/util/string-util.js +77 -0
- package/lib/util/zip-util.d.ts +52 -0
- package/lib/util/zip-util.js +135 -0
- package/migrations/001-initial.sql +122 -0
- package/package.json +62 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResticRepository = exports.resticPackageRepositoryDefinition = exports.resticRepositoryDefinition = exports.resticRepositoryName = void 0;
|
|
4
|
+
const AppError_1 = require("../Error/AppError");
|
|
5
|
+
const ResticUtil_1 = require("../util/ResticUtil");
|
|
6
|
+
const paths_util_1 = require("../util/datatruck/paths-util");
|
|
7
|
+
const fs_util_1 = require("../util/fs-util");
|
|
8
|
+
const string_util_1 = require("../util/string-util");
|
|
9
|
+
const RepositoryAbstract_1 = require("./RepositoryAbstract");
|
|
10
|
+
const assert_1 = require("assert");
|
|
11
|
+
const micromatch_1 = require("micromatch");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
exports.resticRepositoryName = "restic";
|
|
14
|
+
exports.resticRepositoryDefinition = {
|
|
15
|
+
type: "object",
|
|
16
|
+
required: ["passwordFile", "repository"],
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
properties: {
|
|
19
|
+
passwordFile: { type: "string" },
|
|
20
|
+
repository: {
|
|
21
|
+
type: "object",
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
required: ["backend"],
|
|
24
|
+
properties: {
|
|
25
|
+
name: { type: "string" },
|
|
26
|
+
env: {
|
|
27
|
+
type: "object",
|
|
28
|
+
patternProperties: {
|
|
29
|
+
".+": { type: "string" },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
backend: {
|
|
33
|
+
enum: ["local", "rest", "sftp", "s3", "azure", "gs", "rclone"],
|
|
34
|
+
},
|
|
35
|
+
protocol: {
|
|
36
|
+
enum: ["http", "https"],
|
|
37
|
+
},
|
|
38
|
+
host: { type: "string" },
|
|
39
|
+
username: { type: "string" },
|
|
40
|
+
passwordFile: { type: "string" },
|
|
41
|
+
port: { type: "integer" },
|
|
42
|
+
path: { type: "string" },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
exports.resticPackageRepositoryDefinition = {
|
|
48
|
+
type: "object",
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
properties: {},
|
|
51
|
+
};
|
|
52
|
+
class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
53
|
+
async buildEnv() {
|
|
54
|
+
if (this.env)
|
|
55
|
+
return this.env;
|
|
56
|
+
return (this.env = {
|
|
57
|
+
RESTIC_PASSWORD_FILE: this.config.passwordFile,
|
|
58
|
+
RESTIC_REPOSITORY: await ResticUtil_1.ResticUtil.formatRepository(this.config.repository),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
static buildSnapshotTag(name, value) {
|
|
62
|
+
return `${ResticRepository.refPrefix}${name}:${value}`;
|
|
63
|
+
}
|
|
64
|
+
static parseSnapshotTag(tag) {
|
|
65
|
+
for (const metaName in RepositoryAbstract_1.SnapshotTagEnum) {
|
|
66
|
+
const name = RepositoryAbstract_1.SnapshotTagEnum[metaName];
|
|
67
|
+
const prefix = `${ResticRepository.refPrefix}${name}:`;
|
|
68
|
+
if (tag.startsWith(prefix))
|
|
69
|
+
return {
|
|
70
|
+
name: name,
|
|
71
|
+
value: tag.slice(prefix.length),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
static parseSnapshotTags(tags) {
|
|
77
|
+
const result = {
|
|
78
|
+
tags: [],
|
|
79
|
+
};
|
|
80
|
+
for (const tag of tags) {
|
|
81
|
+
const tagItem = ResticRepository.parseSnapshotTag(tag);
|
|
82
|
+
if (tagItem && tagItem.name !== "tags") {
|
|
83
|
+
result[tagItem.name] = tagItem.value;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
result.tags.push(tag);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
onGetSource() {
|
|
92
|
+
return (0, string_util_1.formatUri)({ ...this.config.repository, password: undefined });
|
|
93
|
+
}
|
|
94
|
+
async onInit(data) {
|
|
95
|
+
const restic = new ResticUtil_1.ResticUtil({
|
|
96
|
+
env: await this.buildEnv(),
|
|
97
|
+
log: data.options.verbose,
|
|
98
|
+
});
|
|
99
|
+
if (this.config.repository.backend === "local")
|
|
100
|
+
await (0, fs_util_1.mkdirIfNotExists)(this.env.RESTIC_REPOSITORY);
|
|
101
|
+
if (!(await restic.checkRepository()))
|
|
102
|
+
await restic.exec(["init"]);
|
|
103
|
+
}
|
|
104
|
+
async onSnapshots(data) {
|
|
105
|
+
const restic = new ResticUtil_1.ResticUtil({
|
|
106
|
+
env: await this.buildEnv(),
|
|
107
|
+
log: data.options.verbose,
|
|
108
|
+
});
|
|
109
|
+
const packagePatterns = (0, string_util_1.makePathPatterns)(data.options.packageNames);
|
|
110
|
+
const result = await restic.snapshots({
|
|
111
|
+
json: true,
|
|
112
|
+
tags: [
|
|
113
|
+
...(data.options.ids?.map((id) => ResticRepository.buildSnapshotTag(id.length === 8 ? RepositoryAbstract_1.SnapshotTagEnum.SHORT_ID : RepositoryAbstract_1.SnapshotTagEnum.ID, id)) ?? []),
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
return result.reduce((items, item) => {
|
|
117
|
+
const tag = ResticRepository.parseSnapshotTags(item.tags ?? []);
|
|
118
|
+
if (!tag.id)
|
|
119
|
+
return items;
|
|
120
|
+
if (packagePatterns && !(0, micromatch_1.isMatch)(tag.package, packagePatterns))
|
|
121
|
+
return items;
|
|
122
|
+
const itemTags = tag.tags ?? [];
|
|
123
|
+
if (data.options.tags && !itemTags.some((t) => itemTags.includes(t)))
|
|
124
|
+
return items;
|
|
125
|
+
items.push({
|
|
126
|
+
originalId: item.id,
|
|
127
|
+
packageName: tag.package,
|
|
128
|
+
date: tag.date,
|
|
129
|
+
id: tag.id,
|
|
130
|
+
tags: itemTags,
|
|
131
|
+
});
|
|
132
|
+
return items;
|
|
133
|
+
}, []);
|
|
134
|
+
}
|
|
135
|
+
async onPrune(data) {
|
|
136
|
+
const restic = new ResticUtil_1.ResticUtil({
|
|
137
|
+
env: await this.buildEnv(),
|
|
138
|
+
log: data.options.verbose,
|
|
139
|
+
});
|
|
140
|
+
await restic.forget({
|
|
141
|
+
snapshotId: data.snapshot.originalId,
|
|
142
|
+
prune: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async onBackup(data) {
|
|
146
|
+
const restic = new ResticUtil_1.ResticUtil({
|
|
147
|
+
env: await this.buildEnv(),
|
|
148
|
+
log: data.options.verbose,
|
|
149
|
+
});
|
|
150
|
+
const pkg = data.package;
|
|
151
|
+
const sourcePath = data.targetPath ?? data.package.path;
|
|
152
|
+
(0, assert_1.ok)(sourcePath);
|
|
153
|
+
const include = (await (0, paths_util_1.parsePaths)(pkg.include ?? [], {
|
|
154
|
+
cwd: sourcePath,
|
|
155
|
+
verbose: data.options.verbose,
|
|
156
|
+
})).map(path_1.normalize);
|
|
157
|
+
// https://github.com/restic/restic/issues/233
|
|
158
|
+
// https://github.com/restic/restic/pull/2311
|
|
159
|
+
if (include.length)
|
|
160
|
+
throw new AppError_1.AppError(`Include is not supported`);
|
|
161
|
+
if (data.options.tags?.some((tag) => tag.startsWith(ResticRepository.refPrefix)))
|
|
162
|
+
throw new AppError_1.AppError(`Tag prefix is not allowed`);
|
|
163
|
+
const packageTag = ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.PACKAGE, data.package.name);
|
|
164
|
+
const [lastSnapshot] = await restic.snapshots({
|
|
165
|
+
json: true,
|
|
166
|
+
tags: [packageTag],
|
|
167
|
+
latest: 1,
|
|
168
|
+
});
|
|
169
|
+
const nodePkg = (0, fs_util_1.parsePackageFile)();
|
|
170
|
+
await restic.backup({
|
|
171
|
+
cwd: sourcePath,
|
|
172
|
+
paths: ["."],
|
|
173
|
+
parent: lastSnapshot?.id,
|
|
174
|
+
// https://github.com/restic/restic/pull/3200
|
|
175
|
+
...((await restic.checkBackupSetPathSupport()) && {
|
|
176
|
+
setPaths: [`/datatruck/${data.package.name}`],
|
|
177
|
+
}),
|
|
178
|
+
exclude: (await (0, paths_util_1.parsePaths)(pkg.exclude ?? [], {
|
|
179
|
+
cwd: sourcePath,
|
|
180
|
+
verbose: data.options.verbose,
|
|
181
|
+
})).map(path_1.normalize),
|
|
182
|
+
tags: [
|
|
183
|
+
ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.ID, data.snapshot.id),
|
|
184
|
+
ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.SHORT_ID, data.snapshot.id.slice(0, 8)),
|
|
185
|
+
ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.DATE, data.snapshot.date),
|
|
186
|
+
ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.VERSION, nodePkg.version),
|
|
187
|
+
packageTag,
|
|
188
|
+
...(data.options.tags ?? []),
|
|
189
|
+
],
|
|
190
|
+
onStream: async (streamData) => {
|
|
191
|
+
if (streamData.message_type === "status") {
|
|
192
|
+
await data.onProgress({
|
|
193
|
+
total: streamData.total_bytes,
|
|
194
|
+
current: streamData.bytes_done ?? 0,
|
|
195
|
+
percent: Number((streamData.percent_done * 100).toFixed(2)),
|
|
196
|
+
step: streamData.current_files?.join(", ") ?? "-",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
await data.onProgress({
|
|
202
|
+
current: 100,
|
|
203
|
+
percent: 100,
|
|
204
|
+
step: "",
|
|
205
|
+
total: 100,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async onRestore(data) {
|
|
209
|
+
const restorePath = data.targetPath ?? data.package.restorePath;
|
|
210
|
+
(0, assert_1.ok)(restorePath);
|
|
211
|
+
const restic = new ResticUtil_1.ResticUtil({
|
|
212
|
+
env: await this.buildEnv(),
|
|
213
|
+
log: data.options.verbose,
|
|
214
|
+
});
|
|
215
|
+
const [snapshot] = await this.onSnapshots({
|
|
216
|
+
options: {
|
|
217
|
+
ids: [data.snapshot.id],
|
|
218
|
+
packageNames: [data.package.name],
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
if (!snapshot)
|
|
222
|
+
throw new AppError_1.AppError(`Snapshot not found`);
|
|
223
|
+
await restic.restore({
|
|
224
|
+
id: snapshot.originalId,
|
|
225
|
+
target: restorePath,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.ResticRepository = ResticRepository;
|
|
230
|
+
ResticRepository.refPrefix = "dt-";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { WriteDataType, ReadResultType, SessionDriverAbstract } from "./SessionDriverAbstract";
|
|
3
|
+
declare type BadgeType = {
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
color: (input: string) => string;
|
|
7
|
+
};
|
|
8
|
+
declare type MessageType = {
|
|
9
|
+
sessionId: number;
|
|
10
|
+
level?: number;
|
|
11
|
+
textPrefix?: string;
|
|
12
|
+
text?: string;
|
|
13
|
+
badges: BadgeType[];
|
|
14
|
+
errorBadge?: BadgeType;
|
|
15
|
+
progressCurrent?: number | null;
|
|
16
|
+
progressTotal?: number | null;
|
|
17
|
+
progressPercent?: number | null;
|
|
18
|
+
progressStep?: string | null;
|
|
19
|
+
progressStepPercent?: number | null;
|
|
20
|
+
};
|
|
21
|
+
export declare class ConsoleSessionDriver extends SessionDriverAbstract {
|
|
22
|
+
protected lastMessage: MessageType | undefined;
|
|
23
|
+
protected lastMessageText: string | undefined;
|
|
24
|
+
protected prints: number;
|
|
25
|
+
protected renderInterval: NodeJS.Timeout;
|
|
26
|
+
protected rendering?: boolean;
|
|
27
|
+
protected lastColumns?: number;
|
|
28
|
+
protected startTime: number;
|
|
29
|
+
onInit(): Promise<void>;
|
|
30
|
+
onEnd(): Promise<void>;
|
|
31
|
+
onRead(): Promise<ReadResultType[]>;
|
|
32
|
+
protected printMessage(message: MessageType): void;
|
|
33
|
+
protected renderSpinner(text: string): string;
|
|
34
|
+
protected renderMessage(message: MessageType): string;
|
|
35
|
+
onWrite(data: WriteDataType): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConsoleSessionDriver = void 0;
|
|
4
|
+
const AppError_1 = require("../Error/AppError");
|
|
5
|
+
const cli_util_1 = require("../util/cli-util");
|
|
6
|
+
const SessionDriverAbstract_1 = require("./SessionDriverAbstract");
|
|
7
|
+
const chalk_1 = require("chalk");
|
|
8
|
+
const sep = (0, chalk_1.grey)(`|`);
|
|
9
|
+
const renderBadge = (badge) => `${badge.color(badge.name)}${(0, chalk_1.grey)(`:`)} ${(0, chalk_1.white)(badge.value)}`;
|
|
10
|
+
const renderBadges = (badges) => badges.map(renderBadge).join(` ${sep} `);
|
|
11
|
+
class ConsoleSessionDriver extends SessionDriverAbstract_1.SessionDriverAbstract {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.prints = 0;
|
|
15
|
+
}
|
|
16
|
+
async onInit() {
|
|
17
|
+
this.startTime = Date.now();
|
|
18
|
+
this.renderInterval = setInterval(() => {
|
|
19
|
+
if (this.lastMessage)
|
|
20
|
+
this.printMessage(this.lastMessage);
|
|
21
|
+
}, 100);
|
|
22
|
+
}
|
|
23
|
+
async onEnd() {
|
|
24
|
+
clearInterval(this.renderInterval);
|
|
25
|
+
if (!this.options.verbose)
|
|
26
|
+
process.stdout.write(cli_util_1.showCursorCommand);
|
|
27
|
+
const ellapsed = (Date.now() - this.startTime) / 1000;
|
|
28
|
+
let ellapsedUnit;
|
|
29
|
+
let ellapsedValue;
|
|
30
|
+
if (ellapsed > 60 * 60) {
|
|
31
|
+
ellapsedValue = ellapsed / 60 / 60;
|
|
32
|
+
ellapsedUnit = `hour`;
|
|
33
|
+
}
|
|
34
|
+
else if (ellapsed > 60) {
|
|
35
|
+
ellapsedValue = ellapsed / 60;
|
|
36
|
+
ellapsedUnit = `minute`;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
ellapsedValue = ellapsed;
|
|
40
|
+
ellapsedUnit = `second`;
|
|
41
|
+
}
|
|
42
|
+
if (ellapsedValue !== 1)
|
|
43
|
+
ellapsedUnit += `s`;
|
|
44
|
+
const message = `Completed in ${ellapsedValue.toFixed(2)} ${ellapsedUnit}`;
|
|
45
|
+
console.info(`\n${(0, chalk_1.grey)(message)}`);
|
|
46
|
+
}
|
|
47
|
+
async onRead() {
|
|
48
|
+
throw new AppError_1.AppError("Method not implemented");
|
|
49
|
+
}
|
|
50
|
+
printMessage(message) {
|
|
51
|
+
const text = this.renderMessage(message);
|
|
52
|
+
if (this.options.verbose && this.lastMessageText === text) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (this.options.verbose) {
|
|
56
|
+
process.stdout.write(`${this.renderSpinner(text)}\n`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const columns = process.stdout.columns;
|
|
60
|
+
const line = this.renderSpinner(text);
|
|
61
|
+
const [truncatedLine, truncted] = (0, cli_util_1.truncate)(line, columns);
|
|
62
|
+
if (this.lastColumns && columns !== this.lastColumns && truncted)
|
|
63
|
+
process.stdout.write(`${cli_util_1.clearCommand}\n`);
|
|
64
|
+
process.stdout.write(`${cli_util_1.clearCommand}${truncatedLine}${cli_util_1.hideCursorCommand}`);
|
|
65
|
+
this.lastColumns = columns;
|
|
66
|
+
}
|
|
67
|
+
this.prints++;
|
|
68
|
+
this.lastMessage = message;
|
|
69
|
+
this.lastMessageText = text;
|
|
70
|
+
}
|
|
71
|
+
renderSpinner(text) {
|
|
72
|
+
return text.replace("{spinner}", (0, chalk_1.grey)(this.options.verbose ? "?" : (0, cli_util_1.renderSpinner)(this.prints)));
|
|
73
|
+
}
|
|
74
|
+
renderMessage(message) {
|
|
75
|
+
const badges = renderBadges([
|
|
76
|
+
...message.badges,
|
|
77
|
+
...(message.errorBadge ? [message.errorBadge] : []),
|
|
78
|
+
]);
|
|
79
|
+
const padding = " ".repeat(message.level ?? 0);
|
|
80
|
+
const haveProgressBar = typeof message.progressPercent === "number";
|
|
81
|
+
const sessionId = message.sessionId.toString().padStart(2, "0");
|
|
82
|
+
const parts = [
|
|
83
|
+
`${padding}${message.textPrefix} [${(0, chalk_1.grey)(sessionId)}] ${message.text}`,
|
|
84
|
+
badges,
|
|
85
|
+
...(haveProgressBar
|
|
86
|
+
? [
|
|
87
|
+
(0, chalk_1.cyan)((0, cli_util_1.renderProgressBar)(message.progressPercent ?? 0, 10)),
|
|
88
|
+
`${message.progressPercent?.toFixed(2)}%`,
|
|
89
|
+
`${message.progressCurrent}/${message.progressTotal}`,
|
|
90
|
+
message.progressStep,
|
|
91
|
+
`${message.progressStepPercent
|
|
92
|
+
? (0, chalk_1.cyan)((0, cli_util_1.renderProgressBar)(message.progressStepPercent ?? 0, 10))
|
|
93
|
+
: ""}`,
|
|
94
|
+
].filter((v) => !!v?.length)
|
|
95
|
+
: []),
|
|
96
|
+
];
|
|
97
|
+
return parts.join(` ${sep} `);
|
|
98
|
+
}
|
|
99
|
+
async onWrite(data) {
|
|
100
|
+
if (data.action === SessionDriverAbstract_1.ActionEnum.Init)
|
|
101
|
+
return;
|
|
102
|
+
const message = {
|
|
103
|
+
sessionId: "sessionId" in data.data ? data.data.sessionId : data.data.id,
|
|
104
|
+
badges: [],
|
|
105
|
+
};
|
|
106
|
+
const isHeader = data.entity === SessionDriverAbstract_1.EntityEnum.BackupSession ||
|
|
107
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.RestoreSession;
|
|
108
|
+
const hasProgress = isHeader ||
|
|
109
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.BackupSessionTask ||
|
|
110
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.BackupSessionRepository ||
|
|
111
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.RestoreSessionTask ||
|
|
112
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.RestoreSessionRepository;
|
|
113
|
+
if (data.action === SessionDriverAbstract_1.ActionEnum.Start) {
|
|
114
|
+
if (isHeader) {
|
|
115
|
+
message.textPrefix = data.data.error ? (0, chalk_1.red)("⨉") : (0, chalk_1.green)("✓");
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
message.textPrefix = hasProgress ? "{spinner}" : (0, chalk_1.grey)("?");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (data.action === SessionDriverAbstract_1.ActionEnum.End) {
|
|
122
|
+
message.textPrefix = data.data.error ? (0, chalk_1.red)("⨉") : (0, chalk_1.green)("✓");
|
|
123
|
+
if (data.data.error)
|
|
124
|
+
message.errorBadge = {
|
|
125
|
+
name: "error",
|
|
126
|
+
value: this.options.verbose
|
|
127
|
+
? data.data.error
|
|
128
|
+
: data.data.error.split("\n")[0],
|
|
129
|
+
color: chalk_1.red,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
else if (data.action === SessionDriverAbstract_1.ActionEnum.Progress) {
|
|
133
|
+
message.textPrefix = "{spinner}";
|
|
134
|
+
}
|
|
135
|
+
if (hasProgress) {
|
|
136
|
+
message.progressPercent = data.data.progressPercent;
|
|
137
|
+
message.progressCurrent = data.data.progressCurrent;
|
|
138
|
+
message.progressTotal = data.data.progressTotal;
|
|
139
|
+
message.progressStep = data.data.progressStep;
|
|
140
|
+
message.progressStepPercent = data.data.progressStepPercent;
|
|
141
|
+
}
|
|
142
|
+
if (data.entity === SessionDriverAbstract_1.EntityEnum.BackupSession ||
|
|
143
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.RestoreSession) {
|
|
144
|
+
message.text = data.data.packageName;
|
|
145
|
+
message.badges.push({
|
|
146
|
+
name: "snap",
|
|
147
|
+
value: data.data.snapshotId.slice(0, 8),
|
|
148
|
+
color: chalk_1.cyan,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else if (data.entity === SessionDriverAbstract_1.EntityEnum.BackupSessionTask ||
|
|
152
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.RestoreSessionTask) {
|
|
153
|
+
message.text = data.sessionData.packageName;
|
|
154
|
+
message.badges.push({
|
|
155
|
+
name: "task",
|
|
156
|
+
value: data.data.taskName,
|
|
157
|
+
color: chalk_1.cyan,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else if (data.entity === SessionDriverAbstract_1.EntityEnum.BackupSessionRepository ||
|
|
161
|
+
data.entity === SessionDriverAbstract_1.EntityEnum.RestoreSessionRepository) {
|
|
162
|
+
message.text = data.sessionData.packageName;
|
|
163
|
+
message.badges.push({
|
|
164
|
+
name: "repo",
|
|
165
|
+
value: data.data.repositoryName,
|
|
166
|
+
color: chalk_1.cyan,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (isHeader && data.action === SessionDriverAbstract_1.ActionEnum.End) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.printMessage(message);
|
|
173
|
+
if (!this.options.verbose)
|
|
174
|
+
if (!hasProgress || data.action === SessionDriverAbstract_1.ActionEnum.End || isHeader)
|
|
175
|
+
process.stdout.write("\n");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.ConsoleSessionDriver = ConsoleSessionDriver;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BackupSessionEntity } from "../Entity/BackupSessionEntity";
|
|
2
|
+
import { BackupSessionRepositoryEntity } from "../Entity/BackupSessionRepositoryEntity";
|
|
3
|
+
import { BackupSessionTaskEntity } from "../Entity/BackupSessionTaskEntity";
|
|
4
|
+
import { RestoreSessionEntity } from "../Entity/RestoreSessionEntity";
|
|
5
|
+
import { RestoreSessionRepositoryEntity } from "../Entity/RestoreSessionRepositoryEntity";
|
|
6
|
+
import { RestoreSessionTaskEntity } from "../Entity/RestoreSessionTaskEntity";
|
|
7
|
+
export declare enum ActionEnum {
|
|
8
|
+
Init = 0,
|
|
9
|
+
Start = 1,
|
|
10
|
+
Progress = 2,
|
|
11
|
+
End = 3
|
|
12
|
+
}
|
|
13
|
+
export declare enum EntityEnum {
|
|
14
|
+
BackupSession = 0,
|
|
15
|
+
BackupSessionTask = 1,
|
|
16
|
+
BackupSessionRepository = 2,
|
|
17
|
+
RestoreSession = 3,
|
|
18
|
+
RestoreSessionTask = 4,
|
|
19
|
+
RestoreSessionRepository = 5
|
|
20
|
+
}
|
|
21
|
+
export declare type WriteDataType = {
|
|
22
|
+
action: ActionEnum;
|
|
23
|
+
entity: EntityEnum.BackupSession;
|
|
24
|
+
data: BackupSessionEntity;
|
|
25
|
+
} | {
|
|
26
|
+
action: ActionEnum;
|
|
27
|
+
entity: EntityEnum.BackupSessionRepository;
|
|
28
|
+
data: BackupSessionRepositoryEntity;
|
|
29
|
+
sessionData: BackupSessionEntity;
|
|
30
|
+
} | {
|
|
31
|
+
action: ActionEnum;
|
|
32
|
+
entity: EntityEnum.BackupSessionTask;
|
|
33
|
+
data: BackupSessionTaskEntity;
|
|
34
|
+
sessionData: BackupSessionEntity;
|
|
35
|
+
} | {
|
|
36
|
+
action: ActionEnum;
|
|
37
|
+
entity: EntityEnum.RestoreSession;
|
|
38
|
+
data: RestoreSessionEntity;
|
|
39
|
+
} | {
|
|
40
|
+
action: ActionEnum;
|
|
41
|
+
entity: EntityEnum.RestoreSessionTask;
|
|
42
|
+
data: RestoreSessionTaskEntity;
|
|
43
|
+
sessionData: RestoreSessionEntity;
|
|
44
|
+
} | {
|
|
45
|
+
action: ActionEnum;
|
|
46
|
+
entity: EntityEnum.RestoreSessionRepository;
|
|
47
|
+
data: RestoreSessionRepositoryEntity;
|
|
48
|
+
sessionData: RestoreSessionEntity;
|
|
49
|
+
};
|
|
50
|
+
export declare type ReadDataType = {
|
|
51
|
+
repositoryNames?: string[];
|
|
52
|
+
packageNames?: string[];
|
|
53
|
+
tags?: string[];
|
|
54
|
+
limit?: number | null;
|
|
55
|
+
verbose?: boolean;
|
|
56
|
+
};
|
|
57
|
+
export declare type ReadResultType = {
|
|
58
|
+
id: number;
|
|
59
|
+
snapshotId: string;
|
|
60
|
+
creationDate: string;
|
|
61
|
+
state: "started" | "ended";
|
|
62
|
+
packageName: string;
|
|
63
|
+
repositoryName: string;
|
|
64
|
+
repositoryType: string;
|
|
65
|
+
error: string | null;
|
|
66
|
+
};
|
|
67
|
+
export declare abstract class SessionDriverAbstract {
|
|
68
|
+
readonly options: {
|
|
69
|
+
verbose?: boolean;
|
|
70
|
+
};
|
|
71
|
+
constructor(options: {
|
|
72
|
+
verbose?: boolean;
|
|
73
|
+
});
|
|
74
|
+
onInit(): Promise<void>;
|
|
75
|
+
abstract onWrite(data: WriteDataType): Promise<void>;
|
|
76
|
+
onEnd(): Promise<void>;
|
|
77
|
+
abstract onRead(data: ReadDataType, entity: EntityEnum): Promise<ReadResultType[]>;
|
|
78
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionDriverAbstract = exports.EntityEnum = exports.ActionEnum = void 0;
|
|
4
|
+
var ActionEnum;
|
|
5
|
+
(function (ActionEnum) {
|
|
6
|
+
ActionEnum[ActionEnum["Init"] = 0] = "Init";
|
|
7
|
+
ActionEnum[ActionEnum["Start"] = 1] = "Start";
|
|
8
|
+
ActionEnum[ActionEnum["Progress"] = 2] = "Progress";
|
|
9
|
+
ActionEnum[ActionEnum["End"] = 3] = "End";
|
|
10
|
+
})(ActionEnum = exports.ActionEnum || (exports.ActionEnum = {}));
|
|
11
|
+
var EntityEnum;
|
|
12
|
+
(function (EntityEnum) {
|
|
13
|
+
EntityEnum[EntityEnum["BackupSession"] = 0] = "BackupSession";
|
|
14
|
+
EntityEnum[EntityEnum["BackupSessionTask"] = 1] = "BackupSessionTask";
|
|
15
|
+
EntityEnum[EntityEnum["BackupSessionRepository"] = 2] = "BackupSessionRepository";
|
|
16
|
+
EntityEnum[EntityEnum["RestoreSession"] = 3] = "RestoreSession";
|
|
17
|
+
EntityEnum[EntityEnum["RestoreSessionTask"] = 4] = "RestoreSessionTask";
|
|
18
|
+
EntityEnum[EntityEnum["RestoreSessionRepository"] = 5] = "RestoreSessionRepository";
|
|
19
|
+
})(EntityEnum = exports.EntityEnum || (exports.EntityEnum = {}));
|
|
20
|
+
class SessionDriverAbstract {
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
async onInit() { }
|
|
25
|
+
async onEnd() { }
|
|
26
|
+
}
|
|
27
|
+
exports.SessionDriverAbstract = SessionDriverAbstract;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="./vendor-typings/sqlite3" />
|
|
2
|
+
import { WriteDataType, EntityEnum, ReadDataType, SessionDriverAbstract } from "./SessionDriverAbstract";
|
|
3
|
+
import { Database } from "sqlite";
|
|
4
|
+
import sqlite3 from "sqlite3";
|
|
5
|
+
export declare class SqliteSessionDriver extends SessionDriverAbstract {
|
|
6
|
+
protected idMap: {
|
|
7
|
+
[entity in EntityEnum]?: Record<number, number>;
|
|
8
|
+
};
|
|
9
|
+
protected db: Database<sqlite3.Database, sqlite3.Statement>;
|
|
10
|
+
onInit(): Promise<void>;
|
|
11
|
+
private buildInsertStm;
|
|
12
|
+
private buildUpdateStm;
|
|
13
|
+
private exec;
|
|
14
|
+
onRead(data: ReadDataType, type: EntityEnum): Promise<any[]>;
|
|
15
|
+
private setMapId;
|
|
16
|
+
private getMapId;
|
|
17
|
+
private static getEntityTable;
|
|
18
|
+
private static getParentEntity;
|
|
19
|
+
onWrite(data: WriteDataType): Promise<void>;
|
|
20
|
+
}
|