@ps-aux/nodebup 0.3.0 → 0.6.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.
Files changed (34) hide show
  1. package/lib/bup/AggregateBackupController.js +7 -7
  2. package/lib/bup/BackupController.js +63 -0
  3. package/lib/cli/InvalidInput.js +10 -0
  4. package/lib/cli/app.js +55 -15
  5. package/lib/config/Config.js +2 -10
  6. package/lib/config/expandAndCreatePath.js +3 -1
  7. package/lib/{storage/types.d.js → config/index.js} +0 -0
  8. package/lib/config/validateConfigAgainstSchema.js +3 -16
  9. package/lib/config/validateConfigAgainstSchema.spec.js +0 -4
  10. package/lib/ctx/Context.js +12 -2
  11. package/lib/ctx/ContextSymbols.js +4 -2
  12. package/lib/fs/Fs.js +11 -2
  13. package/lib/fs/fssync/FsSync.spec.js +1 -1
  14. package/lib/log/AppLogger.js +6 -4
  15. package/lib/storage/BackupClient.js +5 -0
  16. package/lib/storage/{restic/ResticStorageBackend.js → BackupClientStorageBackend.js} +8 -4
  17. package/lib/storage/StorageBackendProvider.js +25 -28
  18. package/lib/storage/StorageConfigProvider.js +89 -0
  19. package/lib/storage/StoreConfig.js +39 -0
  20. package/lib/storage/b2/B2CredentialsProvider.js +59 -0
  21. package/lib/storage/rclone/RCloneClientFactory.js +69 -0
  22. package/lib/storage/rclone/RcloneClient.js +87 -0
  23. package/lib/storage/rclone/RcloneClient.spec.js +83 -0
  24. package/lib/storage/restic/ResticClient.js +6 -15
  25. package/lib/storage/restic/ResticClient.spec.js +7 -23
  26. package/lib/storage/restic/ResticClientFactory.js +12 -36
  27. package/lib/storage/restic/ResticController.js +13 -8
  28. package/lib/storage/types.js +13 -0
  29. package/lib/tools/gpg/Gpg.js +8 -1
  30. package/lib/tools/gpg/Gpg.spec.js +18 -7
  31. package/lib/tools/shell/Shell.js +1 -1
  32. package/lib/tools/ssh/SshKeyManager.spec.js +3 -1
  33. package/package.json +17 -16
  34. package/lib/storage/local-file/LocalFileStorageBackend.js +0 -22
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.StorageSchema = exports.NamedStorageSchema = exports.CliStorageConfigSchema = void 0;
7
+
8
+ var _joi = _interopRequireDefault(require("@hapi/joi"));
9
+
10
+ var _types = require("./types");
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ const props = {
15
+ type: _joi.default.string().valid(_types.StorageType.Restic, _types.StorageType.RClone),
16
+ repo: _joi.default.string(),
17
+ credentialsFile: _joi.default.string().optional(),
18
+ passwordFile: _joi.default.string().optional()
19
+ };
20
+
21
+ const StorageSchema = () => _joi.default.object(props);
22
+
23
+ exports.StorageSchema = StorageSchema;
24
+
25
+ const CliStorageConfigSchema = () => _joi.default.object({
26
+ storageType: _joi.default.string().valid(_types.StorageType.Restic, _types.StorageType.RClone),
27
+ storageRepo: _joi.default.string(),
28
+ storageCredentials: _joi.default.string().optional(),
29
+ storagePassword: _joi.default.string().optional()
30
+ });
31
+
32
+ exports.CliStorageConfigSchema = CliStorageConfigSchema;
33
+
34
+ const NamedStorageSchema = () => _joi.default.object({
35
+ name: _joi.default.string(),
36
+ ...props
37
+ });
38
+
39
+ exports.NamedStorageSchema = NamedStorageSchema;
@@ -0,0 +1,59 @@
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
+ return this.fromString(this.fs.readFile(path));
40
+ });
41
+
42
+ _defineProperty(this, "fromString", str => {
43
+ const credJson = JSON.parse(str);
44
+ const validationErr = validateCredentials(credJson);
45
+
46
+ if (validationErr) {
47
+ throw new Error(`Invalid B2 credential in '${str.substring(0, 3)}...': ${validationErr}`);
48
+ }
49
+
50
+ const creds = credJson;
51
+ return {
52
+ accountId: creds.keyID,
53
+ key: creds.applicationKey
54
+ };
55
+ });
56
+ }
57
+
58
+ }) || _class) || _class) || _class);
59
+ 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.encryptionPassword;
52
+ if (password) props.encryption = {
53
+ password
54
+ };
55
+ const creds = cfg.credentials;
56
+
57
+ if (type === 'b2') {
58
+ if (!creds) throw new Error(`Credentials are required for RClone storage repo='${cfg.repo}'`);
59
+ props.creds = this.b2.fromString(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
- let ResticClient = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
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.accountKey
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
- }) || _class) || _class) || _class) || _class);
75
+ }
76
+
86
77
  exports.ResticClient = ResticClient;
@@ -2,40 +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
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
-
23
- const getBackblazeConfig = () => {
24
- const path = _path.default.resolve(__dirname, '../../../.keys.json');
25
-
26
- if (!_fs.default.existsSync(path)) return undefined;
27
- const data = JSON.parse(_fs.default.readFileSync(path).toString());
28
- return {
29
- accountId: data.restic.backblaze.keyID,
30
- accountKey: data.restic.backblaze.applicationKey
31
- };
32
- };
15
+ var _b2TestConfig = require("../../../test/b2TestConfig");
33
16
 
34
17
  describe('ResticClient', () => {
35
- const l = new _AppLogger.AppLogger();
18
+ const l = _testHelper.testLog;
36
19
  const password = 'foo123';
37
- const backupDir = (0, _test.testDir)('backup-me');
38
20
  describe('local', () => {
21
+ const backupDir = (0, _test.testDir)('backup/1');
39
22
  const repoDir = (0, _test.testdataDir)('my-repo');
40
23
  const restoreDir = (0, _test.testdataDir)('restore/local');
41
24
  const sut = new _ResticClient.ResticClient({
@@ -61,7 +44,8 @@ describe('ResticClient', () => {
61
44
  });
62
45
  });
63
46
  describe('b2', () => {
64
- const b2Cfg = getBackblazeConfig();
47
+ const backupDir = (0, _test.testDir)('backup/2');
48
+ const b2Cfg = (0, _b2TestConfig.getB2TestConfig)('restic');
65
49
 
66
50
  if (!b2Cfg) {
67
51
  console.log('No B2 config - skipping');
@@ -69,7 +53,7 @@ describe('ResticClient', () => {
69
53
  }
70
54
 
71
55
  const restoreDir = (0, _test.testdataDir)('restore/b2');
72
- const repoUrl = `b2:psaux-nodepub-test`;
56
+ const repoUrl = `b2:` + b2Cfg.bucket;
73
57
  const sut = new _ResticClient.ResticClient({
74
58
  repo: {
75
59
  password,
@@ -81,7 +65,7 @@ describe('ResticClient', () => {
81
65
  (0, _testHelper.cleanDir)(restoreDir);
82
66
  });
83
67
  it('push adn restore data', () => {
84
- // sut.prepareRepo(repoUrl, password)
68
+ // sut.prepareRepo()
85
69
  sut.backup(_Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'));
86
70
  sut.restore(_Path.AbsPath.from(restoreDir));
87
71
  const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
@@ -15,58 +15,34 @@ var _AppLogger = require("../../log/AppLogger");
15
15
 
16
16
  var _Fs = require("../../fs/Fs");
17
17
 
18
- var _joi = _interopRequireDefault(require("@hapi/joi"));
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
- const CredentialsFileSchema = _joi.default.object({
27
- keyID: _joi.default.string(),
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
- const password = this.fs.readFile(cfg.passwordFile).trim();
32
+ if (!cfg.encryptionPassword) throw new Error(`Restic needs encryptionPassword. Repo=${cfg.repo}`);
48
33
  const props = {
49
34
  repo: {
50
- password,
35
+ password: cfg.encryptionPassword,
51
36
  url: cfg.repo
52
37
  }
53
38
  };
39
+ const creds = cfg.credentials;
54
40
 
55
- if (cfg.credentialsFile) {
56
- if (!cfg.repo.startsWith('b2:')) throw new Error('Credentials can be supported only for the B2 restic backend');
57
- const credJson = this.fs.readJson(cfg.credentialsFile); //
58
-
59
- const validationErr = validateCredentials(credJson);
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 '${props.repo.url}'`);
43
+ props.backblaze = this.b2.fromString(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,29 +9,34 @@ var _inversify = require("inversify");
9
9
 
10
10
  var _ContextSymbols = require("../../ctx/ContextSymbols");
11
11
 
12
- var _Config = require("../../config/Config");
12
+ var _config = require("../../config");
13
13
 
14
14
  var _ResticClientFactory = require("./ResticClientFactory");
15
15
 
16
16
  var _AppLogger = require("../../log/AppLogger");
17
17
 
18
+ var _StorageConfigProvider = require("../StorageConfigProvider");
19
+
20
+ var _types = require("../types");
21
+
18
22
  var _dec, _dec2, _dec3, _dec4, _class;
19
23
 
20
24
  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; }
21
25
 
22
26
  let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
23
27
  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 _Config.Config === "undefined" ? Object : _Config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class ResticController {
25
- constructor(restFact, log, cfg) {
28
+ }, _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, typeof _StorageConfigProvider.StorageConfigProvider === "undefined" ? Object : _StorageConfigProvider.StorageConfigProvider]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class ResticController {
29
+ constructor(restFact, log, cfg, storageConfigProvider) {
26
30
  this.restFact = restFact;
27
31
  this.log = log;
28
32
  this.cfg = cfg;
33
+ this.storageConfigProvider = storageConfigProvider;
29
34
 
30
- _defineProperty(this, "initRepo", storageName => {
31
- const s = this.cfg.storage.filter(s => s.type === 'restic').find(s => s.name === storageName);
32
- if (!s) throw new Error(`No restic storage '${storageName}'`);
33
- this.log.info('Initializing repo', storageName);
34
- this.restFact.create(s).prepareRepo();
35
+ _defineProperty(this, "initRepo", () => {
36
+ const props = this.storageConfigProvider.provide();
37
+ if (props.type !== _types.StorageType.Restic) throw new Error('Storage is not Restic storage');
38
+ this.log.info('Initializing repo', props.repo);
39
+ this.restFact.create(props).prepareRepo();
35
40
  });
36
41
  }
37
42
 
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.StorageType = void 0;
7
+ let StorageType;
8
+ exports.StorageType = StorageType;
9
+
10
+ (function (StorageType) {
11
+ StorageType["Restic"] = "restic";
12
+ StorageType["RClone"] = "rclone";
13
+ })(StorageType || (exports.StorageType = StorageType = {}));
@@ -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
- // TODO find a way hot to provide prompt via stdin
10
- it.skip('works', () => {
11
- const sut = new _Gpg.Gpg(new _Shell.Shell(new _AppLogger.AppLogger()));
12
- const key = sut.exportKey('john.doe@foo.com');
13
- console.log('key', key);
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
  });
@@ -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(`"${stdin}" > ${cmd}`);
35
+ this.log.debug(`<stdin> > ${cmd}`);
36
36
  (0, _shellCmd.shellCmd)(cmd, { ...opts,
37
37
  stdin,
38
38
  returnStdout: false