@internxt/cli 1.6.8 → 1.6.9

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 CHANGED
@@ -52,7 +52,7 @@ $ npm install -g @internxt/cli
52
52
  $ internxt COMMAND
53
53
  running command...
54
54
  $ internxt (--version)
55
- @internxt/cli/1.6.8 linux-x64 node-v22.23.2
55
+ @internxt/cli/1.6.9 linux-x64 node-v22.23.2
56
56
  $ internxt --help [COMMAND]
57
57
  USAGE
58
58
  $ internxt COMMAND
@@ -139,7 +139,7 @@ EXAMPLES
139
139
  $ internxt add-cert
140
140
  ```
141
141
 
142
- _See code: [src/commands/add-cert.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/add-cert.ts)_
142
+ _See code: [src/commands/add-cert.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/add-cert.ts)_
143
143
 
144
144
  ## `internxt add cert`
145
145
 
@@ -197,7 +197,7 @@ EXAMPLES
197
197
  $ internxt autocomplete --refresh-cache
198
198
  ```
199
199
 
200
- _See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.2.56/src/commands/autocomplete/index.ts)_
200
+ _See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v4.0.0/src/commands/autocomplete/index.ts)_
201
201
 
202
202
  ## `internxt config`
203
203
 
@@ -226,7 +226,7 @@ EXAMPLES
226
226
  $ internxt config
227
227
  ```
228
228
 
229
- _See code: [src/commands/config.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/config.ts)_
229
+ _See code: [src/commands/config.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/config.ts)_
230
230
 
231
231
  ## `internxt create-folder`
232
232
 
@@ -260,7 +260,7 @@ EXAMPLES
260
260
  $ internxt create-folder
261
261
  ```
262
262
 
263
- _See code: [src/commands/create-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/create-folder.ts)_
263
+ _See code: [src/commands/create-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/create-folder.ts)_
264
264
 
265
265
  ## `internxt create folder`
266
266
 
@@ -324,7 +324,7 @@ EXAMPLES
324
324
  $ internxt delete-permanently-file
325
325
  ```
326
326
 
327
- _See code: [src/commands/delete-permanently-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/delete-permanently-file.ts)_
327
+ _See code: [src/commands/delete-permanently-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/delete-permanently-file.ts)_
328
328
 
329
329
  ## `internxt delete-permanently-folder`
330
330
 
@@ -356,7 +356,7 @@ EXAMPLES
356
356
  $ internxt delete-permanently-folder
357
357
  ```
358
358
 
359
- _See code: [src/commands/delete-permanently-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/delete-permanently-folder.ts)_
359
+ _See code: [src/commands/delete-permanently-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/delete-permanently-folder.ts)_
360
360
 
361
361
  ## `internxt delete permanently file`
362
362
 
@@ -451,7 +451,7 @@ EXAMPLES
451
451
  $ internxt download-file
452
452
  ```
453
453
 
454
- _See code: [src/commands/download-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/download-file.ts)_
454
+ _See code: [src/commands/download-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/download-file.ts)_
455
455
 
456
456
  ## `internxt download file`
457
457
 
@@ -514,7 +514,7 @@ EXAMPLES
514
514
  $ internxt list
515
515
  ```
516
516
 
517
- _See code: [src/commands/list.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/list.ts)_
517
+ _See code: [src/commands/list.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/list.ts)_
518
518
 
519
519
  ## `internxt login`
520
520
 
@@ -545,7 +545,7 @@ EXAMPLES
545
545
  $ internxt login
546
546
  ```
547
547
 
548
- _See code: [src/commands/login.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/login.ts)_
548
+ _See code: [src/commands/login.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/login.ts)_
549
549
 
550
550
  ## `internxt login-legacy`
551
551
 
@@ -579,7 +579,7 @@ EXAMPLES
579
579
  $ internxt login-legacy
580
580
  ```
581
581
 
582
- _See code: [src/commands/login-legacy.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/login-legacy.ts)_
582
+ _See code: [src/commands/login-legacy.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/login-legacy.ts)_
583
583
 
584
584
  ## `internxt logout`
585
585
 
@@ -605,7 +605,7 @@ EXAMPLES
605
605
  $ internxt logout
606
606
  ```
607
607
 
608
- _See code: [src/commands/logout.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/logout.ts)_
608
+ _See code: [src/commands/logout.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/logout.ts)_
609
609
 
610
610
  ## `internxt logs`
611
611
 
@@ -631,7 +631,7 @@ EXAMPLES
631
631
  $ internxt logs
632
632
  ```
633
633
 
634
- _See code: [src/commands/logs.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/logs.ts)_
634
+ _See code: [src/commands/logs.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/logs.ts)_
635
635
 
636
636
  ## `internxt move-file`
637
637
 
@@ -665,7 +665,7 @@ EXAMPLES
665
665
  $ internxt move-file
666
666
  ```
667
667
 
668
- _See code: [src/commands/move-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/move-file.ts)_
668
+ _See code: [src/commands/move-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/move-file.ts)_
669
669
 
670
670
  ## `internxt move-folder`
671
671
 
@@ -699,7 +699,7 @@ EXAMPLES
699
699
  $ internxt move-folder
700
700
  ```
701
701
 
702
- _See code: [src/commands/move-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/move-folder.ts)_
702
+ _See code: [src/commands/move-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/move-folder.ts)_
703
703
 
704
704
  ## `internxt move file`
705
705
 
@@ -796,7 +796,7 @@ EXAMPLES
796
796
  $ internxt rename-file
797
797
  ```
798
798
 
799
- _See code: [src/commands/rename-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/rename-file.ts)_
799
+ _See code: [src/commands/rename-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/rename-file.ts)_
800
800
 
801
801
  ## `internxt rename-folder`
802
802
 
@@ -829,7 +829,7 @@ EXAMPLES
829
829
  $ internxt rename-folder
830
830
  ```
831
831
 
832
- _See code: [src/commands/rename-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/rename-folder.ts)_
832
+ _See code: [src/commands/rename-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/rename-folder.ts)_
833
833
 
834
834
  ## `internxt rename file`
835
835
 
@@ -923,7 +923,7 @@ EXAMPLES
923
923
  $ internxt trash-clear
924
924
  ```
925
925
 
926
- _See code: [src/commands/trash-clear.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/trash-clear.ts)_
926
+ _See code: [src/commands/trash-clear.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/trash-clear.ts)_
927
927
 
928
928
  ## `internxt trash-file`
929
929
 
@@ -955,7 +955,7 @@ EXAMPLES
955
955
  $ internxt trash-file
956
956
  ```
957
957
 
958
- _See code: [src/commands/trash-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/trash-file.ts)_
958
+ _See code: [src/commands/trash-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/trash-file.ts)_
959
959
 
960
960
  ## `internxt trash-folder`
961
961
 
@@ -987,7 +987,7 @@ EXAMPLES
987
987
  $ internxt trash-folder
988
988
  ```
989
989
 
990
- _See code: [src/commands/trash-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/trash-folder.ts)_
990
+ _See code: [src/commands/trash-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/trash-folder.ts)_
991
991
 
992
992
  ## `internxt trash-list`
993
993
 
@@ -1019,7 +1019,7 @@ EXAMPLES
1019
1019
  $ internxt trash-list
1020
1020
  ```
1021
1021
 
1022
- _See code: [src/commands/trash-list.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/trash-list.ts)_
1022
+ _See code: [src/commands/trash-list.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/trash-list.ts)_
1023
1023
 
1024
1024
  ## `internxt trash-restore-file`
1025
1025
 
@@ -1052,7 +1052,7 @@ EXAMPLES
1052
1052
  $ internxt trash-restore-file
1053
1053
  ```
1054
1054
 
1055
- _See code: [src/commands/trash-restore-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/trash-restore-file.ts)_
1055
+ _See code: [src/commands/trash-restore-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/trash-restore-file.ts)_
1056
1056
 
1057
1057
  ## `internxt trash-restore-folder`
1058
1058
 
@@ -1085,7 +1085,7 @@ EXAMPLES
1085
1085
  $ internxt trash-restore-folder
1086
1086
  ```
1087
1087
 
1088
- _See code: [src/commands/trash-restore-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/trash-restore-folder.ts)_
1088
+ _See code: [src/commands/trash-restore-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/trash-restore-folder.ts)_
1089
1089
 
1090
1090
  ## `internxt trash clear`
1091
1091
 
@@ -1302,7 +1302,7 @@ EXAMPLES
1302
1302
  $ internxt upload-file
1303
1303
  ```
1304
1304
 
1305
- _See code: [src/commands/upload-file.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/upload-file.ts)_
1305
+ _See code: [src/commands/upload-file.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/upload-file.ts)_
1306
1306
 
1307
1307
  ## `internxt upload-folder`
1308
1308
 
@@ -1335,7 +1335,7 @@ EXAMPLES
1335
1335
  $ internxt upload-folder
1336
1336
  ```
1337
1337
 
1338
- _See code: [src/commands/upload-folder.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/upload-folder.ts)_
1338
+ _See code: [src/commands/upload-folder.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/upload-folder.ts)_
1339
1339
 
1340
1340
  ## `internxt upload file`
1341
1341
 
@@ -1431,7 +1431,7 @@ EXAMPLES
1431
1431
  $ internxt webdav status
1432
1432
  ```
1433
1433
 
1434
- _See code: [src/commands/webdav.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/webdav.ts)_
1434
+ _See code: [src/commands/webdav.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/webdav.ts)_
1435
1435
 
1436
1436
  ## `internxt webdav-config`
1437
1437
 
@@ -1470,7 +1470,7 @@ EXAMPLES
1470
1470
  $ internxt webdav-config
1471
1471
  ```
1472
1472
 
1473
- _See code: [src/commands/webdav-config.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/webdav-config.ts)_
1473
+ _See code: [src/commands/webdav-config.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/webdav-config.ts)_
1474
1474
 
1475
1475
  ## `internxt whoami`
1476
1476
 
@@ -1496,7 +1496,7 @@ EXAMPLES
1496
1496
  $ internxt whoami
1497
1497
  ```
1498
1498
 
1499
- _See code: [src/commands/whoami.ts](https://github.com/internxt/cli/blob/v1.6.8/src/commands/whoami.ts)_
1499
+ _See code: [src/commands/whoami.ts](https://github.com/internxt/cli/blob/v1.6.9/src/commands/whoami.ts)_
1500
1500
  <!-- commandsstop -->
1501
1501
 
1502
1502
  # Current Limitations
package/bin/dev.js CHANGED
@@ -1,6 +1,16 @@
1
1
  #!/usr/bin/env node_modules/.bin/ts-node
2
2
  /* eslint-disable */
3
3
  require('dotenv').config({ path: __dirname + '/../.env', quiet: true });
4
+
5
+ // Some dependencies (e.g. @internxt/inxt-js's upload stream pipeline) can leave an
6
+ // orphaned promise that rejects with an AbortError when a network request fails mid-stream.
7
+ // That rejection never reaches our own try/catch blocks, and Node terminates the whole
8
+ // process by default. Log it instead so commands with their own retry/skip logic
9
+ // (e.g. upload-folder) can keep going.
10
+ process.on('unhandledRejection', (reason) => {
11
+ console.error('Unhandled rejection:', reason);
12
+ });
13
+
4
14
  (async () => {
5
15
  const oclif = await import('@oclif/core');
6
16
  await oclif.execute({ development: true, dir: __dirname });
package/bin/run.js CHANGED
@@ -1,6 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  /* eslint-disable */
3
3
  require('dotenv').config({ path: __dirname + '/../.env', quiet: true });
4
+
5
+ // Some dependencies (e.g. @internxt/inxt-js's upload stream pipeline) can leave an
6
+ // orphaned promise that rejects with an AbortError when a network request fails mid-stream.
7
+ // That rejection never reaches our own try/catch blocks, and Node terminates the whole
8
+ // process by default. Log it instead so commands with their own retry/skip logic
9
+ // (e.g. upload-folder) can keep going.
10
+ process.on('unhandledRejection', (reason) => {
11
+ console.error('Unhandled rejection:', reason);
12
+ });
13
+
4
14
  (async () => {
5
15
  const oclif = await import('@oclif/core');
6
16
  await oclif.execute({ development: false, dir: __dirname });
@@ -32,8 +32,10 @@ class DriveFolderService {
32
32
  return folderItem;
33
33
  };
34
34
  getFolderContent = async (folderUuid) => {
35
- const folders = await this.getFolderSubfolders(folderUuid);
36
- const files = await this.getFolderSubfiles(folderUuid);
35
+ const [folders, files] = await Promise.all([
36
+ this.getFolderSubfolders(folderUuid),
37
+ this.getFolderSubfiles(folderUuid),
38
+ ]);
37
39
  return { folders, files };
38
40
  };
39
41
  getFolderSubfolders = async (folderUuid) => {
@@ -28,3 +28,11 @@ export declare class NotImplementedError extends Error {
28
28
  statusCode: number;
29
29
  constructor(message: string);
30
30
  }
31
+ export declare class PreconditionFailedError extends Error {
32
+ statusCode: number;
33
+ constructor(message: string);
34
+ }
35
+ export declare class RangeNotSatisfiableError extends Error {
36
+ statusCode: number;
37
+ constructor(message: string);
38
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NotImplementedError = exports.MethodNotAllowed = exports.UnsupportedMediaTypeError = exports.BadRequestError = exports.NotFoundError = exports.ConflictError = exports.ErrorUtils = void 0;
3
+ exports.RangeNotSatisfiableError = exports.PreconditionFailedError = exports.NotImplementedError = exports.MethodNotAllowed = exports.UnsupportedMediaTypeError = exports.BadRequestError = exports.NotFoundError = exports.ConflictError = exports.ErrorUtils = void 0;
4
4
  const logger_utils_1 = require("./logger.utils");
5
5
  class ErrorUtils {
6
6
  static isError = (error) => {
@@ -80,3 +80,21 @@ class NotImplementedError extends Error {
80
80
  }
81
81
  }
82
82
  exports.NotImplementedError = NotImplementedError;
83
+ class PreconditionFailedError extends Error {
84
+ statusCode = 412;
85
+ constructor(message) {
86
+ super(message);
87
+ this.name = 'PreconditionFailedError';
88
+ Object.setPrototypeOf(this, PreconditionFailedError.prototype);
89
+ }
90
+ }
91
+ exports.PreconditionFailedError = PreconditionFailedError;
92
+ class RangeNotSatisfiableError extends Error {
93
+ statusCode = 416;
94
+ constructor(message) {
95
+ super(message);
96
+ this.name = 'RangeNotSatisfiableError';
97
+ Object.setPrototypeOf(this, RangeNotSatisfiableError.prototype);
98
+ }
99
+ }
100
+ exports.RangeNotSatisfiableError = RangeNotSatisfiableError;
@@ -10,6 +10,7 @@ const node_path_1 = __importDefault(require("node:path"));
10
10
  const selfsigned_1 = __importDefault(require("selfsigned"));
11
11
  const range_parser_1 = __importDefault(require("range-parser"));
12
12
  const configs_1 = require("../constants/configs");
13
+ const errors_utils_1 = require("./errors.utils");
13
14
  class NetworkUtils {
14
15
  static getAuthFromCredentials(creds) {
15
16
  return {
@@ -81,10 +82,10 @@ class NetworkUtils {
81
82
  const parsed = (0, range_parser_1.default)(rangeOptions.totalFileSize, rangeOptions.range);
82
83
  if (Array.isArray(parsed)) {
83
84
  if (parsed.length > 1) {
84
- throw new Error(`Multi Range-Requests functionality is not implemented. ${JSON.stringify(rangeOptions)}`);
85
+ throw new errors_utils_1.BadRequestError(`Multi Range-Requests functionality is not implemented. ${JSON.stringify(rangeOptions)}`);
85
86
  }
86
87
  else if (parsed.length <= 0) {
87
- throw new Error(`Empty Range-Request. ${JSON.stringify(rangeOptions)}`);
88
+ throw new errors_utils_1.RangeNotSatisfiableError(`Empty Range-Request. ${JSON.stringify(rangeOptions)}`);
88
89
  }
89
90
  if (parsed.type === 'bytes') {
90
91
  const rangeSize = parsed[0].end - parsed[0].start + 1;
@@ -96,17 +97,17 @@ class NetworkUtils {
96
97
  };
97
98
  }
98
99
  else {
99
- throw new Error(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
100
+ throw new errors_utils_1.BadRequestError(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
100
101
  }
101
102
  }
102
103
  else if (parsed === -1) {
103
- throw new Error(`Unsatisfiable Range-Request. ${JSON.stringify(rangeOptions)}`);
104
+ throw new errors_utils_1.RangeNotSatisfiableError(`Unsatisfiable Range-Request. ${JSON.stringify(rangeOptions)}`);
104
105
  }
105
106
  else if (parsed === -2) {
106
- throw new Error(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
107
+ throw new errors_utils_1.BadRequestError(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
107
108
  }
108
109
  else {
109
- throw new Error(`Unknown error from Range-Request. ${JSON.stringify(rangeOptions)}`);
110
+ throw new errors_utils_1.BadRequestError(`Unknown error from Range-Request. ${JSON.stringify(rangeOptions)}`);
110
111
  }
111
112
  }
112
113
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DELETERequestHandler = void 0;
4
- const drive_item_repository_1 = require("../../services/database/drive-item/drive-item.repository");
5
4
  const webdav_utils_1 = require("../../utils/webdav.utils");
6
5
  const logger_utils_1 = require("../../utils/logger.utils");
7
6
  const errors_utils_1 = require("../../utils/errors.utils");
@@ -14,7 +13,6 @@ class DELETERequestHandler {
14
13
  throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
15
14
  }
16
15
  await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveItem);
17
- await drive_item_repository_1.DriveItemRepository.instance.delete([driveItem.uuid]);
18
16
  res.status(204).send();
19
17
  };
20
18
  }
@@ -25,14 +25,24 @@ class GETRequestHandler {
25
25
  const fileSize = driveFile.size ?? 0;
26
26
  if (fileSize > 0) {
27
27
  const range = req.headers['range'];
28
- const rangeOptions = network_utils_1.NetworkUtils.parseRangeHeader({
29
- range,
30
- totalFileSize: fileSize,
31
- });
28
+ let rangeOptions;
29
+ try {
30
+ rangeOptions = network_utils_1.NetworkUtils.parseRangeHeader({
31
+ range,
32
+ totalFileSize: fileSize,
33
+ });
34
+ }
35
+ catch (error) {
36
+ if (error instanceof errors_utils_1.RangeNotSatisfiableError) {
37
+ res.header('Content-Range', `bytes */${fileSize}`);
38
+ }
39
+ throw error;
40
+ }
32
41
  let contentLength = fileSize;
33
42
  if (rangeOptions) {
34
43
  logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] Range request received:`, { rangeOptions });
35
44
  contentLength = rangeOptions.rangeSize;
45
+ res.header('Content-Range', `bytes ${rangeOptions.parsed.start}-${rangeOptions.parsed.end}/${fileSize}`);
36
46
  }
37
47
  res.header('Content-length', contentLength.toString());
38
48
  const writable = new WritableStream({
@@ -49,7 +59,7 @@ class GETRequestHandler {
49
59
  const { networkFacade, mnemonic } = await cli_utils_1.CLIUtils.prepareNetwork(user);
50
60
  const [executeDownload] = await networkFacade.downloadToStream(driveFile.bucket, mnemonic, driveFile.fileId, contentLength, writable, rangeOptions);
51
61
  logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] Download prepared, executing...`);
52
- res.status(200);
62
+ res.status(rangeOptions ? 206 : 200);
53
63
  await executeDownload;
54
64
  logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] ✅ Download ready, replying to client`);
55
65
  }
@@ -15,22 +15,32 @@ class HEADRequestHandler {
15
15
  }
16
16
  logger_utils_1.webdavLogger.info(`[HEAD] [${driveItem.uuid}] Found Drive item`);
17
17
  res.header('ETag', webdav_utils_1.WebDavUtils.getItemETag(driveItem));
18
+ let rangeOptions;
18
19
  if (driveItem.itemType === 'file') {
19
20
  const range = req.headers['range'];
20
- const rangeOptions = network_utils_1.NetworkUtils.parseRangeHeader({
21
- range,
22
- totalFileSize: driveItem.size,
23
- });
21
+ try {
22
+ rangeOptions = network_utils_1.NetworkUtils.parseRangeHeader({
23
+ range,
24
+ totalFileSize: driveItem.size,
25
+ });
26
+ }
27
+ catch (error) {
28
+ if (error instanceof errors_utils_1.RangeNotSatisfiableError) {
29
+ res.header('Content-Range', `bytes */${driveItem.size}`);
30
+ }
31
+ throw error;
32
+ }
24
33
  let contentLength = driveItem.size;
34
+ res.header('Content-Type', 'application/octet-stream');
35
+ res.header('Accept-Ranges', 'bytes');
25
36
  if (rangeOptions) {
26
37
  logger_utils_1.webdavLogger.info(`[HEAD] [${driveItem.uuid}] Range request received:`, { rangeOptions });
27
38
  contentLength = rangeOptions.rangeSize;
39
+ res.header('Content-Range', `bytes ${rangeOptions.parsed.start}-${rangeOptions.parsed.end}/${driveItem.size}`);
28
40
  }
29
- res.header('Content-Type', 'application/octet-stream');
30
- res.header('Accept-Ranges', 'bytes');
31
41
  res.header('Content-length', contentLength.toString());
32
42
  }
33
- res.status(200).send();
43
+ res.status(rangeOptions ? 206 : 200).send();
34
44
  };
35
45
  }
36
46
  exports.HEADRequestHandler = HEADRequestHandler;
@@ -4,6 +4,7 @@ exports.LOCKRequestHandler = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const logger_utils_1 = require("../../utils/logger.utils");
6
6
  const xml_utils_1 = require("../../utils/xml.utils");
7
+ const webdav_utils_1 = require("../../utils/webdav.utils");
7
8
  const LOCK_TIMEOUT_SECONDS = 300;
8
9
  class LOCKRequestHandler {
9
10
  handle = async (req, res) => {
@@ -42,9 +43,12 @@ class LOCKRequestHandler {
42
43
  },
43
44
  },
44
45
  }, { suppressEmptyNode: true }, 'prop');
46
+ const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
47
+ const driveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(resource);
48
+ const statusCode = driveItem ? 200 : 201;
45
49
  res.set('Lock-Token', `<${lockToken}>`);
46
50
  res.set('Content-Type', 'application/xml; charset="utf-8"');
47
- res.status(200).send(lockDiscoveryXml);
51
+ res.status(statusCode).send(lockDiscoveryXml);
48
52
  };
49
53
  }
50
54
  exports.LOCKRequestHandler = LOCKRequestHandler;
@@ -23,6 +23,17 @@ class MOVERequestHandler {
23
23
  if (!originalDriveItem) {
24
24
  throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
25
25
  }
26
+ let isOverwrite = false;
27
+ const destinationDriveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(destinationResource);
28
+ if (destinationDriveItem && destinationDriveItem.uuid !== originalDriveItem.uuid) {
29
+ const overwrite = (req.header('overwrite') ?? 'T').toUpperCase();
30
+ if (overwrite === 'F') {
31
+ throw new errors_utils_1.PreconditionFailedError(`Destination '${destinationResource.url}' already exists`);
32
+ }
33
+ isOverwrite = true;
34
+ logger_utils_1.webdavLogger.info(`[MOVE] Destination '${destinationResource.url}' already exists, overwriting it`);
35
+ await webdav_utils_1.WebDavUtils.deleteOrTrashItem(destinationDriveItem);
36
+ }
26
37
  if (destinationResource.path.dir === resource.path.dir) {
27
38
  logger_utils_1.webdavLogger.info(`[MOVE] Renaming ${originalDriveItem.itemType}
28
39
  with UUID ${originalDriveItem.uuid} to ${destinationResource.name}`);
@@ -75,7 +86,8 @@ class MOVERequestHandler {
75
86
  updatedAt: new Date(),
76
87
  },
77
88
  ]);
78
- res.status(204).send();
89
+ const statusCode = isOverwrite ? 204 : 201;
90
+ res.status(statusCode).send();
79
91
  };
80
92
  }
81
93
  exports.MOVERequestHandler = MOVERequestHandler;
@@ -8,21 +8,21 @@ class OPTIONSRequestHandler {
8
8
  const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
9
9
  logger_utils_1.webdavLogger.info(`[OPTIONS] Request received for item at ${resource.url}`);
10
10
  if (resource.url === '/' || resource.url === '') {
11
- const allowedMethods = 'DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PUT, UNLOCK';
11
+ const allowedMethods = 'DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PROPPATCH, PUT, UNLOCK';
12
12
  logger_utils_1.webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
13
13
  res.header('Allow', allowedMethods);
14
14
  res.header('DAV', '1, 2, ordered-collections');
15
15
  res.status(200).send();
16
16
  }
17
17
  else if (resource.url.endsWith('/')) {
18
- const allowedMethods = 'DELETE, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, UNLOCK';
18
+ const allowedMethods = 'DELETE, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PROPPATCH, UNLOCK';
19
19
  logger_utils_1.webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
20
20
  res.header('Allow', allowedMethods);
21
21
  res.header('DAV', '1, 2, ordered-collections');
22
22
  res.status(200).send();
23
23
  }
24
24
  else {
25
- const allowedMethods = 'DELETE, GET, HEAD, LOCK, MOVE, OPTIONS, PROPFIND, PUT, UNLOCK';
25
+ const allowedMethods = 'DELETE, GET, HEAD, LOCK, MOVE, OPTIONS, PROPFIND, PROPPATCH, PUT, UNLOCK';
26
26
  logger_utils_1.webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
27
27
  res.header('Allow', allowedMethods);
28
28
  res.header('DAV', '1, 2, ordered-collections');
@@ -14,14 +14,14 @@ const mime_types_1 = __importDefault(require("mime-types"));
14
14
  const webdav_utils_1 = require("../../utils/webdav.utils");
15
15
  const logger_utils_1 = require("../../utils/logger.utils");
16
16
  const usage_service_1 = require("../../services/usage.service");
17
+ const errors_utils_1 = require("../../utils/errors.utils");
17
18
  class PROPFINDRequestHandler {
18
19
  handle = async (req, res) => {
19
20
  const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
20
21
  logger_utils_1.webdavLogger.info(`[PROPFIND] Request received for item at ${resource.url}`);
21
22
  const driveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(resource);
22
23
  if (!driveItem) {
23
- res.status(404).send();
24
- return;
24
+ throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
25
25
  }
26
26
  res.set('Content-Type', 'application/xml; charset="utf-8"');
27
27
  switch (driveItem.itemType) {
@@ -1,4 +1,6 @@
1
+ import { Request, Response } from 'express';
1
2
  import { WebDavMethodHandler } from '../../types/webdav.types';
2
3
  export declare class PROPPATCHRequestHandler implements WebDavMethodHandler {
3
- handle(): Promise<void>;
4
+ handle: (req: Request, res: Response) => Promise<void>;
5
+ private readonly getRequestedPropertyNames;
4
6
  }
@@ -1,10 +1,60 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PROPPATCHRequestHandler = void 0;
4
+ const webdav_utils_1 = require("../../utils/webdav.utils");
5
+ const logger_utils_1 = require("../../utils/logger.utils");
6
+ const xml_utils_1 = require("../../utils/xml.utils");
4
7
  const errors_utils_1 = require("../../utils/errors.utils");
5
8
  class PROPPATCHRequestHandler {
6
- async handle() {
7
- throw new errors_utils_1.NotImplementedError('PROPPATCH is not implemented yet.');
8
- }
9
+ handle = async (req, res) => {
10
+ const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
11
+ logger_utils_1.webdavLogger.info(`[PROPPATCH] Request received for item at ${resource.url}`);
12
+ const driveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(resource);
13
+ if (!driveItem) {
14
+ throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
15
+ }
16
+ const propertyNames = this.getRequestedPropertyNames(req.body);
17
+ const propNode = Object.fromEntries(propertyNames.map((name) => [name, '']));
18
+ const responseXML = xml_utils_1.XMLUtils.toWebDavXML({
19
+ [xml_utils_1.XMLUtils.addDefaultNamespace('response')]: {
20
+ [xml_utils_1.XMLUtils.addDefaultNamespace('href')]: xml_utils_1.XMLUtils.encodeWebDavUri(resource.url),
21
+ [xml_utils_1.XMLUtils.addDefaultNamespace('propstat')]: {
22
+ [xml_utils_1.XMLUtils.addDefaultNamespace('prop')]: propNode,
23
+ [xml_utils_1.XMLUtils.addDefaultNamespace('status')]: 'HTTP/1.1 403 Forbidden',
24
+ },
25
+ },
26
+ }, { suppressEmptyNode: true });
27
+ res.set('Content-Type', 'application/xml; charset="utf-8"');
28
+ res.status(207).send(responseXML);
29
+ };
30
+ getRequestedPropertyNames = (body) => {
31
+ if (typeof body !== 'string' || body.trim().length === 0) {
32
+ return [];
33
+ }
34
+ try {
35
+ const parsed = xml_utils_1.XMLUtils.toJSON(body, { ignoreAttributes: false });
36
+ const propertyUpdate = parsed?.['D:propertyupdate'] ?? parsed?.propertyupdate;
37
+ const sections = [
38
+ propertyUpdate?.['D:set'] ?? propertyUpdate?.set,
39
+ propertyUpdate?.['D:remove'] ?? propertyUpdate?.remove,
40
+ ];
41
+ const names = new Set();
42
+ for (const section of sections) {
43
+ const prop = section?.['D:prop'] ?? section?.prop;
44
+ if (prop && typeof prop === 'object') {
45
+ for (const key of Object.keys(prop)) {
46
+ if (!key.startsWith('@_')) {
47
+ names.add(key);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ return Array.from(names);
53
+ }
54
+ catch (error) {
55
+ logger_utils_1.webdavLogger.warn(`[PROPPATCH] Failed to parse request body: ${error instanceof Error ? error.message : String(error)}`);
56
+ return [];
57
+ }
58
+ };
9
59
  }
10
60
  exports.PROPPATCHRequestHandler = PROPPATCHRequestHandler;
@@ -83,22 +83,17 @@ class PUTRequestHandler {
83
83
  encryptVersion: types_1.EncryptionVersion.Aes03,
84
84
  };
85
85
  let file;
86
- if (driveFileItem?.itemType === 'file' && contentLength > 0) {
86
+ if (driveFileItem?.itemType === 'file') {
87
87
  try {
88
88
  file = await drive_file_service_1.DriveFileService.instance.replaceFile(driveFileItem.uuid, filePayload);
89
89
  }
90
90
  catch (error) {
91
91
  logger_utils_1.webdavLogger.warn(`[PUT] File replace failed for '${resource.url}', falling back to delete and create: ${error instanceof Error ? error.message : String(error)}`);
92
92
  await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveFileItem);
93
- await drive_item_repository_1.DriveItemRepository.instance.delete([driveFileItem.uuid]);
94
93
  file = await drive_file_service_1.DriveFileService.instance.createFile(filePayload);
95
94
  }
96
95
  }
97
96
  else {
98
- if (driveFileItem?.itemType === 'file') {
99
- await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveFileItem);
100
- await drive_item_repository_1.DriveItemRepository.instance.delete([driveFileItem.uuid]);
101
- }
102
97
  file = await drive_file_service_1.DriveFileService.instance.createFile(filePayload);
103
98
  }
104
99
  timings.driveUpload = driveTimer.stop();
@@ -45,6 +45,8 @@ const ErrorHandlingMiddleware = (err, req, res, _) => {
45
45
  const statusCode = getErrorStatusCode(err) ?? 500;
46
46
  res.set('Content-Type', 'application/xml; charset="utf-8"');
47
47
  res.status(statusCode).send(errorBodyXML);
48
- req.destroy();
48
+ if (!req.complete) {
49
+ req.destroy();
50
+ }
49
51
  };
50
52
  exports.ErrorHandlingMiddleware = ErrorHandlingMiddleware;
@@ -1783,5 +1783,5 @@
1783
1783
  ]
1784
1784
  }
1785
1785
  },
1786
- "version": "1.6.8"
1786
+ "version": "1.6.9"
1787
1787
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "author": "Internxt <hello@internxt.com>",
3
- "version": "1.6.8",
3
+ "version": "1.6.9",
4
4
  "description": "Internxt CLI to manage your encrypted storage",
5
5
  "scripts": {
6
6
  "build": "npm run clean && tsc",
@@ -37,34 +37,34 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@dashlane/pqc-kem-kyber512-node": "1.0.0",
40
- "@inquirer/prompts": "8.5.2",
40
+ "@inquirer/prompts": "8.7.2",
41
41
  "@internxt/inxt-js": "3.3.5",
42
42
  "@internxt/lib": "1.5.2",
43
- "@internxt/sdk": "1.20.1",
44
- "@oclif/core": "4.13.3",
45
- "@oclif/plugin-autocomplete": "3.2.56",
46
- "axios": "1.19.0",
43
+ "@internxt/sdk": "1.20.2",
44
+ "@oclif/core": "5.0.0",
45
+ "@oclif/plugin-autocomplete": "4.0.0",
46
+ "axios": "1.20.0",
47
47
  "better-sqlite3": "12.11.1",
48
48
  "bip39": "3.1.0",
49
49
  "body-parser": "2.3.0",
50
50
  "cli-progress": "3.12.0",
51
- "dayjs": "1.11.21",
51
+ "dayjs": "1.11.23",
52
52
  "dotenv": "17.4.2",
53
53
  "express": "5.2.1",
54
54
  "express-async-handler": "1.2.0",
55
55
  "fast-xml-builder": "1.3.1",
56
- "fast-xml-parser": "5.10.1",
56
+ "fast-xml-parser": "5.11.1",
57
57
  "hash-wasm": "4.12.0",
58
58
  "mime-types": "3.0.2",
59
- "open": "11.0.1",
59
+ "open": "11.0.2",
60
60
  "openpgp": "6.3.1",
61
- "otpauth": "9.5.1",
62
- "pm2": "7.0.3",
61
+ "otpauth": "9.5.2",
62
+ "pm2": "7.0.4",
63
63
  "range-parser": "1.3.0",
64
64
  "selfsigned": "5.5.0",
65
65
  "tty-table": "5.0.0",
66
- "typeorm": "1.1.0",
67
- "uuid": "14.0.1",
66
+ "typeorm": "1.1.1",
67
+ "uuid": "14.0.2",
68
68
  "winston": "3.19.0"
69
69
  },
70
70
  "devDependencies": {
@@ -74,25 +74,25 @@
74
74
  "@types/cli-progress": "^3.11.6",
75
75
  "@types/express": "^5.0.6",
76
76
  "@types/mime-types": "^3.0.1",
77
- "@types/node": "^26.2.0",
77
+ "@types/node": "^26.5.0",
78
78
  "@types/range-parser": "^1.2.7",
79
- "@vitest/coverage-istanbul": "^4.1.10",
80
- "@vitest/spy": "^4.1.10",
81
- "eslint": "^10.8.1",
79
+ "@vitest/coverage-istanbul": "^5.0.0",
80
+ "@vitest/spy": "^5.0.0",
81
+ "eslint": "^10.10.0",
82
82
  "husky": "^9.1.7",
83
- "lint-staged": "^17.3.0",
83
+ "lint-staged": "^17.5.0",
84
84
  "nodemon": "^3.1.14",
85
- "oclif": "^4.23.30",
85
+ "oclif": "^5.0.1",
86
86
  "prettier": "^3.9.6",
87
87
  "rimraf": "^6.1.3",
88
- "sql.js": "^1.14.1",
88
+ "sql.js": "^1.14.2",
89
89
  "ts-node": "^10.9.2",
90
90
  "typescript": "^6.0.3",
91
- "vitest": "^4.1.10",
91
+ "vitest": "^5.0.0",
92
92
  "vitest-mock-express": "^2.2.0"
93
93
  },
94
94
  "optionalDependencies": {
95
- "sharp": "0.35.3"
95
+ "sharp": "0.35.4"
96
96
  },
97
97
  "engines": {
98
98
  "node": "^22.13.0 || >=24.11.0"