@datatruck/cli 0.40.4 → 0.41.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.
@@ -1113,6 +1113,9 @@
1113
1113
  "type": "boolean"
1114
1114
  }
1115
1115
  ]
1116
+ },
1117
+ "insecureTls": {
1118
+ "type": "boolean"
1116
1119
  }
1117
1120
  },
1118
1121
  "additionalProperties": false,
@@ -1249,15 +1252,15 @@
1249
1252
  "path": {
1250
1253
  "type": "string"
1251
1254
  },
1252
- "username": {
1253
- "type": "string"
1254
- },
1255
1255
  "host": {
1256
1256
  "type": "string"
1257
1257
  },
1258
1258
  "port": {
1259
1259
  "type": "number"
1260
1260
  },
1261
+ "username": {
1262
+ "type": "string"
1263
+ },
1261
1264
  "protocol": {
1262
1265
  "enum": [
1263
1266
  "http",
@@ -109,7 +109,7 @@ class RestoreAction {
109
109
  package: pkg,
110
110
  snapshot,
111
111
  });
112
- snapshotPath = taskResult?.snapshotPath;
112
+ snapshotPath = taskResult?.snapshotPath ?? snapshotPath;
113
113
  }
114
114
  (0, assert_1.ok)(snapshotPath);
115
115
  await (0, repository_1.initSnapshotPath)(snapshotPath, this.config.minFreeDiskSpace);
package/lib/cli.js CHANGED
@@ -17,6 +17,7 @@ const chalk_1 = __importDefault(require("chalk"));
17
17
  const commander_1 = require("commander");
18
18
  const fs_2 = require("fs");
19
19
  const path_1 = require("path");
20
+ const util_1 = require("util");
20
21
  function getGlobalOptions() {
21
22
  const result = program.opts();
22
23
  const parseBool = (v) => v === "true" ? true : v === "false" ? false : v;
@@ -49,21 +50,21 @@ function createCommandAction(Constructor) {
49
50
  if (errors?.length) {
50
51
  console.error();
51
52
  errors.forEach((error, index) => {
52
- console.error(chalk_1.default.red(`${index + 1}. ` + (error.stack ?? error.message)));
53
+ console.error(chalk_1.default.red(`${index + 1}. ` + (0, util_1.format)(error)));
53
54
  if (errors[index + 1])
54
55
  console.error();
55
56
  });
56
57
  }
57
58
  if (error) {
58
59
  if (globalOptions.verbose) {
59
- console.error(chalk_1.default.red(error.stack));
60
+ console.error(chalk_1.default.red((0, util_1.format)(error)));
60
61
  }
61
62
  else {
62
63
  if (error instanceof error_1.AppError) {
63
64
  console.error(chalk_1.default.red(error.message));
64
65
  }
65
66
  else {
66
- console.error(chalk_1.default.red(error.stack));
67
+ console.error(chalk_1.default.red((0, util_1.format)(error)));
67
68
  }
68
69
  }
69
70
  }
@@ -97,7 +98,7 @@ function parseArgs(args) {
97
98
  process.stdout.write(cli_1.showCursorCommand);
98
99
  console.info(`\nClosing... (reason: ${eventName})`);
99
100
  if (error instanceof Error)
100
- console.error(error.stack);
101
+ console.error((0, util_1.format)(error));
101
102
  }
102
103
  if (!verbose)
103
104
  try {
@@ -11,6 +11,7 @@ export type MetaData = Omit<SnapshotTagObject, "shortId" | "size"> & {
11
11
  export type DatatruckRepositoryConfig = {
12
12
  backend: string;
13
13
  compress?: boolean | CompressOptions;
14
+ insecureTls?: boolean;
14
15
  };
15
16
  type PackObject = {
16
17
  name?: string;
@@ -46,15 +46,15 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
46
46
  return this.config.backend;
47
47
  }
48
48
  fetchDiskStats(config) {
49
- const fs = (0, client_1.createFs)(config.backend, this.verbose);
49
+ const fs = (0, client_1.createFs)(config, this.verbose);
50
50
  return fs.fetchDiskStats(".");
51
51
  }
52
52
  async init(data) {
53
- const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
53
+ const fs = (0, client_1.createFs)(this.config, this.verbose);
54
54
  await fs.mkdir(".");
55
55
  }
56
56
  async prune(data) {
57
- const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
57
+ const fs = (0, client_1.createFs)(this.config, this.verbose);
58
58
  const snapshotName = DatatruckRepository.createSnapshotName(data.snapshot, {
59
59
  name: data.snapshot.packageName,
60
60
  });
@@ -64,7 +64,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
64
64
  await fs.rmAll(snapshotName);
65
65
  }
66
66
  async fetchSnapshots(data) {
67
- const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
67
+ const fs = (0, client_1.createFs)(this.config, this.verbose);
68
68
  if (!(await fs.existsDir(".")))
69
69
  throw new error_1.AppError(`Repository (${this.repository.name}) out path does not exist: ${fs.resolvePath(".")}`);
70
70
  const snapshots = [];
@@ -110,7 +110,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
110
110
  return snapshots;
111
111
  }
112
112
  async backup(data) {
113
- const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
113
+ const fs = (0, client_1.createFs)(this.config, this.verbose);
114
114
  const snapshotName = DatatruckRepository.createSnapshotName(data.snapshot, data.package);
115
115
  const outPath = fs.isLocal()
116
116
  ? fs.resolvePath(snapshotName)
@@ -231,8 +231,8 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
231
231
  };
232
232
  }
233
233
  async copy(data) {
234
- const sourceFs = (0, client_1.createFs)(this.config.backend, this.verbose);
235
- const targetFs = (0, client_1.createFs)(data.mirrorRepositoryConfig.backend, this.verbose);
234
+ const sourceFs = (0, client_1.createFs)(this.config, this.verbose);
235
+ const targetFs = (0, client_1.createFs)(data.mirrorRepositoryConfig, this.verbose);
236
236
  const snapshotName = DatatruckRepository.createSnapshotName(data.snapshot, data.package);
237
237
  if (data.options.verbose)
238
238
  (0, cli_1.logExec)(`Copying backup files to ${data.mirrorRepositoryConfig.backend}`);
@@ -302,7 +302,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
302
302
  return { bytes };
303
303
  }
304
304
  async restore(data) {
305
- const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
305
+ const fs = (0, client_1.createFs)(this.config, this.verbose);
306
306
  const relRestorePath = data.snapshotPath;
307
307
  (0, assert_1.ok)(relRestorePath);
308
308
  const restorePath = (0, path_1.resolve)(relRestorePath);
@@ -1,16 +1,19 @@
1
1
  import { DiskStats } from "../fs";
2
2
  import { BasicProgress } from "../progress";
3
3
  import { AbstractFs, FsOptions } from "../virtual-fs";
4
+ import { Agent } from "undici";
4
5
  export declare class RemoteFs extends AbstractFs {
5
6
  readonly options: FsOptions & {
6
7
  verbose?: boolean;
7
8
  };
8
9
  protected url: string;
9
10
  protected headers: Record<string, string>;
11
+ protected agent: Agent | undefined;
10
12
  constructor(options: FsOptions & {
11
13
  verbose?: boolean;
12
14
  });
13
15
  isLocal(): boolean;
16
+ private getCurlArgs;
14
17
  protected fetchJson(name: string, params: any[]): Promise<any>;
15
18
  protected post(name: string, params: any[], data: string): Promise<import("undici").Response>;
16
19
  existsDir(path: string): Promise<boolean>;
@@ -32,4 +35,7 @@ export declare class RemoteFs extends AbstractFs {
32
35
  }>;
33
36
  }
34
37
  export declare function isRemoteBackend(backend: string): boolean;
35
- export declare function createFs(backend: string, verbose: boolean | undefined): AbstractFs;
38
+ export declare function createFs(options: {
39
+ backend: string;
40
+ insecureTls?: boolean;
41
+ }, verbose: boolean | undefined): AbstractFs;
@@ -7,10 +7,12 @@ const cli_1 = require("../cli");
7
7
  const http_1 = require("../http");
8
8
  const virtual_fs_1 = require("../virtual-fs");
9
9
  const repository_server_1 = require("./repository-server");
10
+ const undici_1 = require("undici");
10
11
  class RemoteFs extends virtual_fs_1.AbstractFs {
11
12
  options;
12
13
  url;
13
14
  headers;
15
+ agent;
14
16
  constructor(options) {
15
17
  super(options);
16
18
  this.options = options;
@@ -24,24 +26,50 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
24
26
  this.url = url.href;
25
27
  if (this.url.endsWith("/"))
26
28
  this.url = this.url.slice(0, -1);
29
+ if (options.insecureTls)
30
+ this.agent = new undici_1.Agent({
31
+ connect: {
32
+ rejectUnauthorized: false,
33
+ },
34
+ });
27
35
  }
28
36
  isLocal() {
29
37
  return false;
30
38
  }
39
+ getCurlArgs(headers = {}) {
40
+ const args = Object.entries({ ...headers, ...this.headers }).flatMap(([k, v]) => ["-H", `"${k}: ${v}"`]);
41
+ if (this.options.insecureTls)
42
+ args.push("-k");
43
+ args.push("-v");
44
+ return args;
45
+ }
31
46
  async fetchJson(name, params) {
32
47
  const url = (0, http_1.createHref)(`${this.url}/${name}`, {
33
48
  params: JSON.stringify(params),
34
49
  });
50
+ if (this.options.verbose)
51
+ (0, cli_1.logExec)("curl", [...this.getCurlArgs(), `"${url}"`]);
35
52
  return await (0, http_1.fetchJson)(url, {
36
53
  headers: this.headers,
54
+ dispatcher: this.agent,
37
55
  });
38
56
  }
39
57
  async post(name, params, data) {
40
58
  const url = (0, http_1.createHref)(`${this.url}/${name}`, {
41
59
  params: JSON.stringify(params),
42
60
  });
61
+ if (this.options.verbose)
62
+ (0, cli_1.logExec)("curl", [
63
+ ...this.getCurlArgs({ "Content-Type": "application/json" }),
64
+ "--request",
65
+ "POST",
66
+ "--data",
67
+ `"${data}"`,
68
+ `"${url}"`,
69
+ ]);
43
70
  return await (0, http_1.post)(url, data, {
44
71
  headers: this.headers,
72
+ dispatcher: this.agent,
45
73
  });
46
74
  }
47
75
  async existsDir(path) {
@@ -82,9 +110,17 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
82
110
  const url = (0, http_1.createHref)(`${this.url}/upload`, {
83
111
  params: JSON.stringify([target]),
84
112
  });
113
+ if (this.options.verbose)
114
+ (0, cli_1.logExec)("curl", [
115
+ ...this.getCurlArgs(),
116
+ "-F",
117
+ `data=@${source}`,
118
+ `"${url}"`,
119
+ ]);
85
120
  return await (0, http_1.uploadFile)(url, source, {
86
121
  headers: this.headers,
87
122
  checksum: true,
123
+ dispatcher: this.agent,
88
124
  });
89
125
  }
90
126
  async download(source, target, options = {}) {
@@ -93,9 +129,12 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
93
129
  const url = (0, http_1.createHref)(`${this.url}/download`, {
94
130
  params: JSON.stringify([source]),
95
131
  });
132
+ if (this.options.verbose)
133
+ (0, cli_1.logExec)("curl", [...this.getCurlArgs(), `"${url}"`, ">", target]);
96
134
  return await (0, http_1.downloadFile)(url, target, {
97
135
  ...options,
98
136
  headers: this.headers,
137
+ dispatcher: this.agent,
99
138
  });
100
139
  }
101
140
  }
@@ -103,8 +142,12 @@ exports.RemoteFs = RemoteFs;
103
142
  function isRemoteBackend(backend) {
104
143
  return backend.startsWith("http:") || backend.startsWith("https:");
105
144
  }
106
- function createFs(backend, verbose) {
107
- return isRemoteBackend(backend)
108
- ? new RemoteFs({ backend, verbose })
109
- : new virtual_fs_1.LocalFs({ backend });
145
+ function createFs(options, verbose) {
146
+ return isRemoteBackend(options.backend)
147
+ ? new RemoteFs({
148
+ backend: options.backend,
149
+ insecureTls: options.insecureTls,
150
+ verbose,
151
+ })
152
+ : new virtual_fs_1.LocalFs({ backend: options.backend });
110
153
  }
@@ -75,7 +75,7 @@ function filterRepositoryByEnabled(repository, action) {
75
75
  if (typeof enabled === "boolean")
76
76
  return enabled;
77
77
  const defaults = enabled["defaults"] ?? true;
78
- return action ? enabled[action] ?? defaults : true;
78
+ return action ? (enabled[action] ?? defaults) : true;
79
79
  }
80
80
  function filterPackages(config, options) {
81
81
  const filterRepo = (0, string_1.createPatternFilter)(options.repositoryNames);
@@ -14,7 +14,7 @@ function createCrons(jobs, options) {
14
14
  for (const name in jobs) {
15
15
  const job = jobs[name];
16
16
  if (job.schedule)
17
- crons.push((0, croner_1.Cron)(typeof job.schedule === "string"
17
+ crons.push(new croner_1.Cron(typeof job.schedule === "string"
18
18
  ? job.schedule
19
19
  : (0, cron_1.formatCronScheduleObject)(job.schedule), {
20
20
  paused: true,
@@ -60,7 +60,7 @@ async function runJob(job, name, config) {
60
60
  async function runCronJob(job, name, config) {
61
61
  let pid = 0;
62
62
  try {
63
- const log = config.log?.enabled ?? true
63
+ const log = (config.log?.enabled ?? true)
64
64
  ? await createJobLog(config.log, name)
65
65
  : undefined;
66
66
  const cliOptions = getJobCliOptions(job);
package/lib/utils/fs.d.ts CHANGED
@@ -36,7 +36,7 @@ export declare function writeGitIgnoreList(options: {
36
36
  paths: NodeJS.ReadableStream | string[];
37
37
  outDir: string;
38
38
  }): Promise<string>;
39
- export declare function copyFileWithStreams(source: string, target: string): Promise<unknown>;
39
+ export declare function copyFileWithStreams(source: string, target: string): Promise<void>;
40
40
  export declare function updateFileStats(path: string, fileInfo: Stats): Promise<void>;
41
41
  export declare function isNotFoundError(error: unknown): boolean;
42
42
  export declare function readTextFile(path: string): Promise<string>;
package/lib/utils/fs.js CHANGED
@@ -279,7 +279,7 @@ async function copyFileWithStreams(source, target) {
279
279
  const r = (0, fs_1.createReadStream)(source);
280
280
  const w = (0, fs_2.createWriteStream)(target);
281
281
  try {
282
- return await new Promise((resolve, reject) => {
282
+ await new Promise((resolve, reject) => {
283
283
  r.on("error", reject);
284
284
  w.on("error", reject);
285
285
  w.on("finish", resolve);
package/lib/utils/http.js CHANGED
@@ -67,7 +67,7 @@ async function sendFile(req, res, path, options = {}) {
67
67
  try {
68
68
  file = (0, fs_1.createReadStream)(path);
69
69
  const fileStat = await (0, promises_1.stat)(path);
70
- res.setHeader(options.contentLength ?? true ? "Content-Length" : "x-content-length", fileStat.size);
70
+ res.setHeader((options.contentLength ?? true) ? "Content-Length" : "x-content-length", fileStat.size);
71
71
  if (options.checksum)
72
72
  res.setHeader("x-checksum", await (0, crypto_1.calcFileHash)(path, "sha1"));
73
73
  file.pipe(res);
@@ -32,7 +32,7 @@ async function resolveMongoUri(input) {
32
32
  return {
33
33
  ...object,
34
34
  password: object.password !== undefined
35
- ? (await (0, fs_1.fetchData)(object.password, (p) => p.path)) ?? ""
35
+ ? ((await (0, fs_1.fetchData)(object.password, (p) => p.path)) ?? "")
36
36
  : "",
37
37
  };
38
38
  }
@@ -26,7 +26,9 @@ function createCommand(config, action) {
26
26
  }
27
27
  else if (typeof flag === "string") {
28
28
  const flags = [
29
- option.shortFlag ? `-${option.shortFlag},` : "",
29
+ option.shortFlag
30
+ ? `${"-".repeat(option.shortFlag.length > 1 ? 2 : 1)}${option.shortFlag},`
31
+ : "",
30
32
  `--${name}`,
31
33
  option.value !== "boolean"
32
34
  ? option.value === "array"
@@ -30,7 +30,7 @@ class ProgressManager {
30
30
  ? this.tty
31
31
  ? `interval:${300}`
32
32
  : "interval"
33
- : options.mode ?? "interval";
33
+ : (options.mode ?? "interval");
34
34
  this.intervalMs = 1000;
35
35
  if (typeof mode === "string" && mode.startsWith("interval:")) {
36
36
  const [, ms] = mode.split(":");
package/lib/utils/tar.js CHANGED
@@ -156,8 +156,8 @@ function normalizeTarPath(path) {
156
156
  }
157
157
  async function extractTar(options) {
158
158
  let total = options.onEntry
159
- ? options.total ??
160
- (await listTar({ input: options.input, verbose: options.verbose }))
159
+ ? (options.total ??
160
+ (await listTar({ input: options.input, verbose: options.verbose })))
161
161
  : undefined;
162
162
  if (!(await (0, fs_1.existsDir)(options.output))) {
163
163
  if (options.verbose)
@@ -3,6 +3,7 @@ import { BasicProgress } from "./progress";
3
3
  export declare function resolvePath(path: string): string;
4
4
  export type FsOptions = {
5
5
  backend: string;
6
+ insecureTls?: boolean;
6
7
  };
7
8
  export declare abstract class AbstractFs {
8
9
  readonly options: FsOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.40.4",
3
+ "version": "0.41.1",
4
4
  "description": "Tool for creating and managing backups",
5
5
  "homepage": "https://github.com/swordev/datatruck#readme",
6
6
  "bugs": {
@@ -30,23 +30,23 @@
30
30
  "ajv": "^8.17.1",
31
31
  "async": "^3.2.6",
32
32
  "chalk": "^4.1.2",
33
- "commander": "^12.1.0",
34
- "croner": "^8.1.2",
33
+ "commander": "^14.0.0",
34
+ "croner": "^9.1.0",
35
35
  "dayjs": "^1.11.13",
36
- "fast-folder-size": "^2.2.0",
37
- "fast-glob": "^3.3.2",
38
- "listr2": "^8.2.5",
36
+ "fast-folder-size": "^2.4.0",
37
+ "fast-glob": "^3.3.3",
38
+ "listr2": "^9.0.1",
39
39
  "micromatch": "^4.0.8",
40
- "mongodb": "^6.9.0",
41
- "mysql2": "^3.11.3",
40
+ "mongodb": "^6.18.0",
41
+ "mysql2": "^3.14.3",
42
42
  "tty-table": "^4.2.3",
43
- "undici": "^6.19.8",
44
- "yaml": "^2.5.1"
43
+ "undici": "^7.14.0",
44
+ "yaml": "^2.8.1"
45
45
  },
46
46
  "devDependencies": {
47
- "@types/async": "^3.2.24",
47
+ "@types/async": "^3.2.25",
48
48
  "@types/micromatch": "^4.0.9",
49
- "mongodb-memory-server": "^10.0.1"
49
+ "mongodb-memory-server": "^10.2.0"
50
50
  },
51
51
  "optionalDependencies": {
52
52
  "ts-node": "^10.9.2"