@internxt/cli 1.6.5 → 1.6.7

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 (57) hide show
  1. package/README.md +94 -40
  2. package/dist/commands/add-cert.d.ts +4 -1
  3. package/dist/commands/add-cert.js +4 -1
  4. package/dist/commands/config.js +1 -0
  5. package/dist/commands/create-folder.js +1 -0
  6. package/dist/commands/delete-permanently-file.js +1 -0
  7. package/dist/commands/delete-permanently-folder.js +1 -0
  8. package/dist/commands/download-file.js +1 -0
  9. package/dist/commands/list.js +6 -3
  10. package/dist/commands/login-legacy.js +1 -0
  11. package/dist/commands/login.d.ts +2 -0
  12. package/dist/commands/login.js +2 -0
  13. package/dist/commands/logout.d.ts +4 -1
  14. package/dist/commands/logout.js +4 -1
  15. package/dist/commands/logs.d.ts +4 -1
  16. package/dist/commands/logs.js +4 -1
  17. package/dist/commands/move-file.js +1 -0
  18. package/dist/commands/move-folder.js +1 -0
  19. package/dist/commands/rename-file.js +1 -0
  20. package/dist/commands/rename-folder.js +1 -0
  21. package/dist/commands/trash-clear.js +1 -0
  22. package/dist/commands/trash-file.js +1 -0
  23. package/dist/commands/trash-folder.js +1 -0
  24. package/dist/commands/trash-list.d.ts +2 -0
  25. package/dist/commands/trash-list.js +7 -3
  26. package/dist/commands/trash-restore-file.js +1 -0
  27. package/dist/commands/trash-restore-folder.js +1 -0
  28. package/dist/commands/upload-file.js +1 -0
  29. package/dist/commands/upload-folder.js +1 -0
  30. package/dist/commands/webdav-config.js +1 -0
  31. package/dist/commands/webdav.d.ts +4 -1
  32. package/dist/commands/webdav.js +4 -1
  33. package/dist/commands/whoami.d.ts +4 -1
  34. package/dist/commands/whoami.js +10 -4
  35. package/dist/commands/workspaces-list.js +1 -0
  36. package/dist/commands/workspaces-unset.js +1 -0
  37. package/dist/commands/workspaces-use.js +1 -0
  38. package/dist/hooks/prerun/auth_check.js +2 -0
  39. package/dist/services/auth.service.js +12 -11
  40. package/dist/services/validation.service.d.ts +2 -12
  41. package/dist/services/validation.service.js +1 -26
  42. package/dist/types/command.types.d.ts +1 -0
  43. package/dist/utils/cli.utils.d.ts +2 -1
  44. package/dist/utils/cli.utils.js +27 -13
  45. package/dist/utils/drive.utils.d.ts +1 -0
  46. package/dist/utils/drive.utils.js +5 -1
  47. package/dist/utils/format.utils.js +6 -2
  48. package/dist/utils/network.utils.js +2 -2
  49. package/dist/webdav/handlers/GET.handler.js +1 -0
  50. package/dist/webdav/handlers/HEAD.handler.js +1 -0
  51. package/dist/webdav/handlers/MKCOL.handler.js +3 -3
  52. package/dist/webdav/handlers/PROPFIND.handler.js +7 -2
  53. package/dist/webdav/middewares/auth.middleware.js +1 -0
  54. package/dist/webdav/middewares/webdav-auth.middleware.js +1 -0
  55. package/dist/webdav/webdav-server.js +2 -2
  56. package/oclif.manifest.json +134 -1
  57. package/package.json +20 -20
@@ -50,6 +50,7 @@ class TrashClear extends core_1.Command {
50
50
  command: this.id,
51
51
  logReporter: this.log.bind(this),
52
52
  jsonFlag: flags['json'],
53
+ debugMode: flags['debug'],
53
54
  });
54
55
  this.exit(1);
55
56
  };
@@ -39,6 +39,7 @@ class TrashFile extends core_1.Command {
39
39
  command: this.id,
40
40
  logReporter: this.log.bind(this),
41
41
  jsonFlag: flags['json'],
42
+ debugMode: flags['debug'],
42
43
  });
43
44
  this.exit(1);
44
45
  };
@@ -39,6 +39,7 @@ class TrashFolder extends core_1.Command {
39
39
  command: this.id,
40
40
  logReporter: this.log.bind(this),
41
41
  jsonFlag: flags['json'],
42
+ debugMode: flags['debug'],
42
43
  });
43
44
  this.exit(1);
44
45
  };
@@ -6,6 +6,8 @@ export default class TrashList extends Command {
6
6
  static readonly examples: string[];
7
7
  static readonly flags: {
8
8
  extended: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ 'non-interactive': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
11
  };
10
12
  static readonly enableJsonFlag = true;
11
13
  run: () => Promise<{
@@ -12,6 +12,7 @@ class TrashList extends core_1.Command {
12
12
  static aliases = ['trash:list'];
13
13
  static examples = ['<%= config.bin %> <%= command.id %>'];
14
14
  static flags = {
15
+ ...cli_utils_1.CLIUtils.CommonFlags,
15
16
  extended: core_1.Flags.boolean({
16
17
  char: 'e',
17
18
  description: 'Displays additional information in the trash list.',
@@ -32,7 +33,8 @@ class TrashList extends core_1.Command {
32
33
  name: folder.plainName,
33
34
  id: folder.uuid,
34
35
  size: '-',
35
- modified: format_utils_1.FormatUtils.formatDate(folder.updatedAt),
36
+ created: format_utils_1.FormatUtils.formatDate(folder.creationTime ?? folder.createdAt),
37
+ modified: format_utils_1.FormatUtils.formatDate(folder.modificationTime ?? folder.updatedAt),
36
38
  };
37
39
  }),
38
40
  ...files.map((file) => {
@@ -41,7 +43,8 @@ class TrashList extends core_1.Command {
41
43
  name: file.type && file.type.length > 0 ? `${file.plainName}.${file.type}` : file.plainName,
42
44
  id: file.uuid,
43
45
  size: format_utils_1.FormatUtils.humanFileSize(Number(file.size)),
44
- modified: format_utils_1.FormatUtils.formatDate(file.updatedAt),
46
+ created: format_utils_1.FormatUtils.formatDate(file.creationTime ?? file.createdAt),
47
+ modified: format_utils_1.FormatUtils.formatDate(file.modificationTime ?? file.updatedAt),
45
48
  };
46
49
  }),
47
50
  ];
@@ -51,7 +54,7 @@ class TrashList extends core_1.Command {
51
54
  { value: 'id', alias: 'Id' },
52
55
  ];
53
56
  if (flags.extended) {
54
- headers.push({ value: 'modified', alias: 'Modified' }, { value: 'size', alias: 'Size' });
57
+ headers.push({ value: 'created', alias: 'Created' }, { value: 'modified', alias: 'Modified' }, { value: 'size', alias: 'Size' });
55
58
  }
56
59
  cli_utils_1.CLIUtils.table(this.log.bind(this), headers, allItems);
57
60
  return { success: true, list: { folders, files } };
@@ -63,6 +66,7 @@ class TrashList extends core_1.Command {
63
66
  command: this.id,
64
67
  logReporter: this.log.bind(this),
65
68
  jsonFlag: flags['json'],
69
+ debugMode: flags['debug'],
66
70
  });
67
71
  this.exit(1);
68
72
  };
@@ -52,6 +52,7 @@ class TrashRestoreFile extends core_1.Command {
52
52
  command: this.id,
53
53
  logReporter: this.log.bind(this),
54
54
  jsonFlag: flags['json'],
55
+ debugMode: flags['debug'],
55
56
  });
56
57
  this.exit(1);
57
58
  };
@@ -54,6 +54,7 @@ class TrashRestoreFolder extends core_1.Command {
54
54
  command: this.id,
55
55
  logReporter: this.log.bind(this),
56
56
  jsonFlag: flags['json'],
57
+ debugMode: flags['debug'],
57
58
  });
58
59
  this.exit(1);
59
60
  };
@@ -143,6 +143,7 @@ class UploadFile extends core_1.Command {
143
143
  command: this.id,
144
144
  logReporter: this.log.bind(this),
145
145
  jsonFlag: flags['json'],
146
+ debugMode: flags['debug'],
146
147
  });
147
148
  this.exit(1);
148
149
  };
@@ -70,6 +70,7 @@ class UploadFolder extends core_1.Command {
70
70
  command: this.id,
71
71
  logReporter: this.log.bind(this),
72
72
  jsonFlag: flags['json'],
73
+ debugMode: flags['debug'],
73
74
  });
74
75
  this.exit(1);
75
76
  };
@@ -131,6 +131,7 @@ class WebDAVConfig extends core_1.Command {
131
131
  command: this.id,
132
132
  logReporter: this.log.bind(this),
133
133
  jsonFlag: flags['json'],
134
+ debugMode: flags['debug'],
134
135
  });
135
136
  this.exit(1);
136
137
  };
@@ -6,7 +6,10 @@ export default class Webdav extends Command {
6
6
  static readonly description = "Start, stop, restart or get the status of the Internxt CLI WebDAV server";
7
7
  static readonly aliases: never[];
8
8
  static readonly examples: string[];
9
- static readonly flags: {};
9
+ static readonly flags: {
10
+ 'non-interactive': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
10
13
  static readonly enableJsonFlag = true;
11
14
  run: () => Promise<{
12
15
  success: boolean;
@@ -21,7 +21,9 @@ class Webdav extends core_1.Command {
21
21
  '<%= config.bin %> <%= command.id %> restart',
22
22
  '<%= config.bin %> <%= command.id %> status',
23
23
  ];
24
- static flags = {};
24
+ static flags = {
25
+ ...cli_utils_1.CLIUtils.CommonFlags,
26
+ };
25
27
  static enableJsonFlag = true;
26
28
  run = async () => {
27
29
  const { args, flags } = await this.parse(Webdav);
@@ -68,6 +70,7 @@ class Webdav extends core_1.Command {
68
70
  command: this.id,
69
71
  logReporter: this.log.bind(this),
70
72
  jsonFlag: flags['json'],
73
+ debugMode: flags['debug'],
71
74
  });
72
75
  this.exit(1);
73
76
  };
@@ -5,7 +5,10 @@ export default class Whoami extends Command {
5
5
  static readonly description = "Display the current user logged into the Internxt CLI.";
6
6
  static readonly aliases: never[];
7
7
  static readonly examples: string[];
8
- static readonly flags: {};
8
+ static readonly flags: {
9
+ 'non-interactive': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
9
12
  static readonly enableJsonFlag = true;
10
13
  run: () => Promise<{
11
14
  success: boolean;
@@ -5,14 +5,18 @@ const cli_utils_1 = require("../utils/cli.utils");
5
5
  const config_service_1 = require("../services/config.service");
6
6
  const validation_service_1 = require("../services/validation.service");
7
7
  const auth_service_1 = require("../services/auth.service");
8
+ const lib_1 = require("@internxt/lib");
8
9
  class Whoami extends core_1.Command {
9
10
  static args = {};
10
11
  static description = 'Display the current user logged into the Internxt CLI.';
11
12
  static aliases = [];
12
13
  static examples = ['<%= config.bin %> <%= command.id %>'];
13
- static flags = {};
14
+ static flags = {
15
+ ...cli_utils_1.CLIUtils.CommonFlags,
16
+ };
14
17
  static enableJsonFlag = true;
15
18
  run = async () => {
19
+ await this.parse(Whoami);
16
20
  const userCredentials = await config_service_1.ConfigService.instance.readUser();
17
21
  if (!userCredentials) {
18
22
  const message = 'You are not logged in.';
@@ -49,6 +53,7 @@ class Whoami extends core_1.Command {
49
53
  command: this.id,
50
54
  logReporter: this.log.bind(this),
51
55
  jsonFlag: flags['json'],
56
+ debugMode: flags['debug'],
52
57
  });
53
58
  this.exit(1);
54
59
  };
@@ -56,10 +61,11 @@ class Whoami extends core_1.Command {
56
61
  if (!(loginCreds?.token && loginCreds?.user?.mnemonic)) {
57
62
  return { valid: false, refreshRequired: false };
58
63
  }
59
- const tokenDetails = validation_service_1.ValidationService.instance.validateTokenAndCheckExpiration(loginCreds.token);
64
+ const tokenStatus = validation_service_1.ValidationService.instance.validateTokenAndCheckExpiration(loginCreds.token);
60
65
  const goodMnemonic = validation_service_1.ValidationService.instance.validateMnemonic(loginCreds.user.mnemonic);
61
- const goodToken = tokenDetails.isValid && !tokenDetails.expiration.expired;
62
- return { valid: goodToken && goodMnemonic, refreshRequired: tokenDetails.expiration.refreshRequired };
66
+ const isRefreshRequired = tokenStatus === lib_1.TokenStatus.REFRESH_REQUIRED;
67
+ const isTokenValid = tokenStatus === lib_1.TokenStatus.VALID || isRefreshRequired;
68
+ return { valid: isTokenValid && goodMnemonic, refreshRequired: isRefreshRequired };
63
69
  };
64
70
  }
65
71
  exports.default = Whoami;
@@ -60,6 +60,7 @@ class WorkspacesList extends core_1.Command {
60
60
  command: this.id,
61
61
  logReporter: this.log.bind(this),
62
62
  jsonFlag: flags['json'],
63
+ debugMode: flags['debug'],
63
64
  });
64
65
  this.exit(1);
65
66
  };
@@ -31,6 +31,7 @@ class WorkspacesUnset extends core_1.Command {
31
31
  command: this.id,
32
32
  logReporter: this.log.bind(this),
33
33
  jsonFlag: flags['json'],
34
+ debugMode: flags['debug'],
34
35
  });
35
36
  this.exit(1);
36
37
  };
@@ -60,6 +60,7 @@ class WorkspacesUse extends core_1.Command {
60
60
  command: this.id,
61
61
  logReporter: this.log.bind(this),
62
62
  jsonFlag: flags['json'],
63
+ debugMode: flags['debug'],
63
64
  });
64
65
  this.exit(1);
65
66
  };
@@ -22,6 +22,7 @@ const CommandsToSkip = [whoami_1.default, login_1.default, login_legacy_1.defaul
22
22
  const hook = async function (opts) {
23
23
  const { Command, argv } = opts;
24
24
  const jsonFlag = argv.includes('--json');
25
+ const debugFlag = argv.includes('--debug');
25
26
  const commandsToSkipNames = CommandsToSkip.map((command) => command.name?.toLowerCase()).filter(Boolean);
26
27
  const commandsToSkipIds = CommandsToSkip.map((command) => command.id?.toLowerCase()).filter(Boolean);
27
28
  if (!commandsToSkipNames.includes(Command.name?.toLowerCase()) &&
@@ -41,6 +42,7 @@ const hook = async function (opts) {
41
42
  command: Command.id,
42
43
  logReporter: this.log.bind(this),
43
44
  jsonFlag,
45
+ debugMode: debugFlag,
44
46
  });
45
47
  opts.context.exit(1);
46
48
  }
@@ -8,6 +8,7 @@ const config_service_1 = require("./config.service");
8
8
  const command_types_1 = require("../types/command.types");
9
9
  const validation_service_1 = require("./validation.service");
10
10
  const workspace_service_1 = require("./drive/workspace.service");
11
+ const lib_1 = require("@internxt/lib");
11
12
  class AuthService {
12
13
  static instance = new AuthService();
13
14
  doLogin = async (email, password, twoFactorCode) => {
@@ -46,16 +47,16 @@ class AuthService {
46
47
  if (!loginCreds?.token || !loginCreds?.user?.mnemonic) {
47
48
  throw new command_types_1.MissingCredentialsError();
48
49
  }
49
- const tokenDetails = validation_service_1.ValidationService.instance.validateTokenAndCheckExpiration(loginCreds.token);
50
+ const tokenStatus = validation_service_1.ValidationService.instance.validateTokenAndCheckExpiration(loginCreds.token);
50
51
  const isValidMnemonic = validation_service_1.ValidationService.instance.validateMnemonic(loginCreds.user.mnemonic);
51
- if (!tokenDetails.isValid || !isValidMnemonic) {
52
+ if (tokenStatus === lib_1.TokenStatus.INVALID || !isValidMnemonic) {
52
53
  throw new command_types_1.InvalidCredentialsError();
53
54
  }
54
- if (tokenDetails.expiration.expired) {
55
+ if (tokenStatus === lib_1.TokenStatus.EXPIRED) {
55
56
  throw new command_types_1.ExpiredCredentialsError();
56
57
  }
57
58
  let credentialsChanged = false;
58
- if (tokenDetails.expiration.refreshRequired) {
59
+ if (tokenStatus === lib_1.TokenStatus.REFRESH_REQUIRED) {
59
60
  try {
60
61
  loginCreds = await this.refreshUserToken(loginCreds.token, loginCreds.user.mnemonic, loginCreds.user.keys.ecc.privateKey);
61
62
  credentialsChanged = true;
@@ -108,14 +109,17 @@ class AuthService {
108
109
  if (loginCreds.workspace?.workspaceCredentials && loginCreds.workspace?.workspaceData) {
109
110
  const workspaceToken = loginCreds.workspace.workspaceCredentials.token;
110
111
  const workspaceUuid = loginCreds.workspace.workspaceCredentials.id;
111
- const workspaceTokenDetails = validation_service_1.ValidationService.instance.validateTokenAndCheckExpiration(workspaceToken);
112
- if (!workspaceTokenDetails.isValid) {
112
+ const workspaceTokenStatus = validation_service_1.ValidationService.instance.validateTokenAndCheckExpiration(workspaceToken);
113
+ if (workspaceTokenStatus === lib_1.TokenStatus.VALID) {
114
+ return loginCreds.workspace;
115
+ }
116
+ else if (workspaceTokenStatus === lib_1.TokenStatus.INVALID) {
113
117
  throw new command_types_1.InvalidCredentialsError();
114
118
  }
115
- if (workspaceTokenDetails.expiration.expired) {
119
+ else if (workspaceTokenStatus === lib_1.TokenStatus.EXPIRED) {
116
120
  throw new command_types_1.ExpiredCredentialsError();
117
121
  }
118
- if (workspaceTokenDetails.expiration.refreshRequired) {
122
+ else if (workspaceTokenStatus === lib_1.TokenStatus.REFRESH_REQUIRED) {
119
123
  sdk_manager_service_1.SdkManager.init({ token: loginCreds.token, workspaceToken: loginCreds.workspace.workspaceCredentials.token });
120
124
  const workspaceCredentials = await workspace_service_1.WorkspaceService.instance.getWorkspaceCredentials(workspaceUuid);
121
125
  return {
@@ -123,9 +127,6 @@ class AuthService {
123
127
  workspaceData: loginCreds.workspace.workspaceData,
124
128
  };
125
129
  }
126
- else {
127
- return loginCreds.workspace;
128
- }
129
130
  }
130
131
  };
131
132
  getCurrentWorkspace = async () => {
@@ -1,3 +1,4 @@
1
+ import { TokenStatus } from '@internxt/lib';
1
2
  export declare class ValidationService {
2
3
  static readonly instance: ValidationService;
3
4
  validateEmail: (email: string) => boolean;
@@ -9,16 +10,5 @@ export declare class ValidationService {
9
10
  validateStringIsNotEmpty: (str: string) => boolean;
10
11
  validateDirectoryExists: (path: string) => Promise<boolean>;
11
12
  validateFileExists: (path: string) => Promise<boolean>;
12
- validateJwtAndCheckExpiration: (token?: string) => number | null;
13
- checkTokenExpiration: (expirationTimestamp: number) => {
14
- expired: boolean;
15
- refreshRequired: boolean;
16
- };
17
- validateTokenAndCheckExpiration: (token?: string) => {
18
- isValid: boolean;
19
- expiration: {
20
- expired: boolean;
21
- refreshRequired: boolean;
22
- };
23
- };
13
+ validateTokenAndCheckExpiration: (token: string) => TokenStatus;
24
14
  }
@@ -48,33 +48,8 @@ class ValidationService {
48
48
  return false;
49
49
  }
50
50
  };
51
- validateJwtAndCheckExpiration = (token) => {
52
- if (!token || typeof token !== 'string' || token.split('.').length !== 3) {
53
- return null;
54
- }
55
- try {
56
- const payload = JSON.parse(atob(token.split('.')[1]));
57
- return typeof payload.exp === 'number' ? payload.exp : null;
58
- }
59
- catch {
60
- return null;
61
- }
62
- };
63
- checkTokenExpiration = (expirationTimestamp) => {
64
- const TWO_DAYS_IN_SECONDS = 2 * 24 * 60 * 60;
65
- const currentTime = Math.floor(Date.now() / 1000);
66
- const remainingSeconds = expirationTimestamp - currentTime;
67
- return {
68
- expired: remainingSeconds <= 0,
69
- refreshRequired: remainingSeconds > 0 && remainingSeconds <= TWO_DAYS_IN_SECONDS,
70
- };
71
- };
72
51
  validateTokenAndCheckExpiration = (token) => {
73
- const expiration = this.validateJwtAndCheckExpiration(token);
74
- return {
75
- isValid: expiration !== null,
76
- expiration: expiration ? this.checkTokenExpiration(expiration) : { expired: true, refreshRequired: false },
77
- };
52
+ return lib_1.auth.validateTokenAndCheckExpiration(token);
78
53
  };
79
54
  }
80
55
  exports.ValidationService = ValidationService;
@@ -114,6 +114,7 @@ export interface PaginatedItem {
114
114
  type: string;
115
115
  id: string;
116
116
  size: string;
117
+ created: string;
117
118
  modified: string;
118
119
  }
119
120
  export interface PromptOptions {
@@ -49,11 +49,12 @@ export declare class CLIUtils {
49
49
  };
50
50
  static readonly formatDuration: (milliseconds: number) => string;
51
51
  static readonly calculateThroughputMBps: (bytes: number, milliseconds: number) => number;
52
- static readonly catchError: ({ error, logReporter, command, jsonFlag, }: {
52
+ static readonly catchError: ({ error, logReporter, command, jsonFlag, debugMode, }: {
53
53
  error: Error | AppError | AxiosResponseError;
54
54
  command?: string;
55
55
  logReporter: LogReporter;
56
56
  jsonFlag?: boolean;
57
+ debugMode?: boolean;
57
58
  }) => void;
58
59
  static readonly parseEmpty: (input: string) => Promise<string>;
59
60
  static readonly prepareNetwork: (loginUserDetails: LoginUserDetails) => Promise<NetworkOptions>;
@@ -183,8 +183,8 @@ class CLIUtils {
183
183
  const seconds = milliseconds / 1000;
184
184
  return megabytes / seconds;
185
185
  };
186
- static catchError = ({ error, logReporter, command, jsonFlag, }) => {
187
- let message;
186
+ static catchError = ({ error, logReporter, command, jsonFlag, debugMode, }) => {
187
+ let message = '';
188
188
  let requestId;
189
189
  if ('requestId' in error) {
190
190
  requestId = error.requestId;
@@ -192,26 +192,40 @@ class CLIUtils {
192
192
  else if ('xRequestId' in error) {
193
193
  requestId = error.xRequestId;
194
194
  }
195
+ if ('message' in error && typeof error.message === 'string' && error.message?.trim?.().length > 0) {
196
+ message = error.message;
197
+ }
195
198
  if ('data' in error) {
196
199
  const errorData = error.data;
197
- if (errorData.message && errorData.message.trim().length > 0) {
198
- message = errorData.message;
200
+ if (typeof errorData.message === 'string' && errorData.message?.trim?.().length > 0) {
201
+ message += ' [' + errorData.message + ']';
199
202
  }
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);
203
+ else if (Array.isArray(errorData.message) && errorData.message.length > 0) {
204
+ message += ' [' + errorData.message.join(', ') + ']';
207
205
  }
208
206
  }
207
+ if (message.length === 0) {
208
+ message = JSON.stringify(error);
209
+ }
209
210
  CLIUtils.failed(jsonFlag);
211
+ const jsonLog = {
212
+ success: false,
213
+ message,
214
+ command,
215
+ };
216
+ if (requestId) {
217
+ jsonLog.requestId = requestId;
218
+ }
219
+ if (command) {
220
+ jsonLog.command = command;
221
+ }
210
222
  if (jsonFlag) {
211
- CLIUtils.consoleLog(JSON.stringify({ success: false, message, requestId }));
223
+ CLIUtils.consoleLog(JSON.stringify(jsonLog));
212
224
  }
213
225
  else {
214
- errors_utils_1.ErrorUtils.report(error, { command, requestId });
226
+ if (debugMode) {
227
+ errors_utils_1.ErrorUtils.report(error);
228
+ }
215
229
  CLIUtils.error(logReporter, message + (requestId ? ` (requestId: ${requestId})` : ''));
216
230
  }
217
231
  };
@@ -1,6 +1,7 @@
1
1
  import { FileMeta, FolderMeta, CreateFolderResponse } from '@internxt/sdk/dist/drive/storage/types';
2
2
  import { DriveFileItem, DriveFolderItem } from '../types/drive.types';
3
3
  export declare class DriveUtils {
4
+ static parseFileSize(size: string | number): number;
4
5
  static driveFileMetaToItem(fileMeta: FileMeta): DriveFileItem;
5
6
  static driveFolderMetaToItem(folderMeta: FolderMeta): DriveFolderItem;
6
7
  static createFolderResponseToItem(folderResponse: CreateFolderResponse): DriveFolderItem;
@@ -3,13 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DriveUtils = void 0;
4
4
  const types_1 = require("@internxt/sdk/dist/drive/storage/types");
5
5
  class DriveUtils {
6
+ static parseFileSize(size) {
7
+ const parsed = Number(size);
8
+ return Number.isFinite(parsed) ? parsed : 0;
9
+ }
6
10
  static driveFileMetaToItem(fileMeta) {
7
11
  return {
8
12
  itemType: 'file',
9
13
  uuid: fileMeta.uuid ?? '',
10
14
  status: fileMeta.status,
11
15
  folderUuid: fileMeta.folderUuid,
12
- size: Number(fileMeta.size),
16
+ size: DriveUtils.parseFileSize(fileMeta.size),
13
17
  name: fileMeta.plainName ?? fileMeta.name,
14
18
  bucket: fileMeta.bucket,
15
19
  createdAt: new Date(fileMeta.createdAt),
@@ -15,10 +15,14 @@ class FormatUtils {
15
15
  return Number((size / Math.pow(1024, unitIndex)).toFixed(2)) + ' ' + units[unitIndex];
16
16
  };
17
17
  static formatDate = (date) => {
18
- return (0, dayjs_1.default)(date).format('D MMMM, YYYY [at] HH:mm');
18
+ const d = (0, dayjs_1.default)(date);
19
+ return d.isValid() ? d.format('D MMMM, YYYY [at] HH:mm') : '-';
19
20
  };
20
21
  static formatDateForWebDav = (date) => {
21
- return dayjs_1.default.utc(date).format('ddd, DD MMM YYYY HH:mm:ss [GMT]');
22
+ const d = dayjs_1.default.utc(date);
23
+ return d.isValid()
24
+ ? d.format('ddd, DD MMM YYYY HH:mm:ss [GMT]')
25
+ : dayjs_1.default.utc().format('ddd, DD MMM YYYY HH:mm:ss [GMT]');
22
26
  };
23
27
  static formatLimit = (limit) => {
24
28
  let result = '...';
@@ -100,10 +100,10 @@ class NetworkUtils {
100
100
  }
101
101
  }
102
102
  else if (parsed === -1) {
103
- throw new Error(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
103
+ throw new Error(`Unsatisfiable Range-Request. ${JSON.stringify(rangeOptions)}`);
104
104
  }
105
105
  else if (parsed === -2) {
106
- throw new Error(`Unsatisfiable Range-Request. ${JSON.stringify(rangeOptions)}`);
106
+ throw new Error(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
107
107
  }
108
108
  else {
109
109
  throw new Error(`Unknown error from Range-Request. ${JSON.stringify(rangeOptions)}`);
@@ -20,6 +20,7 @@ class GETRequestHandler {
20
20
  const { user } = await auth_service_1.AuthService.instance.getAuthDetails();
21
21
  logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] Network ready for download`);
22
22
  res.header('Content-Type', 'application/octet-stream');
23
+ res.header('Accept-Ranges', 'bytes');
23
24
  const fileSize = driveFile.size ?? 0;
24
25
  if (fileSize > 0) {
25
26
  const range = req.headers['range'];
@@ -26,6 +26,7 @@ class HEADRequestHandler {
26
26
  contentLength = rangeOptions.rangeSize;
27
27
  }
28
28
  res.header('Content-Type', 'application/octet-stream');
29
+ res.header('Accept-Ranges', 'bytes');
29
30
  res.header('Content-length', contentLength.toString());
30
31
  }
31
32
  res.status(200).send();
@@ -7,6 +7,7 @@ const logger_utils_1 = require("../../utils/logger.utils");
7
7
  const xml_utils_1 = require("../../utils/xml.utils");
8
8
  const webdav_folder_service_1 = require("../../services/webdav/webdav-folder.service");
9
9
  const async_utils_1 = require("../../utils/async.utils");
10
+ const errors_utils_1 = require("../../utils/errors.utils");
10
11
  class MKCOLRequestHandler {
11
12
  handle = async (req, res) => {
12
13
  const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
@@ -16,9 +17,8 @@ class MKCOLRequestHandler {
16
17
  const driveFolderItem = await webdav_utils_1.WebDavUtils.getDriveFolderFromResource(resource.url);
17
18
  const folderAlreadyExists = !!driveFolderItem;
18
19
  if (folderAlreadyExists) {
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
+ logger_utils_1.webdavLogger.info(`[MKCOL] Folder '${resource.url}' already exists, throwing 405 error`);
21
+ throw new errors_utils_1.MethodNotAllowed('Folder already exists');
22
22
  }
23
23
  const newFolder = await webdav_folder_service_1.WebDavFolderService.instance.createFolder({
24
24
  folderName: resource.path.base,
@@ -8,6 +8,7 @@ const xml_utils_1 = require("../../utils/xml.utils");
8
8
  const drive_item_domain_1 = require("../../services/database/drive-item/drive-item.domain");
9
9
  const drive_item_repository_1 = require("../../services/database/drive-item/drive-item.repository");
10
10
  const drive_folder_service_1 = require("../../services/drive/drive-folder.service");
11
+ const drive_utils_1 = require("../../utils/drive.utils");
11
12
  const format_utils_1 = require("../../utils/format.utils");
12
13
  const node_crypto_1 = require("node:crypto");
13
14
  const mime_types_1 = __importDefault(require("mime-types"));
@@ -23,6 +24,7 @@ class PROPFINDRequestHandler {
23
24
  res.status(404).send();
24
25
  return;
25
26
  }
27
+ res.set('Content-Type', 'application/xml; charset="utf-8"');
26
28
  switch (driveItem.itemType) {
27
29
  case 'file': {
28
30
  const fileMetaXML = await this.getFileMetaXML(resource, driveItem);
@@ -113,7 +115,7 @@ class PROPFINDRequestHandler {
113
115
  type: file.type,
114
116
  status: file.status,
115
117
  folderUuid: file.folderUuid,
116
- size: Number(file.size),
118
+ size: drive_utils_1.DriveUtils.parseFileSize(file.size),
117
119
  creationTime: new Date(file.creationTime),
118
120
  modificationTime: new Date(file.modificationTime),
119
121
  createdAt: new Date(file.createdAt),
@@ -140,7 +142,8 @@ class PROPFINDRequestHandler {
140
142
  [xml_utils_1.XMLUtils.addDefaultNamespace('propstat')]: {
141
143
  [xml_utils_1.XMLUtils.addDefaultNamespace('status')]: 'HTTP/1.1 200 OK',
142
144
  [xml_utils_1.XMLUtils.addDefaultNamespace('prop')]: {
143
- [xml_utils_1.XMLUtils.addDefaultNamespace('getcontenttype')]: 'application/octet-stream',
145
+ [xml_utils_1.XMLUtils.addDefaultNamespace('getcontenttype')]: 'httpd/unix-directory',
146
+ [xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]: '"' + (0, node_crypto_1.randomUUID)().replaceAll('-', '') + '"',
144
147
  'x1:lastmodified': {
145
148
  '#text': format_utils_1.FormatUtils.formatDateForWebDav(driveFolderItem.updatedAt),
146
149
  '@_xmlns:x1': 'SAR:',
@@ -173,6 +176,8 @@ class PROPFINDRequestHandler {
173
176
  [xml_utils_1.XMLUtils.addDefaultNamespace('displayname')]: displayName,
174
177
  [xml_utils_1.XMLUtils.addDefaultNamespace('getlastmodified')]: format_utils_1.FormatUtils.formatDateForWebDav(driveFolderItem.updatedAt),
175
178
  [xml_utils_1.XMLUtils.addDefaultNamespace('getcontentlength')]: 0,
179
+ [xml_utils_1.XMLUtils.addDefaultNamespace('getcontenttype')]: 'httpd/unix-directory',
180
+ [xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]: '"' + (0, node_crypto_1.randomUUID)().replaceAll('-', '') + '"',
176
181
  [xml_utils_1.XMLUtils.addDefaultNamespace('resourcetype')]: {
177
182
  [xml_utils_1.XMLUtils.addDefaultNamespace('collection')]: '',
178
183
  },
@@ -35,6 +35,7 @@ const AuthMiddleware = () => {
35
35
  const errorBodyXML = xml_utils_1.XMLUtils.toWebDavXML({
36
36
  [xml_utils_1.XMLUtils.addDefaultNamespace('responsedescription')]: message,
37
37
  }, {}, 'error');
38
+ res.set('WWW-Authenticate', 'Basic realm="Internxt WebDAV"');
38
39
  res.status(401).send(errorBodyXML);
39
40
  }
40
41
  })();
@@ -37,5 +37,6 @@ const sendUnauthorizedError = (res, message) => {
37
37
  const errorBodyXML = xml_utils_1.XMLUtils.toWebDavXML({
38
38
  [xml_utils_1.XMLUtils.addDefaultNamespace('responsedescription')]: message,
39
39
  }, {}, 'error');
40
+ res.set('WWW-Authenticate', 'Basic realm="Internxt WebDAV"');
40
41
  res.status(401).send(errorBodyXML);
41
42
  };