@ps-aux/nodebup 0.4.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/bup/{AggregateBackupController.js → aggregate/AggregateBackupController.js} +13 -13
- 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 +81 -16
- package/lib/config/Config.js +22 -14
- package/lib/config/expandAndCreatePath.js +3 -1
- package/lib/config/index.js +1 -10
- package/lib/config/validateConfigAgainstSchema.js +2 -17
- package/lib/config/validateConfigAgainstSchema.spec.js +0 -4
- package/lib/ctx/Context.js +11 -3
- 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/log/AppLogger.js +6 -4
- package/lib/storage/BackupClientStorageBackend.js +4 -0
- package/lib/storage/StorageBackendProvider.js +16 -43
- 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/rclone/RcloneClient.spec.js +1 -3
- package/lib/storage/restic/ResticClient.spec.js +1 -3
- 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/gpg/Gpg.spec.js +3 -3
- 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/storage/local-file/LocalFileStorageBackend.js +0 -22
- 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
|
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
|
|
@@ -32,16 +32,16 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
32
32
|
let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = function (target, key) {
|
33
33
|
return (0, _inversify.inject)(_ContextSymbols.AppConfig_)(target, undefined, 6);
|
34
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,
|
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,20 +13,57 @@ 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 _DataBackupController = require("../bup/dir/DataBackupController");
|
21
|
+
|
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
|
+
}];
|
46
|
+
|
20
47
|
const createApp = () => _nclif.CliApp.of({
|
21
48
|
commands: {
|
22
|
-
|
49
|
+
data: (0, _nclif.cmdGroup)({
|
50
|
+
options: singleStorageOptions,
|
23
51
|
commands: {
|
24
|
-
|
52
|
+
backup: (0, _nclif.cmd)({
|
53
|
+
description: 'Backup a dir to the given storage',
|
25
54
|
positionals: [{
|
26
|
-
name: '
|
55
|
+
name: 'path',
|
27
56
|
required: true
|
28
57
|
}],
|
29
|
-
run: (
|
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()
|
30
67
|
})
|
31
68
|
}
|
32
69
|
}),
|
@@ -34,22 +71,50 @@ const createApp = () => _nclif.CliApp.of({
|
|
34
71
|
commands: {
|
35
72
|
backup: (0, _nclif.cmd)({
|
36
73
|
description: 'Run backup',
|
74
|
+
options: [storageNameOpt],
|
37
75
|
positionals: [{
|
38
76
|
name: 'aggregateName',
|
39
|
-
type: 'string',
|
40
|
-
required: true
|
41
|
-
}, {
|
42
|
-
name: 'storageName',
|
43
|
-
type: 'string',
|
44
77
|
required: true
|
45
78
|
}],
|
46
|
-
run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName
|
79
|
+
run: (a, c) => c.get(_AggregateBackupController.AggregateBackupController).backup(a.aggregateName)
|
47
80
|
})
|
48
81
|
}
|
49
|
-
})
|
82
|
+
}),
|
83
|
+
restic,
|
84
|
+
pg
|
50
85
|
}
|
51
|
-
}).addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
|
52
|
-
config
|
53
|
-
|
86
|
+
}).envConfig('NODEBUP').addObjectConfig(pwd => (0, _Config.readConfig)(pwd)).context(({
|
87
|
+
config,
|
88
|
+
inputs
|
89
|
+
}) => (0, _Context.createContext)(config, inputs));
|
54
90
|
|
55
|
-
exports.createApp = createApp;
|
91
|
+
exports.createApp = createApp;
|
92
|
+
const restic = (0, _nclif.cmdGroup)({
|
93
|
+
commands: {
|
94
|
+
'init-repo': (0, _nclif.cmd)({
|
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)
|
118
|
+
})
|
119
|
+
}
|
120
|
+
});
|
package/lib/config/Config.js
CHANGED
@@ -13,15 +13,11 @@ var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
|
|
13
13
|
|
14
14
|
var _expandAndCreatePath = require("./expandAndCreatePath");
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
});
|
16
|
+
var _Fs = require("../fs/Fs");
|
17
|
+
|
18
|
+
var _AppLogger = require("../log/AppLogger");
|
19
|
+
|
20
|
+
const expandPaths = (cfg, exp) => {
|
25
21
|
cfg.aggregates.forEach(a => {
|
26
22
|
a.sources.file.forEach(f => {
|
27
23
|
f.from = exp(f.from);
|
@@ -34,15 +30,27 @@ const expandPaths = (cfg, exp, cwd) => {
|
|
34
30
|
if (f.to) f.to = _Path.RelativePath.from(f.to);
|
35
31
|
});
|
36
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;
|
37
46
|
};
|
38
47
|
|
39
48
|
const readConfig = pwdStr => {
|
40
49
|
const cwd = _Path.AbsPath.from(pwdStr);
|
41
50
|
|
42
|
-
const
|
43
|
-
|
44
|
-
|
45
|
-
cfg.pwd = cwd;
|
51
|
+
const cfg = { ...readFromFile(cwd),
|
52
|
+
pwd: cwd
|
53
|
+
};
|
46
54
|
cfg.aggregates.forEach(t => {
|
47
55
|
const {
|
48
56
|
sources
|
@@ -57,7 +65,7 @@ const readConfig = pwdStr => {
|
|
57
65
|
return p;
|
58
66
|
};
|
59
67
|
|
60
|
-
expandPaths(cfg, expand
|
68
|
+
expandPaths(cfg, expand);
|
61
69
|
return cfg;
|
62
70
|
};
|
63
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,13 +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["File"] = "file";
|
13
|
-
StorageTypeConst["RClone"] = "rclone";
|
14
|
-
})(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.File, _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,13 +33,21 @@ var _B2CredentialsProvider = require("../storage/b2/B2CredentialsProvider");
|
|
33
33
|
|
34
34
|
var _RCloneClientFactory = require("../storage/rclone/RCloneClientFactory");
|
35
35
|
|
36
|
-
|
36
|
+
var _DataBackupController = require("../bup/dir/DataBackupController");
|
37
|
+
|
38
|
+
var _StorageConfigProvider = require("../storage/StorageConfigProvider");
|
39
|
+
|
40
|
+
var _PgBackupController = require("../bup/pg/PgBackupController");
|
41
|
+
|
42
|
+
const createContext = (cfg, inp) => {
|
37
43
|
const c = new _inversify.Container();
|
38
44
|
c.bind(_ContextSymbols.AppConfig_).toConstantValue(cfg);
|
45
|
+
c.bind(_ContextSymbols.Inputs_).toConstantValue(inp);
|
39
46
|
const log = new _AppLogger.AppLogger();
|
40
47
|
c.bind(_ContextSymbols.Log_).toConstantValue(log);
|
41
48
|
c.bind(_AppLogger.AppLogger).toConstantValue(log);
|
42
|
-
|
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];
|
43
51
|
self.forEach(s => c.bind(s).toSelf());
|
44
52
|
return c;
|
45
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
|
}
|
package/lib/log/AppLogger.js
CHANGED
@@ -9,12 +9,12 @@ var _ConsoleLogger = require("./ConsoleLogger");
|
|
9
9
|
|
10
10
|
var _inversify = require("inversify");
|
11
11
|
|
12
|
-
var _dec, _class;
|
12
|
+
var _dec, _dec2, _dec3, _class;
|
13
13
|
|
14
14
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
15
15
|
|
16
|
-
let AppLogger = (_dec = (0, _inversify.injectable)(), _dec(_class = class AppLogger {
|
17
|
-
constructor() {
|
16
|
+
let AppLogger = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:type", Function), _dec3 = Reflect.metadata("design:paramtypes", [void 0]), _dec(_class = _dec2(_class = _dec3(_class = class AppLogger {
|
17
|
+
constructor(mute = false) {
|
18
18
|
_defineProperty(this, "log", new _ConsoleLogger.ConsoleLogger());
|
19
19
|
|
20
20
|
_defineProperty(this, "debug", this.log.debug);
|
@@ -22,7 +22,9 @@ let AppLogger = (_dec = (0, _inversify.injectable)(), _dec(_class = class AppLog
|
|
22
22
|
_defineProperty(this, "info", this.log.info);
|
23
23
|
|
24
24
|
_defineProperty(this, "error", this.log.error);
|
25
|
+
|
26
|
+
if (mute) this.log.setEnabled(false);
|
25
27
|
}
|
26
28
|
|
27
|
-
}) || _class);
|
29
|
+
}) || _class) || _class) || _class);
|
28
30
|
exports.AppLogger = AppLogger;
|
@@ -7,71 +7,44 @@ exports.StorageBackendProvider = void 0;
|
|
7
7
|
|
8
8
|
var _inversify = require("inversify");
|
9
9
|
|
10
|
-
var
|
11
|
-
|
12
|
-
var _LocalFileStorageBackend = require("./local-file/LocalFileStorageBackend");
|
13
|
-
|
14
|
-
var _ContextSymbols = require("../ctx/ContextSymbols");
|
15
|
-
|
16
|
-
var _config = require("../config");
|
10
|
+
var _types = require("./types");
|
17
11
|
|
18
12
|
var _ResticClientFactory = require("./restic/ResticClientFactory");
|
19
13
|
|
20
14
|
var _BackupClientStorageBackend = require("./BackupClientStorageBackend");
|
21
15
|
|
22
|
-
var _Fs = require("../fs/Fs");
|
23
|
-
|
24
16
|
var _RCloneClientFactory = require("./rclone/RCloneClientFactory");
|
25
17
|
|
26
|
-
var
|
18
|
+
var _StorageConfigProvider = require("./StorageConfigProvider");
|
19
|
+
|
20
|
+
var _dec, _dec2, _dec3, _class;
|
27
21
|
|
28
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; }
|
29
23
|
|
30
|
-
let StorageBackendProvider = (_dec = (0, _inversify.injectable)(), _dec2 =
|
31
|
-
|
32
|
-
}, _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 _RCloneClientFactory.RCloneClientFactory === "undefined" ? Object : _RCloneClientFactory.RCloneClientFactory, typeof _Fs.Fs === "undefined" ? Object : _Fs.Fs, typeof _config.Config === "undefined" ? Object : _config.Config]), _dec(_class = _dec2(_class = _dec3(_class = _dec4(_class = class StorageBackendProvider {
|
33
|
-
constructor(fsSyncer, resticFac, rcloneFac, fs, cfg) {
|
34
|
-
this.fsSyncer = fsSyncer;
|
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) {
|
35
26
|
this.resticFac = resticFac;
|
36
27
|
this.rcloneFac = rcloneFac;
|
37
|
-
this.
|
38
|
-
this.cfg = cfg;
|
28
|
+
this.cfgProvider = cfgProvider;
|
39
29
|
|
40
|
-
_defineProperty(this, "provide",
|
41
|
-
const s = this.
|
42
|
-
|
43
|
-
let b;
|
30
|
+
_defineProperty(this, "provide", () => {
|
31
|
+
const s = this.cfgProvider.provide();
|
32
|
+
let b = null;
|
44
33
|
|
45
34
|
switch (s.type) {
|
46
|
-
case
|
47
|
-
b = this.
|
48
|
-
break;
|
49
|
-
|
50
|
-
case _config.StorageTypeConst.Restic:
|
51
|
-
b = this.restic(s);
|
35
|
+
case _types.StorageType.Restic:
|
36
|
+
b = this.resticFac.create(s);
|
52
37
|
break;
|
53
38
|
|
54
|
-
case
|
55
|
-
b = this.
|
39
|
+
case _types.StorageType.RClone:
|
40
|
+
b = this.rcloneFac.create(s);
|
56
41
|
break;
|
57
42
|
}
|
58
43
|
|
59
44
|
if (!b) throw new Error(`Unsupported storage type ${s.type}`);
|
60
|
-
return b;
|
61
|
-
});
|
62
|
-
|
63
|
-
_defineProperty(this, "rclone", cfg => {
|
64
|
-
return new _BackupClientStorageBackend.BackupClientStorageBackend(this.rcloneFac.create(cfg));
|
65
|
-
});
|
66
|
-
|
67
|
-
_defineProperty(this, "restic", cfg => {
|
68
|
-
return new _BackupClientStorageBackend.BackupClientStorageBackend(this.resticFac.create(cfg));
|
69
|
-
});
|
70
|
-
|
71
|
-
_defineProperty(this, "localFile", cfg => {
|
72
|
-
return new _LocalFileStorageBackend.LocalFileStorageBackend(this.fsSyncer, cfg.path);
|
45
|
+
return new _BackupClientStorageBackend.BackupClientStorageBackend(b);
|
73
46
|
});
|
74
47
|
}
|
75
48
|
|
76
|
-
}) || _class) || _class) || _class)
|
49
|
+
}) || _class) || _class) || _class);
|
77
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() {
|
@@ -1,7 +1,5 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
-
var _AppLogger = require("../../log/AppLogger");
|
4
|
-
|
5
3
|
var _Shell = require("../../tools/shell/Shell");
|
6
4
|
|
7
5
|
var _test = require("../../../test");
|
@@ -17,7 +15,7 @@ var _b2TestConfig = require("../../../test/b2TestConfig");
|
|
17
15
|
var _testHelper = require("../../../test/testHelper");
|
18
16
|
|
19
17
|
describe('RcloneClient', () => {
|
20
|
-
const l =
|
18
|
+
const l = _testHelper.testLog;
|
21
19
|
const getRestoreDir = (0, _test.testdataDirBuilder)('b2/restore');
|
22
20
|
describe('local', () => {
|
23
21
|
const backupDir = (0, _test.testDir)('backup/1');
|
@@ -2,8 +2,6 @@
|
|
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");
|
@@ -17,7 +15,7 @@ var _testHelper = require("../../../test/testHelper");
|
|
17
15
|
var _b2TestConfig = require("../../../test/b2TestConfig");
|
18
16
|
|
19
17
|
describe('ResticClient', () => {
|
20
|
-
const l =
|
18
|
+
const l = _testHelper.testLog;
|
21
19
|
const password = 'foo123';
|
22
20
|
describe('local', () => {
|
23
21
|
const backupDir = (0, _test.testDir)('backup/1');
|
@@ -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
|
});
|
@@ -1,7 +1,5 @@
|
|
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");
|
@@ -10,10 +8,12 @@ var _test = require("../../../test");
|
|
10
8
|
|
11
9
|
var _fs = _interopRequireDefault(require("fs"));
|
12
10
|
|
11
|
+
var _testHelper = require("../../../test/testHelper");
|
12
|
+
|
13
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
14
14
|
|
15
15
|
it('works', () => {
|
16
|
-
const sut = new _Gpg.Gpg(new _Shell.Shell(
|
16
|
+
const sut = new _Gpg.Gpg(new _Shell.Shell(_testHelper.testLog));
|
17
17
|
|
18
18
|
const key = _fs.default.readFileSync((0, _test.testDir)('gpg/john.doe@foo.com.key.asc')).toString();
|
19
19
|
|
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.0",
|
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,22 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
4
|
-
value: true
|
5
|
-
});
|
6
|
-
exports.LocalFileStorageBackend = void 0;
|
7
|
-
|
8
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
9
|
-
|
10
|
-
class LocalFileStorageBackend {
|
11
|
-
constructor(fsSyncer, dst) {
|
12
|
-
this.fsSyncer = fsSyncer;
|
13
|
-
this.dst = dst;
|
14
|
-
|
15
|
-
_defineProperty(this, "store", from => {
|
16
|
-
this.fsSyncer.syncDirs(from, this.dst);
|
17
|
-
});
|
18
|
-
}
|
19
|
-
|
20
|
-
}
|
21
|
-
|
22
|
-
exports.LocalFileStorageBackend = LocalFileStorageBackend;
|