@datatruck/cli 0.36.4 → 0.36.6
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/lib/utils/crypto.d.ts +1 -0
- package/lib/utils/crypto.js +7 -1
- package/lib/utils/datatruck/client.d.ts +7 -6
- package/lib/utils/datatruck/client.js +18 -15
- package/lib/utils/datatruck/repository-server.js +35 -47
- package/lib/utils/http.d.ts +16 -20
- package/lib/utils/http.js +131 -124
- package/lib/utils/math.d.ts +6 -0
- package/lib/utils/math.js +14 -1
- package/package.json +1 -1
package/lib/utils/crypto.d.ts
CHANGED
package/lib/utils/crypto.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calcFileHash = void 0;
|
|
3
|
+
exports.assertFileChecksum = exports.calcFileHash = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
5
|
const fs_1 = require("fs");
|
|
6
6
|
function calcFileHash(path, algorithm) {
|
|
@@ -13,3 +13,9 @@ function calcFileHash(path, algorithm) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
exports.calcFileHash = calcFileHash;
|
|
16
|
+
async function assertFileChecksum(path, checksum, algorithm) {
|
|
17
|
+
const fileChecksum = await calcFileHash(path, algorithm);
|
|
18
|
+
if (fileChecksum !== checksum)
|
|
19
|
+
throw new Error(`Invalid checksum file: ${checksum} != ${fileChecksum}`);
|
|
20
|
+
}
|
|
21
|
+
exports.assertFileChecksum = assertFileChecksum;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { DiskStats } from "../fs";
|
|
2
3
|
import { BasicProgress } from "../progress";
|
|
3
4
|
import { AbstractFs, FsOptions } from "../virtual-fs";
|
|
@@ -12,12 +13,12 @@ export declare class RemoteFs extends AbstractFs {
|
|
|
12
13
|
});
|
|
13
14
|
isLocal(): boolean;
|
|
14
15
|
protected fetchJson(name: string, params: any[]): Promise<any>;
|
|
15
|
-
protected post(name: string, params: any[], data: string): Promise<
|
|
16
|
-
existsDir(path: string): Promise<
|
|
17
|
-
rename(source: string, target: string): Promise<
|
|
18
|
-
mkdir(path: string): Promise<
|
|
19
|
-
readFile(path: string): Promise<
|
|
20
|
-
readdir(path: string): Promise<
|
|
16
|
+
protected post(name: string, params: any[], data: string): Promise<Response>;
|
|
17
|
+
existsDir(path: string): Promise<boolean>;
|
|
18
|
+
rename(source: string, target: string): Promise<void>;
|
|
19
|
+
mkdir(path: string): Promise<void>;
|
|
20
|
+
readFile(path: string): Promise<string>;
|
|
21
|
+
readdir(path: string): Promise<string[]>;
|
|
21
22
|
readFileIfExists(path: string): Promise<string | undefined>;
|
|
22
23
|
ensureEmptyDir(path: string): Promise<void>;
|
|
23
24
|
writeFile(path: string, contents: string): Promise<void>;
|
|
@@ -27,19 +27,19 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
29
|
async fetchJson(name, params) {
|
|
30
|
-
|
|
30
|
+
const url = (0, http_1.createHref)(`${this.url}/${name}`, {
|
|
31
|
+
params: JSON.stringify(params),
|
|
32
|
+
});
|
|
33
|
+
return await (0, http_1.fetchJson)(url, {
|
|
31
34
|
headers: this.headers,
|
|
32
|
-
query: {
|
|
33
|
-
params: JSON.stringify(params),
|
|
34
|
-
},
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
async post(name, params, data) {
|
|
38
|
-
|
|
38
|
+
const url = (0, http_1.createHref)(`${this.url}/${name}`, {
|
|
39
|
+
params: JSON.stringify(params),
|
|
40
|
+
});
|
|
41
|
+
return await (0, http_1.post)(url, data, {
|
|
39
42
|
headers: this.headers,
|
|
40
|
-
query: {
|
|
41
|
-
params: JSON.stringify(params),
|
|
42
|
-
},
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
async existsDir(path) {
|
|
@@ -67,7 +67,7 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
67
67
|
await this.post("writeFile", [path], contents);
|
|
68
68
|
}
|
|
69
69
|
async rmAll(path) {
|
|
70
|
-
await this.fetchJson("rmAll", [path]);
|
|
70
|
+
return await this.fetchJson("rmAll", [path]);
|
|
71
71
|
}
|
|
72
72
|
async fetchDiskStats(path) {
|
|
73
73
|
if (this.options.verbose)
|
|
@@ -77,20 +77,23 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
77
77
|
async upload(source, target) {
|
|
78
78
|
if (this.options.verbose)
|
|
79
79
|
(0, cli_1.logExec)("fs.upload", [source, target]);
|
|
80
|
-
|
|
80
|
+
const url = (0, http_1.createHref)(`${this.url}/upload`, {
|
|
81
|
+
params: JSON.stringify([target]),
|
|
82
|
+
});
|
|
83
|
+
return await (0, http_1.uploadFile)(url, source, {
|
|
81
84
|
headers: this.headers,
|
|
82
|
-
|
|
83
|
-
params: JSON.stringify([target]),
|
|
84
|
-
},
|
|
85
|
+
checksum: true,
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
88
|
async download(source, target, options = {}) {
|
|
88
89
|
if (this.options.verbose)
|
|
89
90
|
(0, cli_1.logExec)("fs.download", [source, target]);
|
|
90
|
-
|
|
91
|
+
const url = (0, http_1.createHref)(`${this.url}/download`, {
|
|
92
|
+
params: JSON.stringify([source]),
|
|
93
|
+
});
|
|
94
|
+
return await (0, http_1.downloadFile)(url, target, {
|
|
91
95
|
...options,
|
|
92
96
|
headers: this.headers,
|
|
93
|
-
query: { params: JSON.stringify([source]) },
|
|
94
97
|
});
|
|
95
98
|
}
|
|
96
99
|
}
|
|
@@ -4,9 +4,8 @@ exports.createDatatruckRepositoryServer = exports.headerKey = void 0;
|
|
|
4
4
|
const ConfigAction_1 = require("../../actions/ConfigAction");
|
|
5
5
|
const cli_1 = require("../cli");
|
|
6
6
|
const http_1 = require("../http");
|
|
7
|
+
const math_1 = require("../math");
|
|
7
8
|
const virtual_fs_1 = require("../virtual-fs");
|
|
8
|
-
const fs_1 = require("fs");
|
|
9
|
-
const promises_1 = require("fs/promises");
|
|
10
9
|
const http_2 = require("http");
|
|
11
10
|
exports.headerKey = {
|
|
12
11
|
user: "x-dtt-user",
|
|
@@ -58,60 +57,43 @@ const getRemoteAddress = (req, options) => {
|
|
|
58
57
|
: undefined) ?? req.socket.remoteAddress);
|
|
59
58
|
};
|
|
60
59
|
function createDatatruckRepositoryServer(inOptions, config = {}) {
|
|
60
|
+
const counter = new math_1.Counter();
|
|
61
61
|
return (0, http_2.createServer)(async (req, res) => {
|
|
62
|
+
const url = req.url || "";
|
|
63
|
+
if (url === "/" || url === "/favicon.ico")
|
|
64
|
+
return res.end();
|
|
65
|
+
const id = counter.next();
|
|
66
|
+
let requestError;
|
|
67
|
+
let responseError;
|
|
68
|
+
req.on("error", (error) => (requestError = error));
|
|
69
|
+
res.on("error", (error) => (responseError = error));
|
|
62
70
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!repository || !action) {
|
|
67
|
-
res.statusCode = 404;
|
|
68
|
-
return res.end();
|
|
69
|
-
}
|
|
71
|
+
const { repository, action, params } = parseUrl(url);
|
|
72
|
+
if (!repository || !action)
|
|
73
|
+
return res.writeHead(404);
|
|
70
74
|
const fileOptions = config.configPath
|
|
71
75
|
? (await ConfigAction_1.ConfigAction.findAndParseFile(config.configPath)).server
|
|
72
76
|
?.repository
|
|
73
77
|
: undefined;
|
|
74
78
|
const options = fileOptions ?? inOptions;
|
|
75
79
|
const backend = findRepositoryBackend(req, repository, options);
|
|
76
|
-
if (!backend)
|
|
77
|
-
res.
|
|
78
|
-
return res.end();
|
|
79
|
-
}
|
|
80
|
+
if (!backend)
|
|
81
|
+
return res.writeHead(401);
|
|
80
82
|
if (config.log)
|
|
81
|
-
(0, cli_1.logJson)("repository-server", "request", {
|
|
82
|
-
|
|
83
|
-
url: req.url,
|
|
84
|
-
});
|
|
85
|
-
const fs = new virtual_fs_1.LocalFs({
|
|
86
|
-
backend: backend.path,
|
|
87
|
-
});
|
|
83
|
+
(0, cli_1.logJson)("repository-server", "request", { id, repository, url });
|
|
84
|
+
const fs = new virtual_fs_1.LocalFs({ backend: backend.path });
|
|
88
85
|
if (action === "comcheck") {
|
|
89
86
|
res.write(JSON.stringify({ success: true }));
|
|
90
87
|
}
|
|
91
88
|
else if (action === "upload") {
|
|
92
89
|
const [target] = params;
|
|
93
90
|
const path = fs.resolvePath(target);
|
|
94
|
-
|
|
95
|
-
req.pipe(file);
|
|
96
|
-
await new Promise((resolve, reject) => {
|
|
97
|
-
req.on("error", reject);
|
|
98
|
-
file.on("error", reject);
|
|
99
|
-
file.on("close", resolve);
|
|
100
|
-
});
|
|
91
|
+
await (0, http_1.recvFile)(req, res, path);
|
|
101
92
|
}
|
|
102
93
|
else if (action === "download") {
|
|
103
94
|
const [target] = params;
|
|
104
95
|
const path = fs.resolvePath(target);
|
|
105
|
-
|
|
106
|
-
const fileStat = await (0, promises_1.stat)(path);
|
|
107
|
-
res.setHeader("Content-Length", fileStat.size);
|
|
108
|
-
file.pipe(res);
|
|
109
|
-
await new Promise((resolve, reject) => {
|
|
110
|
-
req.on("error", reject);
|
|
111
|
-
file.on("error", reject);
|
|
112
|
-
res.on("error", reject);
|
|
113
|
-
res.on("close", resolve);
|
|
114
|
-
});
|
|
96
|
+
await (0, http_1.sendFile)(req, res, path);
|
|
115
97
|
}
|
|
116
98
|
else if (action === "writeFile") {
|
|
117
99
|
const data = await (0, http_1.readRequestData)(req);
|
|
@@ -127,21 +109,27 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
|
|
|
127
109
|
res.write(JSON.stringify(json));
|
|
128
110
|
}
|
|
129
111
|
if (config.log)
|
|
130
|
-
(0, cli_1.logJson)("repository-server", "request finished", {
|
|
131
|
-
url: req.url,
|
|
132
|
-
});
|
|
133
|
-
res.end();
|
|
112
|
+
(0, cli_1.logJson)("repository-server", "request finished", { id });
|
|
134
113
|
}
|
|
135
114
|
catch (error) {
|
|
136
115
|
if (config.log) {
|
|
137
|
-
(0, cli_1.logJson)("repository-server", "request failed", {
|
|
138
|
-
url: req.url,
|
|
139
|
-
});
|
|
116
|
+
(0, cli_1.logJson)("repository-server", "request failed", { id });
|
|
140
117
|
console.error(error);
|
|
141
118
|
}
|
|
142
|
-
res.
|
|
143
|
-
|
|
144
|
-
|
|
119
|
+
if (!res.writableEnded && !res.headersSent)
|
|
120
|
+
res.writeHead(500, error.message);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
if (requestError) {
|
|
124
|
+
(0, cli_1.logJson)("repository-server", "request error", { id });
|
|
125
|
+
console.error(requestError);
|
|
126
|
+
}
|
|
127
|
+
if (responseError) {
|
|
128
|
+
(0, cli_1.logJson)("repository-server", "response error", { id });
|
|
129
|
+
console.error(responseError);
|
|
130
|
+
}
|
|
131
|
+
if (!res.writableEnded)
|
|
132
|
+
res.end();
|
|
145
133
|
}
|
|
146
134
|
});
|
|
147
135
|
}
|
package/lib/utils/http.d.ts
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { BasicProgress } from "./progress";
|
|
3
|
-
import { IncomingMessage, Server } from "http";
|
|
4
|
+
import { IncomingMessage, Server, ServerResponse } from "http";
|
|
5
|
+
export declare function createHref(inUrl: string, query?: Record<string, string>): string;
|
|
4
6
|
export declare function closeServer(server: Server): Promise<void>;
|
|
5
7
|
export declare function readRequestData(req: IncomingMessage): Promise<string | undefined>;
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export declare function post(url: string, data: string, options?: {
|
|
17
|
-
headers?: Record<string, string>;
|
|
18
|
-
query?: Record<string, string>;
|
|
8
|
+
export declare const safeFetch: typeof fetch;
|
|
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>;
|
|
11
|
+
export declare function parseContentLength(value: string | undefined): number;
|
|
12
|
+
export declare function sendFile(req: IncomingMessage, res: ServerResponse, path: string, options?: {
|
|
13
|
+
end?: boolean;
|
|
14
|
+
checksum?: boolean;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
export declare function recvFile(req: IncomingMessage, res: ServerResponse, path: string, options?: {
|
|
17
|
+
end?: boolean;
|
|
19
18
|
}): Promise<void>;
|
|
20
|
-
export declare function downloadFile(url: string, output: string, options?: {
|
|
21
|
-
headers?: Record<string, string>;
|
|
22
|
-
query?: Record<string, string>;
|
|
19
|
+
export declare function downloadFile(url: string, output: string, options?: Omit<RequestInit, "signal"> & {
|
|
23
20
|
timeout?: number;
|
|
24
21
|
onProgress?: (progress: BasicProgress) => void;
|
|
25
22
|
}): Promise<{
|
|
26
23
|
bytes: number;
|
|
27
24
|
}>;
|
|
28
|
-
export declare function uploadFile(url: string, path: string, options?: {
|
|
29
|
-
|
|
30
|
-
query?: Record<string, string>;
|
|
25
|
+
export declare function uploadFile(url: string, path: string, options?: Omit<RequestInit, "method" | "body"> & {
|
|
26
|
+
checksum?: boolean;
|
|
31
27
|
}): Promise<void>;
|
package/lib/utils/http.js
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.uploadFile = exports.downloadFile = exports.post = exports.fetchJson = exports.
|
|
3
|
+
exports.uploadFile = exports.downloadFile = exports.recvFile = exports.sendFile = exports.parseContentLength = exports.post = exports.fetchJson = exports.safeFetch = exports.readRequestData = exports.closeServer = exports.createHref = void 0;
|
|
4
|
+
const crypto_1 = require("./crypto");
|
|
4
5
|
const math_1 = require("./math");
|
|
5
6
|
const fs_1 = require("fs");
|
|
6
7
|
const promises_1 = require("fs/promises");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
return url.startsWith("https://")
|
|
11
|
-
? (0, https_1.request)(url, options, callback)
|
|
12
|
-
: (0, http_1.request)(url, options, callback);
|
|
13
|
-
};
|
|
14
|
-
function href(inUrl, query) {
|
|
8
|
+
const stream_1 = require("stream");
|
|
9
|
+
const promises_2 = require("stream/promises");
|
|
10
|
+
function createHref(inUrl, query) {
|
|
15
11
|
const url = new URL(inUrl);
|
|
16
12
|
for (const key in query || {})
|
|
17
13
|
url.searchParams.set(key, query[key]);
|
|
18
14
|
return url.href;
|
|
19
15
|
}
|
|
16
|
+
exports.createHref = createHref;
|
|
20
17
|
async function closeServer(server) {
|
|
21
18
|
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
|
22
19
|
}
|
|
@@ -37,140 +34,150 @@ function readRequestData(req) {
|
|
|
37
34
|
});
|
|
38
35
|
}
|
|
39
36
|
exports.readRequestData = readRequestData;
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}, (res) => {
|
|
48
|
-
if (throwStatusCodeError && res.statusCode !== 200)
|
|
49
|
-
return reject(new Error(`GET failed: ${res.statusCode} ${res.statusMessage}`));
|
|
50
|
-
res
|
|
51
|
-
.on("data", (chunk) => {
|
|
52
|
-
if (data === undefined)
|
|
53
|
-
data = "";
|
|
54
|
-
data += chunk;
|
|
55
|
-
})
|
|
56
|
-
.on("error", reject)
|
|
57
|
-
.on("close", () => {
|
|
58
|
-
resolve({ data, status: res.statusCode });
|
|
59
|
-
});
|
|
60
|
-
})
|
|
61
|
-
.on("error", reject)
|
|
62
|
-
.end();
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
exports.fetch = fetch;
|
|
37
|
+
const safeFetch = async (...args) => {
|
|
38
|
+
const res = await fetch(...args);
|
|
39
|
+
if (res.status !== 200)
|
|
40
|
+
throw new Error(`Fetch request failed: ${res.status} ${res.statusText}`);
|
|
41
|
+
return res;
|
|
42
|
+
};
|
|
43
|
+
exports.safeFetch = safeFetch;
|
|
66
44
|
async function fetchJson(url, options = {}) {
|
|
67
|
-
const res = await
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return JSON.parse(res.data);
|
|
45
|
+
const res = await (0, exports.safeFetch)(url, options);
|
|
46
|
+
const data = await res.text();
|
|
47
|
+
return data.length ? JSON.parse(data) : undefined;
|
|
71
48
|
}
|
|
72
49
|
exports.fetchJson = fetchJson;
|
|
73
50
|
async function post(url, data, options = {}) {
|
|
74
|
-
await
|
|
75
|
-
|
|
51
|
+
return await (0, exports.safeFetch)(url, { ...options, method: "POST", body: data });
|
|
52
|
+
}
|
|
53
|
+
exports.post = post;
|
|
54
|
+
function parseContentLength(value) {
|
|
55
|
+
if (!value || !/^\d+$/.test(value))
|
|
56
|
+
throw new Error(`Invalid 'content-length': ${value}`);
|
|
57
|
+
return Number(value);
|
|
58
|
+
}
|
|
59
|
+
exports.parseContentLength = parseContentLength;
|
|
60
|
+
async function sendFile(req, res, path, options = {}) {
|
|
61
|
+
let file;
|
|
62
|
+
try {
|
|
63
|
+
file = (0, fs_1.createReadStream)(path);
|
|
64
|
+
const fileStat = await (0, promises_1.stat)(path);
|
|
65
|
+
res.setHeader("Content-Length", fileStat.size);
|
|
66
|
+
if (options.checksum)
|
|
67
|
+
res.setHeader("x-checksum", await (0, crypto_1.calcFileHash)(path, "sha1"));
|
|
68
|
+
file.pipe(res);
|
|
69
|
+
await new Promise((resolve, reject) => {
|
|
70
|
+
file.on("error", reject);
|
|
71
|
+
req.on("error", reject);
|
|
72
|
+
res.on("error", reject).on("close", resolve);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
file?.close();
|
|
77
|
+
if (options.end)
|
|
78
|
+
res.end();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.sendFile = sendFile;
|
|
82
|
+
async function recvFile(req, res, path, options = {}) {
|
|
83
|
+
let file;
|
|
84
|
+
try {
|
|
85
|
+
file = (0, fs_1.createWriteStream)(path);
|
|
86
|
+
req.pipe(file);
|
|
87
|
+
await new Promise((resolve, reject) => {
|
|
88
|
+
file.on("error", reject).on("close", resolve);
|
|
89
|
+
req.on("error", reject);
|
|
76
90
|
res.on("error", reject);
|
|
77
|
-
if (res.statusCode !== 200) {
|
|
78
|
-
reject(new Error(`Post failed: ${res.statusCode} ${res.statusMessage}`));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
resolve();
|
|
82
|
-
}
|
|
83
91
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
92
|
+
const checksum = res.getHeader("x-checksum");
|
|
93
|
+
if (typeof checksum === "string")
|
|
94
|
+
await (0, crypto_1.assertFileChecksum)(path, checksum, "sha1");
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
file?.close();
|
|
98
|
+
if (options.end)
|
|
99
|
+
res.end();
|
|
100
|
+
}
|
|
88
101
|
}
|
|
89
|
-
exports.
|
|
102
|
+
exports.recvFile = recvFile;
|
|
90
103
|
async function downloadFile(url, output, options = {}) {
|
|
91
|
-
const timeout
|
|
104
|
+
const { timeout, onProgress, ...fetchOptions } = options;
|
|
92
105
|
const file = (0, fs_1.createWriteStream)(output);
|
|
93
|
-
let
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
percent: (0, math_1.progressPercent)(total, current),
|
|
109
|
-
current,
|
|
110
|
-
total,
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
res
|
|
115
|
-
.on("error", async (error) => {
|
|
106
|
+
let checksum;
|
|
107
|
+
const length = { total: 0, current: 0 };
|
|
108
|
+
let requestError;
|
|
109
|
+
try {
|
|
110
|
+
const res = await (0, exports.safeFetch)(url, {
|
|
111
|
+
...fetchOptions,
|
|
112
|
+
signal: AbortSignal.timeout(timeout ?? 3600 * 1000), // 60m
|
|
113
|
+
});
|
|
114
|
+
length.total = parseContentLength(res.headers.get("content-length") ?? undefined);
|
|
115
|
+
checksum = res.headers.get("x-checksum") ?? undefined;
|
|
116
|
+
const body = stream_1.Readable.fromWeb(res.body);
|
|
117
|
+
const progress = onProgress &&
|
|
118
|
+
new stream_1.Transform({
|
|
119
|
+
transform(chunk, encoding, callback) {
|
|
120
|
+
let error;
|
|
116
121
|
try {
|
|
117
|
-
|
|
122
|
+
length.current += chunk.byteLength;
|
|
123
|
+
onProgress({
|
|
124
|
+
percent: (0, math_1.progressPercent)(length.total, length.current),
|
|
125
|
+
current: length.current,
|
|
126
|
+
total: length.total,
|
|
127
|
+
});
|
|
118
128
|
}
|
|
119
|
-
catch (
|
|
120
|
-
|
|
121
|
-
await (0, promises_1.unlink)(output);
|
|
129
|
+
catch (progressError) {
|
|
130
|
+
error = progressError;
|
|
122
131
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
});
|
|
149
|
-
const { size: bytes } = await (0, promises_1.stat)(output);
|
|
150
|
-
if (total !== bytes)
|
|
151
|
-
throw new Error(`Invalid download size: ${total} != ${bytes}`);
|
|
152
|
-
return { bytes };
|
|
132
|
+
callback(error, chunk);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
if (progress) {
|
|
136
|
+
await (0, promises_2.pipeline)(body, progress, file);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
await (0, promises_2.pipeline)(body, file);
|
|
140
|
+
}
|
|
141
|
+
const { size: fileLength } = await (0, promises_1.stat)(output);
|
|
142
|
+
if (length.total !== fileLength)
|
|
143
|
+
throw new Error(`Invalid download size: ${length.total} != ${fileLength}`);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
try {
|
|
147
|
+
await (0, promises_1.unlink)(output);
|
|
148
|
+
}
|
|
149
|
+
catch (_) { }
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
if (checksum)
|
|
153
|
+
await (0, crypto_1.assertFileChecksum)(output, checksum, "sha1");
|
|
154
|
+
if (requestError)
|
|
155
|
+
throw requestError;
|
|
156
|
+
return { bytes: length.total };
|
|
153
157
|
}
|
|
154
158
|
exports.downloadFile = downloadFile;
|
|
155
159
|
async function uploadFile(url, path, options = {}) {
|
|
156
160
|
const { size } = await (0, promises_1.stat)(path);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const
|
|
161
|
+
const file = (0, fs_1.createReadStream)(path);
|
|
162
|
+
try {
|
|
163
|
+
const res = await fetch(url, {
|
|
164
|
+
...options,
|
|
160
165
|
method: "POST",
|
|
166
|
+
duplex: "half",
|
|
161
167
|
headers: {
|
|
162
168
|
...options.headers,
|
|
163
|
-
"Content-
|
|
169
|
+
"Content-Length": size.toString(),
|
|
170
|
+
...(options.checksum && {
|
|
171
|
+
"x-checksum": await (0, crypto_1.calcFileHash)(path, "sha1"),
|
|
172
|
+
}),
|
|
164
173
|
},
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
readStream.on("error", reject).pipe(req);
|
|
174
|
-
});
|
|
174
|
+
body: file,
|
|
175
|
+
});
|
|
176
|
+
if (res.status !== 200)
|
|
177
|
+
new Error(`Upload failed: ${res.status} ${res.statusText}`);
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
file.close();
|
|
181
|
+
}
|
|
175
182
|
}
|
|
176
183
|
exports.uploadFile = uploadFile;
|
package/lib/utils/math.d.ts
CHANGED
package/lib/utils/math.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.progressPercent = void 0;
|
|
3
|
+
exports.Counter = exports.progressPercent = void 0;
|
|
4
4
|
function progressPercent(total, current) {
|
|
5
5
|
if (total === 0 && current === 0)
|
|
6
6
|
return 0;
|
|
7
7
|
return Number(((current / total) * 100).toFixed(2));
|
|
8
8
|
}
|
|
9
9
|
exports.progressPercent = progressPercent;
|
|
10
|
+
class Counter {
|
|
11
|
+
maxValue;
|
|
12
|
+
value = 0;
|
|
13
|
+
constructor(maxValue = 4294967295) {
|
|
14
|
+
this.maxValue = maxValue;
|
|
15
|
+
}
|
|
16
|
+
next() {
|
|
17
|
+
if (this.maxValue && this.value >= this.maxValue)
|
|
18
|
+
this.value = 0;
|
|
19
|
+
return ++this.value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.Counter = Counter;
|