@ps-aux/nodebup 0.5.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +1 -0
- package/lib/bup/{AggregateBackupController.js → aggregate/AggregateBackupController.js} +14 -14
- package/lib/bup/dir/DataBackupController.js +44 -0
- package/lib/bup/pg/PgBackupController.js +110 -0
- package/lib/cli/InvalidInput.js +10 -0
- package/lib/cli/app.js +77 -37
- package/lib/config/Config.js +22 -11
- package/lib/config/expandAndCreatePath.js +3 -1
- package/lib/config/index.js +1 -9
- package/lib/config/validateConfigAgainstSchema.js +2 -17
- package/lib/ctx/Context.js +10 -4
- package/lib/ctx/ContextSymbols.js +4 -2
- package/lib/fs/Fs.js +39 -2
- package/lib/fs/fssync/FsSync.spec.js +1 -1
- package/lib/fs/path/Path.js +5 -1
- package/lib/storage/StorageBackendProvider.js +13 -16
- package/lib/storage/StorageConfigProvider.js +89 -0
- package/lib/storage/StoreConfig.js +39 -0
- package/lib/storage/b2/B2CredentialsProvider.js +6 -2
- package/lib/storage/rclone/RCloneClientFactory.js +4 -4
- package/lib/storage/rclone/RcloneClient.js +1 -1
- package/lib/storage/restic/ResticClientFactory.js +6 -6
- package/lib/storage/restic/ResticController.js +12 -7
- package/lib/storage/types.js +13 -0
- package/lib/tools/gpg/Gpg.js +1 -1
- package/lib/tools/shell/Shell.js +12 -6
- package/lib/tools/shell/shellCmd.js +8 -2
- package/lib/tools/ssh/SshKeyManager.spec.js +3 -1
- package/package.json +8 -2
- package/lib/bup/BackupController.js +0 -56
- package/lib/storage/types.d.js +0 -5
package/README.md
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -5,25 +5,25 @@ Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
});
|
6
6
|
exports.AggregateBackupController = void 0;
|
7
7
|
|
8
|
-
var _FsSyncer = require("
|
8
|
+
var _FsSyncer = require("../../fs/fssync/FsSyncer");
|
9
9
|
|
10
|
-
var _Gpg = require("
|
10
|
+
var _Gpg = require("../../tools/gpg/Gpg");
|
11
11
|
|
12
|
-
var _Fs = require("
|
12
|
+
var _Fs = require("../../fs/Fs");
|
13
13
|
|
14
|
-
var _SshKeyManager = require("
|
14
|
+
var _SshKeyManager = require("../../tools/ssh/SshKeyManager");
|
15
15
|
|
16
16
|
var _inversify = require("inversify");
|
17
17
|
|
18
|
-
var _ContextSymbols = require("
|
18
|
+
var _ContextSymbols = require("../../ctx/ContextSymbols");
|
19
19
|
|
20
|
-
var _AppLogger = require("
|
20
|
+
var _AppLogger = require("../../log/AppLogger");
|
21
21
|
|
22
|
-
var _Path = require("
|
22
|
+
var _Path = require("../../fs/path/Path");
|
23
23
|
|
24
|
-
var _config = require("
|
24
|
+
var _config = require("../../config");
|
25
25
|
|
26
|
-
var
|
26
|
+
var _StorageBackendProvider = require("../../storage/StorageBackendProvider");
|
27
27
|
|
28
28
|
var _dec, _dec2, _dec3, _dec4, _class;
|
29
29
|
|
@@ -31,17 +31,17 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
31
31
|
|
32
32
|
let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
33
33
|
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 6);
|
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
|
35
|
-
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 _StorageBackendProvider.StorageBackendProvider === "undefined" ? Object : _StorageBackendProvider.StorageBackendProvider, typeof _config.Config === "undefined" ? Object : _config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class AggregateBackupController {
|
35
|
+
constructor(gpg, ssh, fs, fsSyncer, log, storageProvider, cfg) {
|
36
36
|
this.gpg = gpg;
|
37
37
|
this.ssh = ssh;
|
38
38
|
this.fs = fs;
|
39
39
|
this.fsSyncer = fsSyncer;
|
40
40
|
this.log = log;
|
41
|
-
this.
|
41
|
+
this.storageProvider = storageProvider;
|
42
42
|
this.cfg = cfg;
|
43
43
|
|
44
|
-
_defineProperty(this, "backup", async
|
44
|
+
_defineProperty(this, "backup", async aggrName => {
|
45
45
|
const aggrDir = this.cfg.pwd.resolve('.aggr');
|
46
46
|
const aggr = this.cfg.aggregates.find(a => a.name === aggrName);
|
47
47
|
if (!aggr) throw new Error(`No backup aggregate with name '${aggrName}'`);
|
@@ -56,7 +56,7 @@ let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = fu
|
|
56
56
|
file.forEach(f => this.runFileTask(f, aggrDir));
|
57
57
|
sshKey.forEach(k => this.runSshKeyTask(k, aggrDir));
|
58
58
|
gpgKey.forEach(k => this.runGpgKeyTask(k, aggrDir));
|
59
|
-
this.
|
59
|
+
this.storageProvider.provide().store(aggrDir);
|
60
60
|
} finally {
|
61
61
|
this.log.debug();
|
62
62
|
this.fs.rmDir(aggrDir);
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.DataBackupController = void 0;
|
7
|
+
|
8
|
+
var _inversify = require("inversify");
|
9
|
+
|
10
|
+
var _AppLogger = require("../../log/AppLogger");
|
11
|
+
|
12
|
+
var _Path = require("../../fs/path/Path");
|
13
|
+
|
14
|
+
var _StorageBackendProvider = require("../../storage/StorageBackendProvider");
|
15
|
+
|
16
|
+
var _dec, _dec2, _dec3, _class;
|
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
|
+
let DataBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _StorageBackendProvider.StorageBackendProvider === "undefined" ? Object : _StorageBackendProvider.StorageBackendProvider]), _dec(_class = _dec2(_class = _dec3(_class = class DataBackupController {
|
21
|
+
constructor(log, storageBackendProvider) {
|
22
|
+
this.log = log;
|
23
|
+
this.storageBackendProvider = storageBackendProvider;
|
24
|
+
|
25
|
+
_defineProperty(this, "storage", inp => {
|
26
|
+
const storage = this.storageBackendProvider.provide();
|
27
|
+
|
28
|
+
const path = _Path.AbsPath.from(inp.path);
|
29
|
+
|
30
|
+
return {
|
31
|
+
backup: () => {
|
32
|
+
this.log.info(`Backing up from ${path} to '${storage}'`);
|
33
|
+
storage.store(path);
|
34
|
+
},
|
35
|
+
restore: () => {
|
36
|
+
this.log.info(`Restoring from '${storage}' to ${path}`);
|
37
|
+
storage.restore(path);
|
38
|
+
}
|
39
|
+
};
|
40
|
+
});
|
41
|
+
}
|
42
|
+
|
43
|
+
}) || _class) || _class) || _class);
|
44
|
+
exports.DataBackupController = DataBackupController;
|
@@ -0,0 +1,110 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.PgBackupController = void 0;
|
7
|
+
|
8
|
+
var _Fs = require("../../fs/Fs");
|
9
|
+
|
10
|
+
var _inversify = require("inversify");
|
11
|
+
|
12
|
+
var _AppLogger = require("../../log/AppLogger");
|
13
|
+
|
14
|
+
var _Shell = require("../../tools/shell/Shell");
|
15
|
+
|
16
|
+
var _Path = require("../../fs/path/Path");
|
17
|
+
|
18
|
+
var _StorageBackendProvider = require("../../storage/StorageBackendProvider");
|
19
|
+
|
20
|
+
var _dec, _dec2, _dec3, _class, _class2, _temp;
|
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 parseConnectionUrl = url => {
|
25
|
+
var _url$match;
|
26
|
+
|
27
|
+
const regex = /postgres:\/\/(?<username>.*):(?<password>.*)@(?<host>.*):(?<port>\d*)$/;
|
28
|
+
const match = (_url$match = url.match(regex)) === null || _url$match === void 0 ? void 0 : _url$match.groups;
|
29
|
+
if (!match || !match.username || !match.password || !match.host || !match.port) throw new Error(`The Postgres connection URL does not match required regex: ${regex.toString()}`);
|
30
|
+
return {
|
31
|
+
username: match.username,
|
32
|
+
password: match.password,
|
33
|
+
host: match.host,
|
34
|
+
port: parseInt(match.port, 10)
|
35
|
+
};
|
36
|
+
};
|
37
|
+
|
38
|
+
let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger, typeof _StorageBackendProvider.StorageBackendProvider === "undefined" ? Object : _StorageBackendProvider.StorageBackendProvider, typeof _Shell.Shell === "undefined" ? Object : _Shell.Shell]), _dec(_class = _dec2(_class = _dec3(_class = (_temp = _class2 = class PgBackupController {
|
39
|
+
constructor( // private gpg: Gpg,
|
40
|
+
// private ssh: SshKeyManager,
|
41
|
+
fs, // private fsSyncer: FsSyncer,
|
42
|
+
log, storageBackendProvider, // @inject(AppConfig_) private cfg: Config,
|
43
|
+
sh) {
|
44
|
+
this.fs = fs;
|
45
|
+
this.log = log;
|
46
|
+
this.storageBackendProvider = storageBackendProvider;
|
47
|
+
this.sh = sh;
|
48
|
+
|
49
|
+
_defineProperty(this, "now", () => {
|
50
|
+
const d = new Date();
|
51
|
+
const fields = [d.getFullYear(), d.getMonth(), d.getDay(), d.getUTCHours(), d.getMinutes(), d.getSeconds()];
|
52
|
+
return fields.join('-');
|
53
|
+
});
|
54
|
+
|
55
|
+
_defineProperty(this, "getVersion", version => version || PgBackupController.defaultPgVersion);
|
56
|
+
|
57
|
+
_defineProperty(this, "backup", ({
|
58
|
+
pgUrl,
|
59
|
+
pgVersion
|
60
|
+
}) => {
|
61
|
+
const storage = this.storageBackendProvider.provide();
|
62
|
+
const version = this.getVersion(pgVersion); // TODO validate url
|
63
|
+
|
64
|
+
this.log.info(`Backing up Postgres database, version=${version}`);
|
65
|
+
const connParams = parseConnectionUrl(pgUrl);
|
66
|
+
const pass = connParams.password;
|
67
|
+
this.fs.inTmpDir('pg-backup', dir => {
|
68
|
+
const outputDir = _Path.AbsPath.from(dir);
|
69
|
+
|
70
|
+
this.log.info('Dumping database to a file');
|
71
|
+
const bashCmds = [`echo "*:*:*:*:${pass}" > ~/.pgpass`, `chmod 400 ~/.pgpass`, `pg_dumpall -d ${pgUrl}`]; // TODO improve sh logging to avoid showing secrets
|
72
|
+
|
73
|
+
const b = this.sh.execAndReturnBuffer(`docker run --network host ` + `postgres:${version} ` + `bash -c '${bashCmds.join(' && ')}'`);
|
74
|
+
const outFileName = PgBackupController.outFileName;
|
75
|
+
const dumpOut = outputDir.resolve(outFileName);
|
76
|
+
this.fs.writeFile(dumpOut, b);
|
77
|
+
this.log.info('Compressing');
|
78
|
+
const zipOutputName = `pg-backup-${this.now()}.zip`;
|
79
|
+
this.sh.exec(`cd ${outputDir.str()}; zip ${zipOutputName} ${outFileName}`);
|
80
|
+
this.fs.rmFile(dumpOut);
|
81
|
+
this.log.info('Uploading');
|
82
|
+
storage.store(_Path.AbsPath.from(dir));
|
83
|
+
});
|
84
|
+
});
|
85
|
+
|
86
|
+
_defineProperty(this, "restore", ({
|
87
|
+
pgUrl,
|
88
|
+
pgVersion
|
89
|
+
}) => {
|
90
|
+
const version = this.getVersion(pgVersion);
|
91
|
+
const storage = this.storageBackendProvider.provide(); // To validate
|
92
|
+
|
93
|
+
parseConnectionUrl(pgUrl);
|
94
|
+
this.log.info(`Restoring Postgres database, version=${version}`);
|
95
|
+
this.fs.inTmpDir('pg-restore', dirStr => {
|
96
|
+
const dir = _Path.AbsPath.from(dirStr);
|
97
|
+
|
98
|
+
storage.restore(dir);
|
99
|
+
const zipFile = this.fs.listFiles(dir)[0]; //
|
100
|
+
|
101
|
+
this.sh.exec(`unzip ${zipFile.str()} -d ${dir.str()}`);
|
102
|
+
this.fs.rmFile(zipFile);
|
103
|
+
const outFile = this.fs.listFiles(dir)[0];
|
104
|
+
this.sh.exec(`docker run --network host ` + `-v "${outFile.str()}":/pg/import.sql -w /pg ` + `postgres:${version} ` + `psql ${pgUrl} -v ON_ERROR_STOP=0 -f import.sql`);
|
105
|
+
});
|
106
|
+
});
|
107
|
+
}
|
108
|
+
|
109
|
+
}, _defineProperty(_class2, "defaultPgVersion", '14.2'), _defineProperty(_class2, "outFileName", 'out.sql'), _temp)) || _class) || _class) || _class);
|
110
|
+
exports.PgBackupController = PgBackupController;
|
package/lib/cli/app.js
CHANGED
@@ -13,68 +13,108 @@ var _Config = require("../config/Config");
|
|
13
13
|
|
14
14
|
var _Context = require("../ctx/Context");
|
15
15
|
|
16
|
-
var _AggregateBackupController = require("../bup/AggregateBackupController");
|
16
|
+
var _AggregateBackupController = require("../bup/aggregate/AggregateBackupController");
|
17
17
|
|
18
18
|
var _ResticController = require("../storage/restic/ResticController");
|
19
19
|
|
20
|
-
var
|
20
|
+
var _DataBackupController = require("../bup/dir/DataBackupController");
|
21
21
|
|
22
|
-
var
|
22
|
+
var _PgBackupController = require("../bup/pg/PgBackupController");
|
23
|
+
|
24
|
+
const storageNameOpt = {
|
25
|
+
name: 'storage-name',
|
26
|
+
convertCase: true,
|
27
|
+
fromConfig: 'storage.name'
|
28
|
+
};
|
29
|
+
const singleStorageOptions = [storageNameOpt, {
|
30
|
+
name: 'storage-type',
|
31
|
+
convertCase: true,
|
32
|
+
fromConfig: 'storage.type'
|
33
|
+
}, {
|
34
|
+
name: 'storage-repo',
|
35
|
+
convertCase: true,
|
36
|
+
fromConfig: 'storage.repo'
|
37
|
+
}, {
|
38
|
+
name: 'storage-password',
|
39
|
+
convertCase: true,
|
40
|
+
fromConfig: 'storage.password'
|
41
|
+
}, {
|
42
|
+
name: 'storage-credentials',
|
43
|
+
convertCase: true,
|
44
|
+
fromConfig: 'storage.credentials'
|
45
|
+
}];
|
23
46
|
|
24
47
|
const createApp = () => _nclif.CliApp.of({
|
25
48
|
commands: {
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
}
|
46
|
-
run: (a, c) => c.get(_BackupController.BackupController).restore(a.storageName, _Path.AbsPath.from(a.path))
|
49
|
+
data: (0, _nclif.cmdGroup)({
|
50
|
+
options: singleStorageOptions,
|
51
|
+
commands: {
|
52
|
+
backup: (0, _nclif.cmd)({
|
53
|
+
description: 'Backup a dir to the given storage',
|
54
|
+
positionals: [{
|
55
|
+
name: 'path',
|
56
|
+
required: true
|
57
|
+
}],
|
58
|
+
run: (cmd, c) => c.get(_DataBackupController.DataBackupController).storage(cmd).backup()
|
59
|
+
}),
|
60
|
+
restore: (0, _nclif.cmd)({
|
61
|
+
description: 'Restore the storage to the given dir',
|
62
|
+
positionals: [{
|
63
|
+
name: 'path',
|
64
|
+
required: true
|
65
|
+
}],
|
66
|
+
run: (cmd, c) => c.get(_DataBackupController.DataBackupController).storage(cmd).restore()
|
67
|
+
})
|
68
|
+
}
|
47
69
|
}),
|
48
70
|
aggr: (0, _nclif.cmdGroup)({
|
49
71
|
commands: {
|
50
72
|
backup: (0, _nclif.cmd)({
|
51
73
|
description: 'Run backup',
|
74
|
+
options: [storageNameOpt],
|
52
75
|
positionals: [{
|
53
76
|
name: 'aggregateName',
|
54
77
|
required: true
|
55
|
-
}, {
|
56
|
-
name: 'storageName',
|
57
|
-
required: true
|
58
78
|
}],
|
59
|
-
run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName
|
79
|
+
run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName)
|
60
80
|
})
|
61
81
|
}
|
62
82
|
}),
|
63
|
-
restic
|
83
|
+
restic,
|
84
|
+
pg
|
64
85
|
}
|
65
|
-
}).addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
|
66
|
-
config
|
67
|
-
|
86
|
+
}).envConfig('NODEBUP').addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
|
87
|
+
config,
|
88
|
+
inputs
|
89
|
+
}) => (0, _Context.createContext)(config, inputs));
|
68
90
|
|
69
91
|
exports.createApp = createApp;
|
70
92
|
const restic = (0, _nclif.cmdGroup)({
|
71
93
|
commands: {
|
72
94
|
'init-repo': (0, _nclif.cmd)({
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
95
|
+
options: singleStorageOptions,
|
96
|
+
run: (_, c) => c.get(_ResticController.ResticController).initRepo()
|
97
|
+
})
|
98
|
+
}
|
99
|
+
});
|
100
|
+
const pg = (0, _nclif.cmdGroup)({
|
101
|
+
options: [...singleStorageOptions, {
|
102
|
+
name: 'pg-url',
|
103
|
+
convertCase: true,
|
104
|
+
fromConfig: 'pg.url',
|
105
|
+
required: true
|
106
|
+
}, {
|
107
|
+
name: 'pg-version',
|
108
|
+
convertCase: true,
|
109
|
+
fromConfig: 'pg.version',
|
110
|
+
description: `Postgres version - default is ${_PgBackupController.PgBackupController.defaultPgVersion}`
|
111
|
+
}],
|
112
|
+
commands: {
|
113
|
+
backup: (0, _nclif.cmd)({
|
114
|
+
run: (inp, c) => c.get(_PgBackupController.PgBackupController).backup(inp)
|
115
|
+
}),
|
116
|
+
restore: (0, _nclif.cmd)({
|
117
|
+
run: (inp, c) => c.get(_PgBackupController.PgBackupController).restore(inp)
|
78
118
|
})
|
79
119
|
}
|
80
120
|
});
|
package/lib/config/Config.js
CHANGED
@@ -13,12 +13,11 @@ var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
|
|
13
13
|
|
14
14
|
var _expandAndCreatePath = require("./expandAndCreatePath");
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
});
|
16
|
+
var _Fs = require("../fs/Fs");
|
17
|
+
|
18
|
+
var _AppLogger = require("../log/AppLogger");
|
19
|
+
|
20
|
+
const expandPaths = (cfg, exp) => {
|
22
21
|
cfg.aggregates.forEach(a => {
|
23
22
|
a.sources.file.forEach(f => {
|
24
23
|
f.from = exp(f.from);
|
@@ -31,15 +30,27 @@ const expandPaths = (cfg, exp, cwd) => {
|
|
31
30
|
if (f.to) f.to = _Path.RelativePath.from(f.to);
|
32
31
|
});
|
33
32
|
});
|
33
|
+
}; // TODO
|
34
|
+
|
35
|
+
|
36
|
+
const readFromFile = cwd => {
|
37
|
+
// TODO get from the context, maybe move FS to CLI framework later?
|
38
|
+
const fs = new _Fs.Fs(cwd, new _AppLogger.AppLogger());
|
39
|
+
const cfgPath = cwd.resolve('bup.yaml');
|
40
|
+
const cfg = fs.exists(cfgPath) ? (0, _readYaml.readYaml)(cfgPath.str()) : {
|
41
|
+
aggregates: [],
|
42
|
+
storage: []
|
43
|
+
};
|
44
|
+
(0, _validateConfigAgainstSchema.validateConfigAgainstSchema)(cfg);
|
45
|
+
return cfg;
|
34
46
|
};
|
35
47
|
|
36
48
|
const readConfig = pwdStr => {
|
37
49
|
const cwd = _Path.AbsPath.from(pwdStr);
|
38
50
|
|
39
|
-
const
|
40
|
-
|
41
|
-
|
42
|
-
cfg.pwd = cwd;
|
51
|
+
const cfg = { ...readFromFile(cwd),
|
52
|
+
pwd: cwd
|
53
|
+
};
|
43
54
|
cfg.aggregates.forEach(t => {
|
44
55
|
const {
|
45
56
|
sources
|
@@ -54,7 +65,7 @@ const readConfig = pwdStr => {
|
|
54
65
|
return p;
|
55
66
|
};
|
56
67
|
|
57
|
-
expandPaths(cfg, expand
|
68
|
+
expandPaths(cfg, expand);
|
58
69
|
return cfg;
|
59
70
|
};
|
60
71
|
|
@@ -9,12 +9,14 @@ var _Path = require("../fs/path/Path");
|
|
9
9
|
|
10
10
|
var _os = _interopRequireDefault(require("os"));
|
11
11
|
|
12
|
+
var _path = _interopRequireDefault(require("path"));
|
13
|
+
|
12
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
13
15
|
|
14
16
|
const expandStrPath = (path, pwd) => {
|
15
17
|
path = path.replace('<cwd>', pwd.str());
|
16
18
|
path = path.replace('~', _os.default.homedir());
|
17
|
-
return path;
|
19
|
+
return _path.default.resolve(path);
|
18
20
|
};
|
19
21
|
|
20
22
|
exports.expandStrPath = expandStrPath;
|
package/lib/config/index.js
CHANGED
@@ -2,12 +2,4 @@
|
|
2
2
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
4
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 = {}));
|
5
|
+
});
|
@@ -7,7 +7,7 @@ exports.validateConfigAgainstSchema = void 0;
|
|
7
7
|
|
8
8
|
var _joi = _interopRequireDefault(require("@hapi/joi"));
|
9
9
|
|
10
|
-
var
|
10
|
+
var _StoreConfig = require("../storage/StoreConfig");
|
11
11
|
|
12
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
13
13
|
|
@@ -23,21 +23,6 @@ const KeyTaskSchema = _joi.default.object({
|
|
23
23
|
to: _joi.default.string().optional()
|
24
24
|
});
|
25
25
|
|
26
|
-
const createStorageSchema = props => _joi.default.object({
|
27
|
-
name: _joi.default.string(),
|
28
|
-
type: _joi.default.string().valid(_index.StorageTypeConst.Restic, _index.StorageTypeConst.RClone),
|
29
|
-
...props
|
30
|
-
});
|
31
|
-
|
32
|
-
const FileStorageSchema = createStorageSchema({
|
33
|
-
path: _joi.default.string()
|
34
|
-
});
|
35
|
-
const ResticStorageSchema = createStorageSchema({
|
36
|
-
repo: _joi.default.string(),
|
37
|
-
credentialsFile: _joi.default.string().optional(),
|
38
|
-
passwordFile: _joi.default.string()
|
39
|
-
});
|
40
|
-
|
41
26
|
const Sources = _joi.default.object({
|
42
27
|
dir: _joi.default.array().items(DirTaskSchema).optional(),
|
43
28
|
file: _joi.default.array().items(FileTaskSchema).optional(),
|
@@ -52,7 +37,7 @@ const Aggregate = _joi.default.object({
|
|
52
37
|
|
53
38
|
const Schema = () => _joi.default.object({
|
54
39
|
// TODO undescriptive error msg. Invalid config."storage[0]" does not match any of the allowed types
|
55
|
-
storage: _joi.default.array().items(
|
40
|
+
storage: _joi.default.array().items((0, _StoreConfig.NamedStorageSchema)()),
|
56
41
|
aggregates: _joi.default.array().items(Aggregate)
|
57
42
|
});
|
58
43
|
|
package/lib/ctx/Context.js
CHANGED
@@ -19,7 +19,7 @@ var _inversify = require("inversify");
|
|
19
19
|
|
20
20
|
var _ContextSymbols = require("./ContextSymbols");
|
21
21
|
|
22
|
-
var _AggregateBackupController = require("../bup/AggregateBackupController");
|
22
|
+
var _AggregateBackupController = require("../bup/aggregate/AggregateBackupController");
|
23
23
|
|
24
24
|
var _AppLogger = require("../log/AppLogger");
|
25
25
|
|
@@ -33,15 +33,21 @@ var _B2CredentialsProvider = require("../storage/b2/B2CredentialsProvider");
|
|
33
33
|
|
34
34
|
var _RCloneClientFactory = require("../storage/rclone/RCloneClientFactory");
|
35
35
|
|
36
|
-
var
|
36
|
+
var _DataBackupController = require("../bup/dir/DataBackupController");
|
37
37
|
|
38
|
-
|
38
|
+
var _StorageConfigProvider = require("../storage/StorageConfigProvider");
|
39
|
+
|
40
|
+
var _PgBackupController = require("../bup/pg/PgBackupController");
|
41
|
+
|
42
|
+
const createContext = (cfg, inp) => {
|
39
43
|
const c = new _inversify.Container();
|
40
44
|
c.bind(_ContextSymbols.AppConfig_).toConstantValue(cfg);
|
45
|
+
c.bind(_ContextSymbols.Inputs_).toConstantValue(inp);
|
41
46
|
const log = new _AppLogger.AppLogger();
|
42
47
|
c.bind(_ContextSymbols.Log_).toConstantValue(log);
|
43
48
|
c.bind(_AppLogger.AppLogger).toConstantValue(log);
|
44
|
-
|
49
|
+
c.bind(_Fs.Fs).toConstantValue(new _Fs.Fs(cfg.pwd, log));
|
50
|
+
const self = [_Gpg.Gpg, _Shell.Shell, _FsSyncer.FsSyncer, _SshKeyManager.SshKeyManager, _StorageBackendProvider.StorageBackendProvider, _StorageConfigProvider.StorageConfigProvider, _ResticClientFactory.ResticClientFactory, _AggregateBackupController.AggregateBackupController, _ResticController.ResticController, _B2CredentialsProvider.B2CredentialsProvider, _RCloneClientFactory.RCloneClientFactory, _DataBackupController.DataBackupController, _PgBackupController.PgBackupController];
|
45
51
|
self.forEach(s => c.bind(s).toSelf());
|
46
52
|
return c;
|
47
53
|
};
|
@@ -3,7 +3,7 @@
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
4
4
|
value: true
|
5
5
|
});
|
6
|
-
exports.TaskConfig_ = exports.StorageConfig_ = exports.Log_ = exports.AppConfig_ = void 0;
|
6
|
+
exports.TaskConfig_ = exports.StorageConfig_ = exports.Log_ = exports.Inputs_ = exports.AppConfig_ = void 0;
|
7
7
|
const Log_ = Symbol.for('Log');
|
8
8
|
exports.Log_ = Log_;
|
9
9
|
const TaskConfig_ = Symbol.for('config.Tasks');
|
@@ -11,4 +11,6 @@ exports.TaskConfig_ = TaskConfig_;
|
|
11
11
|
const StorageConfig_ = Symbol.for('config.Storage');
|
12
12
|
exports.StorageConfig_ = StorageConfig_;
|
13
13
|
const AppConfig_ = Symbol.for('config.app');
|
14
|
-
exports.AppConfig_ = AppConfig_;
|
14
|
+
exports.AppConfig_ = AppConfig_;
|
15
|
+
const Inputs_ = Symbol.for('cmd.input');
|
16
|
+
exports.Inputs_ = Inputs_;
|
package/lib/fs/Fs.js
CHANGED
@@ -7,18 +7,27 @@ exports.Fs = void 0;
|
|
7
7
|
|
8
8
|
var _fs = _interopRequireDefault(require("fs"));
|
9
9
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
11
|
+
|
12
|
+
var _Path = require("./path/Path");
|
13
|
+
|
10
14
|
var _inversify = require("inversify");
|
11
15
|
|
12
16
|
var _AppLogger = require("../log/AppLogger");
|
13
17
|
|
18
|
+
var _expandAndCreatePath = require("../config/expandAndCreatePath");
|
19
|
+
|
20
|
+
var _os = _interopRequireDefault(require("os"));
|
21
|
+
|
14
22
|
var _dec, _dec2, _dec3, _class;
|
15
23
|
|
16
24
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17
25
|
|
18
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; }
|
19
27
|
|
20
|
-
let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger]), _dec(_class = _dec2(_class = _dec3(_class = class Fs {
|
21
|
-
constructor(log) {
|
28
|
+
let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _Path.AbsPath === "undefined" ? Object : _Path.AbsPath, typeof _AppLogger.AppLogger === "undefined" ? Object : _AppLogger.AppLogger]), _dec(_class = _dec2(_class = _dec3(_class = class Fs {
|
29
|
+
constructor(cwd, log) {
|
30
|
+
this.cwd = cwd;
|
22
31
|
this.log = log;
|
23
32
|
|
24
33
|
_defineProperty(this, "writeFile", (path, content) => {
|
@@ -42,6 +51,30 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
42
51
|
});
|
43
52
|
});
|
44
53
|
|
54
|
+
_defineProperty(this, "rmFile", path => {
|
55
|
+
_fs.default.rmSync(path.str());
|
56
|
+
});
|
57
|
+
|
58
|
+
_defineProperty(this, "listFiles", path => {
|
59
|
+
return _fs.default.readdirSync(path.str()).map(f => path.resolve(f));
|
60
|
+
});
|
61
|
+
|
62
|
+
_defineProperty(this, "inTmpDir", (name, withDir) => {
|
63
|
+
const dir = this.mkTmpDir(name);
|
64
|
+
|
65
|
+
try {
|
66
|
+
withDir(dir);
|
67
|
+
} finally {
|
68
|
+
this.rmDir(_Path.AbsPath.from(dir));
|
69
|
+
}
|
70
|
+
});
|
71
|
+
|
72
|
+
_defineProperty(this, "mkTmpDir", name => {
|
73
|
+
const dir = _fs.default.mkdtempSync(_path.default.resolve(_os.default.tmpdir(), name));
|
74
|
+
|
75
|
+
return dir;
|
76
|
+
});
|
77
|
+
|
45
78
|
_defineProperty(this, "ensureIsDir", p => {
|
46
79
|
if (!this.isDir(p)) throw new Error(`${p} is not a dir`);
|
47
80
|
});
|
@@ -75,6 +108,10 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
|
|
75
108
|
});
|
76
109
|
|
77
110
|
_defineProperty(this, "exists", path => _fs.default.existsSync(path.str()));
|
111
|
+
|
112
|
+
_defineProperty(this, "expand", strPath => (0, _expandAndCreatePath.expandAndCreatePath)(strPath, this.cwd));
|
113
|
+
|
114
|
+
_defineProperty(this, "expandStrPath", strPath => (0, _expandAndCreatePath.expandStrPath)(strPath, this.cwd));
|
78
115
|
}
|
79
116
|
|
80
117
|
}) || _class) || _class) || _class);
|
@@ -23,7 +23,7 @@ describe('FsSync', () => {
|
|
23
23
|
|
24
24
|
it('syncs dir', () => {
|
25
25
|
const l = new _AppLogger.AppLogger();
|
26
|
-
const sut = new _FsSyncer.FsSyncer(new _Shell.Shell(l), new _Fs.Fs(l), l);
|
26
|
+
const sut = new _FsSyncer.FsSyncer(new _Shell.Shell(l), new _Fs.Fs(_Path.AbsPath.from(__dirname), l), l);
|
27
27
|
sut.syncDirs(from, to);
|
28
28
|
expect((0, _areDirsSame.areDirsSame)(from, to)).toBe(true);
|
29
29
|
});
|
package/lib/fs/path/Path.js
CHANGED
@@ -24,7 +24,11 @@ class AbsPath {
|
|
24
24
|
return AbsPath.from(_path.default.resolve(this.path, '..'));
|
25
25
|
});
|
26
26
|
|
27
|
-
_defineProperty(this, "
|
27
|
+
_defineProperty(this, "basename", () => {
|
28
|
+
return _path.default.basename(this.path);
|
29
|
+
});
|
30
|
+
|
31
|
+
_defineProperty(this, "str", () => _path.default.resolve(this.path));
|
28
32
|
|
29
33
|
if (!_path.default.isAbsolute(path)) throw new Error(`Path ${path} must be absolute`);
|
30
34
|
}
|
@@ -7,9 +7,7 @@ exports.StorageBackendProvider = void 0;
|
|
7
7
|
|
8
8
|
var _inversify = require("inversify");
|
9
9
|
|
10
|
-
var
|
11
|
-
|
12
|
-
var _config = require("../config");
|
10
|
+
var _types = require("./types");
|
13
11
|
|
14
12
|
var _ResticClientFactory = require("./restic/ResticClientFactory");
|
15
13
|
|
@@ -17,29 +15,28 @@ var _BackupClientStorageBackend = require("./BackupClientStorageBackend");
|
|
17
15
|
|
18
16
|
var _RCloneClientFactory = require("./rclone/RCloneClientFactory");
|
19
17
|
|
20
|
-
var
|
18
|
+
var _StorageConfigProvider = require("./StorageConfigProvider");
|
19
|
+
|
20
|
+
var _dec, _dec2, _dec3, _class;
|
21
21
|
|
22
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
23
|
|
24
|
-
let StorageBackendProvider = (_dec = (0, _inversify.injectable)(), _dec2 =
|
25
|
-
|
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) {
|
24
|
+
let StorageBackendProvider = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [typeof _ResticClientFactory.ResticClientFactory === "undefined" ? Object : _ResticClientFactory.ResticClientFactory, typeof _RCloneClientFactory.RCloneClientFactory === "undefined" ? Object : _RCloneClientFactory.RCloneClientFactory, typeof _StorageConfigProvider.StorageConfigProvider === "undefined" ? Object : _StorageConfigProvider.StorageConfigProvider]), _dec(_class = _dec2(_class = _dec3(_class = class StorageBackendProvider {
|
25
|
+
constructor(resticFac, rcloneFac, cfgProvider) {
|
28
26
|
this.resticFac = resticFac;
|
29
27
|
this.rcloneFac = rcloneFac;
|
30
|
-
this.
|
28
|
+
this.cfgProvider = cfgProvider;
|
31
29
|
|
32
|
-
_defineProperty(this, "provide",
|
33
|
-
const s = this.
|
34
|
-
|
35
|
-
let b;
|
30
|
+
_defineProperty(this, "provide", () => {
|
31
|
+
const s = this.cfgProvider.provide();
|
32
|
+
let b = null;
|
36
33
|
|
37
34
|
switch (s.type) {
|
38
|
-
case
|
35
|
+
case _types.StorageType.Restic:
|
39
36
|
b = this.resticFac.create(s);
|
40
37
|
break;
|
41
38
|
|
42
|
-
case
|
39
|
+
case _types.StorageType.RClone:
|
43
40
|
b = this.rcloneFac.create(s);
|
44
41
|
break;
|
45
42
|
}
|
@@ -49,5 +46,5 @@ let StorageBackendProvider = (_dec = (0, _inversify.injectable)(), _dec2 = funct
|
|
49
46
|
});
|
50
47
|
}
|
51
48
|
|
52
|
-
}) || _class) || _class) || _class)
|
49
|
+
}) || _class) || _class) || _class);
|
53
50
|
exports.StorageBackendProvider = StorageBackendProvider;
|
@@ -0,0 +1,89 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.StorageConfigProvider = void 0;
|
7
|
+
|
8
|
+
var _inversify = require("inversify");
|
9
|
+
|
10
|
+
var _ContextSymbols = require("../ctx/ContextSymbols");
|
11
|
+
|
12
|
+
var _config = require("../config");
|
13
|
+
|
14
|
+
var _Fs = require("../fs/Fs");
|
15
|
+
|
16
|
+
var _InvalidInput = require("../cli/InvalidInput");
|
17
|
+
|
18
|
+
var _StoreConfig = require("./StoreConfig");
|
19
|
+
|
20
|
+
var _dec, _dec2, _dec3, _dec4, _dec5, _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 StorageConfigProvider = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
25
|
+
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 1);
|
26
|
+
}, _dec3 = function (target, key) {
|
27
|
+
return (0, _inversify.inject)(_ContextSymbols.Inputs_)(target, undefined, 2);
|
28
|
+
}, _dec4 = Reflect.metadata("design:type", Function), _dec5 = Reflect.metadata("design:paramtypes", [typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _config.Config === "undefined" ? Object : _config.Config, typeof StorageInputs === "undefined" ? Object : StorageInputs]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = _dec5(_class = class StorageConfigProvider {
|
29
|
+
constructor(fs, cfg, inputs) {
|
30
|
+
this.fs = fs;
|
31
|
+
this.cfg = cfg;
|
32
|
+
this.inputs = inputs;
|
33
|
+
|
34
|
+
_defineProperty(this, "provide", () => {
|
35
|
+
const {
|
36
|
+
storageName,
|
37
|
+
storagePassword,
|
38
|
+
storageRepo,
|
39
|
+
storageType
|
40
|
+
} = this.inputs;
|
41
|
+
let res;
|
42
|
+
|
43
|
+
if (storageName) {
|
44
|
+
if (storageType || storageRepo || storagePassword) throw new _InvalidInput.InvalidInput('Storage config provided. Storage name is therefore invalid input');
|
45
|
+
res = this.getByName(storageName);
|
46
|
+
} else {
|
47
|
+
res = this.getByCliConfig(this.inputs);
|
48
|
+
}
|
49
|
+
|
50
|
+
if (res.repo.startsWith('local:')) {
|
51
|
+
const path = res.repo.split('local:')[1];
|
52
|
+
res.repo = 'local:' + this.fs.expandStrPath(path);
|
53
|
+
}
|
54
|
+
|
55
|
+
return res;
|
56
|
+
});
|
57
|
+
|
58
|
+
_defineProperty(this, "getByCliConfig", inp => {
|
59
|
+
// // TODO unify validation usage
|
60
|
+
const r = (0, _StoreConfig.CliStorageConfigSchema)().validate(inp, {
|
61
|
+
presence: 'required',
|
62
|
+
allowUnknown: true
|
63
|
+
});
|
64
|
+
if (r.error) throw new _InvalidInput.InvalidInput(r.error.toString());
|
65
|
+
const cfg = inp;
|
66
|
+
return {
|
67
|
+
type: cfg.storageType,
|
68
|
+
repo: cfg.storageRepo,
|
69
|
+
encryptionPassword: cfg.storagePassword,
|
70
|
+
credentials: cfg.storageCredentials
|
71
|
+
};
|
72
|
+
});
|
73
|
+
|
74
|
+
_defineProperty(this, "getByName", name => {
|
75
|
+
const cfg = this.cfg.storage.find(s => s.name === name);
|
76
|
+
if (!cfg) throw new Error(`No such storage '${name}'`);
|
77
|
+
const encryptionPassword = cfg.passwordFile ? this.fs.readFile(this.fs.expand(cfg.passwordFile)).trim() : undefined;
|
78
|
+
const credentials = cfg.credentialsFile ? this.fs.readFile(this.fs.expand(cfg.credentialsFile)).trim() : undefined;
|
79
|
+
return {
|
80
|
+
type: cfg.type,
|
81
|
+
repo: cfg.repo,
|
82
|
+
credentials,
|
83
|
+
encryptionPassword
|
84
|
+
};
|
85
|
+
});
|
86
|
+
}
|
87
|
+
|
88
|
+
}) || _class) || _class) || _class) || _class) || _class);
|
89
|
+
exports.StorageConfigProvider = StorageConfigProvider;
|
@@ -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().allow(null).optional(),
|
29
|
+
storagePassword: _joi.default.string().allow(null).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;
|
@@ -36,11 +36,15 @@ let B2CredentialsProvider = (_dec = (0, _inversify.injectable)(), _dec2 = Reflec
|
|
36
36
|
this.fs = fs;
|
37
37
|
|
38
38
|
_defineProperty(this, "fromFile", path => {
|
39
|
-
|
39
|
+
return this.fromString(this.fs.readFile(path));
|
40
|
+
});
|
41
|
+
|
42
|
+
_defineProperty(this, "fromString", str => {
|
43
|
+
const credJson = JSON.parse(str);
|
40
44
|
const validationErr = validateCredentials(credJson);
|
41
45
|
|
42
46
|
if (validationErr) {
|
43
|
-
throw new Error(`Invalid B2 credential in ${
|
47
|
+
throw new Error(`Invalid B2 credential in '${str.substring(0, 3)}...': ${validationErr}`);
|
44
48
|
}
|
45
49
|
|
46
50
|
const creds = credJson;
|
@@ -48,15 +48,15 @@ let RCloneClientFactory = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.
|
|
48
48
|
type,
|
49
49
|
location
|
50
50
|
};
|
51
|
-
const password = cfg.
|
51
|
+
const password = cfg.encryptionPassword;
|
52
52
|
if (password) props.encryption = {
|
53
53
|
password
|
54
54
|
};
|
55
|
-
const creds = cfg.
|
55
|
+
const creds = cfg.credentials;
|
56
56
|
|
57
57
|
if (type === 'b2') {
|
58
|
-
if (!creds) throw new Error(`Credentials are required for '${cfg.
|
59
|
-
props.creds = creds;
|
58
|
+
if (!creds) throw new Error(`Credentials are required for RClone storage repo='${cfg.repo}'`);
|
59
|
+
props.creds = this.b2.fromString(creds);
|
60
60
|
} else {
|
61
61
|
if (creds) throw new Error(`Credentials not supported for local rclone`);
|
62
62
|
}
|
@@ -76,7 +76,7 @@ let RcloneClient = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadat
|
|
76
76
|
return res;
|
77
77
|
});
|
78
78
|
|
79
|
-
_defineProperty(this, "obscure", str => this.shell.
|
79
|
+
_defineProperty(this, "obscure", str => this.shell.execAndReturnString(`rclone obscure ${str}`));
|
80
80
|
}
|
81
81
|
|
82
82
|
get actualRemoteName() {
|
@@ -29,20 +29,20 @@ let ResticClientFactory = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.
|
|
29
29
|
this.b2 = b2;
|
30
30
|
|
31
31
|
_defineProperty(this, "create", cfg => {
|
32
|
-
|
32
|
+
if (!cfg.encryptionPassword) throw new Error(`Restic needs encryptionPassword. Repo=${cfg.repo}`);
|
33
33
|
const props = {
|
34
34
|
repo: {
|
35
|
-
password,
|
35
|
+
password: cfg.encryptionPassword,
|
36
36
|
url: cfg.repo
|
37
37
|
}
|
38
38
|
};
|
39
|
-
const creds = cfg.
|
39
|
+
const creds = cfg.credentials;
|
40
40
|
|
41
41
|
if (cfg.repo.startsWith('b2:')) {
|
42
|
-
if (!creds) throw new Error(`Credentials must be provided for b2 repo in '${
|
43
|
-
props.backblaze = creds;
|
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
44
|
} else {
|
45
|
-
if (creds) throw new Error('Credentials can be
|
45
|
+
if (creds) throw new Error('Credentials can be set only for the B2 restic backend');
|
46
46
|
}
|
47
47
|
|
48
48
|
return new _ResticClient.ResticClient(props, this.sh, this.log);
|
@@ -15,23 +15,28 @@ 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",
|
31
|
-
const
|
32
|
-
if (
|
33
|
-
this.log.info('Initializing repo',
|
34
|
-
this.restFact.create(
|
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 = {}));
|
package/lib/tools/gpg/Gpg.js
CHANGED
@@ -25,7 +25,7 @@ let Gpg = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design
|
|
25
25
|
});
|
26
26
|
|
27
27
|
_defineProperty(this, "exportKey", id => {
|
28
|
-
const res = this.sh.
|
28
|
+
const res = this.sh.execAndReturnString(`gpg --batch --pinentry-mode=loopback --pinentry-mode=loopback --yes --passphrase foo123 --export-secret-key --armor ${id}`);
|
29
29
|
if (!res) throw new Error(`Key '${id}' not in keyring`);
|
30
30
|
return res;
|
31
31
|
});
|
package/lib/tools/shell/Shell.js
CHANGED
@@ -22,8 +22,7 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("desi
|
|
22
22
|
|
23
23
|
_defineProperty(this, "exec", (cmd, opts = {}) => {
|
24
24
|
this.log.debug(cmd);
|
25
|
-
(0, _shellCmd.shellCmd)(cmd, { ...opts
|
26
|
-
returnStdout: false
|
25
|
+
(0, _shellCmd.shellCmd)(cmd, { ...opts
|
27
26
|
});
|
28
27
|
});
|
29
28
|
|
@@ -34,15 +33,22 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("desi
|
|
34
33
|
}) => {
|
35
34
|
this.log.debug(`<stdin> > ${cmd}`);
|
36
35
|
(0, _shellCmd.shellCmd)(cmd, { ...opts,
|
37
|
-
stdin
|
38
|
-
|
36
|
+
stdin
|
37
|
+
});
|
38
|
+
});
|
39
|
+
|
40
|
+
_defineProperty(this, "execAndReturnString", (cmd, ops = {}) => {
|
41
|
+
this.log.debug(cmd, '[stdout consumed]');
|
42
|
+
return (0, _shellCmd.shellCmd)(cmd, {
|
43
|
+
returnStdout: 'string',
|
44
|
+
...ops
|
39
45
|
});
|
40
46
|
});
|
41
47
|
|
42
|
-
_defineProperty(this, "
|
48
|
+
_defineProperty(this, "execAndReturnBuffer", (cmd, ops = {}) => {
|
43
49
|
this.log.debug(cmd, '[stdout consumed]');
|
44
50
|
return (0, _shellCmd.shellCmd)(cmd, {
|
45
|
-
returnStdout:
|
51
|
+
returnStdout: 'buffer',
|
46
52
|
...ops
|
47
53
|
});
|
48
54
|
});
|
@@ -16,10 +16,16 @@ const shellCmd = (cmd, {
|
|
16
16
|
const res = (0, _child_process.execSync)(cmd, {
|
17
17
|
cwd,
|
18
18
|
input: stdin,
|
19
|
-
stdio: [stdin ? undefined : 'inherit', returnStdout ? undefined : 'inherit', 'inherit'
|
19
|
+
stdio: [stdin ? undefined : 'inherit', returnStdout ? undefined : 'inherit', 'pipe' // TODO changed from 'inherit' as the message is not shown in the console - find out how to catch the stderr output
|
20
|
+
],
|
20
21
|
env
|
21
22
|
});
|
22
|
-
|
23
|
+
|
24
|
+
if (returnStdout) {
|
25
|
+
if (returnStdout === 'string') return res.toString().trim();
|
26
|
+
return res;
|
27
|
+
}
|
28
|
+
|
23
29
|
return null;
|
24
30
|
};
|
25
31
|
|
@@ -6,9 +6,11 @@ var _Fs = require("../../fs/Fs");
|
|
6
6
|
|
7
7
|
var _AppLogger = require("../../log/AppLogger");
|
8
8
|
|
9
|
+
var _Path = require("../../fs/path/Path");
|
10
|
+
|
9
11
|
// TODO setup ci to make tests work
|
10
12
|
it.skip('works', () => {
|
11
|
-
const sut = new _SshKeyManager.SshKeyManager(new _Fs.Fs(new _AppLogger.AppLogger()));
|
13
|
+
const sut = new _SshKeyManager.SshKeyManager(new _Fs.Fs(_Path.AbsPath.from(__dirname), new _AppLogger.AppLogger()));
|
12
14
|
const key = sut.exportKey('id_rsa');
|
13
15
|
console.log('key', key);
|
14
16
|
});
|
package/package.json
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ps-aux/nodebup",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.7.1",
|
4
4
|
"description": "",
|
5
5
|
"module": "lib/index.js",
|
6
6
|
"main": "lib/index.js",
|
7
7
|
"scripts": {
|
8
8
|
"build": "rm -rf build && babel --extensions '.ts,.js,.md' src -d lib src",
|
9
|
+
"publish:local": "npm run build && chmod +x lib/cli/bin.js && npm link",
|
9
10
|
"pub": "npm publish --access public",
|
10
11
|
"test": "jest",
|
12
|
+
"test:ci": "jest --group=-no-ci",
|
11
13
|
"tc": "tsc --noEmit",
|
12
14
|
"format": "prettier \"**/*.{js,ts,tsx}\" --write",
|
13
15
|
"build-n-run": "npm run build && ",
|
@@ -41,12 +43,14 @@
|
|
41
43
|
"@types/jest": "^27.4.0",
|
42
44
|
"@types/jest-when": "^2.7.4",
|
43
45
|
"@types/node": "^17.0.6",
|
46
|
+
"@types/pg": "^8.6.5",
|
44
47
|
"@types/ramda": "^0.27.62",
|
45
48
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
46
49
|
"@typescript-eslint/parser": "^5.8.1",
|
47
50
|
"babel-plugin-module-resolver": "^4.1.0",
|
48
51
|
"babel-plugin-transform-typescript-metadata": "^0.3.2",
|
49
52
|
"dir-compare": "^4.0.0",
|
53
|
+
"docker-compose": "^0.23.17",
|
50
54
|
"eslint": "^8.6.0",
|
51
55
|
"eslint-config-standard": "^16.0.3",
|
52
56
|
"eslint-plugin-import": "^2.25.3",
|
@@ -57,9 +61,11 @@
|
|
57
61
|
"husky": "^7.0.4",
|
58
62
|
"jest": "^27.4.5",
|
59
63
|
"jest-extended": "^1.2.0",
|
64
|
+
"jest-runner-groups": "^2.2.0",
|
60
65
|
"jest-when": "^3.5.0",
|
61
66
|
"lint-staged": "^12.1.4",
|
62
67
|
"npm-check-updates": "^12.0.5",
|
68
|
+
"pg": "^8.7.3",
|
63
69
|
"prettier": "^2.5.1",
|
64
70
|
"ts-jest": "^27.1.2",
|
65
71
|
"typescript": "^4.5.4"
|
@@ -72,7 +78,7 @@
|
|
72
78
|
},
|
73
79
|
"dependencies": {
|
74
80
|
"@hapi/joi": "^17.1.1",
|
75
|
-
"@ps-aux/nclif": "^0.0.
|
81
|
+
"@ps-aux/nclif": "^0.0.7-alpha.1",
|
76
82
|
"@types/hapi__joi": "^17.1.8",
|
77
83
|
"axios": "^0.24.0",
|
78
84
|
"handlebars": "^4.7.7",
|
@@ -1,56 +0,0 @@
|
|
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;
|