@datatruck/cli 0.39.1 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.schema.json +51 -16
- package/lib/tasks/MongoDumpTask.d.ts +9 -7
- package/lib/tasks/MongoDumpTask.js +62 -11
- package/lib/utils/datatruck/client.d.ts +1 -2
- package/lib/utils/datatruck/job.js +29 -26
- package/lib/utils/datatruck/repository-server.js +5 -1
- package/lib/utils/http.d.ts +3 -6
- package/lib/utils/http.js +6 -6
- package/lib/utils/mongodb.d.ts +11 -0
- package/lib/utils/mongodb.js +39 -0
- package/lib/utils/options.js +3 -1
- package/package.json +10 -7
package/config.schema.json
CHANGED
|
@@ -409,30 +409,47 @@
|
|
|
409
409
|
"config": {
|
|
410
410
|
"type": "object",
|
|
411
411
|
"properties": {
|
|
412
|
-
"
|
|
413
|
-
"type": "string"
|
|
414
|
-
},
|
|
415
|
-
"hostname": {
|
|
416
|
-
"type": "string"
|
|
417
|
-
},
|
|
418
|
-
"port": {
|
|
419
|
-
"type": "number"
|
|
420
|
-
},
|
|
421
|
-
"username": {
|
|
422
|
-
"type": "string"
|
|
423
|
-
},
|
|
424
|
-
"password": {
|
|
412
|
+
"uri": {
|
|
425
413
|
"anyOf": [
|
|
426
414
|
{
|
|
427
415
|
"type": "object",
|
|
428
416
|
"properties": {
|
|
429
|
-
"
|
|
417
|
+
"host": {
|
|
418
|
+
"type": "string"
|
|
419
|
+
},
|
|
420
|
+
"username": {
|
|
421
|
+
"type": "string"
|
|
422
|
+
},
|
|
423
|
+
"password": {
|
|
424
|
+
"anyOf": [
|
|
425
|
+
{
|
|
426
|
+
"type": "object",
|
|
427
|
+
"properties": {
|
|
428
|
+
"path": {
|
|
429
|
+
"type": "string"
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
"additionalProperties": false,
|
|
433
|
+
"required": [
|
|
434
|
+
"path"
|
|
435
|
+
]
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
"type": "string"
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
},
|
|
442
|
+
"port": {
|
|
443
|
+
"type": "number"
|
|
444
|
+
},
|
|
445
|
+
"database": {
|
|
430
446
|
"type": "string"
|
|
431
447
|
}
|
|
432
448
|
},
|
|
433
449
|
"additionalProperties": false,
|
|
434
450
|
"required": [
|
|
435
|
-
"
|
|
451
|
+
"database",
|
|
452
|
+
"host"
|
|
436
453
|
]
|
|
437
454
|
},
|
|
438
455
|
{
|
|
@@ -440,14 +457,32 @@
|
|
|
440
457
|
}
|
|
441
458
|
]
|
|
442
459
|
},
|
|
460
|
+
"command": {
|
|
461
|
+
"type": "string"
|
|
462
|
+
},
|
|
443
463
|
"compress": {
|
|
444
464
|
"type": "boolean"
|
|
445
465
|
},
|
|
446
466
|
"concurrency": {
|
|
447
467
|
"type": "number"
|
|
468
|
+
},
|
|
469
|
+
"targetDatabase": {
|
|
470
|
+
"type": "object",
|
|
471
|
+
"properties": {
|
|
472
|
+
"name": {
|
|
473
|
+
"type": "string"
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
"additionalProperties": false,
|
|
477
|
+
"required": [
|
|
478
|
+
"name"
|
|
479
|
+
]
|
|
448
480
|
}
|
|
449
481
|
},
|
|
450
|
-
"additionalProperties": false
|
|
482
|
+
"additionalProperties": false,
|
|
483
|
+
"required": [
|
|
484
|
+
"uri"
|
|
485
|
+
]
|
|
451
486
|
}
|
|
452
487
|
},
|
|
453
488
|
"additionalProperties": false,
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MongoUriObject } from "../utils/mongodb";
|
|
2
|
+
import { TaskBackupData, TaskRestoreData, TaskAbstract, TaskPrepareRestoreData } from "./TaskAbstract";
|
|
2
3
|
export type MongoDumpTaskConfig = {
|
|
4
|
+
uri: string | MongoUriObject;
|
|
3
5
|
command?: string;
|
|
4
|
-
hostname?: string;
|
|
5
|
-
port?: number;
|
|
6
|
-
username?: string;
|
|
7
|
-
password?: string | {
|
|
8
|
-
path: string;
|
|
9
|
-
};
|
|
10
6
|
compress?: boolean;
|
|
11
7
|
concurrency?: number;
|
|
8
|
+
targetDatabase?: {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
12
11
|
};
|
|
13
12
|
export declare const mongodumpTaskName = "mongo-dump";
|
|
14
13
|
export declare class MongoDumpTask extends TaskAbstract<MongoDumpTaskConfig> {
|
|
@@ -17,5 +16,8 @@ export declare class MongoDumpTask extends TaskAbstract<MongoDumpTaskConfig> {
|
|
|
17
16
|
backup(data: TaskBackupData): Promise<{
|
|
18
17
|
snapshotPath: string;
|
|
19
18
|
}>;
|
|
19
|
+
prepareRestore(data: TaskPrepareRestoreData): Promise<{
|
|
20
|
+
snapshotPath: string;
|
|
21
|
+
}>;
|
|
20
22
|
restore(data: TaskRestoreData): Promise<void>;
|
|
21
23
|
}
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MongoDumpTask = exports.mongodumpTaskName = void 0;
|
|
4
4
|
const async_process_1 = require("../utils/async-process");
|
|
5
|
+
const config_1 = require("../utils/datatruck/config");
|
|
6
|
+
const error_1 = require("../utils/error");
|
|
5
7
|
const fs_1 = require("../utils/fs");
|
|
8
|
+
const mongodb_1 = require("../utils/mongodb");
|
|
6
9
|
const temp_1 = require("../utils/temp");
|
|
7
10
|
const TaskAbstract_1 = require("./TaskAbstract");
|
|
11
|
+
const promises_1 = require("fs/promises");
|
|
12
|
+
const mongodb_2 = require("mongodb");
|
|
13
|
+
const path_1 = require("path");
|
|
8
14
|
exports.mongodumpTaskName = "mongo-dump";
|
|
9
15
|
class MongoDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
10
16
|
verbose;
|
|
@@ -17,21 +23,19 @@ class MongoDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
17
23
|
(await (0, temp_1.mkTmpDir)(exports.mongodumpTaskName, "task", "backup", "snapshot"));
|
|
18
24
|
await (0, fs_1.mkdirIfNotExists)(snapshotPath);
|
|
19
25
|
await (0, fs_1.ensureEmptyDir)(snapshotPath);
|
|
26
|
+
const config = await (0, mongodb_1.resolveMongoUri)(this.config.uri);
|
|
20
27
|
const p = new async_process_1.AsyncProcess(this.command, [
|
|
21
|
-
...(
|
|
22
|
-
...(
|
|
23
|
-
...
|
|
28
|
+
...(config.host ? ["/h", config.host] : []),
|
|
29
|
+
...(config.port ? [`/port:${config.port}`] : []),
|
|
30
|
+
...["/authenticationDatabase:admin"],
|
|
31
|
+
...["/d", config.database],
|
|
32
|
+
...(config.username ? ["/u", config.username] : []),
|
|
24
33
|
...(this.config.compress ? ["/gzip"] : []),
|
|
25
34
|
...(this.config.concurrency ? ["/j", this.config.concurrency] : []),
|
|
26
35
|
"/o",
|
|
27
36
|
snapshotPath,
|
|
28
|
-
], {
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
const password = this.config.password !== undefined
|
|
32
|
-
? (await (0, fs_1.fetchData)(this.config.password, (p) => p.path)) ?? ""
|
|
33
|
-
: "";
|
|
34
|
-
p.stdin.writable.write(`${password}\n`);
|
|
37
|
+
], { $log: this.verbose });
|
|
38
|
+
p.stdin.writable.write(`${config.password ?? ""}\n`);
|
|
35
39
|
await p.stderr.parseLines((line) => {
|
|
36
40
|
data.onProgress({
|
|
37
41
|
absolute: {
|
|
@@ -39,10 +43,57 @@ class MongoDumpTask extends TaskAbstract_1.TaskAbstract {
|
|
|
39
43
|
},
|
|
40
44
|
});
|
|
41
45
|
});
|
|
46
|
+
const tmpDir = (0, path_1.join)(snapshotPath, config.database);
|
|
47
|
+
for (const file of await (0, promises_1.readdir)(tmpDir))
|
|
48
|
+
await (0, promises_1.rename)((0, path_1.join)(tmpDir, file), (0, path_1.join)(snapshotPath, file));
|
|
49
|
+
await (0, promises_1.rmdir)(tmpDir);
|
|
42
50
|
return { snapshotPath };
|
|
43
51
|
}
|
|
52
|
+
async prepareRestore(data) {
|
|
53
|
+
return {
|
|
54
|
+
snapshotPath: data.package.restorePath ??
|
|
55
|
+
(await (0, temp_1.mkTmpDir)(exports.mongodumpTaskName, "task", "restore", "snapshot")),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
44
58
|
async restore(data) {
|
|
45
|
-
|
|
59
|
+
this.verbose = data.options.verbose;
|
|
60
|
+
const config = await (0, mongodb_1.resolveMongoUri)(this.config.uri);
|
|
61
|
+
const uri = (0, mongodb_1.toMongoUri)(config);
|
|
62
|
+
const client = new mongodb_2.MongoClient(`${uri}?authSource=admin`);
|
|
63
|
+
const params = {
|
|
64
|
+
packageName: data.package.name,
|
|
65
|
+
snapshotId: data.options.id,
|
|
66
|
+
snapshotDate: data.snapshot.date,
|
|
67
|
+
action: "restore",
|
|
68
|
+
database: undefined,
|
|
69
|
+
};
|
|
70
|
+
const database = {
|
|
71
|
+
name: (0, config_1.resolveDatabaseName)(config.database, params),
|
|
72
|
+
};
|
|
73
|
+
if (this.config.targetDatabase && !data.options.initial)
|
|
74
|
+
database.name = (0, config_1.resolveDatabaseName)(this.config.targetDatabase.name, {
|
|
75
|
+
...params,
|
|
76
|
+
database: database.name,
|
|
77
|
+
});
|
|
78
|
+
const restoreCollections = (await (0, promises_1.readdir)(data.snapshotPath))
|
|
79
|
+
.filter((name) => name.endsWith(".bson"))
|
|
80
|
+
.map((name) => name.replace(/\.bson$/, ""));
|
|
81
|
+
const collections = (await client.db(database.name).collections()).map((v) => v.collectionName);
|
|
82
|
+
const duplicatedCollections = restoreCollections.filter((v) => collections.includes(v));
|
|
83
|
+
if (duplicatedCollections.length)
|
|
84
|
+
throw new error_1.AppError(`Target collections already exists: ${duplicatedCollections.join(", ")}`);
|
|
85
|
+
const p = new async_process_1.AsyncProcess("mongorestore", [
|
|
86
|
+
...(config.host ? ["/h", config.host] : []),
|
|
87
|
+
...(config.port ? [`/port:${config.port}`] : []),
|
|
88
|
+
...["/authenticationDatabase:admin"],
|
|
89
|
+
...["/d", database.name],
|
|
90
|
+
...(config.username ? ["/u", config.username] : []),
|
|
91
|
+
...(this.config.compress ? ["/gzip"] : []),
|
|
92
|
+
...(this.config.concurrency ? ["/j", this.config.concurrency] : []),
|
|
93
|
+
data.snapshotPath,
|
|
94
|
+
], { $log: this.verbose });
|
|
95
|
+
p.stdin.writable.write(`${config.password ?? ""}\n`);
|
|
96
|
+
await p.waitForClose();
|
|
46
97
|
}
|
|
47
98
|
}
|
|
48
99
|
exports.MongoDumpTask = MongoDumpTask;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
import { DiskStats } from "../fs";
|
|
3
2
|
import { BasicProgress } from "../progress";
|
|
4
3
|
import { AbstractFs, FsOptions } from "../virtual-fs";
|
|
@@ -13,7 +12,7 @@ export declare class RemoteFs extends AbstractFs {
|
|
|
13
12
|
});
|
|
14
13
|
isLocal(): boolean;
|
|
15
14
|
protected fetchJson(name: string, params: any[]): Promise<any>;
|
|
16
|
-
protected post(name: string, params: any[], data: string): Promise<Response>;
|
|
15
|
+
protected post(name: string, params: any[], data: string): Promise<import("undici").Response>;
|
|
17
16
|
existsDir(path: string): Promise<boolean>;
|
|
18
17
|
rename(source: string, target: string): Promise<void>;
|
|
19
18
|
mkdir(path: string): Promise<void>;
|
|
@@ -11,17 +11,22 @@ const fs_2 = require("fs");
|
|
|
11
11
|
const promises_1 = require("fs/promises");
|
|
12
12
|
const path_1 = require("path");
|
|
13
13
|
async function createJobLog(config, name) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
stream
|
|
23
|
-
}
|
|
24
|
-
|
|
14
|
+
const dt = new Date().toISOString().replaceAll(":", "-");
|
|
15
|
+
const dir = config?.path ?? logs_1.defaultsLogPath;
|
|
16
|
+
const tmpPath = (0, path_1.join)(dir, `${dt}_${name}.tmp.log`);
|
|
17
|
+
await (0, promises_1.mkdir)(dir, { recursive: true });
|
|
18
|
+
const stream = (0, fs_2.createWriteStream)(tmpPath);
|
|
19
|
+
return {
|
|
20
|
+
stream,
|
|
21
|
+
write(data) {
|
|
22
|
+
stream.write(data);
|
|
23
|
+
},
|
|
24
|
+
async finish(pid) {
|
|
25
|
+
const path = (0, path_1.join)(dir, `${dt}_${name}_${pid}.log`);
|
|
26
|
+
await (0, fs_1.safeRename)(tmpPath, path);
|
|
27
|
+
return path;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
25
30
|
}
|
|
26
31
|
function getJobCliOptions(job) {
|
|
27
32
|
const Command = command_1.datatruckCommands[job.action];
|
|
@@ -55,7 +60,9 @@ exports.runJob = runJob;
|
|
|
55
60
|
async function runCronJob(job, name, config) {
|
|
56
61
|
let pid = 0;
|
|
57
62
|
try {
|
|
58
|
-
const log =
|
|
63
|
+
const log = config.log?.enabled ?? true
|
|
64
|
+
? await createJobLog(config.log, name)
|
|
65
|
+
: undefined;
|
|
59
66
|
const cliOptions = getJobCliOptions(job);
|
|
60
67
|
const [node, bin] = process.argv;
|
|
61
68
|
const argv = [
|
|
@@ -69,7 +76,7 @@ async function runCronJob(job, name, config) {
|
|
|
69
76
|
job.action,
|
|
70
77
|
...cliOptions,
|
|
71
78
|
];
|
|
72
|
-
log?.
|
|
79
|
+
log?.write(`+ dtt ${argv.slice(1).join(" ")}\n`);
|
|
73
80
|
const p = new async_process_1.AsyncProcess(node, argv, {
|
|
74
81
|
$log: config.verbose,
|
|
75
82
|
$exitCode: false,
|
|
@@ -81,22 +88,18 @@ async function runCronJob(job, name, config) {
|
|
|
81
88
|
},
|
|
82
89
|
});
|
|
83
90
|
pid = p.child.pid || 0;
|
|
84
|
-
|
|
85
|
-
(0, cli_1.logJson)("job", `'${name}' started`, { pid });
|
|
91
|
+
(0, cli_1.logJson)("job", `'${name}' started`, { pid });
|
|
86
92
|
const [exitCode] = await Promise.all([
|
|
87
93
|
p.waitForClose(),
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: []),
|
|
94
|
+
log && p.stderr.pipe(log.stream),
|
|
95
|
+
log && p.stdout.pipe(log.stream),
|
|
91
96
|
]);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
(0, cli_1.logJson)("job", `'${name}' finished`, { pid, exitCode, ...logData });
|
|
97
|
+
const logPath = await log?.finish(pid);
|
|
98
|
+
(0, cli_1.logJson)("job", `'${name}' finished`, {
|
|
99
|
+
pid,
|
|
100
|
+
exitCode,
|
|
101
|
+
...(logPath && { log: logPath }),
|
|
102
|
+
});
|
|
100
103
|
}
|
|
101
104
|
catch (error) {
|
|
102
105
|
(0, cli_1.logJson)("job", `'${name}' failed`, { pid });
|
|
@@ -65,7 +65,8 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
|
|
|
65
65
|
const id = counter.next();
|
|
66
66
|
let requestError;
|
|
67
67
|
let responseError;
|
|
68
|
-
|
|
68
|
+
const requestErrorListener = (error) => (requestError = error);
|
|
69
|
+
req.on("error", requestErrorListener);
|
|
69
70
|
res.on("error", (error) => (responseError = error));
|
|
70
71
|
res.setHeader("X-Accel-Buffering", "no");
|
|
71
72
|
try {
|
|
@@ -88,16 +89,19 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
|
|
|
88
89
|
else if (action === "upload") {
|
|
89
90
|
const [target] = params;
|
|
90
91
|
const path = fs.resolvePath(target);
|
|
92
|
+
req.off("error", requestErrorListener);
|
|
91
93
|
await (0, http_1.recvFile)(req, res, path);
|
|
92
94
|
}
|
|
93
95
|
else if (action === "download") {
|
|
94
96
|
const [target] = params;
|
|
95
97
|
const path = fs.resolvePath(target);
|
|
98
|
+
req.off("error", requestErrorListener);
|
|
96
99
|
await (0, http_1.sendFile)(req, res, path, {
|
|
97
100
|
contentLength: options.contentLength ?? true,
|
|
98
101
|
});
|
|
99
102
|
}
|
|
100
103
|
else if (action === "writeFile") {
|
|
104
|
+
req.off("error", requestErrorListener);
|
|
101
105
|
const data = await (0, http_1.readRequestData)(req);
|
|
102
106
|
const [target] = params;
|
|
103
107
|
await fs.writeFile(target, data);
|
package/lib/utils/http.d.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
2
|
import { BasicProgress } from "./progress";
|
|
4
3
|
import { IncomingMessage, Server, ServerResponse } from "http";
|
|
4
|
+
import { fetch, type RequestInit } from "undici";
|
|
5
5
|
export declare function createHref(inUrl: string, query?: Record<string, string>): string;
|
|
6
6
|
export declare function closeServer(server: Server): Promise<void>;
|
|
7
7
|
export declare function readRequestData(req: IncomingMessage): Promise<string | undefined>;
|
|
8
8
|
export declare const safeFetch: typeof fetch;
|
|
9
9
|
export declare function fetchJson<T = any>(url: string, options?: RequestInit): Promise<T | undefined>;
|
|
10
|
-
export declare function post(url: string, data: string, options?: Omit<RequestInit, "method" | "body">): Promise<Response>;
|
|
10
|
+
export declare function post(url: string, data: string, options?: Omit<RequestInit, "method" | "body">): Promise<import("undici").Response>;
|
|
11
11
|
export declare function parseContentLength(value: string | undefined): number;
|
|
12
12
|
export declare function sendFile(req: IncomingMessage, res: ServerResponse, path: string, options?: {
|
|
13
13
|
contentLength?: boolean;
|
|
14
|
-
end?: boolean;
|
|
15
14
|
checksum?: boolean;
|
|
16
15
|
}): Promise<void>;
|
|
17
|
-
export declare function recvFile(req: IncomingMessage, res: ServerResponse, path: string
|
|
18
|
-
end?: boolean;
|
|
19
|
-
}): Promise<void>;
|
|
16
|
+
export declare function recvFile(req: IncomingMessage, res: ServerResponse, path: string): Promise<void>;
|
|
20
17
|
export declare function downloadFile(url: string, output: string, options?: Omit<RequestInit, "signal"> & {
|
|
21
18
|
timeout?: number;
|
|
22
19
|
onProgress?: (progress: BasicProgress) => void;
|
package/lib/utils/http.js
CHANGED
|
@@ -7,6 +7,7 @@ const fs_1 = require("fs");
|
|
|
7
7
|
const promises_1 = require("fs/promises");
|
|
8
8
|
const stream_1 = require("stream");
|
|
9
9
|
const promises_2 = require("stream/promises");
|
|
10
|
+
const undici_1 = require("undici");
|
|
10
11
|
function createHref(inUrl, query) {
|
|
11
12
|
const url = new URL(inUrl);
|
|
12
13
|
for (const key in query || {})
|
|
@@ -35,7 +36,7 @@ function readRequestData(req) {
|
|
|
35
36
|
}
|
|
36
37
|
exports.readRequestData = readRequestData;
|
|
37
38
|
const safeFetch = async (...args) => {
|
|
38
|
-
const res = await fetch(...args);
|
|
39
|
+
const res = await (0, undici_1.fetch)(...args);
|
|
39
40
|
if (res.status !== 200)
|
|
40
41
|
throw new Error(`Fetch request failed: ${res.status} ${res.statusText}`);
|
|
41
42
|
return res;
|
|
@@ -74,12 +75,12 @@ async function sendFile(req, res, path, options = {}) {
|
|
|
74
75
|
}
|
|
75
76
|
finally {
|
|
76
77
|
file?.close();
|
|
77
|
-
if (
|
|
78
|
+
if (!res.writableEnded)
|
|
78
79
|
res.end();
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
exports.sendFile = sendFile;
|
|
82
|
-
async function recvFile(req, res, path
|
|
83
|
+
async function recvFile(req, res, path) {
|
|
83
84
|
let file;
|
|
84
85
|
try {
|
|
85
86
|
file = (0, fs_1.createWriteStream)(path);
|
|
@@ -95,8 +96,7 @@ async function recvFile(req, res, path, options = {}) {
|
|
|
95
96
|
}
|
|
96
97
|
finally {
|
|
97
98
|
file?.close();
|
|
98
|
-
|
|
99
|
-
res.end();
|
|
99
|
+
res.end();
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
exports.recvFile = recvFile;
|
|
@@ -162,7 +162,7 @@ async function uploadFile(url, path, options = {}) {
|
|
|
162
162
|
const { size } = await (0, promises_1.stat)(path);
|
|
163
163
|
const file = (0, fs_1.createReadStream)(path);
|
|
164
164
|
try {
|
|
165
|
-
const res = await fetch(url, {
|
|
165
|
+
const res = await (0, undici_1.fetch)(url, {
|
|
166
166
|
...options,
|
|
167
167
|
method: "POST",
|
|
168
168
|
duplex: "half",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type MongoUriObject<Resolved = false> = {
|
|
2
|
+
host: string;
|
|
3
|
+
username?: string;
|
|
4
|
+
password?: [Resolved] extends [true] ? string : string | {
|
|
5
|
+
path: string;
|
|
6
|
+
};
|
|
7
|
+
port?: number;
|
|
8
|
+
database: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function toMongoUri(object: MongoUriObject<true>): string;
|
|
11
|
+
export declare function resolveMongoUri(input: string | MongoUriObject): Promise<MongoUriObject<true>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveMongoUri = exports.toMongoUri = void 0;
|
|
4
|
+
const fs_1 = require("./fs");
|
|
5
|
+
function toMongoUri(object) {
|
|
6
|
+
const url = new URL(`mongodb://${object.host}`);
|
|
7
|
+
if (typeof object.username === "string")
|
|
8
|
+
url.username = object.username;
|
|
9
|
+
if (typeof object.password === "string")
|
|
10
|
+
url.password = object.password;
|
|
11
|
+
if (typeof object.port === "number")
|
|
12
|
+
url.port = object.port.toString();
|
|
13
|
+
url.pathname = `/${object.database}`;
|
|
14
|
+
return url.href;
|
|
15
|
+
}
|
|
16
|
+
exports.toMongoUri = toMongoUri;
|
|
17
|
+
async function resolveMongoUri(input) {
|
|
18
|
+
let object;
|
|
19
|
+
if (typeof input === "string") {
|
|
20
|
+
const url = new URL(input);
|
|
21
|
+
object = {
|
|
22
|
+
host: url.hostname,
|
|
23
|
+
password: url.password,
|
|
24
|
+
port: url.port ? Number(url.port) : undefined,
|
|
25
|
+
username: url.username,
|
|
26
|
+
database: url.pathname.slice(1),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
object = input;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...object,
|
|
34
|
+
password: object.password !== undefined
|
|
35
|
+
? (await (0, fs_1.fetchData)(object.password, (p) => p.path)) ?? ""
|
|
36
|
+
: "",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
exports.resolveMongoUri = resolveMongoUri;
|
package/lib/utils/options.js
CHANGED
|
@@ -49,7 +49,9 @@ function createCommand(config, action) {
|
|
|
49
49
|
for (const name in config.options) {
|
|
50
50
|
const option = config.options[name];
|
|
51
51
|
if (option.flag !== false) {
|
|
52
|
-
const parse = typeof option.value === "string"
|
|
52
|
+
const parse = typeof option.value === "string"
|
|
53
|
+
? parsers[option.value]
|
|
54
|
+
: option.value;
|
|
53
55
|
const cliValue = cliOptions[option.flag ?? name] ?? option.defaults;
|
|
54
56
|
if (cliValue !== undefined)
|
|
55
57
|
commandOptions[name] = parse ? parse(cliValue) : cliValue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"description": "Tool for creating and managing backups",
|
|
5
5
|
"homepage": "https://github.com/swordev/datatruck#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -27,23 +27,26 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@supercharge/promise-pool": "^3.2.0",
|
|
30
|
-
"ajv": "^8.
|
|
30
|
+
"ajv": "^8.13.0",
|
|
31
31
|
"async": "^3.2.5",
|
|
32
32
|
"chalk": "^4.1.2",
|
|
33
33
|
"commander": "^12.0.0",
|
|
34
|
-
"croner": "^8.0.
|
|
35
|
-
"dayjs": "^1.11.
|
|
34
|
+
"croner": "^8.0.2",
|
|
35
|
+
"dayjs": "^1.11.11",
|
|
36
36
|
"fast-folder-size": "^2.2.0",
|
|
37
37
|
"fast-glob": "^3.3.2",
|
|
38
38
|
"listr2": "^8.2.1",
|
|
39
39
|
"micromatch": "^4.0.5",
|
|
40
|
-
"
|
|
40
|
+
"mongodb": "^6.6.1",
|
|
41
|
+
"mysql2": "^3.9.7",
|
|
41
42
|
"tty-table": "^4.2.3",
|
|
42
|
-
"
|
|
43
|
+
"undici": "^6.15.0",
|
|
44
|
+
"yaml": "^2.4.2"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@types/async": "^3.2.24",
|
|
46
|
-
"@types/micromatch": "^4.0.
|
|
48
|
+
"@types/micromatch": "^4.0.7",
|
|
49
|
+
"mongodb-memory-server": "^9.2.0"
|
|
47
50
|
},
|
|
48
51
|
"optionalDependencies": {
|
|
49
52
|
"ts-node": "^10.9.2"
|