@ps-aux/nodebup 0.2.2 → 0.5.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/lib/bup/AggregateBackupController.js +36 -25
- package/lib/bup/BackupController.js +56 -0
- package/lib/cli/app.js +39 -14
- package/lib/config/Config.js +7 -4
- package/lib/config/index.js +13 -0
- package/lib/config/validateConfigAgainstSchema.js +3 -1
- package/lib/config/validateConfigAgainstSchema.spec.js +0 -4
- package/lib/ctx/Context.js +7 -1
- package/lib/fs/Fs.js +1 -1
- package/lib/fs/fssync/FsSync.spec.js +2 -6
- package/lib/log/AppLogger.js +6 -4
- package/lib/log/ConsoleLogger.js +5 -3
- package/lib/storage/BackupClient.js +5 -0
- package/lib/storage/{restic/ResticStorageBackend.js → BackupClientStorageBackend.js} +8 -4
- package/lib/storage/StorageBackendProvider.js +19 -19
- package/lib/storage/b2/B2CredentialsProvider.js +55 -0
- package/lib/storage/rclone/RCloneClientFactory.js +69 -0
- package/lib/storage/rclone/RcloneClient.js +87 -0
- package/lib/storage/rclone/RcloneClient.spec.js +83 -0
- package/lib/storage/restic/ResticClient.js +6 -15
- package/lib/storage/restic/ResticClient.spec.js +11 -32
- package/lib/storage/restic/ResticClientFactory.js +10 -34
- package/lib/storage/restic/ResticController.js +2 -2
- package/lib/tools/gpg/Gpg.js +8 -1
- package/lib/tools/gpg/Gpg.spec.js +18 -7
- package/lib/tools/shell/Shell.js +1 -1
- package/lib/tools/ssh/SshKeyManager.js +1 -1
- package/package.json +17 -15
- package/lib/storage/local-file/LocalFileStorageBackend.js +0 -22
@@ -7,8 +7,6 @@ exports.AggregateBackupController = void 0;
|
|
7
7
|
|
8
8
|
var _FsSyncer = require("../fs/fssync/FsSyncer");
|
9
9
|
|
10
|
-
var _Config = require("../config/Config");
|
11
|
-
|
12
10
|
var _Gpg = require("../tools/gpg/Gpg");
|
13
11
|
|
14
12
|
var _Fs = require("../fs/Fs");
|
@@ -21,7 +19,11 @@ var _ContextSymbols = require("../ctx/ContextSymbols");
|
|
21
19
|
|
22
20
|
var _AppLogger = require("../log/AppLogger");
|
23
21
|
|
24
|
-
var
|
22
|
+
var _Path = require("../fs/path/Path");
|
23
|
+
|
24
|
+
var _config = require("../config");
|
25
|
+
|
26
|
+
var _BackupController = require("./BackupController");
|
25
27
|
|
26
28
|
var _dec, _dec2, _dec3, _dec4, _class;
|
27
29
|
|
@@ -29,41 +31,54 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
29
31
|
|
30
32
|
let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
31
33
|
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 6);
|
32
|
-
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _Gpg.Gpg === "undefined" ? Object : _Gpg.Gpg, typeof _SshKeyManager.SshKeyManager === "undefined" ? Object : _SshKeyManager.SshKeyManager, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _FsSyncer.FsSyncer === "undefined" ? Object : _FsSyncer.FsSyncer, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof
|
33
|
-
constructor(gpg, ssh, fs, fsSyncer, log,
|
34
|
+
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _Gpg.Gpg === "undefined" ? Object : _Gpg.Gpg, typeof _SshKeyManager.SshKeyManager === "undefined" ? Object : _SshKeyManager.SshKeyManager, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _FsSyncer.FsSyncer === "undefined" ? Object : _FsSyncer.FsSyncer, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _BackupController.BackupController === "undefined" ? Object : _BackupController.BackupController, typeof _config.Config === "undefined" ? Object : _config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class AggregateBackupController {
|
35
|
+
constructor(gpg, ssh, fs, fsSyncer, log, backupController, cfg) {
|
34
36
|
this.gpg = gpg;
|
35
37
|
this.ssh = ssh;
|
36
38
|
this.fs = fs;
|
37
39
|
this.fsSyncer = fsSyncer;
|
38
40
|
this.log = log;
|
39
|
-
this.
|
41
|
+
this.backupController = backupController;
|
40
42
|
this.cfg = cfg;
|
41
43
|
|
42
|
-
_defineProperty(this, "backup", async (
|
44
|
+
_defineProperty(this, "backup", async (aggrName, storage) => {
|
43
45
|
const aggrDir = this.cfg.pwd.resolve('.aggr');
|
44
|
-
this.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
const aggr = this.cfg.aggregates.find(a => a.name === aggrName);
|
47
|
+
if (!aggr) throw new Error(`No backup aggregate with name '${aggrName}'`);
|
48
|
+
const {
|
49
|
+
file,
|
50
|
+
sshKey,
|
51
|
+
gpgKey
|
52
|
+
} = aggr.sources;
|
53
|
+
|
54
|
+
try {
|
55
|
+
this.fs.ensureDir(aggrDir);
|
56
|
+
file.forEach(f => this.runFileTask(f, aggrDir));
|
57
|
+
sshKey.forEach(k => this.runSshKeyTask(k, aggrDir));
|
58
|
+
gpgKey.forEach(k => this.runGpgKeyTask(k, aggrDir));
|
59
|
+
this.backupController.backup(storage, aggrDir);
|
60
|
+
} finally {
|
61
|
+
this.log.debug();
|
62
|
+
this.fs.rmDir(aggrDir);
|
49
63
|
}
|
50
|
-
|
51
|
-
this.storageBackendProvider.provide(storage).store(aggrDir);
|
52
|
-
this.fs.rmDir(aggrDir);
|
53
64
|
});
|
54
65
|
|
55
|
-
_defineProperty(this, "runGpgKeyTask", t => {
|
66
|
+
_defineProperty(this, "runGpgKeyTask", (t, dir) => {
|
56
67
|
this.log.info('Running GPG key task', t);
|
57
68
|
const key = this.gpg.exportKey(t.id);
|
58
|
-
|
59
|
-
|
69
|
+
|
70
|
+
const to = t.to || _Path.RelativePath.from(`keys/gpg/${t.id}.asc`);
|
71
|
+
|
72
|
+
this.fs.writeFile(dir.resolve(to), key);
|
60
73
|
});
|
61
74
|
|
62
|
-
_defineProperty(this, "runSshKeyTask", t => {
|
75
|
+
_defineProperty(this, "runSshKeyTask", (t, dir) => {
|
63
76
|
this.log.info('Running SSH key task', t);
|
64
77
|
const key = this.ssh.exportKey(t.id);
|
65
|
-
|
66
|
-
|
78
|
+
|
79
|
+
const to = t.to || _Path.RelativePath.from(`keys/ssh/${t.id}`);
|
80
|
+
|
81
|
+
this.fs.writeFile(dir.resolve(to), key);
|
67
82
|
});
|
68
83
|
|
69
84
|
_defineProperty(this, "runFileTask", (f, dir) => {
|
@@ -74,10 +89,6 @@ let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = fu
|
|
74
89
|
} = f;
|
75
90
|
this.fsSyncer.sync(from, dir.resolve(to));
|
76
91
|
});
|
77
|
-
|
78
|
-
_defineProperty(this, "storagePath", path => {
|
79
|
-
throw new Error('Unspported');
|
80
|
-
});
|
81
92
|
}
|
82
93
|
|
83
94
|
}) || _class) || _class) || _class) || _class);
|
@@ -0,0 +1,56 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.BackupController = void 0;
|
7
|
+
|
8
|
+
var _FsSyncer = require("../fs/fssync/FsSyncer");
|
9
|
+
|
10
|
+
var _Gpg = require("../tools/gpg/Gpg");
|
11
|
+
|
12
|
+
var _Fs = require("../fs/Fs");
|
13
|
+
|
14
|
+
var _SshKeyManager = require("../tools/ssh/SshKeyManager");
|
15
|
+
|
16
|
+
var _inversify = require("inversify");
|
17
|
+
|
18
|
+
var _ContextSymbols = require("../ctx/ContextSymbols");
|
19
|
+
|
20
|
+
var _AppLogger = require("../log/AppLogger");
|
21
|
+
|
22
|
+
var _StorageBackendProvider = require("../storage/StorageBackendProvider");
|
23
|
+
|
24
|
+
var _config = require("../config");
|
25
|
+
|
26
|
+
var _dec, _dec2, _dec3, _dec4, _class;
|
27
|
+
|
28
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
29
|
+
|
30
|
+
let BackupController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
31
|
+
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 6);
|
32
|
+
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _Gpg.Gpg === "undefined" ? Object : _Gpg.Gpg, typeof _SshKeyManager.SshKeyManager === "undefined" ? Object : _SshKeyManager.SshKeyManager, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _FsSyncer.FsSyncer === "undefined" ? Object : _FsSyncer.FsSyncer, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _StorageBackendProvider.StorageBackendProvider === "undefined" ? Object : _StorageBackendProvider.StorageBackendProvider, typeof _config.Config === "undefined" ? Object : _config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class BackupController {
|
33
|
+
constructor(gpg, ssh, fs, fsSyncer, log, storageBackendProvider, cfg) {
|
34
|
+
this.gpg = gpg;
|
35
|
+
this.ssh = ssh;
|
36
|
+
this.fs = fs;
|
37
|
+
this.fsSyncer = fsSyncer;
|
38
|
+
this.log = log;
|
39
|
+
this.storageBackendProvider = storageBackendProvider;
|
40
|
+
this.cfg = cfg;
|
41
|
+
|
42
|
+
_defineProperty(this, "backup", (storageName, path) => {
|
43
|
+
this.log.info(`Backing up from ${path} to '${storageName}'`);
|
44
|
+
this.storage(storageName).store(path);
|
45
|
+
});
|
46
|
+
|
47
|
+
_defineProperty(this, "restore", (storageName, path) => {
|
48
|
+
this.log.info(`Restoring from '${storageName}' to ${path}`);
|
49
|
+
this.storage(storageName).restore(path);
|
50
|
+
});
|
51
|
+
|
52
|
+
_defineProperty(this, "storage", name => this.storageBackendProvider.provide(name));
|
53
|
+
}
|
54
|
+
|
55
|
+
}) || _class) || _class) || _class) || _class);
|
56
|
+
exports.BackupController = BackupController;
|
package/lib/cli/app.js
CHANGED
@@ -17,18 +17,33 @@ var _AggregateBackupController = require("../bup/AggregateBackupController");
|
|
17
17
|
|
18
18
|
var _ResticController = require("../storage/restic/ResticController");
|
19
19
|
|
20
|
+
var _BackupController = require("../bup/BackupController");
|
21
|
+
|
22
|
+
var _Path = require("../fs/path/Path");
|
23
|
+
|
20
24
|
const createApp = () => _nclif.CliApp.of({
|
21
25
|
commands: {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
backup: (0, _nclif.cmd)({
|
27
|
+
description: 'Backup a dir to the given storage',
|
28
|
+
positionals: [{
|
29
|
+
name: 'path',
|
30
|
+
required: true
|
31
|
+
}, {
|
32
|
+
name: 'storageName',
|
33
|
+
required: true
|
34
|
+
}],
|
35
|
+
run: (a, c) => c.get(_BackupController.BackupController).backup(a.storageName, _Path.AbsPath.from(a.path))
|
36
|
+
}),
|
37
|
+
restore: (0, _nclif.cmd)({
|
38
|
+
description: 'Restore the storage to the given dir',
|
39
|
+
positionals: [{
|
40
|
+
name: 'storageName',
|
41
|
+
required: true
|
42
|
+
}, {
|
43
|
+
name: 'path',
|
44
|
+
required: true
|
45
|
+
}],
|
46
|
+
run: (a, c) => c.get(_BackupController.BackupController).restore(a.storageName, _Path.AbsPath.from(a.path))
|
32
47
|
}),
|
33
48
|
aggr: (0, _nclif.cmdGroup)({
|
34
49
|
commands: {
|
@@ -36,20 +51,30 @@ const createApp = () => _nclif.CliApp.of({
|
|
36
51
|
description: 'Run backup',
|
37
52
|
positionals: [{
|
38
53
|
name: 'aggregateName',
|
39
|
-
type: 'string',
|
40
54
|
required: true
|
41
55
|
}, {
|
42
56
|
name: 'storageName',
|
43
|
-
type: 'string',
|
44
57
|
required: true
|
45
58
|
}],
|
46
59
|
run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName, a.storageName)
|
47
60
|
})
|
48
61
|
}
|
49
|
-
})
|
62
|
+
}),
|
63
|
+
restic
|
50
64
|
}
|
51
65
|
}).addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
|
52
66
|
config
|
53
67
|
}) => (0, _Context.createContext)(config));
|
54
68
|
|
55
|
-
exports.createApp = createApp;
|
69
|
+
exports.createApp = createApp;
|
70
|
+
const restic = (0, _nclif.cmdGroup)({
|
71
|
+
commands: {
|
72
|
+
'init-repo': (0, _nclif.cmd)({
|
73
|
+
positionals: [{
|
74
|
+
name: 'resticStorageName',
|
75
|
+
required: true
|
76
|
+
}],
|
77
|
+
run: (arg, c) => c.get(_ResticController.ResticController).initRepo(arg.resticStorageName)
|
78
|
+
})
|
79
|
+
}
|
80
|
+
});
|
package/lib/config/Config.js
CHANGED
@@ -14,10 +14,7 @@ var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
|
|
14
14
|
var _expandAndCreatePath = require("./expandAndCreatePath");
|
15
15
|
|
16
16
|
const expandPaths = (cfg, exp, cwd) => {
|
17
|
-
cfg.storage.filter(s => s.type === '
|
18
|
-
s.path = exp(s.path);
|
19
|
-
});
|
20
|
-
cfg.storage.filter(s => s.type === 'restic').map(s => s).forEach(s => {
|
17
|
+
cfg.storage.filter(s => s.type === 'restic' || s.type === 'rclone').map(s => s).forEach(s => {
|
21
18
|
if (s.credentialsFile) s.credentialsFile = exp(s.credentialsFile);
|
22
19
|
s.passwordFile = exp(s.passwordFile);
|
23
20
|
if (s.repo.startsWith('local:')) s.repo = (0, _expandAndCreatePath.expandStrPath)(s.repo, cwd);
|
@@ -27,6 +24,12 @@ const expandPaths = (cfg, exp, cwd) => {
|
|
27
24
|
f.from = exp(f.from);
|
28
25
|
f.to = _Path.RelativePath.from(f.to);
|
29
26
|
});
|
27
|
+
a.sources.sshKey.forEach(f => {
|
28
|
+
if (f.to) f.to = _Path.RelativePath.from(f.to);
|
29
|
+
});
|
30
|
+
a.sources.gpgKey.forEach(f => {
|
31
|
+
if (f.to) f.to = _Path.RelativePath.from(f.to);
|
32
|
+
});
|
30
33
|
});
|
31
34
|
};
|
32
35
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.StorageTypeConst = void 0;
|
7
|
+
let StorageTypeConst;
|
8
|
+
exports.StorageTypeConst = StorageTypeConst;
|
9
|
+
|
10
|
+
(function (StorageTypeConst) {
|
11
|
+
StorageTypeConst["Restic"] = "restic";
|
12
|
+
StorageTypeConst["RClone"] = "rclone";
|
13
|
+
})(StorageTypeConst || (exports.StorageTypeConst = StorageTypeConst = {}));
|
@@ -7,6 +7,8 @@ exports.validateConfigAgainstSchema = void 0;
|
|
7
7
|
|
8
8
|
var _joi = _interopRequireDefault(require("@hapi/joi"));
|
9
9
|
|
10
|
+
var _index = require("./index");
|
11
|
+
|
10
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
11
13
|
|
12
14
|
const DirTaskSchema = _joi.default.object({
|
@@ -23,7 +25,7 @@ const KeyTaskSchema = _joi.default.object({
|
|
23
25
|
|
24
26
|
const createStorageSchema = props => _joi.default.object({
|
25
27
|
name: _joi.default.string(),
|
26
|
-
type: _joi.default.string(),
|
28
|
+
type: _joi.default.string().valid(_index.StorageTypeConst.Restic, _index.StorageTypeConst.RClone),
|
27
29
|
...props
|
28
30
|
});
|
29
31
|
|
package/lib/ctx/Context.js
CHANGED
@@ -29,13 +29,19 @@ var _ResticClientFactory = require("../storage/restic/ResticClientFactory");
|
|
29
29
|
|
30
30
|
var _ResticController = require("../storage/restic/ResticController");
|
31
31
|
|
32
|
+
var _B2CredentialsProvider = require("../storage/b2/B2CredentialsProvider");
|
33
|
+
|
34
|
+
var _RCloneClientFactory = require("../storage/rclone/RCloneClientFactory");
|
35
|
+
|
36
|
+
var _BackupController = require("../bup/BackupController");
|
37
|
+
|
32
38
|
const createContext = cfg => {
|
33
39
|
const c = new _inversify.Container();
|
34
40
|
c.bind(_ContextSymbols.AppConfig_).toConstantValue(cfg);
|
35
41
|
const log = new _AppLogger.AppLogger();
|
36
42
|
c.bind(_ContextSymbols.Log_).toConstantValue(log);
|
37
43
|
c.bind(_AppLogger.AppLogger).toConstantValue(log);
|
38
|
-
const self = [_Gpg.Gpg, _Shell.Shell, _Fs.Fs, _FsSyncer.FsSyncer, _SshKeyManager.SshKeyManager, _StorageBackendProvider.StorageBackendProvider, _ResticClientFactory.ResticClientFactory, _AggregateBackupController.AggregateBackupController, _ResticController.ResticController];
|
44
|
+
const self = [_Gpg.Gpg, _Shell.Shell, _Fs.Fs, _FsSyncer.FsSyncer, _SshKeyManager.SshKeyManager, _StorageBackendProvider.StorageBackendProvider, _ResticClientFactory.ResticClientFactory, _AggregateBackupController.AggregateBackupController, _ResticController.ResticController, _B2CredentialsProvider.B2CredentialsProvider, _RCloneClientFactory.RCloneClientFactory, _BackupController.BackupController];
|
39
45
|
self.forEach(s => c.bind(s).toSelf());
|
40
46
|
return c;
|
41
47
|
};
|
package/lib/fs/Fs.js
CHANGED
@@ -37,7 +37,7 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
37
37
|
_defineProperty(this, "isDir", path => _fs.default.lstatSync(path.str()).isDirectory());
|
38
38
|
|
39
39
|
_defineProperty(this, "rmDir", path => {
|
40
|
-
_fs.default.
|
40
|
+
_fs.default.rmSync(path.str(), {
|
41
41
|
recursive: true
|
42
42
|
});
|
43
43
|
});
|
@@ -6,8 +6,6 @@ var _Shell = require("../../tools/shell/Shell");
|
|
6
6
|
|
7
7
|
var _test = require("../../../test");
|
8
8
|
|
9
|
-
var _fs = _interopRequireDefault(require("fs"));
|
10
|
-
|
11
9
|
var _areDirsSame = require("../areDirsSame");
|
12
10
|
|
13
11
|
var _Fs = require("../Fs");
|
@@ -16,7 +14,7 @@ var _Path = require("../path/Path");
|
|
16
14
|
|
17
15
|
var _AppLogger = require("../../log/AppLogger");
|
18
16
|
|
19
|
-
|
17
|
+
var _testHelper = require("../../../test/testHelper");
|
20
18
|
|
21
19
|
describe('FsSync', () => {
|
22
20
|
const from = _Path.AbsPath.from((0, _test.testDir)('fs/fssync/src'));
|
@@ -30,8 +28,6 @@ describe('FsSync', () => {
|
|
30
28
|
expect((0, _areDirsSame.areDirsSame)(from, to)).toBe(true);
|
31
29
|
});
|
32
30
|
afterAll(() => {
|
33
|
-
|
34
|
-
recursive: true
|
35
|
-
});
|
31
|
+
(0, _testHelper.cleanDir)(to.str());
|
36
32
|
});
|
37
33
|
});
|
package/lib/log/AppLogger.js
CHANGED
@@ -9,12 +9,12 @@ var _ConsoleLogger = require("./ConsoleLogger");
|
|
9
9
|
|
10
10
|
var _inversify = require("inversify");
|
11
11
|
|
12
|
-
var _dec, _class;
|
12
|
+
var _dec, _dec2, _dec3, _class;
|
13
13
|
|
14
14
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
15
15
|
|
16
|
-
let AppLogger = (_dec = (0, _inversify.injectable)(), _dec(_class = class AppLogger {
|
17
|
-
constructor() {
|
16
|
+
let AppLogger = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [void 0]), _dec(_class = _dec2(_class = _dec3(_class = class AppLogger {
|
17
|
+
constructor(mute = false) {
|
18
18
|
_defineProperty(this, "log", new _ConsoleLogger.ConsoleLogger());
|
19
19
|
|
20
20
|
_defineProperty(this, "debug", this.log.debug);
|
@@ -22,7 +22,9 @@ let AppLogger = (_dec = (0, _inversify.injectable)(), _dec(_class = class AppLog
|
|
22
22
|
_defineProperty(this, "info", this.log.info);
|
23
23
|
|
24
24
|
_defineProperty(this, "error", this.log.error);
|
25
|
+
|
26
|
+
if (mute) this.log.setEnabled(false);
|
25
27
|
}
|
26
28
|
|
27
|
-
}) || _class);
|
29
|
+
}) || _class) || _class) || _class);
|
28
30
|
exports.AppLogger = AppLogger;
|
package/lib/log/ConsoleLogger.js
CHANGED
@@ -11,15 +11,17 @@ class ConsoleLogger {
|
|
11
11
|
constructor() {
|
12
12
|
_defineProperty(this, "enabled", true);
|
13
13
|
|
14
|
+
_defineProperty(this, "print", (...args) => console.log(...args.map(a => a.toString())));
|
15
|
+
|
14
16
|
_defineProperty(this, "debug", (...args) => {
|
15
|
-
if (this.enabled)
|
17
|
+
if (this.enabled) this.print(args);
|
16
18
|
});
|
17
19
|
|
18
20
|
_defineProperty(this, "info", (...args) => {
|
19
|
-
if (this.enabled)
|
21
|
+
if (this.enabled) this.print(args);
|
20
22
|
});
|
21
23
|
|
22
|
-
_defineProperty(this, "error",
|
24
|
+
_defineProperty(this, "error", this.print);
|
23
25
|
|
24
26
|
_defineProperty(this, "setEnabled", enabled => {
|
25
27
|
this.enabled = enabled;
|
@@ -3,21 +3,25 @@
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
4
4
|
value: true
|
5
5
|
});
|
6
|
-
exports.
|
6
|
+
exports.BackupClientStorageBackend = void 0;
|
7
7
|
|
8
|
-
var _Path = require("
|
8
|
+
var _Path = require("../fs/path/Path");
|
9
9
|
|
10
10
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
11
11
|
|
12
|
-
class
|
12
|
+
class BackupClientStorageBackend {
|
13
13
|
constructor(client) {
|
14
14
|
this.client = client;
|
15
15
|
|
16
16
|
_defineProperty(this, "store", from => {
|
17
17
|
this.client.backup(from, _Path.RelativePath.from('.'));
|
18
18
|
});
|
19
|
+
|
20
|
+
_defineProperty(this, "restore", to => {
|
21
|
+
this.client.restore(to);
|
22
|
+
});
|
19
23
|
}
|
20
24
|
|
21
25
|
}
|
22
26
|
|
23
|
-
exports.
|
27
|
+
exports.BackupClientStorageBackend = BackupClientStorageBackend;
|
@@ -7,45 +7,45 @@ exports.StorageBackendProvider = void 0;
|
|
7
7
|
|
8
8
|
var _inversify = require("inversify");
|
9
9
|
|
10
|
-
var _FsSyncer = require("../fs/fssync/FsSyncer");
|
11
|
-
|
12
|
-
var _LocalFileStorageBackend = require("./local-file/LocalFileStorageBackend");
|
13
|
-
|
14
10
|
var _ContextSymbols = require("../ctx/ContextSymbols");
|
15
11
|
|
16
|
-
var
|
12
|
+
var _config = require("../config");
|
17
13
|
|
18
14
|
var _ResticClientFactory = require("./restic/ResticClientFactory");
|
19
15
|
|
20
|
-
var
|
16
|
+
var _BackupClientStorageBackend = require("./BackupClientStorageBackend");
|
21
17
|
|
22
|
-
var
|
18
|
+
var _RCloneClientFactory = require("./rclone/RCloneClientFactory");
|
23
19
|
|
24
20
|
var _dec, _dec2, _dec3, _dec4, _class;
|
25
21
|
|
26
22
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
27
23
|
|
28
24
|
let StorageBackendProvider = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
29
|
-
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined,
|
30
|
-
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof
|
31
|
-
constructor(
|
32
|
-
this.fsSyncer = fsSyncer;
|
25
|
+
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 2);
|
26
|
+
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _ResticClientFactory.ResticClientFactory === "undefined" ? Object : _ResticClientFactory.ResticClientFactory, typeof _RCloneClientFactory.RCloneClientFactory === "undefined" ? Object : _RCloneClientFactory.RCloneClientFactory, typeof _config.Config === "undefined" ? Object : _config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class StorageBackendProvider {
|
27
|
+
constructor(resticFac, rcloneFac, cfg) {
|
33
28
|
this.resticFac = resticFac;
|
34
|
-
this.
|
29
|
+
this.rcloneFac = rcloneFac;
|
35
30
|
this.cfg = cfg;
|
36
31
|
|
37
32
|
_defineProperty(this, "provide", name => {
|
38
33
|
const s = this.cfg.storage.find(s => s.name === name);
|
39
34
|
if (!s) throw new Error(`No such storage '${name}'`);
|
40
|
-
|
41
|
-
});
|
35
|
+
let b;
|
42
36
|
|
43
|
-
|
44
|
-
|
45
|
-
|
37
|
+
switch (s.type) {
|
38
|
+
case _config.StorageTypeConst.Restic:
|
39
|
+
b = this.resticFac.create(s);
|
40
|
+
break;
|
41
|
+
|
42
|
+
case _config.StorageTypeConst.RClone:
|
43
|
+
b = this.rcloneFac.create(s);
|
44
|
+
break;
|
45
|
+
}
|
46
46
|
|
47
|
-
|
48
|
-
return new
|
47
|
+
if (!b) throw new Error(`Unsupported storage type ${s.type}`);
|
48
|
+
return new _BackupClientStorageBackend.BackupClientStorageBackend(b);
|
49
49
|
});
|
50
50
|
}
|
51
51
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.B2CredentialsProvider = void 0;
|
7
|
+
|
8
|
+
var _joi = _interopRequireDefault(require("@hapi/joi"));
|
9
|
+
|
10
|
+
var _inversify = require("inversify");
|
11
|
+
|
12
|
+
var _Fs = require("../../fs/Fs");
|
13
|
+
|
14
|
+
var _dec, _dec2, _dec3, _class;
|
15
|
+
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17
|
+
|
18
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
19
|
+
|
20
|
+
const CredentialsFileSchema = _joi.default.object({
|
21
|
+
keyID: _joi.default.string(),
|
22
|
+
keyName: _joi.default.string().optional(),
|
23
|
+
applicationKey: _joi.default.string()
|
24
|
+
});
|
25
|
+
|
26
|
+
const validateCredentials = obj => {
|
27
|
+
const r = CredentialsFileSchema.validate(obj, {
|
28
|
+
presence: 'required',
|
29
|
+
allowUnknown: false
|
30
|
+
});
|
31
|
+
return r.error ? JSON.stringify(r.error) : null;
|
32
|
+
};
|
33
|
+
|
34
|
+
let B2CredentialsProvider = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs]), _dec(_class = _dec2(_class = _dec3(_class = class B2CredentialsProvider {
|
35
|
+
constructor(fs) {
|
36
|
+
this.fs = fs;
|
37
|
+
|
38
|
+
_defineProperty(this, "fromFile", path => {
|
39
|
+
const credJson = this.fs.readJson(path);
|
40
|
+
const validationErr = validateCredentials(credJson);
|
41
|
+
|
42
|
+
if (validationErr) {
|
43
|
+
throw new Error(`Invalid B2 credential in ${path}: ${validationErr}`);
|
44
|
+
}
|
45
|
+
|
46
|
+
const creds = credJson;
|
47
|
+
return {
|
48
|
+
accountId: creds.keyID,
|
49
|
+
key: creds.applicationKey
|
50
|
+
};
|
51
|
+
});
|
52
|
+
}
|
53
|
+
|
54
|
+
}) || _class) || _class) || _class);
|
55
|
+
exports.B2CredentialsProvider = B2CredentialsProvider;
|
@@ -0,0 +1,69 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.RCloneClientFactory = void 0;
|
7
|
+
|
8
|
+
var _inversify = require("inversify");
|
9
|
+
|
10
|
+
var _Shell = require("../../tools/shell/Shell");
|
11
|
+
|
12
|
+
var _AppLogger = require("../../log/AppLogger");
|
13
|
+
|
14
|
+
var _Fs = require("../../fs/Fs");
|
15
|
+
|
16
|
+
var _RcloneClient = require("./RcloneClient");
|
17
|
+
|
18
|
+
var _B2CredentialsProvider = require("../b2/B2CredentialsProvider");
|
19
|
+
|
20
|
+
var _dec, _dec2, _dec3, _class;
|
21
|
+
|
22
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
23
|
+
|
24
|
+
const parseRepoUrl = url => {
|
25
|
+
const split = url.split(':');
|
26
|
+
if (split.length !== 2) throw new Error(`Illegal rclone URL ${url}`);
|
27
|
+
const type = split[0];
|
28
|
+
if (!['b2', 'local'].includes(type)) throw new Error(`Unknown rclone repo type ${type}`);
|
29
|
+
return {
|
30
|
+
type: type,
|
31
|
+
location: split[1]
|
32
|
+
};
|
33
|
+
};
|
34
|
+
|
35
|
+
let RCloneClientFactory = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _B2CredentialsProvider.B2CredentialsProvider === "undefined" ? Object : _B2CredentialsProvider.B2CredentialsProvider]), _dec(_class = _dec2(_class = _dec3(_class = class RCloneClientFactory {
|
36
|
+
constructor(sh, log, fs, b2) {
|
37
|
+
this.sh = sh;
|
38
|
+
this.log = log;
|
39
|
+
this.fs = fs;
|
40
|
+
this.b2 = b2;
|
41
|
+
|
42
|
+
_defineProperty(this, "create", cfg => {
|
43
|
+
const {
|
44
|
+
type,
|
45
|
+
location
|
46
|
+
} = parseRepoUrl(cfg.repo);
|
47
|
+
const props = {
|
48
|
+
type,
|
49
|
+
location
|
50
|
+
};
|
51
|
+
const password = cfg.passwordFile && this.fs.readFile(cfg.passwordFile).trim();
|
52
|
+
if (password) props.encryption = {
|
53
|
+
password
|
54
|
+
};
|
55
|
+
const creds = cfg.credentialsFile && this.b2.fromFile(cfg.credentialsFile);
|
56
|
+
|
57
|
+
if (type === 'b2') {
|
58
|
+
if (!creds) throw new Error(`Credentials are required for '${cfg.name}'`);
|
59
|
+
props.creds = creds;
|
60
|
+
} else {
|
61
|
+
if (creds) throw new Error(`Credentials not supported for local rclone`);
|
62
|
+
}
|
63
|
+
|
64
|
+
return new _RcloneClient.RcloneClient(props, this.sh, this.log);
|
65
|
+
});
|
66
|
+
}
|
67
|
+
|
68
|
+
}) || _class) || _class) || _class);
|
69
|
+
exports.RCloneClientFactory = RCloneClientFactory;
|
@@ -0,0 +1,87 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.RcloneClient = void 0;
|
7
|
+
|
8
|
+
var _inversify = require("inversify");
|
9
|
+
|
10
|
+
var _types = require("../../types");
|
11
|
+
|
12
|
+
var _Shell = require("../../tools/shell/Shell");
|
13
|
+
|
14
|
+
var _dec, _dec2, _dec3, _class;
|
15
|
+
|
16
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Tested with: rclone v1.57.0
|
20
|
+
* TODO forwarding stdout to the logger
|
21
|
+
*/
|
22
|
+
let RcloneClient = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof RcloneConfig === "undefined" ? Object : RcloneConfig, typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell, typeof _types.Log === "undefined" ? Object : _types.Log]), _dec(_class = _dec2(_class = _dec3(_class = class RcloneClient {
|
23
|
+
constructor(cfg, shell, log) {
|
24
|
+
this.cfg = cfg;
|
25
|
+
this.shell = shell;
|
26
|
+
this.log = log;
|
27
|
+
|
28
|
+
_defineProperty(this, "remoteName", 'my');
|
29
|
+
|
30
|
+
_defineProperty(this, "cryptRemoteName", 'crypt');
|
31
|
+
|
32
|
+
_defineProperty(this, "backup", from => {
|
33
|
+
this.log.debug('Running backup for ', this.cfg.type, this.cfg.location);
|
34
|
+
this.shell.exec(`rclone sync ${from.str()} ${this.actualRemoteName}:${this.cfg.location} --config=noconfig`, // Rclone logs error if the file is not set even though it supports env var interface
|
35
|
+
{
|
36
|
+
env: this.env()
|
37
|
+
});
|
38
|
+
});
|
39
|
+
|
40
|
+
_defineProperty(this, "restore", to => {
|
41
|
+
this.log.debug('Running restore for ', this.cfg.type, this.cfg.location);
|
42
|
+
this.shell.exec(`rclone sync ${this.actualRemoteName}:${this.cfg.location} ${to.str()} --config=noconfig`, {
|
43
|
+
env: this.env()
|
44
|
+
});
|
45
|
+
});
|
46
|
+
|
47
|
+
_defineProperty(this, "env", () => {
|
48
|
+
const {
|
49
|
+
type,
|
50
|
+
creds
|
51
|
+
} = this.cfg;
|
52
|
+
|
53
|
+
const myKey = val => `RCLONE_CONFIG_${this.remoteName.toUpperCase()}_${val.toUpperCase()}`;
|
54
|
+
|
55
|
+
const res = {
|
56
|
+
[myKey('type')]: type,
|
57
|
+
[myKey('hard_delete')]: true + ''
|
58
|
+
};
|
59
|
+
|
60
|
+
if (type === 'b2') {
|
61
|
+
if (!creds) throw new Error('B2 Rclone remote has to have credentials');
|
62
|
+
res[myKey('account')] = creds.accountId;
|
63
|
+
res[myKey('key')] = creds.key;
|
64
|
+
}
|
65
|
+
|
66
|
+
if (this.cfg.encryption) {
|
67
|
+
const pass = this.cfg.encryption.password;
|
68
|
+
|
69
|
+
const cryptKey = val => `RCLONE_CONFIG_${this.cryptRemoteName.toUpperCase()}_${val.toUpperCase()}`;
|
70
|
+
|
71
|
+
res[cryptKey('type')] = 'crypt';
|
72
|
+
res[cryptKey('remote')] = this.remoteName + ':' + this.cfg.location;
|
73
|
+
res[cryptKey('password')] = this.obscure(pass);
|
74
|
+
}
|
75
|
+
|
76
|
+
return res;
|
77
|
+
});
|
78
|
+
|
79
|
+
_defineProperty(this, "obscure", str => this.shell.execAndReturnVal(`rclone obscure ${str}`));
|
80
|
+
}
|
81
|
+
|
82
|
+
get actualRemoteName() {
|
83
|
+
return this.cfg.encryption ? this.cryptRemoteName : this.remoteName;
|
84
|
+
}
|
85
|
+
|
86
|
+
}) || _class) || _class) || _class);
|
87
|
+
exports.RcloneClient = RcloneClient;
|
@@ -0,0 +1,83 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _Shell = require("../../tools/shell/Shell");
|
4
|
+
|
5
|
+
var _test = require("../../../test");
|
6
|
+
|
7
|
+
var _Path = require("../../fs/path/Path");
|
8
|
+
|
9
|
+
var _RcloneClient = require("./RcloneClient");
|
10
|
+
|
11
|
+
var _dirCompare = require("dir-compare");
|
12
|
+
|
13
|
+
var _b2TestConfig = require("../../../test/b2TestConfig");
|
14
|
+
|
15
|
+
var _testHelper = require("../../../test/testHelper");
|
16
|
+
|
17
|
+
describe('RcloneClient', () => {
|
18
|
+
const l = _testHelper.testLog;
|
19
|
+
const getRestoreDir = (0, _test.testdataDirBuilder)('b2/restore');
|
20
|
+
describe('local', () => {
|
21
|
+
const backupDir = (0, _test.testDir)('backup/1');
|
22
|
+
const restoreDir = getRestoreDir('local');
|
23
|
+
const storeDir = (0, _test.testdataDir)('b2/store');
|
24
|
+
const cfg = {
|
25
|
+
type: 'local',
|
26
|
+
location: (0, _test.testdataDir)(storeDir),
|
27
|
+
encryption: {
|
28
|
+
password: 'foo123'
|
29
|
+
}
|
30
|
+
};
|
31
|
+
afterEach(() => {
|
32
|
+
(0, _testHelper.cleanDir)(storeDir);
|
33
|
+
(0, _testHelper.cleanDir)(restoreDir);
|
34
|
+
});
|
35
|
+
it('backup & restore', () => {
|
36
|
+
const sut = new _RcloneClient.RcloneClient(cfg, new _Shell.Shell(l), l);
|
37
|
+
sut.backup(_Path.AbsPath.from(backupDir));
|
38
|
+
sut.restore(_Path.AbsPath.from(restoreDir));
|
39
|
+
const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
|
40
|
+
expect(res.same).toBeTrue();
|
41
|
+
});
|
42
|
+
it('backup & restore unencrypted', () => {
|
43
|
+
const sut = new _RcloneClient.RcloneClient({ ...cfg,
|
44
|
+
encryption: undefined
|
45
|
+
}, new _Shell.Shell(l), l);
|
46
|
+
sut.backup(_Path.AbsPath.from(backupDir));
|
47
|
+
sut.restore(_Path.AbsPath.from(restoreDir));
|
48
|
+
const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
|
49
|
+
expect(res.same).toBeTrue();
|
50
|
+
});
|
51
|
+
});
|
52
|
+
describe('b2', () => {
|
53
|
+
const backupDir = (0, _test.testDir)('backup/2');
|
54
|
+
const b2Cfg = (0, _b2TestConfig.getB2TestConfig)('rclone');
|
55
|
+
|
56
|
+
if (!b2Cfg) {
|
57
|
+
console.log('No B2 config - skipping');
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
|
61
|
+
const sut = new _RcloneClient.RcloneClient({
|
62
|
+
type: 'b2',
|
63
|
+
location: b2Cfg.bucket,
|
64
|
+
creds: {
|
65
|
+
accountId: b2Cfg.accountId,
|
66
|
+
key: b2Cfg.key
|
67
|
+
},
|
68
|
+
encryption: {
|
69
|
+
password: 'foo123'
|
70
|
+
}
|
71
|
+
}, new _Shell.Shell(l), l);
|
72
|
+
const restoreDir = getRestoreDir('b2');
|
73
|
+
afterAll(() => {
|
74
|
+
(0, _testHelper.cleanDir)(restoreDir);
|
75
|
+
});
|
76
|
+
it('push adn restore data', () => {
|
77
|
+
sut.backup(_Path.AbsPath.from(backupDir));
|
78
|
+
sut.restore(_Path.AbsPath.from(restoreDir));
|
79
|
+
const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
|
80
|
+
expect(res.same).toBeTrue();
|
81
|
+
});
|
82
|
+
});
|
83
|
+
});
|
@@ -5,16 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.ResticClient = void 0;
|
7
7
|
|
8
|
-
var _inversify = require("inversify");
|
9
|
-
|
10
|
-
var _types = require("../../types");
|
11
|
-
|
12
|
-
var _ContextSymbols = require("../../ctx/ContextSymbols");
|
13
|
-
|
14
|
-
var _Shell = require("../../tools/shell/Shell");
|
15
|
-
|
16
|
-
var _dec, _dec2, _dec3, _dec4, _class;
|
17
|
-
|
18
8
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
19
9
|
|
20
10
|
const normalizeUrl = url => {
|
@@ -30,9 +20,7 @@ const normalizeUrl = url => {
|
|
30
20
|
*/
|
31
21
|
|
32
22
|
|
33
|
-
|
34
|
-
return (0, _inversify.inject)(_ContextSymbols.TaskConfig_)(target, undefined, 2);
|
35
|
-
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof ResticConfig === "undefined" ? Object : ResticConfig, typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell, typeof _types.Log === "undefined" ? Object : _types.Log]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class ResticClient {
|
23
|
+
class ResticClient {
|
36
24
|
constructor(cfg, shell, log) {
|
37
25
|
this.cfg = cfg;
|
38
26
|
this.shell = shell;
|
@@ -48,6 +36,7 @@ let ResticClient = (_dec = (0, _inversify.injectable)(), _dec2 = function (targe
|
|
48
36
|
});
|
49
37
|
|
50
38
|
_defineProperty(this, "backup", (cwd, from) => {
|
39
|
+
this.log.debug(`Running backup for repo=${this.url}`);
|
51
40
|
this.shell.exec(`restic backup ${from.str()}`, {
|
52
41
|
cwd: cwd.str(),
|
53
42
|
env: this.env()
|
@@ -55,6 +44,7 @@ let ResticClient = (_dec = (0, _inversify.injectable)(), _dec2 = function (targe
|
|
55
44
|
});
|
56
45
|
|
57
46
|
_defineProperty(this, "restore", to => {
|
47
|
+
this.log.debug('Running restore for repo=', this.url);
|
58
48
|
this.shell.exec(`restic restore latest --target ${to.str()}`, {
|
59
49
|
env: this.env()
|
60
50
|
});
|
@@ -72,7 +62,7 @@ let ResticClient = (_dec = (0, _inversify.injectable)(), _dec2 = function (targe
|
|
72
62
|
if (!cfg) throw new Error(`Repo url ${url} refers to Backblaze but no Backblaze config provided`);
|
73
63
|
return {
|
74
64
|
B2_ACCOUNT_ID: cfg.accountId,
|
75
|
-
B2_ACCOUNT_KEY: cfg.
|
65
|
+
B2_ACCOUNT_KEY: cfg.key
|
76
66
|
};
|
77
67
|
}
|
78
68
|
|
@@ -82,5 +72,6 @@ let ResticClient = (_dec = (0, _inversify.injectable)(), _dec2 = function (targe
|
|
82
72
|
this.url = normalizeUrl(cfg.repo.url);
|
83
73
|
}
|
84
74
|
|
85
|
-
}
|
75
|
+
}
|
76
|
+
|
86
77
|
exports.ResticClient = ResticClient;
|
@@ -2,38 +2,23 @@
|
|
2
2
|
|
3
3
|
var _ResticClient = require("./ResticClient");
|
4
4
|
|
5
|
-
var _AppLogger = require("../../log/AppLogger");
|
6
|
-
|
7
5
|
var _Shell = require("../../tools/shell/Shell");
|
8
6
|
|
9
7
|
var _test = require("../../../test");
|
10
8
|
|
11
|
-
var _fs = _interopRequireDefault(require("fs"));
|
12
|
-
|
13
|
-
var _path = _interopRequireDefault(require("path"));
|
14
|
-
|
15
9
|
var _Path = require("../../fs/path/Path");
|
16
10
|
|
17
11
|
var _dirCompare = require("dir-compare");
|
18
12
|
|
19
|
-
|
13
|
+
var _testHelper = require("../../../test/testHelper");
|
20
14
|
|
21
|
-
|
22
|
-
const path = _path.default.resolve(__dirname, '../../../.keys.json');
|
23
|
-
|
24
|
-
if (!_fs.default.existsSync(path)) return undefined;
|
25
|
-
const data = JSON.parse(_fs.default.readFileSync(path).toString());
|
26
|
-
return {
|
27
|
-
accountId: data.restic.backblaze.keyID,
|
28
|
-
accountKey: data.restic.backblaze.applicationKey
|
29
|
-
};
|
30
|
-
};
|
15
|
+
var _b2TestConfig = require("../../../test/b2TestConfig");
|
31
16
|
|
32
17
|
describe('ResticClient', () => {
|
33
|
-
const l =
|
18
|
+
const l = _testHelper.testLog;
|
34
19
|
const password = 'foo123';
|
35
|
-
const backupDir = (0, _test.testDir)('backup-me');
|
36
20
|
describe('local', () => {
|
21
|
+
const backupDir = (0, _test.testDir)('backup/1');
|
37
22
|
const repoDir = (0, _test.testdataDir)('my-repo');
|
38
23
|
const restoreDir = (0, _test.testdataDir)('restore/local');
|
39
24
|
const sut = new _ResticClient.ResticClient({
|
@@ -43,13 +28,8 @@ describe('ResticClient', () => {
|
|
43
28
|
}
|
44
29
|
}, new _Shell.Shell(l), l);
|
45
30
|
afterAll(() => {
|
46
|
-
|
47
|
-
|
48
|
-
});
|
49
|
-
|
50
|
-
_fs.default.rmdirSync(restoreDir, {
|
51
|
-
recursive: true
|
52
|
-
});
|
31
|
+
(0, _testHelper.cleanDir)(repoDir);
|
32
|
+
(0, _testHelper.cleanDir)(restoreDir);
|
53
33
|
});
|
54
34
|
it('create repo', () => {
|
55
35
|
sut.prepareRepo();
|
@@ -64,7 +44,8 @@ describe('ResticClient', () => {
|
|
64
44
|
});
|
65
45
|
});
|
66
46
|
describe('b2', () => {
|
67
|
-
const
|
47
|
+
const backupDir = (0, _test.testDir)('backup/2');
|
48
|
+
const b2Cfg = (0, _b2TestConfig.getB2TestConfig)('restic');
|
68
49
|
|
69
50
|
if (!b2Cfg) {
|
70
51
|
console.log('No B2 config - skipping');
|
@@ -72,7 +53,7 @@ describe('ResticClient', () => {
|
|
72
53
|
}
|
73
54
|
|
74
55
|
const restoreDir = (0, _test.testdataDir)('restore/b2');
|
75
|
-
const repoUrl = `b2
|
56
|
+
const repoUrl = `b2:` + b2Cfg.bucket;
|
76
57
|
const sut = new _ResticClient.ResticClient({
|
77
58
|
repo: {
|
78
59
|
password,
|
@@ -81,12 +62,10 @@ describe('ResticClient', () => {
|
|
81
62
|
backblaze: b2Cfg
|
82
63
|
}, new _Shell.Shell(l), l);
|
83
64
|
afterAll(() => {
|
84
|
-
|
85
|
-
recursive: true
|
86
|
-
});
|
65
|
+
(0, _testHelper.cleanDir)(restoreDir);
|
87
66
|
});
|
88
67
|
it('push adn restore data', () => {
|
89
|
-
//
|
68
|
+
// sut.prepareRepo()
|
90
69
|
sut.backup(_Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'));
|
91
70
|
sut.restore(_Path.AbsPath.from(restoreDir));
|
92
71
|
const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
|
@@ -15,33 +15,18 @@ var _AppLogger = require("../../log/AppLogger");
|
|
15
15
|
|
16
16
|
var _Fs = require("../../fs/Fs");
|
17
17
|
|
18
|
-
var
|
18
|
+
var _B2CredentialsProvider = require("../b2/B2CredentialsProvider");
|
19
19
|
|
20
20
|
var _dec, _dec2, _dec3, _class;
|
21
21
|
|
22
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
23
|
-
|
24
22
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
keyName: _joi.default.string().optional(),
|
29
|
-
applicationKey: _joi.default.string()
|
30
|
-
});
|
31
|
-
|
32
|
-
const validateCredentials = obj => {
|
33
|
-
const r = CredentialsFileSchema.validate(obj, {
|
34
|
-
presence: 'required',
|
35
|
-
allowUnknown: false
|
36
|
-
});
|
37
|
-
return r.error ? JSON.stringify(r.error) : null;
|
38
|
-
};
|
39
|
-
|
40
|
-
let ResticClientFactory = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs]), _dec(_class = _dec2(_class = _dec3(_class = class ResticClientFactory {
|
41
|
-
constructor(sh, log, fs) {
|
24
|
+
let ResticClientFactory = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _B2CredentialsProvider.B2CredentialsProvider === "undefined" ? Object : _B2CredentialsProvider.B2CredentialsProvider]), _dec(_class = _dec2(_class = _dec3(_class = class ResticClientFactory {
|
25
|
+
constructor(sh, log, fs, b2) {
|
42
26
|
this.sh = sh;
|
43
27
|
this.log = log;
|
44
28
|
this.fs = fs;
|
29
|
+
this.b2 = b2;
|
45
30
|
|
46
31
|
_defineProperty(this, "create", cfg => {
|
47
32
|
const password = this.fs.readFile(cfg.passwordFile).trim();
|
@@ -51,22 +36,13 @@ let ResticClientFactory = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.
|
|
51
36
|
url: cfg.repo
|
52
37
|
}
|
53
38
|
};
|
39
|
+
const creds = cfg.credentialsFile && this.b2.fromFile(cfg.credentialsFile);
|
54
40
|
|
55
|
-
if (cfg.
|
56
|
-
if (!
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
if (validationErr) {
|
62
|
-
throw new Error(`Invalid Restic credential in ${cfg.credentialsFile}: ${validationErr}`);
|
63
|
-
}
|
64
|
-
|
65
|
-
const creds = credJson;
|
66
|
-
props.backblaze = {
|
67
|
-
accountId: creds.keyID,
|
68
|
-
accountKey: creds.applicationKey
|
69
|
-
};
|
41
|
+
if (cfg.repo.startsWith('b2:')) {
|
42
|
+
if (!creds) throw new Error(`Credentials must be provided for b2 repo in '${cfg.name}'`);
|
43
|
+
props.backblaze = creds;
|
44
|
+
} else {
|
45
|
+
if (creds) throw new Error('Credentials can be supported only for the B2 restic backend');
|
70
46
|
}
|
71
47
|
|
72
48
|
return new _ResticClient.ResticClient(props, this.sh, this.log);
|
@@ -9,7 +9,7 @@ var _inversify = require("inversify");
|
|
9
9
|
|
10
10
|
var _ContextSymbols = require("../../ctx/ContextSymbols");
|
11
11
|
|
12
|
-
var
|
12
|
+
var _config = require("../../config");
|
13
13
|
|
14
14
|
var _ResticClientFactory = require("./ResticClientFactory");
|
15
15
|
|
@@ -21,7 +21,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
21
21
|
|
22
22
|
let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
23
23
|
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 2);
|
24
|
-
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _ResticClientFactory.ResticClientFactory === "undefined" ? Object : _ResticClientFactory.ResticClientFactory, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof
|
24
|
+
}, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _ResticClientFactory.ResticClientFactory === "undefined" ? Object : _ResticClientFactory.ResticClientFactory, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _config.Config === "undefined" ? Object : _config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class ResticController {
|
25
25
|
constructor(restFact, log, cfg) {
|
26
26
|
this.restFact = restFact;
|
27
27
|
this.log = log;
|
package/lib/tools/gpg/Gpg.js
CHANGED
@@ -17,8 +17,15 @@ let Gpg = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design
|
|
17
17
|
constructor(sh) {
|
18
18
|
this.sh = sh;
|
19
19
|
|
20
|
+
_defineProperty(this, "importKey", key => {
|
21
|
+
this.sh.execWithStdIn({
|
22
|
+
cmd: `gpg --import`,
|
23
|
+
stdin: key
|
24
|
+
});
|
25
|
+
});
|
26
|
+
|
20
27
|
_defineProperty(this, "exportKey", id => {
|
21
|
-
const res = this.sh.execAndReturnVal(`gpg --export-secret-key --armor ${id}`);
|
28
|
+
const res = this.sh.execAndReturnVal(`gpg --batch --pinentry-mode=loopback --pinentry-mode=loopback --yes --passphrase foo123 --export-secret-key --armor ${id}`);
|
22
29
|
if (!res) throw new Error(`Key '${id}' not in keyring`);
|
23
30
|
return res;
|
24
31
|
});
|
@@ -1,14 +1,25 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
-
var _AppLogger = require("../../log/AppLogger");
|
4
|
-
|
5
3
|
var _Gpg = require("./Gpg");
|
6
4
|
|
7
5
|
var _Shell = require("../shell/Shell");
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
var _test = require("../../../test");
|
8
|
+
|
9
|
+
var _fs = _interopRequireDefault(require("fs"));
|
10
|
+
|
11
|
+
var _testHelper = require("../../../test/testHelper");
|
12
|
+
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
14
|
+
|
15
|
+
it('works', () => {
|
16
|
+
const sut = new _Gpg.Gpg(new _Shell.Shell(_testHelper.testLog));
|
17
|
+
|
18
|
+
const key = _fs.default.readFileSync((0, _test.testDir)('gpg/john.doe@foo.com.key.asc')).toString();
|
19
|
+
|
20
|
+
sut.importKey(key);
|
21
|
+
const exportedKey = sut.exportKey('john.doe@foo.com');
|
22
|
+
expect(exportedKey).toStartWith('-----BEGIN PGP PRIVATE KEY BLOCK-----');
|
23
|
+
expect(exportedKey).toEndWith('-----END PGP PRIVATE KEY BLOCK-----'); // Wont' work
|
24
|
+
// expect(key).toBe(exportedKey)
|
14
25
|
});
|
package/lib/tools/shell/Shell.js
CHANGED
@@ -32,7 +32,7 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("desi
|
|
32
32
|
stdin,
|
33
33
|
opts = {}
|
34
34
|
}) => {
|
35
|
-
this.log.debug(
|
35
|
+
this.log.debug(`<stdin> > ${cmd}`);
|
36
36
|
(0, _shellCmd.shellCmd)(cmd, { ...opts,
|
37
37
|
stdin,
|
38
38
|
returnStdout: false
|
@@ -26,7 +26,7 @@ let SshKeyManager = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metada
|
|
26
26
|
_defineProperty(this, "sshDir", void 0);
|
27
27
|
|
28
28
|
_defineProperty(this, "exportKey", id => {
|
29
|
-
return this.fs.readFile(this.sshDir);
|
29
|
+
return this.fs.readFile(this.sshDir.resolve(id));
|
30
30
|
});
|
31
31
|
|
32
32
|
this.sshDir = _Path.AbsPath.from(_os.default.homedir()).resolve('.ssh');
|
package/package.json
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ps-aux/nodebup",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.5.0",
|
4
4
|
"description": "",
|
5
5
|
"module": "lib/index.js",
|
6
6
|
"main": "lib/index.js",
|
7
7
|
"scripts": {
|
8
|
-
"build": "rm -rf build && babel --extensions '.ts,.js' src -d lib src",
|
8
|
+
"build": "rm -rf build && babel --extensions '.ts,.js,.md' src -d lib src",
|
9
9
|
"pub": "npm publish --access public",
|
10
10
|
"test": "jest",
|
11
11
|
"tc": "tsc --noEmit",
|
@@ -27,26 +27,27 @@
|
|
27
27
|
},
|
28
28
|
"typings": "src/types.d.ts",
|
29
29
|
"devDependencies": {
|
30
|
-
"@babel/cli": "^7.16.
|
31
|
-
"@babel/core": "^7.16.
|
30
|
+
"@babel/cli": "^7.16.7",
|
31
|
+
"@babel/core": "^7.16.7",
|
32
32
|
"@babel/eslint-parser": "^7.16.5",
|
33
|
-
"@babel/node": "^7.16.
|
34
|
-
"@babel/plugin-proposal-class-properties": "^7.16.
|
35
|
-
"@babel/plugin-proposal-decorators": "^7.16.
|
36
|
-
"@babel/plugin-proposal-object-rest-spread": "^7.16.
|
37
|
-
"@babel/plugin-proposal-optional-chaining": "^7.16.
|
38
|
-
"@babel/preset-env": "^7.16.
|
39
|
-
"@babel/preset-typescript": "^7.16.
|
40
|
-
"@
|
33
|
+
"@babel/node": "^7.16.7",
|
34
|
+
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
35
|
+
"@babel/plugin-proposal-decorators": "^7.16.7",
|
36
|
+
"@babel/plugin-proposal-object-rest-spread": "^7.16.7",
|
37
|
+
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
38
|
+
"@babel/preset-env": "^7.16.7",
|
39
|
+
"@babel/preset-typescript": "^7.16.7",
|
40
|
+
"@ps-aux/cibs": "^0.6.4",
|
41
|
+
"@types/jest": "^27.4.0",
|
41
42
|
"@types/jest-when": "^2.7.4",
|
42
|
-
"@types/node": "^17.0.
|
43
|
+
"@types/node": "^17.0.6",
|
43
44
|
"@types/ramda": "^0.27.62",
|
44
45
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
45
46
|
"@typescript-eslint/parser": "^5.8.1",
|
46
47
|
"babel-plugin-module-resolver": "^4.1.0",
|
47
48
|
"babel-plugin-transform-typescript-metadata": "^0.3.2",
|
48
49
|
"dir-compare": "^4.0.0",
|
49
|
-
"eslint": "^8.
|
50
|
+
"eslint": "^8.6.0",
|
50
51
|
"eslint-config-standard": "^16.0.3",
|
51
52
|
"eslint-plugin-import": "^2.25.3",
|
52
53
|
"eslint-plugin-node": "^11.1.0",
|
@@ -71,10 +72,11 @@
|
|
71
72
|
},
|
72
73
|
"dependencies": {
|
73
74
|
"@hapi/joi": "^17.1.1",
|
74
|
-
"@ps-aux/nclif": "^0.0.6-alpha.
|
75
|
+
"@ps-aux/nclif": "^0.0.6-alpha.3",
|
75
76
|
"@types/hapi__joi": "^17.1.8",
|
76
77
|
"axios": "^0.24.0",
|
77
78
|
"handlebars": "^4.7.7",
|
79
|
+
"ini": "^2.0.0",
|
78
80
|
"inversify": "^6.0.1",
|
79
81
|
"js-yaml": "^4.1.0",
|
80
82
|
"pino": "^7.6.2",
|
@@ -1,22 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
4
|
-
value: true
|
5
|
-
});
|
6
|
-
exports.LocalFileStorageBackend = void 0;
|
7
|
-
|
8
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
9
|
-
|
10
|
-
class LocalFileStorageBackend {
|
11
|
-
constructor(fsSyncer, dst) {
|
12
|
-
this.fsSyncer = fsSyncer;
|
13
|
-
this.dst = dst;
|
14
|
-
|
15
|
-
_defineProperty(this, "store", from => {
|
16
|
-
this.fsSyncer.syncDirs(from, this.dst);
|
17
|
-
});
|
18
|
-
}
|
19
|
-
|
20
|
-
}
|
21
|
-
|
22
|
-
exports.LocalFileStorageBackend = LocalFileStorageBackend;
|