@internxt/cli 1.6.4 → 1.6.5

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 (71) hide show
  1. package/README.md +31 -31
  2. package/dist/commands/login-legacy.js +2 -0
  3. package/dist/commands/login.js +2 -0
  4. package/dist/commands/upload-file.js +15 -26
  5. package/dist/services/auth.service.js +10 -1
  6. package/dist/services/cache.service.d.ts +13 -0
  7. package/dist/services/cache.service.js +36 -0
  8. package/dist/services/config.service.js +19 -3
  9. package/dist/services/database/database.service.d.ts +1 -1
  10. package/dist/services/database/database.service.js +6 -5
  11. package/dist/services/database/drive-item/drive-item.attributes.d.ts +7 -0
  12. package/dist/services/database/drive-item/drive-item.domain.d.ts +10 -0
  13. package/dist/services/database/drive-item/drive-item.domain.js +27 -0
  14. package/dist/services/database/drive-item/drive-item.model.d.ts +8 -0
  15. package/dist/services/database/{drive-folder/drive-folder.model.js → drive-item/drive-item.model.js} +11 -23
  16. package/dist/services/database/drive-item/drive-item.repository.d.ts +12 -0
  17. package/dist/services/database/drive-item/drive-item.repository.js +88 -0
  18. package/dist/services/drive/drive-file.service.d.ts +2 -3
  19. package/dist/services/drive/drive-file.service.js +9 -34
  20. package/dist/services/drive/drive-folder.service.d.ts +1 -3
  21. package/dist/services/drive/drive-folder.service.js +9 -87
  22. package/dist/services/drive/drive-item.service.d.ts +8 -0
  23. package/dist/services/drive/drive-item.service.js +98 -0
  24. package/dist/services/network/network-facade.service.d.ts +1 -3
  25. package/dist/services/network/network-facade.service.js +5 -8
  26. package/dist/services/network/upload/upload-file.service.js +2 -1
  27. package/dist/services/thumbnail.service.js +2 -2
  28. package/dist/services/usage.service.d.ts +2 -0
  29. package/dist/services/usage.service.js +18 -0
  30. package/dist/utils/cli.utils.d.ts +3 -2
  31. package/dist/utils/cli.utils.js +22 -18
  32. package/dist/utils/format.utils.js +3 -2
  33. package/dist/utils/upload.utils.d.ts +19 -0
  34. package/dist/utils/upload.utils.js +40 -0
  35. package/dist/utils/webdav.utils.d.ts +4 -2
  36. package/dist/utils/webdav.utils.js +14 -18
  37. package/dist/utils/xml.utils.js +5 -1
  38. package/dist/webdav/handlers/DELETE.handler.js +2 -0
  39. package/dist/webdav/handlers/LOCK.handler.d.ts +5 -0
  40. package/dist/webdav/handlers/LOCK.handler.js +50 -0
  41. package/dist/webdav/handlers/MKCOL.handler.js +15 -3
  42. package/dist/webdav/handlers/MOVE.handler.js +11 -0
  43. package/dist/webdav/handlers/OPTIONS.handler.js +4 -4
  44. package/dist/webdav/handlers/PROPFIND.handler.js +30 -9
  45. package/dist/webdav/handlers/PUT.handler.js +44 -40
  46. package/dist/webdav/handlers/UNLOCK.handler.d.ts +5 -0
  47. package/dist/webdav/handlers/UNLOCK.handler.js +12 -0
  48. package/dist/webdav/middewares/auth.middleware.js +9 -2
  49. package/dist/webdav/middewares/errors.middleware.js +2 -0
  50. package/dist/webdav/middewares/request-logger.middleware.js +10 -6
  51. package/dist/webdav/middewares/webdav-auth.middleware.js +0 -3
  52. package/dist/webdav/webdav-server.js +4 -0
  53. package/oclif.manifest.json +1 -1
  54. package/package.json +47 -46
  55. package/dist/services/database/drive-file/drive-file.attributes.d.ts +0 -14
  56. package/dist/services/database/drive-file/drive-file.domain.d.ts +0 -20
  57. package/dist/services/database/drive-file/drive-file.domain.js +0 -68
  58. package/dist/services/database/drive-file/drive-file.model.d.ts +0 -15
  59. package/dist/services/database/drive-file/drive-file.model.js +0 -67
  60. package/dist/services/database/drive-file/drive-file.repository.d.ts +0 -11
  61. package/dist/services/database/drive-file/drive-file.repository.js +0 -63
  62. package/dist/services/database/drive-folder/drive-folder.attributes.d.ts +0 -10
  63. package/dist/services/database/drive-folder/drive-folder.attributes.js +0 -2
  64. package/dist/services/database/drive-folder/drive-folder.domain.d.ts +0 -16
  65. package/dist/services/database/drive-folder/drive-folder.domain.js +0 -53
  66. package/dist/services/database/drive-folder/drive-folder.model.d.ts +0 -11
  67. package/dist/services/database/drive-folder/drive-folder.repository.d.ts +0 -13
  68. package/dist/services/database/drive-folder/drive-folder.repository.js +0 -99
  69. package/dist/utils/database.utils.d.ts +0 -11
  70. package/dist/utils/database.utils.js +0 -26
  71. /package/dist/services/database/{drive-file/drive-file.attributes.js → drive-item/drive-item.attributes.js} +0 -0
@@ -2,18 +2,36 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UsageService = void 0;
4
4
  const sdk_manager_service_1 = require("./sdk-manager.service");
5
+ const cache_service_1 = require("./cache.service");
5
6
  class UsageService {
6
7
  static instance = new UsageService();
7
8
  static INFINITE_LIMIT = 99 * Math.pow(1024, 4);
8
9
  fetchUsage = async () => {
10
+ const cached = cache_service_1.CacheService.instance.get(cache_service_1.CacheService.FETCH_USAGE_CACHE_KEY);
11
+ if (cached !== null)
12
+ return cached;
9
13
  const storageClient = sdk_manager_service_1.SdkManager.instance.getStorage();
10
14
  const driveUsage = await storageClient.spaceUsageV2();
15
+ cache_service_1.CacheService.instance.set(cache_service_1.CacheService.FETCH_USAGE_CACHE_KEY, driveUsage.total);
11
16
  return driveUsage.total;
12
17
  };
13
18
  fetchSpaceLimit = async () => {
19
+ const cached = cache_service_1.CacheService.instance.get(cache_service_1.CacheService.FETCH_SPACE_LIMIT_CACHE_KEY);
20
+ if (cached !== null)
21
+ return cached;
14
22
  const storageClient = sdk_manager_service_1.SdkManager.instance.getStorage();
15
23
  const spaceLimit = await storageClient.spaceLimitV2();
24
+ cache_service_1.CacheService.instance.set(cache_service_1.CacheService.FETCH_SPACE_LIMIT_CACHE_KEY, spaceLimit.maxSpaceBytes);
16
25
  return spaceLimit.maxSpaceBytes;
17
26
  };
27
+ fetchLimits = async () => {
28
+ const cached = cache_service_1.CacheService.instance.get(cache_service_1.CacheService.FETCH_LIMITS_CACHE_KEY);
29
+ if (cached !== null)
30
+ return cached;
31
+ const storageClient = sdk_manager_service_1.SdkManager.instance.getStorage();
32
+ const limits = await storageClient.getFileVersionLimits();
33
+ cache_service_1.CacheService.instance.set(cache_service_1.CacheService.FETCH_LIMITS_CACHE_KEY, limits);
34
+ return limits;
35
+ };
18
36
  }
19
37
  exports.UsageService = UsageService;
@@ -2,6 +2,8 @@ import cliProgress from 'cli-progress';
2
2
  import { Header } from 'tty-table';
3
3
  import { LoginCredentials, LoginUserDetails, PromptOptions } from '../types/command.types';
4
4
  import { NetworkCredentials, NetworkOptions } from '../types/network.types';
5
+ import { AppError } from '@internxt/sdk';
6
+ import { AxiosResponseError } from '@internxt/sdk/dist/shared/types/errors';
5
7
  export type LogReporter = (message: string) => void;
6
8
  export declare class CLIUtils {
7
9
  static readonly clearPreviousLine: (jsonFlag?: boolean) => void;
@@ -46,10 +48,9 @@ export declare class CLIUtils {
46
48
  stop: () => number;
47
49
  };
48
50
  static readonly formatDuration: (milliseconds: number) => string;
49
- static readonly formatBytesToString: (bytes: number) => string;
50
51
  static readonly calculateThroughputMBps: (bytes: number, milliseconds: number) => number;
51
52
  static readonly catchError: ({ error, logReporter, command, jsonFlag, }: {
52
- error: Error;
53
+ error: Error | AppError | AxiosResponseError;
53
54
  command?: string;
54
55
  logReporter: LogReporter;
55
56
  jsonFlag?: boolean;
@@ -175,17 +175,6 @@ class CLIUtils {
175
175
  const msFormated = ms.toString().padStart(3, '0');
176
176
  return `${hoursFormated}:${minutesFormated}:${secondsFormated}.${msFormated}`;
177
177
  };
178
- static formatBytesToString = (bytes) => {
179
- if (bytes <= 0) {
180
- return '0.00 KB';
181
- }
182
- const kb = bytes / 1024;
183
- if (kb < 1024) {
184
- return `${kb.toFixed(2)} KB`;
185
- }
186
- const mb = kb / 1024;
187
- return `${mb.toFixed(2)} MB`;
188
- };
189
178
  static calculateThroughputMBps = (bytes, milliseconds) => {
190
179
  if (bytes <= 0 || milliseconds <= 0) {
191
180
  return 0;
@@ -196,19 +185,34 @@ class CLIUtils {
196
185
  };
197
186
  static catchError = ({ error, logReporter, command, jsonFlag, }) => {
198
187
  let message;
199
- if ('message' in error && error.message.trim().length > 0) {
200
- message = error.message;
188
+ let requestId;
189
+ if ('requestId' in error) {
190
+ requestId = error.requestId;
201
191
  }
202
- else {
203
- message = JSON.stringify(error);
192
+ else if ('xRequestId' in error) {
193
+ requestId = error.xRequestId;
194
+ }
195
+ if ('data' in error) {
196
+ const errorData = error.data;
197
+ if (errorData.message && errorData.message.trim().length > 0) {
198
+ message = errorData.message;
199
+ }
200
+ }
201
+ if (!message) {
202
+ if ('message' in error && error.message.trim().length > 0) {
203
+ message = error.message;
204
+ }
205
+ else {
206
+ message = JSON.stringify(error);
207
+ }
204
208
  }
205
209
  CLIUtils.failed(jsonFlag);
206
210
  if (jsonFlag) {
207
- CLIUtils.consoleLog(JSON.stringify({ success: false, message }));
211
+ CLIUtils.consoleLog(JSON.stringify({ success: false, message, requestId }));
208
212
  }
209
213
  else {
210
- errors_utils_1.ErrorUtils.report(error, { command });
211
- CLIUtils.error(logReporter, message);
214
+ errors_utils_1.ErrorUtils.report(error, { command, requestId });
215
+ CLIUtils.error(logReporter, message + (requestId ? ` (requestId: ${requestId})` : ''));
212
216
  }
213
217
  };
214
218
  static parseEmpty = async (input) => (input.trim().length === 0 ? ' ' : input);
@@ -10,8 +10,9 @@ const usage_service_1 = require("../services/usage.service");
10
10
  dayjs_1.default.extend(utc_1.default);
11
11
  class FormatUtils {
12
12
  static humanFileSize = (size) => {
13
- const i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
14
- return Number((size / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
13
+ const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
14
+ const unitIndex = size === 0 ? 0 : Math.min(Math.floor(Math.log(size) / Math.log(1024)), units.length - 1);
15
+ return Number((size / Math.pow(1024, unitIndex)).toFixed(2)) + ' ' + units[unitIndex];
15
16
  };
16
17
  static formatDate = (date) => {
17
18
  return (0, dayjs_1.default)(date).format('D MMMM, YYYY [at] HH:mm');
@@ -0,0 +1,19 @@
1
+ import { Readable } from 'node:stream';
2
+ import { BufferStream } from './stream.utils';
3
+ export declare class UploadUtils {
4
+ static readonly checkUploadSizeLimits: (size: number) => Promise<void>;
5
+ static readonly prepareUploadStreams: (readable: Readable, fileType: string) => {
6
+ fileStream: Readable;
7
+ thumbnailStream: BufferStream | undefined;
8
+ isThumbnailable: boolean;
9
+ };
10
+ static readonly getTimings: (size: number, timings: {
11
+ networkUpload: number;
12
+ driveUpload: number;
13
+ thumbnailUpload: number;
14
+ }) => {
15
+ totalTime: number;
16
+ throughputMBps: number;
17
+ timingBreakdown: string;
18
+ };
19
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UploadUtils = void 0;
4
+ const usage_service_1 = require("../services/usage.service");
5
+ const format_utils_1 = require("./format.utils");
6
+ const stream_utils_1 = require("./stream.utils");
7
+ const thumbnail_utils_1 = require("./thumbnail.utils");
8
+ const cli_utils_1 = require("./cli.utils");
9
+ const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024 * 1024;
10
+ class UploadUtils {
11
+ static checkUploadSizeLimits = async (size) => {
12
+ const limits = await usage_service_1.UsageService.instance.fetchLimits();
13
+ if (limits?.maxUploadFileSize && size > limits.maxUploadFileSize) {
14
+ const formattedSize = format_utils_1.FormatUtils.humanFileSize(size);
15
+ const formattedLimit = format_utils_1.FormatUtils.humanFileSize(limits.maxUploadFileSize);
16
+ throw new Error(`File is too big (${formattedSize} exceeds account upload limit of ${formattedLimit})`);
17
+ }
18
+ if (size > DEFAULT_MAX_FILE_SIZE) {
19
+ throw new Error('File is too big (more than 100 GB)');
20
+ }
21
+ };
22
+ static prepareUploadStreams = (readable, fileType) => {
23
+ const isThumbnailable = thumbnail_utils_1.ThumbnailUtils.isFileThumbnailable(fileType);
24
+ if (!isThumbnailable) {
25
+ return { fileStream: readable, thumbnailStream: undefined, isThumbnailable };
26
+ }
27
+ const bufferStream = new stream_utils_1.BufferStream();
28
+ const fileStream = readable.pipe(bufferStream);
29
+ return { fileStream, thumbnailStream: bufferStream, isThumbnailable };
30
+ };
31
+ static getTimings = (size, timings) => {
32
+ const totalTime = Object.values(timings).reduce((sum, time) => sum + time, 0);
33
+ const throughputMBps = cli_utils_1.CLIUtils.calculateThroughputMBps(size, timings.networkUpload);
34
+ const timingBreakdown = `Network upload: ${cli_utils_1.CLIUtils.formatDuration(timings.networkUpload)} (${throughputMBps.toFixed(2)} MB/s)\n` +
35
+ `Drive upload: ${cli_utils_1.CLIUtils.formatDuration(timings.driveUpload)}\n` +
36
+ `Thumbnail: ${cli_utils_1.CLIUtils.formatDuration(timings.thumbnailUpload)}\n`;
37
+ return { totalTime, throughputMBps, timingBreakdown };
38
+ };
39
+ }
40
+ exports.UploadUtils = UploadUtils;
@@ -6,9 +6,11 @@ export declare class WebDavUtils {
6
6
  static decodeUrl(requestUrl: string, decodeUri?: boolean): string;
7
7
  static normalizeFolderPath(path: string): string;
8
8
  static getRequestedResource(requestUrl: string, decodeUri?: boolean): Promise<WebDavRequestedResource>;
9
- static tryGetFileOrFolderMetadata(url: string): Promise<DriveItem | undefined>;
10
9
  static getDriveFileFromResource(url: string): Promise<DriveFileItem | undefined>;
11
10
  static getDriveFolderFromResource(url: string): Promise<DriveFolderItem | undefined>;
12
11
  static getDriveItemFromResource(resource: WebDavRequestedResource): Promise<DriveItem | undefined>;
13
- static deleteOrTrashItem(driveItem: DriveItem): Promise<void>;
12
+ static deleteOrTrashItem<T extends {
13
+ itemType: 'file' | 'folder';
14
+ uuid: string;
15
+ }>(driveItem: T): Promise<void>;
14
16
  }
@@ -5,12 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.WebDavUtils = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
- const drive_folder_service_1 = require("../services/drive/drive-folder.service");
9
- const drive_file_service_1 = require("../services/drive/drive-file.service");
8
+ const drive_item_service_1 = require("../services/drive/drive-item.service");
10
9
  const logger_utils_1 = require("./logger.utils");
11
10
  const config_service_1 = require("../services/config.service");
12
11
  const trash_service_1 = require("../services/drive/trash.service");
13
12
  const format_utils_1 = require("./format.utils");
13
+ const drive_item_repository_1 = require("../services/database/drive-item/drive-item.repository");
14
14
  class WebDavUtils {
15
15
  static joinURL(...pathComponents) {
16
16
  return node_path_1.default.posix.join(...pathComponents);
@@ -49,28 +49,18 @@ class WebDavUtils {
49
49
  parentPath,
50
50
  };
51
51
  }
52
- static async tryGetFileOrFolderMetadata(url) {
53
- try {
54
- return await drive_file_service_1.DriveFileService.instance.getFileMetadataByPath(url);
55
- }
56
- catch {
57
- return await drive_folder_service_1.DriveFolderService.instance.getFolderMetadataByPath(url);
58
- }
59
- }
60
52
  static async getDriveFileFromResource(url) {
61
53
  try {
62
- return await drive_file_service_1.DriveFileService.instance.getFileMetadataByPath(url);
54
+ return await drive_item_service_1.DriveItemService.instance.getFileByPath(url);
63
55
  }
64
- catch (err) {
65
- logger_utils_1.webdavLogger.error('Exception while getting the file metadata by path', err);
56
+ catch {
66
57
  }
67
58
  }
68
59
  static async getDriveFolderFromResource(url) {
69
60
  try {
70
- return await drive_folder_service_1.DriveFolderService.instance.getFolderMetadataByPath(url);
61
+ return await drive_item_service_1.DriveItemService.instance.getFolderByPath(url);
71
62
  }
72
- catch (err) {
73
- logger_utils_1.webdavLogger.error('Exception while getting the folder metadata by path', err);
63
+ catch {
74
64
  }
75
65
  }
76
66
  static async getDriveItemFromResource(resource) {
@@ -78,10 +68,15 @@ class WebDavUtils {
78
68
  const isFolder = resource.url.endsWith('/');
79
69
  try {
80
70
  if (isFolder) {
81
- item = await drive_folder_service_1.DriveFolderService.instance.getFolderMetadataByPath(resource.url);
71
+ item = await drive_item_service_1.DriveItemService.instance.getFolderByPath(resource.url);
82
72
  }
83
73
  else {
84
- item = await this.tryGetFileOrFolderMetadata(resource.url);
74
+ try {
75
+ item = await drive_item_service_1.DriveItemService.instance.getFileByPath(resource.url);
76
+ }
77
+ catch {
78
+ item = await drive_item_service_1.DriveItemService.instance.getFolderByPath(resource.url);
79
+ }
85
80
  }
86
81
  }
87
82
  catch {
@@ -103,6 +98,7 @@ class WebDavUtils {
103
98
  });
104
99
  logger_utils_1.webdavLogger.info(`[DELETE] [${driveItem.uuid}] ${type} trashed successfully`);
105
100
  }
101
+ await drive_item_repository_1.DriveItemRepository.instance.delete([driveItem.uuid]);
106
102
  }
107
103
  }
108
104
  exports.WebDavUtils = WebDavUtils;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.XMLUtils = void 0;
4
7
  const fast_xml_parser_1 = require("fast-xml-parser");
8
+ const fast_xml_builder_1 = __importDefault(require("fast-xml-builder"));
5
9
  class XMLUtils {
6
10
  static DEFAULT_NAMESPACE_LETTER = 'D';
7
11
  static toJSON(xml, options = {}) {
@@ -9,7 +13,7 @@ class XMLUtils {
9
13
  return parser.parse(xml);
10
14
  }
11
15
  static toXML(object, options) {
12
- const builder = new fast_xml_parser_1.XMLBuilder(options);
16
+ const builder = new fast_xml_builder_1.default(options);
13
17
  return builder.build(object);
14
18
  }
15
19
  static toWebDavXML(object, options, rootObject = 'multistatus') {
@@ -1,6 +1,7 @@
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");
4
5
  const webdav_utils_1 = require("../../utils/webdav.utils");
5
6
  const logger_utils_1 = require("../../utils/logger.utils");
6
7
  const errors_utils_1 = require("../../utils/errors.utils");
@@ -13,6 +14,7 @@ class DELETERequestHandler {
13
14
  throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
14
15
  }
15
16
  await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveItem);
17
+ await drive_item_repository_1.DriveItemRepository.instance.delete([driveItem.uuid]);
16
18
  res.status(204).send();
17
19
  };
18
20
  }
@@ -0,0 +1,5 @@
1
+ import { Request, Response } from 'express';
2
+ import { WebDavMethodHandler } from '../../types/webdav.types';
3
+ export declare class LOCKRequestHandler implements WebDavMethodHandler {
4
+ handle: (req: Request, res: Response) => Promise<void>;
5
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LOCKRequestHandler = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const logger_utils_1 = require("../../utils/logger.utils");
6
+ const xml_utils_1 = require("../../utils/xml.utils");
7
+ const LOCK_TIMEOUT_SECONDS = 300;
8
+ class LOCKRequestHandler {
9
+ handle = async (req, res) => {
10
+ const lockToken = `opaquelocktoken:${(0, node_crypto_1.randomUUID)()}`;
11
+ logger_utils_1.webdavLogger.info(`[LOCK] Granted lock token ${lockToken} for ${req.url}`);
12
+ const depth = req.headers['depth'] ?? '0';
13
+ const timeout = req.headers['timeout'] ?? `Second-${LOCK_TIMEOUT_SECONDS}`;
14
+ let owner;
15
+ try {
16
+ const parsed = xml_utils_1.XMLUtils.toJSON(req.body, { ignoreAttributes: false });
17
+ const maybeOwner = parsed?.['D:lockinfo']?.['D:owner'];
18
+ if (maybeOwner != null) {
19
+ owner = { [xml_utils_1.XMLUtils.addDefaultNamespace('owner')]: maybeOwner };
20
+ }
21
+ }
22
+ catch {
23
+ }
24
+ const lockDiscoveryXml = xml_utils_1.XMLUtils.toWebDavXML({
25
+ [xml_utils_1.XMLUtils.addDefaultNamespace('lockdiscovery')]: {
26
+ [xml_utils_1.XMLUtils.addDefaultNamespace('activelock')]: {
27
+ [xml_utils_1.XMLUtils.addDefaultNamespace('locktype')]: {
28
+ [xml_utils_1.XMLUtils.addDefaultNamespace('write')]: '',
29
+ },
30
+ [xml_utils_1.XMLUtils.addDefaultNamespace('lockscope')]: {
31
+ [xml_utils_1.XMLUtils.addDefaultNamespace('exclusive')]: '',
32
+ },
33
+ [xml_utils_1.XMLUtils.addDefaultNamespace('depth')]: depth,
34
+ [xml_utils_1.XMLUtils.addDefaultNamespace('timeout')]: timeout,
35
+ [xml_utils_1.XMLUtils.addDefaultNamespace('locktoken')]: {
36
+ [xml_utils_1.XMLUtils.addDefaultNamespace('href')]: lockToken,
37
+ },
38
+ [xml_utils_1.XMLUtils.addDefaultNamespace('lockroot')]: {
39
+ [xml_utils_1.XMLUtils.addDefaultNamespace('href')]: req.url,
40
+ },
41
+ ...owner,
42
+ },
43
+ },
44
+ }, { suppressEmptyNode: true }, 'prop');
45
+ res.set('Lock-Token', `<${lockToken}>`);
46
+ res.set('Content-Type', 'application/xml; charset="utf-8"');
47
+ res.status(200).send(lockDiscoveryXml);
48
+ };
49
+ }
50
+ exports.LOCKRequestHandler = LOCKRequestHandler;
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MKCOLRequestHandler = void 0;
4
+ const drive_item_repository_1 = require("../../services/database/drive-item/drive-item.repository");
4
5
  const webdav_utils_1 = require("../../utils/webdav.utils");
5
6
  const logger_utils_1 = require("../../utils/logger.utils");
6
7
  const xml_utils_1 = require("../../utils/xml.utils");
7
8
  const webdav_folder_service_1 = require("../../services/webdav/webdav-folder.service");
8
- const errors_utils_1 = require("../../utils/errors.utils");
9
+ const async_utils_1 = require("../../utils/async.utils");
9
10
  class MKCOLRequestHandler {
10
11
  handle = async (req, res) => {
11
12
  const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
@@ -15,14 +16,25 @@ class MKCOLRequestHandler {
15
16
  const driveFolderItem = await webdav_utils_1.WebDavUtils.getDriveFolderFromResource(resource.url);
16
17
  const folderAlreadyExists = !!driveFolderItem;
17
18
  if (folderAlreadyExists) {
18
- logger_utils_1.webdavLogger.info(`[MKCOL] ❌ Folder '${resource.url}' already exists`);
19
- throw new errors_utils_1.MethodNotAllowed('Folder already exists');
19
+ logger_utils_1.webdavLogger.info(`[MKCOL] Folder '${resource.url}' already exists, ignoring the creation request`);
20
+ res.status(200).send(xml_utils_1.XMLUtils.toWebDavXML({}, {}));
21
+ return;
20
22
  }
21
23
  const newFolder = await webdav_folder_service_1.WebDavFolderService.instance.createFolder({
22
24
  folderName: resource.path.base,
23
25
  parentFolderUuid: parentDriveFolderItem.uuid,
24
26
  });
25
27
  logger_utils_1.webdavLogger.info(`[MKCOL] ✅ Folder created with UUID ${newFolder.uuid}`);
28
+ await drive_item_repository_1.DriveItemRepository.instance.createOrUpdate([
29
+ {
30
+ uuid: newFolder.uuid,
31
+ path: resource.url,
32
+ type: 'folder',
33
+ createdAt: newFolder.createdAt,
34
+ updatedAt: new Date(),
35
+ },
36
+ ]);
37
+ await async_utils_1.AsyncUtils.sleep(500);
26
38
  res.status(201).send(xml_utils_1.XMLUtils.toWebDavXML({}, {}));
27
39
  };
28
40
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MOVERequestHandler = void 0;
4
4
  const drive_file_service_1 = require("../../services/drive/drive-file.service");
5
5
  const drive_folder_service_1 = require("../../services/drive/drive-folder.service");
6
+ const drive_item_repository_1 = require("../../services/database/drive-item/drive-item.repository");
6
7
  const errors_utils_1 = require("../../utils/errors.utils");
7
8
  const logger_utils_1 = require("../../utils/logger.utils");
8
9
  const webdav_utils_1 = require("../../utils/webdav.utils");
@@ -64,6 +65,16 @@ class MOVERequestHandler {
64
65
  });
65
66
  }
66
67
  }
68
+ await drive_item_repository_1.DriveItemRepository.instance.delete([originalDriveItem.uuid]);
69
+ await drive_item_repository_1.DriveItemRepository.instance.createOrUpdate([
70
+ {
71
+ uuid: originalDriveItem.uuid,
72
+ path: destinationResource.url,
73
+ type: originalDriveItem.itemType,
74
+ createdAt: new Date(originalDriveItem.createdAt),
75
+ updatedAt: new Date(),
76
+ },
77
+ ]);
67
78
  res.status(204).send();
68
79
  };
69
80
  }
@@ -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, MKCOL, MOVE, OPTIONS, PROPFIND, PUT';
11
+ const allowedMethods = 'DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PUT, UNLOCK';
12
12
  logger_utils_1.webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
13
- res.header('Allow', 'DELETE, GET, HEAD, MKCOL, MOVE, OPTIONS, PROPFIND, PUT');
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, MKCOL, MOVE, OPTIONS, PROPFIND';
18
+ const allowedMethods = 'DELETE, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, 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, MOVE, OPTIONS, PROPFIND, PUT';
25
+ const allowedMethods = 'DELETE, GET, HEAD, LOCK, MOVE, OPTIONS, PROPFIND, 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');
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PROPFINDRequestHandler = void 0;
7
7
  const xml_utils_1 = require("../../utils/xml.utils");
8
+ const drive_item_domain_1 = require("../../services/database/drive-item/drive-item.domain");
9
+ const drive_item_repository_1 = require("../../services/database/drive-item/drive-item.repository");
8
10
  const drive_folder_service_1 = require("../../services/drive/drive-folder.service");
9
11
  const format_utils_1 = require("../../utils/format.utils");
10
12
  const node_crypto_1 = require("node:crypto");
@@ -76,9 +78,11 @@ class PROPFINDRequestHandler {
76
78
  };
77
79
  getFolderChildsXMLNode = async (relativePath, folderUuid) => {
78
80
  const folderContent = await drive_folder_service_1.DriveFolderService.instance.getFolderContent(folderUuid);
79
- const foldersXML = folderContent.folders.map((folder) => {
81
+ const xmlNodes = [];
82
+ const cachedItems = [];
83
+ for (const folder of folderContent.folders) {
80
84
  const folderRelativePath = webdav_utils_1.WebDavUtils.joinURL(relativePath, folder.plainName, '/');
81
- return this.driveFolderItemToXMLNode({
85
+ xmlNodes.push(this.driveFolderItemToXMLNode({
82
86
  itemType: 'folder',
83
87
  name: folder.plainName,
84
88
  bucket: folder.bucket,
@@ -89,11 +93,18 @@ class PROPFINDRequestHandler {
89
93
  modificationTime: new Date(folder.modificationTime),
90
94
  uuid: folder.uuid,
91
95
  parentUuid: folder.parentUuid,
92
- }, folderRelativePath);
93
- });
94
- const filesXML = folderContent.files.map((file) => {
96
+ }, folderRelativePath));
97
+ cachedItems.push(new drive_item_domain_1.DriveItemBD({
98
+ uuid: folder.uuid,
99
+ path: folderRelativePath,
100
+ type: 'folder',
101
+ createdAt: new Date(folder.createdAt),
102
+ updatedAt: new Date(),
103
+ }));
104
+ }
105
+ for (const file of folderContent.files) {
95
106
  const fileRelativePath = webdav_utils_1.WebDavUtils.joinURL(relativePath, file.type ? `${file.plainName}.${file.type}` : file.plainName);
96
- return this.driveFileItemToXMLNode({
107
+ xmlNodes.push(this.driveFileItemToXMLNode({
97
108
  itemType: 'file',
98
109
  name: file.plainName,
99
110
  bucket: file.bucket,
@@ -107,9 +118,19 @@ class PROPFINDRequestHandler {
107
118
  modificationTime: new Date(file.modificationTime),
108
119
  createdAt: new Date(file.createdAt),
109
120
  updatedAt: new Date(file.updatedAt),
110
- }, fileRelativePath);
111
- });
112
- return foldersXML.concat(filesXML);
121
+ }, fileRelativePath));
122
+ cachedItems.push(new drive_item_domain_1.DriveItemBD({
123
+ uuid: file.uuid,
124
+ path: fileRelativePath,
125
+ type: 'file',
126
+ createdAt: new Date(file.createdAt),
127
+ updatedAt: new Date(),
128
+ }));
129
+ }
130
+ if (cachedItems.length > 0) {
131
+ await drive_item_repository_1.DriveItemRepository.instance.createOrUpdate(cachedItems);
132
+ }
133
+ return xmlNodes;
113
134
  };
114
135
  driveFolderRootStatsToXMLNode = async (driveFolderItem, relativePath) => {
115
136
  const totalUsage = await usage_service_1.UsageService.instance.fetchUsage();