@internxt/cli 1.5.6 → 1.5.8

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.
Files changed (53) hide show
  1. package/README.md +38 -32
  2. package/dist/commands/add-cert.js +0 -1
  3. package/dist/commands/config.js +0 -1
  4. package/dist/commands/create-folder.js +0 -1
  5. package/dist/commands/delete-permanently-file.js +0 -1
  6. package/dist/commands/delete-permanently-folder.js +0 -1
  7. package/dist/commands/download-file.js +0 -1
  8. package/dist/commands/list.js +0 -1
  9. package/dist/commands/login.js +0 -1
  10. package/dist/commands/logout.js +0 -1
  11. package/dist/commands/logs.js +0 -1
  12. package/dist/commands/move-file.js +0 -1
  13. package/dist/commands/move-folder.js +0 -1
  14. package/dist/commands/rename-file.js +0 -1
  15. package/dist/commands/rename-folder.js +0 -1
  16. package/dist/commands/trash-clear.js +0 -1
  17. package/dist/commands/trash-file.js +0 -1
  18. package/dist/commands/trash-folder.js +0 -1
  19. package/dist/commands/trash-list.js +0 -1
  20. package/dist/commands/trash-restore-file.js +0 -1
  21. package/dist/commands/trash-restore-folder.js +0 -1
  22. package/dist/commands/upload-file.js +1 -2
  23. package/dist/commands/webdav-config.d.ts +1 -0
  24. package/dist/commands/webdav-config.js +10 -7
  25. package/dist/commands/webdav.js +0 -1
  26. package/dist/commands/whoami.js +0 -1
  27. package/dist/hooks/prerun/auth_check.js +0 -1
  28. package/dist/services/config.service.d.ts +1 -0
  29. package/dist/services/config.service.js +3 -0
  30. package/dist/types/command.types.d.ts +1 -0
  31. package/dist/types/drive.types.d.ts +1 -0
  32. package/dist/utils/cli.utils.d.ts +1 -2
  33. package/dist/utils/cli.utils.js +2 -2
  34. package/dist/utils/drive.utils.d.ts +2 -1
  35. package/dist/utils/drive.utils.js +14 -0
  36. package/dist/utils/errors.utils.d.ts +2 -1
  37. package/dist/utils/errors.utils.js +10 -4
  38. package/dist/utils/logger.utils.js +0 -10
  39. package/dist/utils/webdav.utils.d.ts +17 -5
  40. package/dist/utils/webdav.utils.js +15 -6
  41. package/dist/webdav/handlers/MKCOL.handler.d.ts +2 -0
  42. package/dist/webdav/handlers/MKCOL.handler.js +6 -16
  43. package/dist/webdav/handlers/MOVE.handler.d.ts +2 -0
  44. package/dist/webdav/handlers/MOVE.handler.js +3 -10
  45. package/dist/webdav/handlers/PUT.handler.d.ts +2 -2
  46. package/dist/webdav/handlers/PUT.handler.js +13 -18
  47. package/dist/webdav/middewares/auth.middleware.js +8 -2
  48. package/dist/webdav/middewares/errors.middleware.js +9 -2
  49. package/dist/webdav/services/webdav-folder.service.d.ts +17 -0
  50. package/dist/webdav/services/webdav-folder.service.js +51 -0
  51. package/dist/webdav/webdav-server.js +8 -1
  52. package/oclif.manifest.json +9 -1
  53. package/package.json +8 -8
@@ -34,5 +34,19 @@ class DriveUtils {
34
34
  updatedAt: new Date(folderMeta.updatedAt),
35
35
  };
36
36
  }
37
+ static createFolderResponseToItem(folderResponse) {
38
+ return {
39
+ uuid: folderResponse.uuid,
40
+ id: folderResponse.id,
41
+ bucket: folderResponse.bucket,
42
+ status: folderResponse.deleted || folderResponse.removed ? 'TRASHED' : 'EXISTS',
43
+ name: folderResponse.plainName ?? folderResponse.name,
44
+ encryptedName: folderResponse.name,
45
+ parentId: folderResponse.parentId,
46
+ parentUuid: folderResponse.parentUuid,
47
+ createdAt: new Date(folderResponse.createdAt),
48
+ updatedAt: new Date(folderResponse.updatedAt),
49
+ };
50
+ }
37
51
  }
38
52
  exports.DriveUtils = DriveUtils;
@@ -1,5 +1,6 @@
1
+ export declare function isError(error: unknown): error is Error;
1
2
  export declare class ErrorUtils {
2
- static report(reporter: (error: string) => void, error: unknown, props?: Record<string, unknown>): void;
3
+ static report(error: unknown, props?: Record<string, unknown>): void;
3
4
  }
4
5
  export declare class ConflictError extends Error {
5
6
  statusCode: number;
@@ -1,13 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NotImplementedError = exports.MethodNotAllowed = exports.UnsupportedMediaTypeError = exports.BadRequestError = exports.NotFoundError = exports.ConflictError = exports.ErrorUtils = void 0;
4
+ exports.isError = isError;
5
+ const logger_utils_1 = require("./logger.utils");
6
+ const node_util_1 = require("node:util");
7
+ function isError(error) {
8
+ return node_util_1.types.isNativeError(error);
9
+ }
4
10
  class ErrorUtils {
5
- static report(reporter, error, props = {}) {
6
- if (error instanceof Error) {
7
- reporter(`[REPORTED_ERROR]: ${error.message}\nProperties => ${JSON.stringify(props, null, 2)}\nStack => ${error.stack}`);
11
+ static report(error, props = {}) {
12
+ if (isError(error)) {
13
+ logger_utils_1.logger.error(`[REPORTED_ERROR]: ${error.message}\nProperties => ${JSON.stringify(props, null, 2)}\nStack => ${error.stack}`);
8
14
  }
9
15
  else {
10
- reporter(`[REPORTED_ERROR]: ${JSON.stringify(error)}\nProperties => ${JSON.stringify(props, null, 2)}\n`);
16
+ logger_utils_1.logger.error(`[REPORTED_ERROR]: ${JSON.stringify(error)}\nProperties => ${JSON.stringify(props, null, 2)}\n`);
11
17
  }
12
18
  }
13
19
  }
@@ -52,13 +52,3 @@ exports.webdavLogger = winston_1.default.createLogger({
52
52
  }),
53
53
  ],
54
54
  });
55
- if (process.env.NODE_ENV !== 'production') {
56
- exports.webdavLogger.add(new winston_1.default.transports.Console({
57
- format: winston_1.default.format.simple(),
58
- }));
59
- }
60
- if (process.env.NODE_ENV !== 'production') {
61
- exports.logger.add(new winston_1.default.transports.Console({
62
- format: winston_1.default.format.simple(),
63
- }));
64
- }
@@ -2,14 +2,26 @@ import { Request } from 'express';
2
2
  import { WebDavRequestedResource } from '../types/webdav.types';
3
3
  import { DriveFolderService } from '../services/drive/drive-folder.service';
4
4
  import { DriveFileService } from '../services/drive/drive-file.service';
5
- import { DriveFileItem, DriveFolderItem } from '../types/drive.types';
5
+ import { DriveFileItem, DriveFolderItem, DriveItem } from '../types/drive.types';
6
6
  export declare class WebDavUtils {
7
7
  static joinURL(...pathComponents: string[]): string;
8
8
  static removeHostFromURL(completeURL: string): string;
9
+ static decodeUrl(requestUrl: string, decodeUri?: boolean): string;
10
+ static normalizeFolderPath(path: string): string;
9
11
  static getRequestedResource(urlObject: string | Request, decodeUri?: boolean): Promise<WebDavRequestedResource>;
10
- static getDriveItemFromResource({ resource, driveFolderService, driveFileService, }: {
12
+ static getDriveItemFromResource(params: {
11
13
  resource: WebDavRequestedResource;
12
- driveFolderService?: DriveFolderService;
13
- driveFileService?: DriveFileService;
14
- }): Promise<DriveFileItem | DriveFolderItem | undefined>;
14
+ driveFolderService: DriveFolderService;
15
+ driveFileService?: never;
16
+ }): Promise<DriveFolderItem | undefined>;
17
+ static getDriveItemFromResource(params: {
18
+ resource: WebDavRequestedResource;
19
+ driveFolderService?: never;
20
+ driveFileService: DriveFileService;
21
+ }): Promise<DriveFileItem | undefined>;
22
+ static getDriveItemFromResource(params: {
23
+ resource: WebDavRequestedResource;
24
+ driveFolderService: DriveFolderService;
25
+ driveFileService: DriveFileService;
26
+ }): Promise<DriveItem | undefined>;
15
27
  }
@@ -19,6 +19,19 @@ class WebDavUtils {
19
19
  url = '/'.concat(url);
20
20
  return url;
21
21
  }
22
+ static decodeUrl(requestUrl, decodeUri = true) {
23
+ return (decodeUri ? decodeURIComponent(requestUrl) : requestUrl).replaceAll('/./', '/');
24
+ }
25
+ static normalizeFolderPath(path) {
26
+ let normalizedPath = path;
27
+ if (!normalizedPath.startsWith('/')) {
28
+ normalizedPath = `/${normalizedPath}`;
29
+ }
30
+ if (!normalizedPath.endsWith('/')) {
31
+ normalizedPath = `${normalizedPath}/`;
32
+ }
33
+ return normalizedPath;
34
+ }
22
35
  static async getRequestedResource(urlObject, decodeUri = true) {
23
36
  let requestUrl;
24
37
  if (typeof urlObject === 'string') {
@@ -27,13 +40,9 @@ class WebDavUtils {
27
40
  else {
28
41
  requestUrl = urlObject.url;
29
42
  }
30
- const decodedUrl = (decodeUri ? decodeURIComponent(requestUrl) : requestUrl).replaceAll('/./', '/');
43
+ const decodedUrl = this.decodeUrl(requestUrl, decodeUri);
31
44
  const parsedPath = node_path_1.default.parse(decodedUrl);
32
- let parentPath = node_path_1.default.dirname(decodedUrl);
33
- if (!parentPath.startsWith('/'))
34
- parentPath = '/'.concat(parentPath);
35
- if (!parentPath.endsWith('/'))
36
- parentPath = parentPath.concat('/');
45
+ const parentPath = this.normalizeFolderPath(node_path_1.default.dirname(decodedUrl));
37
46
  const isFolder = requestUrl.endsWith('/');
38
47
  if (isFolder) {
39
48
  return {
@@ -1,10 +1,12 @@
1
1
  import { WebDavMethodHandler } from '../../types/webdav.types';
2
2
  import { Request, Response } from 'express';
3
3
  import { DriveFolderService } from '../../services/drive/drive-folder.service';
4
+ import { WebDavFolderService } from '../services/webdav-folder.service';
4
5
  export declare class MKCOLRequestHandler implements WebDavMethodHandler {
5
6
  private readonly dependencies;
6
7
  constructor(dependencies: {
7
8
  driveFolderService: DriveFolderService;
9
+ webDavFolderService: WebDavFolderService;
8
10
  });
9
11
  handle: (req: Request, res: Response) => Promise<void>;
10
12
  }
@@ -4,7 +4,6 @@ exports.MKCOLRequestHandler = void 0;
4
4
  const webdav_utils_1 = require("../../utils/webdav.utils");
5
5
  const logger_utils_1 = require("../../utils/logger.utils");
6
6
  const xml_utils_1 = require("../../utils/xml.utils");
7
- const async_utils_1 = require("../../utils/async.utils");
8
7
  const errors_utils_1 = require("../../utils/errors.utils");
9
8
  class MKCOLRequestHandler {
10
9
  dependencies;
@@ -12,18 +11,11 @@ class MKCOLRequestHandler {
12
11
  this.dependencies = dependencies;
13
12
  }
14
13
  handle = async (req, res) => {
15
- const { driveFolderService } = this.dependencies;
14
+ const { driveFolderService, webDavFolderService } = this.dependencies;
16
15
  const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req);
17
16
  logger_utils_1.webdavLogger.info(`[MKCOL] Request received for ${resource.type} at ${resource.url}`);
18
- const parentResource = await webdav_utils_1.WebDavUtils.getRequestedResource(resource.parentPath, false);
19
- const parentDriveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
20
- resource: parentResource,
21
- driveFolderService,
22
- });
23
- if (!parentDriveItem) {
24
- throw new errors_utils_1.ConflictError(`Parent folders not found on Internxt Drive at ${resource.url}`);
25
- }
26
- const parentFolderItem = parentDriveItem;
17
+ const parentDriveFolderItem = (await webDavFolderService.getDriveFolderItemFromPath(resource.parentPath)) ??
18
+ (await webDavFolderService.createParentPathOrThrow(resource.parentPath));
27
19
  const driveFolderItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
28
20
  resource,
29
21
  driveFolderService,
@@ -33,13 +25,11 @@ class MKCOLRequestHandler {
33
25
  logger_utils_1.webdavLogger.info(`[MKCOL] ❌ Folder '${resource.url}' already exists`);
34
26
  throw new errors_utils_1.MethodNotAllowed('Folder already exists');
35
27
  }
36
- const [createFolder] = driveFolderService.createFolder({
37
- plainName: resource.path.base,
38
- parentFolderUuid: parentFolderItem.uuid,
28
+ const newFolder = await webDavFolderService.createFolder({
29
+ folderName: resource.path.base,
30
+ parentFolderUuid: parentDriveFolderItem.uuid,
39
31
  });
40
- const newFolder = await createFolder;
41
32
  logger_utils_1.webdavLogger.info(`[MKCOL] ✅ Folder created with UUID ${newFolder.uuid}`);
42
- await async_utils_1.AsyncUtils.sleep(500);
43
33
  res.status(201).send(xml_utils_1.XMLUtils.toWebDavXML({}, {}));
44
34
  };
45
35
  }
@@ -2,11 +2,13 @@ import { Request, Response } from 'express';
2
2
  import { DriveFileService } from '../../services/drive/drive-file.service';
3
3
  import { DriveFolderService } from '../../services/drive/drive-folder.service';
4
4
  import { WebDavMethodHandler } from '../../types/webdav.types';
5
+ import { WebDavFolderService } from '../services/webdav-folder.service';
5
6
  export declare class MOVERequestHandler implements WebDavMethodHandler {
6
7
  private readonly dependencies;
7
8
  constructor(dependencies: {
8
9
  driveFolderService: DriveFolderService;
9
10
  driveFileService: DriveFileService;
11
+ webDavFolderService: WebDavFolderService;
10
12
  });
11
13
  handle: (req: Request, res: Response) => Promise<void>;
12
14
  }
@@ -10,7 +10,7 @@ class MOVERequestHandler {
10
10
  this.dependencies = dependencies;
11
11
  }
12
12
  handle = async (req, res) => {
13
- const { driveFolderService, driveFileService } = this.dependencies;
13
+ const { driveFolderService, driveFileService, webDavFolderService } = this.dependencies;
14
14
  const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req);
15
15
  logger_utils_1.webdavLogger.info(`[MOVE] Request received for ${resource.type} at ${resource.url}`);
16
16
  const destinationUrl = req.header('destination');
@@ -49,15 +49,8 @@ class MOVERequestHandler {
49
49
  }
50
50
  else {
51
51
  logger_utils_1.webdavLogger.info(`[MOVE] Moving ${resource.type} with UUID ${originalDriveItem.uuid} to ${destinationPath}`);
52
- const destinationFolderResource = await webdav_utils_1.WebDavUtils.getRequestedResource(destinationResource.parentPath);
53
- const destinationDriveFolderItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
54
- resource: destinationFolderResource,
55
- driveFolderService,
56
- });
57
- if (!destinationDriveFolderItem) {
58
- throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
59
- }
60
- const destinationFolderItem = destinationDriveFolderItem;
52
+ const destinationFolderItem = (await webDavFolderService.getDriveFolderItemFromPath(destinationResource.parentPath)) ??
53
+ (await webDavFolderService.createParentPathOrThrow(destinationResource.parentPath));
61
54
  if (resource.type === 'folder') {
62
55
  const folder = originalDriveItem;
63
56
  await driveFolderService.moveFolder(folder.uuid, {
@@ -3,13 +3,13 @@ import { DriveFileService } from '../../services/drive/drive-file.service';
3
3
  import { NetworkFacade } from '../../services/network/network-facade.service';
4
4
  import { AuthService } from '../../services/auth.service';
5
5
  import { WebDavMethodHandler } from '../../types/webdav.types';
6
- import { DriveFolderService } from '../../services/drive/drive-folder.service';
7
6
  import { TrashService } from '../../services/drive/trash.service';
7
+ import { WebDavFolderService } from '../services/webdav-folder.service';
8
8
  export declare class PUTRequestHandler implements WebDavMethodHandler {
9
9
  private readonly dependencies;
10
10
  constructor(dependencies: {
11
11
  driveFileService: DriveFileService;
12
- driveFolderService: DriveFolderService;
12
+ webDavFolderService: WebDavFolderService;
13
13
  trashService: TrashService;
14
14
  authService: AuthService;
15
15
  networkFacade: NetworkFacade;
@@ -10,13 +10,13 @@ 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
12
  const thumbnail_service_1 = require("../../services/thumbnail.service");
13
+ const async_utils_1 = require("../../utils/async.utils");
13
14
  class PUTRequestHandler {
14
15
  dependencies;
15
16
  constructor(dependencies) {
16
17
  this.dependencies = dependencies;
17
18
  }
18
19
  handle = async (req, res) => {
19
- const { authService, networkFacade, driveFileService, driveFolderService, trashService } = this.dependencies;
20
20
  const contentLength = Number(req.headers['content-length']);
21
21
  if (!contentLength || isNaN(contentLength) || contentLength <= 0) {
22
22
  throw new errors_utils_1.UnsupportedMediaTypeError('Empty files are not supported');
@@ -26,30 +26,23 @@ class PUTRequestHandler {
26
26
  throw new errors_utils_1.NotFoundError('Folders cannot be created with PUT. Use MKCOL instead.');
27
27
  logger_utils_1.webdavLogger.info(`[PUT] Request received for ${resource.type} at ${resource.url}`);
28
28
  logger_utils_1.webdavLogger.info(`[PUT] Uploading '${resource.name}' to '${resource.parentPath}'`);
29
- const parentResource = await webdav_utils_1.WebDavUtils.getRequestedResource(resource.parentPath, false);
30
- const parentDriveFolderItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
31
- resource: parentResource,
32
- driveFolderService,
33
- });
34
- if (!parentDriveFolderItem) {
35
- throw new errors_utils_1.ConflictError(`Parent folders not found on Internxt Drive at ${resource.url}`);
36
- }
37
- const parentFolderItem = parentDriveFolderItem;
29
+ const parentDriveFolderItem = (await this.dependencies.webDavFolderService.getDriveFolderItemFromPath(resource.parentPath)) ??
30
+ (await this.dependencies.webDavFolderService.createParentPathOrThrow(resource.parentPath));
38
31
  try {
39
- const driveFileItem = (await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
32
+ const driveFileItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
40
33
  resource: resource,
41
- driveFileService,
42
- }));
34
+ driveFileService: this.dependencies.driveFileService,
35
+ });
43
36
  if (driveFileItem && driveFileItem.status === 'EXISTS') {
44
37
  logger_utils_1.webdavLogger.info(`[PUT] File '${resource.name}' already exists in '${resource.path.dir}', trashing it...`);
45
- await trashService.trashItems({
38
+ await this.dependencies.trashService.trashItems({
46
39
  items: [{ type: resource.type, uuid: driveFileItem.uuid, id: null }],
47
40
  });
48
41
  }
49
42
  }
50
43
  catch {
51
44
  }
52
- const { user } = await authService.getAuthDetails();
45
+ const { user } = await this.dependencies.authService.getAuthDetails();
53
46
  const fileType = resource.path.ext.replace('.', '');
54
47
  const timer = cli_utils_1.CLIUtils.timer();
55
48
  let bufferStream;
@@ -66,7 +59,7 @@ class PUTRequestHandler {
66
59
  }
67
60
  };
68
61
  const fileId = await new Promise((resolve, reject) => {
69
- const state = networkFacade.uploadFile(fileStream, contentLength, user.bucket, (err, res) => {
62
+ const state = this.dependencies.networkFacade.uploadFile(fileStream, contentLength, user.bucket, (err, res) => {
70
63
  if (err) {
71
64
  aborted = true;
72
65
  return reject(err);
@@ -87,7 +80,7 @@ class PUTRequestHandler {
87
80
  plainName: resource.path.name,
88
81
  type: fileType,
89
82
  size: contentLength,
90
- folderUuid: parentFolderItem.uuid,
83
+ folderUuid: parentDriveFolderItem.uuid,
91
84
  fileId: fileId,
92
85
  bucket: user.bucket,
93
86
  encryptVersion: types_1.EncryptionVersion.Aes03,
@@ -96,7 +89,7 @@ class PUTRequestHandler {
96
89
  if (isThumbnailable && bufferStream) {
97
90
  const thumbnailBuffer = bufferStream.getBuffer();
98
91
  if (thumbnailBuffer) {
99
- await thumbnail_service_1.ThumbnailService.instance.uploadThumbnail(thumbnailBuffer, fileType, user.bucket, file.uuid, networkFacade);
92
+ await thumbnail_service_1.ThumbnailService.instance.uploadThumbnail(thumbnailBuffer, fileType, user.bucket, file.uuid, this.dependencies.networkFacade);
100
93
  }
101
94
  }
102
95
  }
@@ -105,6 +98,8 @@ class PUTRequestHandler {
105
98
  }
106
99
  const uploadTime = timer.stop();
107
100
  logger_utils_1.webdavLogger.info(`[PUT] ✅ File uploaded in ${uploadTime}ms to Internxt Drive`);
101
+ await async_utils_1.AsyncUtils.sleep(500);
102
+ logger_utils_1.webdavLogger.info(`[PUT] [RESPONSE-201] ${resource.url} - Returning 201 Created after ${uploadTime}ms (+ 500ms propagation delay)`);
108
103
  res.status(201).send();
109
104
  };
110
105
  }
@@ -4,6 +4,7 @@ exports.AuthMiddleware = void 0;
4
4
  const sdk_manager_service_1 = require("../../services/sdk-manager.service");
5
5
  const logger_utils_1 = require("../../utils/logger.utils");
6
6
  const xml_utils_1 = require("../../utils/xml.utils");
7
+ const errors_utils_1 = require("../../utils/errors.utils");
7
8
  const AuthMiddleware = (authService) => {
8
9
  return (_, res, next) => {
9
10
  (async () => {
@@ -14,10 +15,15 @@ const AuthMiddleware = (authService) => {
14
15
  }
15
16
  catch (error) {
16
17
  let message = 'Authentication required to access this resource.';
17
- if ('message' in error && error.message.trim().length > 0) {
18
+ if ((0, errors_utils_1.isError)(error)) {
18
19
  message = error.message;
20
+ if (error.stack) {
21
+ logger_utils_1.webdavLogger.error(`Error from AuthMiddleware: ${message}\nStack: ${error.stack}`);
22
+ }
23
+ else {
24
+ logger_utils_1.webdavLogger.error(`Error from AuthMiddleware: ${message}`);
25
+ }
19
26
  }
20
- logger_utils_1.webdavLogger.error('Error from AuthMiddleware: ' + message);
21
27
  const errorBodyXML = xml_utils_1.XMLUtils.toWebDavXML({
22
28
  [xml_utils_1.XMLUtils.addDefaultNamespace('responsedescription')]: message,
23
29
  }, {}, 'error');
@@ -3,10 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ErrorHandlingMiddleware = void 0;
4
4
  const logger_utils_1 = require("../../utils/logger.utils");
5
5
  const xml_utils_1 = require("../../utils/xml.utils");
6
+ const errors_utils_1 = require("../../utils/errors.utils");
6
7
  const ErrorHandlingMiddleware = (err, req, res, _) => {
7
- logger_utils_1.webdavLogger.error(`[ERROR MIDDLEWARE] [${req.method.toUpperCase()} - ${req.url}]`, err);
8
+ const message = (0, errors_utils_1.isError)(err) ? err.message : 'Something went wrong';
9
+ if ((0, errors_utils_1.isError)(err) && err.stack) {
10
+ logger_utils_1.webdavLogger.error(`[ERROR MIDDLEWARE] [${req.method.toUpperCase()} - ${req.url}] ${message}\nStack: ${err.stack}`);
11
+ }
12
+ else {
13
+ logger_utils_1.webdavLogger.error(`[ERROR MIDDLEWARE] [${req.method.toUpperCase()} - ${req.url}] ${message}`);
14
+ }
8
15
  const errorBodyXML = xml_utils_1.XMLUtils.toWebDavXML({
9
- [xml_utils_1.XMLUtils.addDefaultNamespace('responsedescription')]: 'message' in err ? err.message : 'Something went wrong',
16
+ [xml_utils_1.XMLUtils.addDefaultNamespace('responsedescription')]: message,
10
17
  }, {}, 'error');
11
18
  let statusCode = 500;
12
19
  if ('statusCode' in err && !isNaN(err.statusCode)) {
@@ -0,0 +1,17 @@
1
+ import { ConfigService } from '../../services/config.service';
2
+ import { DriveFolderService } from '../../services/drive/drive-folder.service';
3
+ import { DriveFolderItem } from '../../types/drive.types';
4
+ export declare class WebDavFolderService {
5
+ private readonly dependencies;
6
+ constructor(dependencies: {
7
+ driveFolderService: DriveFolderService;
8
+ configService: ConfigService;
9
+ });
10
+ getDriveFolderItemFromPath: (path: string) => Promise<DriveFolderItem | undefined>;
11
+ createFolder: ({ folderName, parentFolderUuid, }: {
12
+ folderName: string;
13
+ parentFolderUuid: string;
14
+ }) => Promise<DriveFolderItem>;
15
+ createParentPathOrThrow: (parentPath: string) => Promise<DriveFolderItem>;
16
+ private createFolderRecursively;
17
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebDavFolderService = void 0;
4
+ const errors_utils_1 = require("../../utils/errors.utils");
5
+ const webdav_utils_1 = require("../../utils/webdav.utils");
6
+ const async_utils_1 = require("../../utils/async.utils");
7
+ const auth_service_1 = require("../../services/auth.service");
8
+ const drive_utils_1 = require("../../utils/drive.utils");
9
+ class WebDavFolderService {
10
+ dependencies;
11
+ constructor(dependencies) {
12
+ this.dependencies = dependencies;
13
+ }
14
+ getDriveFolderItemFromPath = async (path) => {
15
+ const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(path, false);
16
+ return await webdav_utils_1.WebDavUtils.getDriveItemFromResource({
17
+ resource,
18
+ driveFolderService: this.dependencies.driveFolderService,
19
+ });
20
+ };
21
+ createFolder = async ({ folderName, parentFolderUuid, }) => {
22
+ const [createFolderPromise] = this.dependencies.driveFolderService.createFolder({
23
+ plainName: folderName,
24
+ parentFolderUuid: parentFolderUuid,
25
+ });
26
+ const newFolder = await createFolderPromise;
27
+ await async_utils_1.AsyncUtils.sleep(500);
28
+ return drive_utils_1.DriveUtils.createFolderResponseToItem(newFolder);
29
+ };
30
+ createParentPathOrThrow = async (parentPath) => {
31
+ const { createFullPath } = await this.dependencies.configService.readWebdavConfig();
32
+ if (!createFullPath) {
33
+ throw new errors_utils_1.ConflictError(`Parent folders not found on Internxt Drive at ${webdav_utils_1.WebDavUtils.decodeUrl(parentPath, false)}`);
34
+ }
35
+ const folders = parentPath.split('/').filter((f) => f.length > 0);
36
+ const { user } = await auth_service_1.AuthService.instance.getAuthDetails();
37
+ return await this.createFolderRecursively(folders, user.rootFolderId);
38
+ };
39
+ async createFolderRecursively(remainingFolders, parentFolderUuid, accumulatedPath = '') {
40
+ const [currentFolderName, ...rest] = remainingFolders;
41
+ const newPath = webdav_utils_1.WebDavUtils.joinURL(accumulatedPath, currentFolderName);
42
+ const folderPath = webdav_utils_1.WebDavUtils.normalizeFolderPath(newPath);
43
+ const folder = (await this.getDriveFolderItemFromPath(folderPath)) ??
44
+ (await this.createFolder({ folderName: currentFolderName, parentFolderUuid }));
45
+ if (rest.length === 0) {
46
+ return folder;
47
+ }
48
+ return await this.createFolderRecursively(rest, folder.uuid, newPath);
49
+ }
50
+ }
51
+ exports.WebDavFolderService = WebDavFolderService;
@@ -31,6 +31,7 @@ const MOVE_handler_1 = require("./handlers/MOVE.handler");
31
31
  const COPY_handler_1 = require("./handlers/COPY.handler");
32
32
  const inxt_js_1 = require("@internxt/inxt-js");
33
33
  const mkcol_middleware_1 = require("./middewares/mkcol.middleware");
34
+ const webdav_folder_service_1 = require("./services/webdav-folder.service");
34
35
  class WebDavServer {
35
36
  app;
36
37
  configService;
@@ -82,6 +83,10 @@ class WebDavServer {
82
83
  registerHandlers = async () => {
83
84
  const serverListenPath = /(.*)/;
84
85
  const networkFacade = await this.getNetworkFacade();
86
+ const webDavFolderService = new webdav_folder_service_1.WebDavFolderService({
87
+ driveFolderService: this.driveFolderService,
88
+ configService: this.configService,
89
+ });
85
90
  this.app.head(serverListenPath, (0, express_async_handler_1.default)(new HEAD_handler_1.HEADRequestHandler({
86
91
  driveFileService: this.driveFileService,
87
92
  }).handle));
@@ -99,13 +104,14 @@ class WebDavServer {
99
104
  }).handle));
100
105
  this.app.put(serverListenPath, (0, express_async_handler_1.default)(new PUT_handler_1.PUTRequestHandler({
101
106
  driveFileService: this.driveFileService,
102
- driveFolderService: this.driveFolderService,
107
+ webDavFolderService: webDavFolderService,
103
108
  authService: this.authService,
104
109
  trashService: this.trashService,
105
110
  networkFacade: networkFacade,
106
111
  }).handle));
107
112
  this.app.mkcol(serverListenPath, (0, express_async_handler_1.default)(new MKCOL_handler_1.MKCOLRequestHandler({
108
113
  driveFolderService: this.driveFolderService,
114
+ webDavFolderService: webDavFolderService,
109
115
  }).handle));
110
116
  this.app.delete(serverListenPath, (0, express_async_handler_1.default)(new DELETE_handler_1.DELETERequestHandler({
111
117
  trashService: this.trashService,
@@ -116,6 +122,7 @@ class WebDavServer {
116
122
  this.app.move(serverListenPath, (0, express_async_handler_1.default)(new MOVE_handler_1.MOVERequestHandler({
117
123
  driveFolderService: this.driveFolderService,
118
124
  driveFileService: this.driveFileService,
125
+ webDavFolderService,
119
126
  }).handle));
120
127
  this.app.copy(serverListenPath, (0, express_async_handler_1.default)(new COPY_handler_1.COPYRequestHandler().handle));
121
128
  };
@@ -1196,6 +1196,14 @@
1196
1196
  "hasDynamicHelp": false,
1197
1197
  "multiple": false,
1198
1198
  "type": "option"
1199
+ },
1200
+ "createFullPath": {
1201
+ "char": "c",
1202
+ "description": "Auto-create missing parent directories during file uploads.",
1203
+ "name": "createFullPath",
1204
+ "required": false,
1205
+ "allowNo": true,
1206
+ "type": "boolean"
1199
1207
  }
1200
1208
  },
1201
1209
  "hasDynamicHelp": false,
@@ -1290,5 +1298,5 @@
1290
1298
  ]
1291
1299
  }
1292
1300
  },
1293
- "version": "1.5.6"
1301
+ "version": "1.5.8"
1294
1302
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "author": "Internxt <hello@internxt.com>",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "description": "Internxt CLI to manage your encrypted storage",
5
5
  "scripts": {
6
6
  "build": "yarn clean && tsc",
@@ -40,8 +40,8 @@
40
40
  "@internxt/inxt-js": "2.2.9",
41
41
  "@internxt/lib": "1.3.1",
42
42
  "@internxt/sdk": "1.11.12",
43
- "@oclif/core": "4.5.4",
44
- "@oclif/plugin-autocomplete": "3.2.35",
43
+ "@oclif/core": "4.5.5",
44
+ "@oclif/plugin-autocomplete": "3.2.36",
45
45
  "axios": "1.12.2",
46
46
  "bip39": "3.1.0",
47
47
  "body-parser": "2.2.0",
@@ -50,7 +50,7 @@
50
50
  "dotenv": "17.2.3",
51
51
  "express": "5.1.0",
52
52
  "express-async-handler": "1.2.0",
53
- "fast-xml-parser": "5.2.5",
53
+ "fast-xml-parser": "5.3.0",
54
54
  "mime-types": "3.0.1",
55
55
  "openpgp": "6.2.2",
56
56
  "otpauth": "9.4.1",
@@ -67,15 +67,15 @@
67
67
  "@types/cli-progress": "3.11.6",
68
68
  "@types/express": "5.0.3",
69
69
  "@types/mime-types": "3.0.1",
70
- "@types/node": "22.18.6",
70
+ "@types/node": "22.18.9",
71
71
  "@types/range-parser": "1.2.7",
72
72
  "@vitest/coverage-istanbul": "3.2.4",
73
73
  "@vitest/spy": "3.2.4",
74
- "eslint": "9.36.0",
74
+ "eslint": "9.37.0",
75
75
  "husky": "9.1.7",
76
- "lint-staged": "16.2.3",
76
+ "lint-staged": "16.2.4",
77
77
  "nodemon": "3.1.10",
78
- "oclif": "4.22.27",
78
+ "oclif": "4.22.32",
79
79
  "prettier": "3.6.2",
80
80
  "rimraf": "6.0.1",
81
81
  "ts-node": "10.9.2",