@ps-aux/nodebup 0.2.0 → 0.2.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.
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AggregateBackupController = void 0;
7
+
8
+ var _FsSyncer = require("../fs/fssync/FsSyncer");
9
+
10
+ var _Config = require("../config/Config");
11
+
12
+ var _Gpg = require("../tools/gpg/Gpg");
13
+
14
+ var _Fs = require("../fs/Fs");
15
+
16
+ var _SshKeyManager = require("../tools/ssh/SshKeyManager");
17
+
18
+ var _inversify = require("inversify");
19
+
20
+ var _ContextSymbols = require("../ctx/ContextSymbols");
21
+
22
+ var _AppLogger = require("../log/AppLogger");
23
+
24
+ var _StorageBackendProvider = require("../storage/StorageBackendProvider");
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 AggregateBackupController = (_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 AggregateBackupController {
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", async (aggregate, storage) => {
43
+ const aggrDir = this.cfg.pwd.resolve('.aggr');
44
+ this.fs.ensureDir(aggrDir);
45
+ const aggr = this.cfg.aggregates.find(a => a.name === aggregate);
46
+
47
+ for (const f of aggr.sources.file) {
48
+ await this.runFileTask(f, aggrDir);
49
+ }
50
+
51
+ this.storageBackendProvider.provide(storage).store(aggrDir);
52
+ this.fs.rmDir(aggrDir);
53
+ });
54
+
55
+ _defineProperty(this, "runGpgKeyTask", t => {
56
+ this.log.info('Running GPG key task', t);
57
+ const key = this.gpg.exportKey(t.id);
58
+ const to = this.storagePath(t.to || `keys/gpg/${t.id}.asc`);
59
+ this.fs.writeFile(to, key);
60
+ });
61
+
62
+ _defineProperty(this, "runSshKeyTask", t => {
63
+ this.log.info('Running SSH key task', t);
64
+ const key = this.ssh.exportKey(t.id);
65
+ const to = this.storagePath(t.to || `keys/ssh/${t.id}`);
66
+ this.fs.writeFile(to, key);
67
+ });
68
+
69
+ _defineProperty(this, "runFileTask", (f, dir) => {
70
+ this.log.info('Running file task', f);
71
+ const {
72
+ from,
73
+ to
74
+ } = f;
75
+ this.fsSyncer.sync(from, dir.resolve(to));
76
+ });
77
+
78
+ _defineProperty(this, "storagePath", path => {
79
+ throw new Error('Unspported');
80
+ });
81
+ }
82
+
83
+ }) || _class) || _class) || _class) || _class);
84
+ exports.AggregateBackupController = AggregateBackupController;
package/lib/cli/app.js CHANGED
@@ -13,17 +13,42 @@ var _Config = require("../config/Config");
13
13
 
14
14
  var _Context = require("../ctx/Context");
15
15
 
16
- var _TaskRunner = require("../bup/TaskRunner");
16
+ var _AggregateBackupController = require("../bup/AggregateBackupController");
17
+
18
+ var _ResticController = require("../storage/restic/ResticController");
17
19
 
18
20
  const createApp = () => _nclif.CliApp.of({
19
- options: [],
20
21
  commands: {
21
- go: (0, _nclif.cmd)({
22
- description: 'Run backup',
23
- run: (_, ctx) => ctx.get(_TaskRunner.TaskRunner).runAll()
22
+ restic: (0, _nclif.cmdGroup)({
23
+ commands: {
24
+ 'init-repo': (0, _nclif.cmd)({
25
+ positionals: [{
26
+ name: 'resticStorageName',
27
+ required: true
28
+ }],
29
+ run: (arg, c) => c.get(_ResticController.ResticController).initRepo(arg.resticStorageName)
30
+ })
31
+ }
32
+ }),
33
+ aggr: (0, _nclif.cmdGroup)({
34
+ commands: {
35
+ backup: (0, _nclif.cmd)({
36
+ description: 'Run backup',
37
+ positionals: [{
38
+ name: 'aggregateName',
39
+ type: 'string',
40
+ required: true
41
+ }, {
42
+ name: 'storageName',
43
+ type: 'string',
44
+ required: true
45
+ }],
46
+ run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName, a.storageName)
47
+ })
48
+ }
24
49
  })
25
50
  }
26
- }).addObjectConfig(pwd => (0, _Config.readConfig)(pwd + '/bup.yaml')).context(({
51
+ }).addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
27
52
  config
28
53
  }) => (0, _Context.createContext)(config));
29
54
 
@@ -11,31 +11,48 @@ var _readYaml = require("../fs/readYaml");
11
11
 
12
12
  var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
13
13
 
14
- var _expandPath = require("./expandPath");
14
+ var _expandAndCreatePath = require("./expandAndCreatePath");
15
15
 
16
- const readConfig = path => {
17
- const file = _Path.AbsPath.from(path);
16
+ const expandPaths = (cfg, exp, cwd) => {
17
+ cfg.storage.filter(s => s.type === 'file').map(s => s).forEach(s => {
18
+ s.path = exp(s.path);
19
+ });
20
+ cfg.storage.filter(s => s.type === 'restic').map(s => s).forEach(s => {
21
+ if (s.credentialsFile) s.credentialsFile = exp(s.credentialsFile);
22
+ s.passwordFile = exp(s.passwordFile);
23
+ if (s.repo.startsWith('local:')) s.repo = (0, _expandAndCreatePath.expandStrPath)(s.repo, cwd);
24
+ });
25
+ cfg.aggregates.forEach(a => {
26
+ a.sources.file.forEach(f => {
27
+ f.from = exp(f.from);
28
+ f.to = _Path.RelativePath.from(f.to);
29
+ });
30
+ });
31
+ };
18
32
 
19
- const obj = (0, _readYaml.readYaml)(file.str());
20
- (0, _validateConfigAgainstSchema.validateConfigAgainstSchema)(obj);
21
- const rootDir = file.parent();
33
+ const readConfig = pwdStr => {
34
+ const cwd = _Path.AbsPath.from(pwdStr);
35
+
36
+ const cfgFile = (0, _readYaml.readYaml)(cwd.resolve('bup.yaml').str());
37
+ (0, _validateConfigAgainstSchema.validateConfigAgainstSchema)(cfgFile);
38
+ const cfg = cfgFile;
39
+ cfg.pwd = cwd;
40
+ cfg.aggregates.forEach(t => {
41
+ const {
42
+ sources
43
+ } = t;
44
+ sources.file = sources.file ?? [];
45
+ sources.sshKey = sources.sshKey ?? [];
46
+ sources.gpgKey = sources.gpgKey ?? [];
47
+ });
22
48
 
23
49
  const expand = str => {
24
- const p = (0, _expandPath.expandPath)(str, rootDir);
50
+ const p = (0, _expandAndCreatePath.expandAndCreatePath)(str, cwd);
25
51
  return p;
26
52
  };
27
53
 
28
- if (!obj.tasks.file) obj.tasks.file = [];
29
- if (!obj.tasks.gpgKey) obj.tasks.gpgKey = [];
30
- if (!obj.tasks.sshKey) obj.tasks.sshKey = [];
31
- obj.tasks.file.forEach(t => {
32
- t.from = expand(t.from);
33
- t.to = _Path.RelativePath.from(t.to);
34
- });
35
- obj.storage.forEach(s => {
36
- s.path = expand(s.path);
37
- });
38
- return obj;
54
+ expandPaths(cfg, expand, cwd);
55
+ return cfg;
39
56
  };
40
57
 
41
58
  exports.readConfig = readConfig;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.expandStrPath = exports.expandAndCreatePath = void 0;
7
+
8
+ var _Path = require("../fs/path/Path");
9
+
10
+ var _os = _interopRequireDefault(require("os"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ const expandStrPath = (path, pwd) => {
15
+ path = path.replace('<cwd>', pwd.str());
16
+ path = path.replace('~', _os.default.homedir());
17
+ return path;
18
+ };
19
+
20
+ exports.expandStrPath = expandStrPath;
21
+
22
+ const expandAndCreatePath = (path, pwd) => _Path.AbsPath.from(expandStrPath(path, pwd));
23
+
24
+ exports.expandAndCreatePath = expandAndCreatePath;
@@ -21,18 +21,37 @@ const KeyTaskSchema = _joi.default.object({
21
21
  to: _joi.default.string().optional()
22
22
  });
23
23
 
24
- const StorageSchema = _joi.default.object({
24
+ const createStorageSchema = props => _joi.default.object({
25
+ name: _joi.default.string(),
26
+ type: _joi.default.string(),
27
+ ...props
28
+ });
29
+
30
+ const FileStorageSchema = createStorageSchema({
25
31
  path: _joi.default.string()
26
32
  });
33
+ const ResticStorageSchema = createStorageSchema({
34
+ repo: _joi.default.string(),
35
+ credentialsFile: _joi.default.string().optional(),
36
+ passwordFile: _joi.default.string()
37
+ });
38
+
39
+ const Sources = _joi.default.object({
40
+ dir: _joi.default.array().items(DirTaskSchema).optional(),
41
+ file: _joi.default.array().items(FileTaskSchema).optional(),
42
+ gpgKey: _joi.default.array().items(KeyTaskSchema).optional(),
43
+ sshKey: _joi.default.array().items(KeyTaskSchema).optional()
44
+ });
45
+
46
+ const Aggregate = _joi.default.object({
47
+ name: _joi.default.string(),
48
+ sources: Sources
49
+ });
27
50
 
28
51
  const Schema = () => _joi.default.object({
29
- storage: _joi.default.array().items(StorageSchema),
30
- tasks: _joi.default.object({
31
- dir: _joi.default.array().items(DirTaskSchema).optional(),
32
- file: _joi.default.array().items(FileTaskSchema).optional(),
33
- gpgKey: _joi.default.array().items(KeyTaskSchema).optional(),
34
- sshKey: _joi.default.array().items(KeyTaskSchema).optional()
35
- })
52
+ // TODO undescriptive error msg. Invalid config."storage[0]" does not match any of the allowed types
53
+ storage: _joi.default.array().items(FileStorageSchema, ResticStorageSchema),
54
+ aggregates: _joi.default.array().items(Aggregate)
36
55
  });
37
56
 
38
57
  const validateConfigAgainstSchema = cfg => {
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
4
+
5
+ const f1 = {
6
+ storage: [{
7
+ name: 'fs',
8
+ type: 'file',
9
+ path: '~/tmp/nodepub/test-store'
10
+ }, {
11
+ name: 'restic',
12
+ type: 'restic',
13
+ repo: 'b2:reo',
14
+ credentialsFile: '.b2-credentials.json',
15
+ passwordFile: 'pass-file'
16
+ }],
17
+ aggregates: [{
18
+ name: 'my-task',
19
+ sources: {
20
+ file: [{
21
+ from: 'foo',
22
+ to: 'foo'
23
+ }]
24
+ }
25
+ }]
26
+ };
27
+ it('validate', () => {
28
+ (0, _validateConfigAgainstSchema.validateConfigAgainstSchema)(f1);
29
+ });
@@ -17,23 +17,25 @@ var _SshKeyManager = require("../tools/ssh/SshKeyManager");
17
17
 
18
18
  var _inversify = require("inversify");
19
19
 
20
- var _PlainCopyDataStorage = require("../storage/PlainCopyDataStorage");
21
-
22
20
  var _ContextSymbols = require("./ContextSymbols");
23
21
 
24
- var _TaskRunner = require("../bup/TaskRunner");
22
+ var _AggregateBackupController = require("../bup/AggregateBackupController");
25
23
 
26
24
  var _AppLogger = require("../log/AppLogger");
27
25
 
26
+ var _StorageBackendProvider = require("../storage/StorageBackendProvider");
27
+
28
+ var _ResticClientFactory = require("../storage/restic/ResticClientFactory");
29
+
30
+ var _ResticController = require("../storage/restic/ResticController");
31
+
28
32
  const createContext = cfg => {
29
- if (cfg.storage.length !== 1) throw new Error('Exactly one storage path is required');
30
33
  const c = new _inversify.Container();
31
- c.bind(_ContextSymbols.TaskConfig_).toConstantValue(cfg.tasks);
32
- c.bind(_ContextSymbols.StorageConfig_).toConstantValue(cfg.storage[0]);
34
+ c.bind(_ContextSymbols.AppConfig_).toConstantValue(cfg);
33
35
  const log = new _AppLogger.AppLogger();
34
36
  c.bind(_ContextSymbols.Log_).toConstantValue(log);
35
37
  c.bind(_AppLogger.AppLogger).toConstantValue(log);
36
- const self = [_Gpg.Gpg, _Shell.Shell, _Fs.Fs, _FsSyncer.FsSyncer, _SshKeyManager.SshKeyManager, _PlainCopyDataStorage.PlainCopyDataStorage, _TaskRunner.TaskRunner];
38
+ const self = [_Gpg.Gpg, _Shell.Shell, _Fs.Fs, _FsSyncer.FsSyncer, _SshKeyManager.SshKeyManager, _StorageBackendProvider.StorageBackendProvider, _ResticClientFactory.ResticClientFactory, _AggregateBackupController.AggregateBackupController, _ResticController.ResticController];
37
39
  self.forEach(s => c.bind(s).toSelf());
38
40
  return c;
39
41
  };
@@ -3,10 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TaskConfig_ = exports.StorageConfig_ = exports.Log_ = void 0;
6
+ exports.TaskConfig_ = exports.StorageConfig_ = exports.Log_ = exports.AppConfig_ = void 0;
7
7
  const Log_ = Symbol.for('Log');
8
8
  exports.Log_ = Log_;
9
9
  const TaskConfig_ = Symbol.for('config.Tasks');
10
10
  exports.TaskConfig_ = TaskConfig_;
11
11
  const StorageConfig_ = Symbol.for('config.Storage');
12
- exports.StorageConfig_ = StorageConfig_;
12
+ exports.StorageConfig_ = StorageConfig_;
13
+ const AppConfig_ = Symbol.for('config.app');
14
+ exports.AppConfig_ = AppConfig_;
package/lib/fs/Fs.js CHANGED
@@ -28,13 +28,34 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
28
28
  _fs.default.writeFileSync(path.str(), content);
29
29
  });
30
30
 
31
- _defineProperty(this, "isFile", path => !_fs.default.lstatSync(path.str()).isDirectory());
31
+ _defineProperty(this, "isFile", path => !this.isDir(path));
32
+
33
+ _defineProperty(this, "ensureIsFile", p => {
34
+ if (!this.isFile(p)) throw new Error(`${p} is not a file`);
35
+ });
36
+
37
+ _defineProperty(this, "isDir", path => _fs.default.lstatSync(path.str()).isDirectory());
38
+
39
+ _defineProperty(this, "rmDir", path => {
40
+ _fs.default.rmdirSync(path.str(), {
41
+ recursive: true
42
+ });
43
+ });
44
+
45
+ _defineProperty(this, "ensureIsDir", p => {
46
+ if (!this.isDir(p)) throw new Error(`${p} is not a dir`);
47
+ });
32
48
 
33
49
  _defineProperty(this, "readFile", path => {
34
- this.log.debug('Reading from file', path.str);
50
+ this.log.debug('Reading from file', path.str());
35
51
  return _fs.default.readFileSync(path.str()).toString();
36
52
  });
37
53
 
54
+ _defineProperty(this, "readJson", path => {
55
+ const file = this.readFile(path);
56
+ return JSON.parse(file);
57
+ });
58
+
38
59
  _defineProperty(this, "ensureDir", path => {
39
60
  if (!this.exists(path)) {
40
61
  this.log.debug('Dir ', path, 'does not exist. Wil be created');
@@ -29,15 +29,21 @@ let FsSyncer = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("d
29
29
  this.fs = fs;
30
30
  this.log = log;
31
31
 
32
+ _defineProperty(this, "sync", (from, to) => {
33
+ if (this.fs.isFile(from)) this.syncFile(from, to);else this.syncDirs(from, to);
34
+ });
35
+
32
36
  _defineProperty(this, "syncDirs", (from, to) => {
37
+ this.fs.ensureIsDir(from);
33
38
  this.log.info('Syncing dir', from.str(), '==>', to.str());
34
39
  const fromPath = toUnixDirString(from);
35
40
  const toPath = toUnixDirString(to);
36
41
  this.fs.ensureParentDir(to);
37
- this.sh.exec(`rsync -rtv ${fromPath} ${toPath}`);
42
+ this.sh.exec(`rsync -rtv --delete ${fromPath} ${toPath}`);
38
43
  });
39
44
 
40
45
  _defineProperty(this, "syncFile", (from, to) => {
46
+ this.fs.ensureIsFile(from);
41
47
  this.log.info('Syncing file', from.str(), '==>', to.str());
42
48
  this.fs.ensureParentDir(to);
43
49
  this.sh.exec(`rsync -rtv ${from.str()} ${to.str()}`);
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.StorageBackendProvider = void 0;
7
+
8
+ var _inversify = require("inversify");
9
+
10
+ var _FsSyncer = require("../fs/fssync/FsSyncer");
11
+
12
+ var _LocalFileStorageBackend = require("./local-file/LocalFileStorageBackend");
13
+
14
+ var _ContextSymbols = require("../ctx/ContextSymbols");
15
+
16
+ var _Config = require("../config/Config");
17
+
18
+ var _ResticClientFactory = require("./restic/ResticClientFactory");
19
+
20
+ var _ResticStorageBackend = require("./restic/ResticStorageBackend");
21
+
22
+ var _Fs = require("../fs/Fs");
23
+
24
+ var _dec, _dec2, _dec3, _dec4, _class;
25
+
26
+ 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
+
28
+ let StorageBackendProvider = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
29
+ return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 3);
30
+ }, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _FsSyncer.FsSyncer === "undefined" ? Object : _FsSyncer.FsSyncer, typeof _ResticClientFactory.ResticClientFactory === "undefined" ? Object : _ResticClientFactory.ResticClientFactory, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _Config.Config === "undefined" ? Object : _Config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class StorageBackendProvider {
31
+ constructor(fsSyncer, resticFac, fs, cfg) {
32
+ this.fsSyncer = fsSyncer;
33
+ this.resticFac = resticFac;
34
+ this.fs = fs;
35
+ this.cfg = cfg;
36
+
37
+ _defineProperty(this, "provide", name => {
38
+ const s = this.cfg.storage.find(s => s.name === name);
39
+ if (!s) throw new Error(`No such storage '${name}'`);
40
+ if (s.type === 'restic') return this.restic(s);else if (s.type === 'file') return this.localFile(s);else throw new Error(`Unsupported storage type ${s.type}`);
41
+ });
42
+
43
+ _defineProperty(this, "restic", cfg => {
44
+ return new _ResticStorageBackend.ResticStorageBackend(this.resticFac.create(cfg));
45
+ });
46
+
47
+ _defineProperty(this, "localFile", cfg => {
48
+ return new _LocalFileStorageBackend.LocalFileStorageBackend(this.fsSyncer, cfg.path);
49
+ });
50
+ }
51
+
52
+ }) || _class) || _class) || _class) || _class);
53
+ exports.StorageBackendProvider = StorageBackendProvider;
@@ -0,0 +1,22 @@
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;
@@ -25,45 +25,61 @@ const normalizeUrl = url => {
25
25
  return url;
26
26
  };
27
27
  /**
28
+ * Tested with: restic 0.9.6 compiled with go1.12.12 on linux/amd64
28
29
  * TODO forwarding stdout to the logger
29
30
  */
30
31
 
31
32
 
32
33
  let ResticClient = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
33
- return (0, _inversify.inject)(_ContextSymbols.TaskConfig_)(target, undefined, 1);
34
- }, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell, typeof _types.Log === "undefined" ? Object : _types.Log]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class ResticClient {
35
- constructor(shell, log) {
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 {
36
+ constructor(cfg, shell, log) {
37
+ this.cfg = cfg;
36
38
  this.shell = shell;
37
39
  this.log = log;
38
40
 
39
- _defineProperty(this, "prepareRepo", (url, password) => {
40
- url = normalizeUrl(url);
41
- this.log.info('Preparing a restic repo', url);
42
- this.shell.exec(`restic init --repo ${url}`, {
43
- env: {
44
- RESTIC_PASSWORD: password
45
- }
41
+ _defineProperty(this, "url", void 0);
42
+
43
+ _defineProperty(this, "prepareRepo", () => {
44
+ this.log.info(`Preparing a restic repo at '${this.url}`);
45
+ this.shell.exec(`restic init`, {
46
+ env: this.env()
46
47
  });
47
48
  });
48
49
 
49
- _defineProperty(this, "backup", (url, cwd, from, password) => {
50
- url = normalizeUrl(url);
51
- this.shell.exec(`restic --repo ${url} backup ${from.str()}`, {
50
+ _defineProperty(this, "backup", (cwd, from) => {
51
+ this.shell.exec(`restic backup ${from.str()}`, {
52
52
  cwd: cwd.str(),
53
- env: {
54
- RESTIC_PASSWORD: password
55
- }
53
+ env: this.env()
56
54
  });
57
55
  });
58
56
 
59
- _defineProperty(this, "restore", (url, to, password) => {
60
- url = normalizeUrl(url);
61
- this.shell.exec(`restic --repo ${url} restore latest --target ${to.str()}`, {
62
- env: {
63
- RESTIC_PASSWORD: password
64
- }
57
+ _defineProperty(this, "restore", to => {
58
+ this.shell.exec(`restic restore latest --target ${to.str()}`, {
59
+ env: this.env()
65
60
  });
66
61
  });
62
+
63
+ _defineProperty(this, "env", () => ({
64
+ RESTIC_PASSWORD: this.cfg.repo.password,
65
+ RESTIC_REPOSITORY: this.url,
66
+ ...this.getCredentialsForUrl(this.url)
67
+ }));
68
+
69
+ _defineProperty(this, "getCredentialsForUrl", url => {
70
+ if (url.startsWith('b2:')) {
71
+ const cfg = this.cfg.backblaze;
72
+ if (!cfg) throw new Error(`Repo url ${url} refers to Backblaze but no Backblaze config provided`);
73
+ return {
74
+ B2_ACCOUNT_ID: cfg.accountId,
75
+ B2_ACCOUNT_KEY: cfg.accountKey
76
+ };
77
+ }
78
+
79
+ return {};
80
+ });
81
+
82
+ this.url = normalizeUrl(cfg.repo.url);
67
83
  }
68
84
 
69
85
  }) || _class) || _class) || _class) || _class);
@@ -10,41 +10,87 @@ var _test = require("../../../test");
10
10
 
11
11
  var _fs = _interopRequireDefault(require("fs"));
12
12
 
13
+ var _path = _interopRequireDefault(require("path"));
14
+
13
15
  var _Path = require("../../fs/path/Path");
14
16
 
15
17
  var _dirCompare = require("dir-compare");
16
18
 
17
19
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
20
 
19
- /*
20
- * Tested with: restic 0.9.6 compiled with go1.12.12 on linux/amd64
21
- */
22
- describe.skip('ResticClient', () => {
21
+ const getBackblazeConfig = () => {
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
+ };
31
+
32
+ describe('ResticClient', () => {
23
33
  const l = new _AppLogger.AppLogger();
24
- const sut = new _ResticClient.ResticClient(new _Shell.Shell(l), l);
25
34
  const password = 'foo123';
26
- const repoDir = (0, _test.testdataDir)('my-repo');
27
35
  const backupDir = (0, _test.testDir)('backup-me');
28
- const restoreDir = (0, _test.testdataDir)('restore');
29
- const repoUrl = `local:${repoDir}`;
30
- afterAll(() => {
31
- _fs.default.rmdirSync(repoDir, {
32
- recursive: true
33
- });
36
+ describe('local', () => {
37
+ const repoDir = (0, _test.testdataDir)('my-repo');
38
+ const restoreDir = (0, _test.testdataDir)('restore/local');
39
+ const sut = new _ResticClient.ResticClient({
40
+ repo: {
41
+ password,
42
+ url: `local:${repoDir}`
43
+ }
44
+ }, new _Shell.Shell(l), l);
45
+ afterAll(() => {
46
+ _fs.default.rmdirSync(repoDir, {
47
+ recursive: true
48
+ });
34
49
 
35
- _fs.default.rmdirSync(restoreDir, {
36
- recursive: true
50
+ _fs.default.rmdirSync(restoreDir, {
51
+ recursive: true
52
+ });
53
+ });
54
+ it('create repo', () => {
55
+ sut.prepareRepo();
56
+ });
57
+ it('push data', () => {
58
+ sut.backup(_Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'));
59
+ });
60
+ it('restore data', () => {
61
+ sut.restore(_Path.AbsPath.from(restoreDir));
62
+ const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
63
+ expect(res.same).toBeTrue();
37
64
  });
38
65
  });
39
- it('create repo', () => {
40
- sut.prepareRepo(repoUrl, password);
41
- });
42
- it('push data', () => {
43
- sut.backup(repoUrl, _Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'), password);
44
- });
45
- it('restore data', () => {
46
- sut.restore(repoUrl, _Path.AbsPath.from(restoreDir), password);
47
- const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
48
- expect(res.same).toBeTrue();
66
+ describe('b2', () => {
67
+ const b2Cfg = getBackblazeConfig();
68
+
69
+ if (!b2Cfg) {
70
+ console.log('No B2 config - skipping');
71
+ return;
72
+ }
73
+
74
+ const restoreDir = (0, _test.testdataDir)('restore/b2');
75
+ const repoUrl = `b2:psaux-nodepub-test`;
76
+ const sut = new _ResticClient.ResticClient({
77
+ repo: {
78
+ password,
79
+ url: repoUrl
80
+ },
81
+ backblaze: b2Cfg
82
+ }, new _Shell.Shell(l), l);
83
+ afterAll(() => {
84
+ _fs.default.rmdirSync(restoreDir, {
85
+ recursive: true
86
+ });
87
+ });
88
+ it('push adn restore data', () => {
89
+ // sut.prepareRepo(repoUrl, password)
90
+ sut.backup(_Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'));
91
+ sut.restore(_Path.AbsPath.from(restoreDir));
92
+ const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
93
+ expect(res.same).toBeTrue();
94
+ });
49
95
  });
50
96
  });
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ResticClientFactory = void 0;
7
+
8
+ var _inversify = require("inversify");
9
+
10
+ var _ResticClient = require("./ResticClient");
11
+
12
+ var _Shell = require("../../tools/shell/Shell");
13
+
14
+ var _AppLogger = require("../../log/AppLogger");
15
+
16
+ var _Fs = require("../../fs/Fs");
17
+
18
+ var _joi = _interopRequireDefault(require("@hapi/joi"));
19
+
20
+ var _dec, _dec2, _dec3, _class;
21
+
22
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
+
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; }
25
+
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) {
42
+ this.sh = sh;
43
+ this.log = log;
44
+ this.fs = fs;
45
+
46
+ _defineProperty(this, "create", cfg => {
47
+ const password = this.fs.readFile(cfg.passwordFile).trim();
48
+ const props = {
49
+ repo: {
50
+ password,
51
+ url: cfg.repo
52
+ }
53
+ };
54
+
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
+ };
70
+ }
71
+
72
+ return new _ResticClient.ResticClient(props, this.sh, this.log);
73
+ });
74
+ }
75
+
76
+ }) || _class) || _class) || _class);
77
+ exports.ResticClientFactory = ResticClientFactory;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ResticController = void 0;
7
+
8
+ var _inversify = require("inversify");
9
+
10
+ var _ContextSymbols = require("../../ctx/ContextSymbols");
11
+
12
+ var _Config = require("../../config/Config");
13
+
14
+ var _ResticClientFactory = require("./ResticClientFactory");
15
+
16
+ var _AppLogger = require("../../log/AppLogger");
17
+
18
+ var _dec, _dec2, _dec3, _dec4, _class;
19
+
20
+ 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
+
22
+ let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
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 _Config.Config === "undefined" ? Object : _Config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class ResticController {
25
+ constructor(restFact, log, cfg) {
26
+ this.restFact = restFact;
27
+ this.log = log;
28
+ this.cfg = cfg;
29
+
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
+ });
36
+ }
37
+
38
+ }) || _class) || _class) || _class) || _class);
39
+ exports.ResticController = ResticController;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ResticStorageBackend = void 0;
7
+
8
+ var _Path = require("../../fs/path/Path");
9
+
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
+
12
+ class ResticStorageBackend {
13
+ constructor(client) {
14
+ this.client = client;
15
+
16
+ _defineProperty(this, "store", from => {
17
+ this.client.backup(from, _Path.RelativePath.from('.'));
18
+ });
19
+ }
20
+
21
+ }
22
+
23
+ exports.ResticStorageBackend = ResticStorageBackend;
@@ -6,9 +6,9 @@ var _Gpg = require("./Gpg");
6
6
 
7
7
  var _Shell = require("../shell/Shell");
8
8
 
9
- // TODO setup ci to make tests work
9
+ // TODO find a way hot to provide prompt via stdin
10
10
  it.skip('works', () => {
11
11
  const sut = new _Gpg.Gpg(new _Shell.Shell(new _AppLogger.AppLogger()));
12
- const key = sut.exportKey('foo');
12
+ const key = sut.exportKey('john.doe@foo.com');
13
13
  console.log('key', key);
14
14
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ps-aux/nodebup",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "",
5
5
  "module": "lib/index.js",
6
6
  "main": "lib/index.js",
@@ -1,82 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.TaskRunner = void 0;
7
-
8
- var _FsSyncer = require("../fs/fssync/FsSyncer");
9
-
10
- var _Config = require("../config/Config");
11
-
12
- var _Gpg = require("../tools/gpg/Gpg");
13
-
14
- var _Fs = require("../fs/Fs");
15
-
16
- var _SshKeyManager = require("../tools/ssh/SshKeyManager");
17
-
18
- var _inversify = require("inversify");
19
-
20
- var _ContextSymbols = require("../ctx/ContextSymbols");
21
-
22
- var _PlainCopyDataStorage = require("../storage/PlainCopyDataStorage");
23
-
24
- var _AppLogger = require("../log/AppLogger");
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 TaskRunner = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
31
- return (0, _inversify.inject)(_ContextSymbols.TaskConfig_)(target, undefined, 6);
32
- }, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _FsSyncer.FsSyncer === "undefined" ? Object : _FsSyncer.FsSyncer, typeof _Gpg.Gpg === "undefined" ? Object : _Gpg.Gpg, typeof _SshKeyManager.SshKeyManager === "undefined" ? Object : _SshKeyManager.SshKeyManager, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _PlainCopyDataStorage.PlainCopyDataStorage === "undefined" ? Object : _PlainCopyDataStorage.PlainCopyDataStorage, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _Config.Tasks === "undefined" ? Object : _Config.Tasks]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class TaskRunner {
33
- constructor(sync, gpg, ssh, fs, storage, log, tasks) {
34
- this.sync = sync;
35
- this.gpg = gpg;
36
- this.ssh = ssh;
37
- this.fs = fs;
38
- this.storage = storage;
39
- this.log = log;
40
- this.tasks = tasks;
41
-
42
- _defineProperty(this, "runAll", async () => {
43
- const tasks = this.tasks;
44
-
45
- if (tasks.file.length > 0) {
46
- for (const t of tasks.file) {
47
- await this.runFileTask(t);
48
- }
49
- }
50
-
51
- return; // eslint-disable-next-line no-unreachable
52
-
53
- if (tasks.gpgKey.length > 0) tasks.gpgKey.forEach(this.runGpgKeyTask);
54
- if (tasks.sshKey.length > 0) tasks.sshKey.forEach(this.runSshKeyTask);
55
- });
56
-
57
- _defineProperty(this, "runGpgKeyTask", t => {
58
- this.log.info('Running GPG key task', t);
59
- const key = this.gpg.exportKey(t.id);
60
- const to = this.storagePath(t.to || `keys/gpg/${t.id}.asc`);
61
- this.fs.writeFile(to, key);
62
- });
63
-
64
- _defineProperty(this, "runSshKeyTask", t => {
65
- this.log.info('Running SSH key task', t);
66
- const key = this.ssh.exportKey(t.id);
67
- const to = this.storagePath(t.to || `keys/ssh/${t.id}`);
68
- this.fs.writeFile(to, key);
69
- });
70
-
71
- _defineProperty(this, "runFileTask", async t => {
72
- this.log.info('Running file task', t);
73
- this.storage.store(t.from, t.to);
74
- });
75
-
76
- _defineProperty(this, "storagePath", path => {
77
- throw new Error('Unspported');
78
- });
79
- }
80
-
81
- }) || _class) || _class) || _class) || _class);
82
- exports.TaskRunner = TaskRunner;
@@ -1,20 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.expandPath = void 0;
7
-
8
- var _Path = require("../fs/path/Path");
9
-
10
- var _os = _interopRequireDefault(require("os"));
11
-
12
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
-
14
- const expandPath = (path, rootDir) => {
15
- path = path.replace('<cwd>', rootDir.str);
16
- path = path.replace('~', _os.default.homedir());
17
- return _Path.AbsPath.from(path);
18
- };
19
-
20
- exports.expandPath = expandPath;
@@ -1,40 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.PlainCopyDataStorage = void 0;
7
-
8
- var _inversify = require("inversify");
9
-
10
- var _FsSyncer = require("../fs/fssync/FsSyncer");
11
-
12
- var _Config = require("../config/Config");
13
-
14
- var _ContextSymbols = require("../ctx/ContextSymbols");
15
-
16
- var _Fs = require("../fs/Fs");
17
-
18
- var _AppLogger = require("../log/AppLogger");
19
-
20
- var _dec, _dec2, _dec3, _dec4, _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
- let PlainCopyDataStorage = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
25
- return (0, _inversify.inject)(_ContextSymbols.StorageConfig_)(target, undefined, 1);
26
- }, _dec3 = Reflect.metadata("design:type", Function), _dec4 = Reflect.metadata("design:paramtypes", [typeof _FsSyncer.FsSyncer === "undefined" ? Object : _FsSyncer.FsSyncer, typeof _Config.StorageInstance === "undefined" ? Object : _Config.StorageInstance, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class PlainCopyDataStorage {
27
- constructor(fsSyncer, storage, fs, log) {
28
- this.fsSyncer = fsSyncer;
29
- this.storage = storage;
30
- this.fs = fs;
31
- this.log = log;
32
-
33
- _defineProperty(this, "store", async (from, to) => {
34
- this.log.info(`Storing ${from} to ${to}`);
35
- if (this.fs.isFile(from)) this.fsSyncer.syncFile(from, this.storage.path.resolve(to));else this.fsSyncer.syncDirs(from, this.storage.path.resolve(to));
36
- });
37
- }
38
-
39
- }) || _class) || _class) || _class) || _class);
40
- exports.PlainCopyDataStorage = PlainCopyDataStorage;