@internxt/cli 1.6.1 → 1.6.2
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 +27 -27
- package/dist/commands/add-cert.js +2 -2
- package/dist/commands/logs.js +3 -3
- package/dist/commands/upload-file.d.ts +1 -1
- package/dist/commands/upload-file.js +55 -41
- package/dist/constants/configs.d.ts +12 -0
- package/dist/constants/configs.js +20 -0
- package/dist/services/auth.service.js +11 -6
- package/dist/services/config.service.d.ts +0 -12
- package/dist/services/config.service.js +33 -38
- package/dist/services/network/upload/upload-facade.service.js +1 -1
- package/dist/services/network/upload/upload-file.service.d.ts +5 -4
- package/dist/services/network/upload/upload-file.service.js +51 -29
- package/dist/services/network/upload/upload.types.d.ts +2 -2
- package/dist/services/network/upload/upload.types.js +1 -1
- package/dist/services/sdk-manager.service.d.ts +2 -2
- package/dist/services/sdk-manager.service.js +2 -2
- package/dist/services/validation.service.d.ts +5 -0
- package/dist/services/validation.service.js +22 -18
- package/dist/utils/cli.utils.d.ts +3 -0
- package/dist/utils/cli.utils.js +37 -3
- package/dist/utils/errors.utils.d.ts +1 -0
- package/dist/utils/errors.utils.js +8 -2
- package/dist/utils/logger.utils.js +5 -5
- package/dist/utils/network.utils.js +17 -18
- package/dist/utils/thumbnail.utils.d.ts +1 -1
- package/dist/utils/thumbnail.utils.js +8 -8
- package/dist/utils/xml.utils.d.ts +1 -1
- package/dist/utils/xml.utils.js +1 -1
- package/dist/webdav/handlers/GET.handler.js +0 -2
- package/dist/webdav/handlers/PUT.handler.js +56 -41
- package/dist/webdav/middewares/errors.middleware.js +1 -1
- package/dist/webdav/webdav-server.js +4 -4
- package/oclif.manifest.json +1 -1
- package/package.json +10 -10
|
@@ -25,30 +25,30 @@ const imageExtensions = {
|
|
|
25
25
|
const pdfExtensions = {
|
|
26
26
|
pdf: ['pdf'],
|
|
27
27
|
};
|
|
28
|
-
const thumbnailableImageExtension = [
|
|
28
|
+
const thumbnailableImageExtension = new Set([
|
|
29
29
|
...imageExtensions['jpg'],
|
|
30
30
|
...imageExtensions['png'],
|
|
31
31
|
...imageExtensions['webp'],
|
|
32
32
|
...imageExtensions['gif'],
|
|
33
33
|
...imageExtensions['tiff'],
|
|
34
|
-
];
|
|
35
|
-
const thumbnailablePdfExtension = pdfExtensions['pdf'];
|
|
36
|
-
const thumbnailableExtension =
|
|
34
|
+
]);
|
|
35
|
+
const thumbnailablePdfExtension = new Set(pdfExtensions['pdf']);
|
|
36
|
+
const thumbnailableExtension = new Set(thumbnailableImageExtension);
|
|
37
37
|
const isFileThumbnailable = (fileType) => {
|
|
38
|
-
return fileType.trim().length > 0 && thumbnailableExtension.
|
|
38
|
+
return fileType.trim().length > 0 && thumbnailableExtension.has(fileType.trim().toLowerCase());
|
|
39
39
|
};
|
|
40
40
|
exports.isFileThumbnailable = isFileThumbnailable;
|
|
41
41
|
const isPDFThumbnailable = (fileType) => {
|
|
42
|
-
return fileType.trim().length > 0 && thumbnailablePdfExtension.
|
|
42
|
+
return fileType.trim().length > 0 && thumbnailablePdfExtension.has(fileType.trim().toLowerCase());
|
|
43
43
|
};
|
|
44
44
|
exports.isPDFThumbnailable = isPDFThumbnailable;
|
|
45
45
|
const isImageThumbnailable = (fileType) => {
|
|
46
|
-
return fileType.trim().length > 0 && thumbnailableImageExtension.
|
|
46
|
+
return fileType.trim().length > 0 && thumbnailableImageExtension.has(fileType.trim().toLowerCase());
|
|
47
47
|
};
|
|
48
48
|
exports.isImageThumbnailable = isImageThumbnailable;
|
|
49
49
|
const tryUploadThumbnail = async ({ bufferStream, fileType, userBucket, fileUuid, networkFacade, }) => {
|
|
50
50
|
try {
|
|
51
|
-
const thumbnailBuffer = bufferStream
|
|
51
|
+
const thumbnailBuffer = bufferStream?.getBuffer();
|
|
52
52
|
if (thumbnailBuffer) {
|
|
53
53
|
await thumbnail_service_1.ThumbnailService.instance.uploadThumbnail(thumbnailBuffer, fileType, userBucket, fileUuid, networkFacade);
|
|
54
54
|
}
|
|
@@ -2,7 +2,7 @@ import { X2jOptions, XmlBuilderOptions } from '../types/fast-xml-parser.types';
|
|
|
2
2
|
export declare class XMLUtils {
|
|
3
3
|
static readonly DEFAULT_NAMESPACE_LETTER = "D";
|
|
4
4
|
static toJSON(xml: string, options?: X2jOptions): any;
|
|
5
|
-
static toXML(object: object, options
|
|
5
|
+
static toXML(object: object, options: XmlBuilderOptions): string;
|
|
6
6
|
static toWebDavXML(object: object, options: XmlBuilderOptions, rootObject?: string): string;
|
|
7
7
|
static addDefaultNamespace(key: string): string;
|
|
8
8
|
static encodeWebDavUri(uri: string): string;
|
package/dist/utils/xml.utils.js
CHANGED
|
@@ -8,7 +8,7 @@ class XMLUtils {
|
|
|
8
8
|
const parser = new fast_xml_parser_1.XMLParser(options);
|
|
9
9
|
return parser.parse(xml);
|
|
10
10
|
}
|
|
11
|
-
static toXML(object, options
|
|
11
|
+
static toXML(object, options) {
|
|
12
12
|
const builder = new fast_xml_parser_1.XMLBuilder(options);
|
|
13
13
|
return builder.build(object);
|
|
14
14
|
}
|
|
@@ -14,8 +14,6 @@ class GETRequestHandler {
|
|
|
14
14
|
handle = async (req, res) => {
|
|
15
15
|
const { driveFileService, authService, networkFacade } = this.dependencies;
|
|
16
16
|
const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
|
|
17
|
-
if (resource.name.startsWith('._'))
|
|
18
|
-
throw new errors_utils_1.NotFoundError('File not found');
|
|
19
17
|
logger_utils_1.webdavLogger.info(`[GET] Request received item at ${resource.url}`);
|
|
20
18
|
const driveFile = await webdav_utils_1.WebDavUtils.getDriveFileFromResource({
|
|
21
19
|
url: resource.url,
|
|
@@ -9,7 +9,6 @@ const types_1 = require("@internxt/sdk/dist/drive/storage/types");
|
|
|
9
9
|
const cli_utils_1 = require("../../utils/cli.utils");
|
|
10
10
|
const stream_utils_1 = require("../../utils/stream.utils");
|
|
11
11
|
const thumbnail_utils_1 = require("../../utils/thumbnail.utils");
|
|
12
|
-
const thumbnail_service_1 = require("../../services/thumbnail.service");
|
|
13
12
|
const async_utils_1 = require("../../utils/async.utils");
|
|
14
13
|
class PUTRequestHandler {
|
|
15
14
|
dependencies;
|
|
@@ -17,9 +16,9 @@ class PUTRequestHandler {
|
|
|
17
16
|
this.dependencies = dependencies;
|
|
18
17
|
}
|
|
19
18
|
handle = async (req, res) => {
|
|
20
|
-
|
|
21
|
-
if (!contentLength || isNaN(contentLength) || contentLength <= 0) {
|
|
22
|
-
|
|
19
|
+
let contentLength = Number(req.headers['content-length']);
|
|
20
|
+
if (!contentLength || Number.isNaN(contentLength) || contentLength <= 0) {
|
|
21
|
+
contentLength = 0;
|
|
23
22
|
}
|
|
24
23
|
const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
|
|
25
24
|
const driveFileItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
|
|
@@ -31,7 +30,12 @@ class PUTRequestHandler {
|
|
|
31
30
|
throw new errors_utils_1.NotFoundError('Folders cannot be created with PUT. Use MKCOL instead.');
|
|
32
31
|
}
|
|
33
32
|
logger_utils_1.webdavLogger.info(`[PUT] Request received for file at ${resource.url}`);
|
|
34
|
-
logger_utils_1.webdavLogger.info(`[PUT] Uploading '${resource.name}' to '${resource.parentPath}'`);
|
|
33
|
+
logger_utils_1.webdavLogger.info(`[PUT] Uploading '${resource.name}' (${cli_utils_1.CLIUtils.formatBytesToString(contentLength)}) to '${resource.parentPath}'`);
|
|
34
|
+
const timings = {
|
|
35
|
+
networkUpload: 0,
|
|
36
|
+
driveUpload: 0,
|
|
37
|
+
thumbnailUpload: 0,
|
|
38
|
+
};
|
|
35
39
|
const parentDriveFolderItem = (await this.dependencies.webDavFolderService.getDriveFolderItemFromPath(resource.parentPath)) ??
|
|
36
40
|
(await this.dependencies.webDavFolderService.createParentPathOrThrow(resource.parentPath));
|
|
37
41
|
try {
|
|
@@ -46,7 +50,6 @@ class PUTRequestHandler {
|
|
|
46
50
|
}
|
|
47
51
|
const { user } = await this.dependencies.authService.getAuthDetails();
|
|
48
52
|
const fileType = resource.path.ext.replace('.', '');
|
|
49
|
-
const timer = cli_utils_1.CLIUtils.timer();
|
|
50
53
|
let bufferStream;
|
|
51
54
|
let fileStream = req;
|
|
52
55
|
const isThumbnailable = (0, thumbnail_utils_1.isFileThumbnailable)(fileType);
|
|
@@ -54,30 +57,36 @@ class PUTRequestHandler {
|
|
|
54
57
|
bufferStream = new stream_utils_1.BufferStream();
|
|
55
58
|
fileStream = req.pipe(bufferStream);
|
|
56
59
|
}
|
|
57
|
-
let
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const fileId = await new Promise((resolve, reject) => {
|
|
64
|
-
const state = this.dependencies.networkFacade.uploadFile(fileStream, contentLength, user.bucket, (err, res) => {
|
|
65
|
-
if (err) {
|
|
66
|
-
aborted = true;
|
|
67
|
-
return reject(err);
|
|
68
|
-
}
|
|
69
|
-
resolve(res);
|
|
70
|
-
}, progressCallback);
|
|
71
|
-
res.on('close', async () => {
|
|
72
|
-
aborted = true;
|
|
73
|
-
if (!uploaded) {
|
|
74
|
-
logger_utils_1.webdavLogger.info('[PUT] ❌ HTTP Client has been disconnected, res has been closed.');
|
|
75
|
-
state.stop();
|
|
60
|
+
let fileId;
|
|
61
|
+
if (contentLength > 0) {
|
|
62
|
+
let uploaded = false, aborted = false;
|
|
63
|
+
const progressCallback = (progress) => {
|
|
64
|
+
if (!uploaded && !aborted) {
|
|
65
|
+
logger_utils_1.webdavLogger.info(`[PUT] Upload progress for file ${resource.name}: ${(progress * 100).toFixed(2)}%`);
|
|
76
66
|
}
|
|
67
|
+
};
|
|
68
|
+
const networkUploadTimer = cli_utils_1.CLIUtils.timer();
|
|
69
|
+
fileId = await new Promise((resolve, reject) => {
|
|
70
|
+
const state = this.dependencies.networkFacade.uploadFile(fileStream, contentLength, user.bucket, (err, res) => {
|
|
71
|
+
if (err) {
|
|
72
|
+
aborted = true;
|
|
73
|
+
return reject(err);
|
|
74
|
+
}
|
|
75
|
+
resolve(res);
|
|
76
|
+
}, progressCallback);
|
|
77
|
+
res.on('close', async () => {
|
|
78
|
+
aborted = true;
|
|
79
|
+
if (!uploaded) {
|
|
80
|
+
logger_utils_1.webdavLogger.info('[PUT] ❌ HTTP Client has been disconnected, res has been closed.');
|
|
81
|
+
state.stop();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
77
84
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
uploaded = true;
|
|
86
|
+
timings.networkUpload = networkUploadTimer.stop();
|
|
87
|
+
logger_utils_1.webdavLogger.info('[PUT] ✅ File uploaded to network');
|
|
88
|
+
}
|
|
89
|
+
const driveTimer = cli_utils_1.CLIUtils.timer();
|
|
81
90
|
const file = await drive_file_service_1.DriveFileService.instance.createFile({
|
|
82
91
|
plainName: resource.path.name,
|
|
83
92
|
type: fileType,
|
|
@@ -87,21 +96,27 @@ class PUTRequestHandler {
|
|
|
87
96
|
bucket: user.bucket,
|
|
88
97
|
encryptVersion: types_1.EncryptionVersion.Aes03,
|
|
89
98
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
timings.driveUpload = driveTimer.stop();
|
|
100
|
+
const thumbnailTimer = cli_utils_1.CLIUtils.timer();
|
|
101
|
+
if (contentLength > 0 && isThumbnailable && bufferStream) {
|
|
102
|
+
void (0, thumbnail_utils_1.tryUploadThumbnail)({
|
|
103
|
+
bufferStream,
|
|
104
|
+
fileType,
|
|
105
|
+
userBucket: user.bucket,
|
|
106
|
+
fileUuid: file.uuid,
|
|
107
|
+
networkFacade: this.dependencies.networkFacade,
|
|
108
|
+
});
|
|
100
109
|
}
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
timings.thumbnailUpload = thumbnailTimer.stop();
|
|
111
|
+
const totalTime = Object.values(timings).reduce((sum, time) => sum + time, 0);
|
|
112
|
+
const throughputMBps = cli_utils_1.CLIUtils.calculateThroughputMBps(contentLength, timings.networkUpload);
|
|
113
|
+
logger_utils_1.webdavLogger.info(`[PUT] ✅ File uploaded in ${cli_utils_1.CLIUtils.formatDuration(totalTime)} to Internxt Drive`);
|
|
114
|
+
logger_utils_1.webdavLogger.info(`[PUT] Timing breakdown:\n
|
|
115
|
+
Network upload: ${cli_utils_1.CLIUtils.formatDuration(timings.networkUpload)} (${throughputMBps.toFixed(2)} MB/s)\n
|
|
116
|
+
Drive upload: ${cli_utils_1.CLIUtils.formatDuration(timings.driveUpload)}\n
|
|
117
|
+
Thumbnail: ${cli_utils_1.CLIUtils.formatDuration(timings.thumbnailUpload)}\n`);
|
|
103
118
|
await async_utils_1.AsyncUtils.sleep(500);
|
|
104
|
-
logger_utils_1.webdavLogger.info(`[PUT] [RESPONSE-201] ${resource.url} - Returning 201 Created after ${
|
|
119
|
+
logger_utils_1.webdavLogger.info(`[PUT] [RESPONSE-201] ${resource.url} - Returning 201 Created after ${cli_utils_1.CLIUtils.formatDuration(totalTime)}`);
|
|
105
120
|
res.status(201).send();
|
|
106
121
|
};
|
|
107
122
|
}
|
|
@@ -16,7 +16,7 @@ const ErrorHandlingMiddleware = (err, req, res, _) => {
|
|
|
16
16
|
[xml_utils_1.XMLUtils.addDefaultNamespace('responsedescription')]: message,
|
|
17
17
|
}, {}, 'error');
|
|
18
18
|
let statusCode = 500;
|
|
19
|
-
if ('statusCode' in err && !isNaN(err.statusCode)) {
|
|
19
|
+
if ('statusCode' in err && !Number.isNaN(err.statusCode)) {
|
|
20
20
|
statusCode = err.statusCode;
|
|
21
21
|
}
|
|
22
22
|
res.status(statusCode).send(errorBodyXML);
|
|
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.WebDavServer = void 0;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const node_https_1 = __importDefault(require("node:https"));
|
|
8
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
9
9
|
const config_service_1 = require("../services/config.service");
|
|
10
10
|
const OPTIONS_handler_1 = require("./handlers/OPTIONS.handler");
|
|
11
11
|
const PROPFIND_handler_1 = require("./handlers/PROPFIND.handler");
|
|
@@ -136,11 +136,11 @@ class WebDavServer {
|
|
|
136
136
|
const plainHttp = configs.protocol === 'http';
|
|
137
137
|
let server;
|
|
138
138
|
if (plainHttp) {
|
|
139
|
-
server =
|
|
139
|
+
server = node_http_1.default.createServer(this.app);
|
|
140
140
|
}
|
|
141
141
|
else {
|
|
142
142
|
const httpsCerts = await network_utils_1.NetworkUtils.getWebdavSSLCerts(configs);
|
|
143
|
-
server =
|
|
143
|
+
server = node_https_1.default.createServer(httpsCerts, this.app);
|
|
144
144
|
}
|
|
145
145
|
server.requestTimeout = configs.timeoutMinutes * 60 * 1000;
|
|
146
146
|
server.listen(Number(configs.port), configs.host, undefined, () => {
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Internxt <hello@internxt.com>",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Internxt CLI to manage your encrypted storage",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "yarn clean && tsc",
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
"/oclif.manifest.json"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@inquirer/prompts": "8.
|
|
39
|
+
"@inquirer/prompts": "8.2.0",
|
|
40
40
|
"@internxt/inxt-js": "2.2.9",
|
|
41
41
|
"@internxt/lib": "1.4.1",
|
|
42
|
-
"@internxt/sdk": "1.
|
|
42
|
+
"@internxt/sdk": "1.12.0",
|
|
43
43
|
"@oclif/core": "4.8.0",
|
|
44
44
|
"@oclif/plugin-autocomplete": "3.2.39",
|
|
45
45
|
"axios": "1.13.2",
|
|
46
46
|
"bip39": "3.1.0",
|
|
47
|
-
"body-parser": "2.2.
|
|
47
|
+
"body-parser": "2.2.2",
|
|
48
48
|
"cli-progress": "3.12.0",
|
|
49
49
|
"dayjs": "1.11.19",
|
|
50
50
|
"dotenv": "17.2.3",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"otpauth": "9.4.1",
|
|
58
58
|
"pm2": "6.0.14",
|
|
59
59
|
"range-parser": "1.2.1",
|
|
60
|
-
"selfsigned": "5.
|
|
60
|
+
"selfsigned": "5.5.0",
|
|
61
61
|
"tty-table": "5.0.0",
|
|
62
62
|
"winston": "3.19.0"
|
|
63
63
|
},
|
|
@@ -68,20 +68,20 @@
|
|
|
68
68
|
"@types/cli-progress": "3.11.6",
|
|
69
69
|
"@types/express": "5.0.6",
|
|
70
70
|
"@types/mime-types": "3.0.1",
|
|
71
|
-
"@types/node": "25.0.
|
|
71
|
+
"@types/node": "25.0.7",
|
|
72
72
|
"@types/range-parser": "1.2.7",
|
|
73
|
-
"@vitest/coverage-istanbul": "4.0.
|
|
74
|
-
"@vitest/spy": "4.0.
|
|
73
|
+
"@vitest/coverage-istanbul": "4.0.17",
|
|
74
|
+
"@vitest/spy": "4.0.17",
|
|
75
75
|
"eslint": "9.39.2",
|
|
76
76
|
"husky": "9.1.7",
|
|
77
77
|
"lint-staged": "16.2.7",
|
|
78
78
|
"nodemon": "3.1.11",
|
|
79
|
-
"oclif": "4.22.
|
|
79
|
+
"oclif": "4.22.65",
|
|
80
80
|
"prettier": "3.7.4",
|
|
81
81
|
"rimraf": "6.1.2",
|
|
82
82
|
"ts-node": "10.9.2",
|
|
83
83
|
"typescript": "5.9.3",
|
|
84
|
-
"vitest": "4.0.
|
|
84
|
+
"vitest": "4.0.17",
|
|
85
85
|
"vitest-mock-express": "2.2.0"
|
|
86
86
|
},
|
|
87
87
|
"optionalDependencies": {
|