@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.
- package/README.md +94 -40
- package/dist/commands/add-cert.d.ts +4 -1
- package/dist/commands/add-cert.js +4 -1
- package/dist/commands/config.js +1 -0
- package/dist/commands/create-folder.js +1 -0
- package/dist/commands/delete-permanently-file.js +1 -0
- package/dist/commands/delete-permanently-folder.js +1 -0
- package/dist/commands/download-file.js +1 -0
- package/dist/commands/list.js +6 -3
- package/dist/commands/login-legacy.js +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +2 -0
- package/dist/commands/logout.d.ts +4 -1
- package/dist/commands/logout.js +4 -1
- package/dist/commands/logs.d.ts +4 -1
- package/dist/commands/logs.js +4 -1
- package/dist/commands/move-file.js +1 -0
- package/dist/commands/move-folder.js +1 -0
- package/dist/commands/rename-file.js +1 -0
- package/dist/commands/rename-folder.js +1 -0
- package/dist/commands/trash-clear.js +1 -0
- package/dist/commands/trash-file.js +1 -0
- package/dist/commands/trash-folder.js +1 -0
- package/dist/commands/trash-list.d.ts +2 -0
- package/dist/commands/trash-list.js +7 -3
- package/dist/commands/trash-restore-file.js +1 -0
- package/dist/commands/trash-restore-folder.js +1 -0
- package/dist/commands/upload-file.js +1 -0
- package/dist/commands/upload-folder.js +1 -0
- package/dist/commands/webdav-config.js +1 -0
- package/dist/commands/webdav.d.ts +4 -1
- package/dist/commands/webdav.js +4 -1
- package/dist/commands/whoami.d.ts +4 -1
- package/dist/commands/whoami.js +10 -4
- package/dist/commands/workspaces-list.js +1 -0
- package/dist/commands/workspaces-unset.js +1 -0
- package/dist/commands/workspaces-use.js +1 -0
- package/dist/hooks/prerun/auth_check.js +2 -0
- package/dist/services/auth.service.js +12 -11
- package/dist/services/validation.service.d.ts +2 -12
- package/dist/services/validation.service.js +1 -26
- package/dist/types/command.types.d.ts +1 -0
- package/dist/utils/cli.utils.d.ts +2 -1
- package/dist/utils/cli.utils.js +27 -13
- package/dist/utils/drive.utils.d.ts +1 -0
- package/dist/utils/drive.utils.js +5 -1
- package/dist/utils/format.utils.js +6 -2
- package/dist/utils/network.utils.js +2 -2
- package/dist/webdav/handlers/GET.handler.js +1 -0
- package/dist/webdav/handlers/HEAD.handler.js +1 -0
- package/dist/webdav/handlers/MKCOL.handler.js +3 -3
- package/dist/webdav/handlers/PROPFIND.handler.js +7 -2
- package/dist/webdav/middewares/auth.middleware.js +1 -0
- package/dist/webdav/middewares/webdav-auth.middleware.js +1 -0
- package/dist/webdav/webdav-server.js +2 -2
- package/oclif.manifest.json +134 -1
- package/package.json +20 -20
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
};
|
|
@@ -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;
|
package/dist/commands/webdav.js
CHANGED
|
@@ -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;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -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
|
|
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
|
|
62
|
-
|
|
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;
|
|
@@ -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
|
|
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 (
|
|
52
|
+
if (tokenStatus === lib_1.TokenStatus.INVALID || !isValidMnemonic) {
|
|
52
53
|
throw new command_types_1.InvalidCredentialsError();
|
|
53
54
|
}
|
|
54
|
-
if (
|
|
55
|
+
if (tokenStatus === lib_1.TokenStatus.EXPIRED) {
|
|
55
56
|
throw new command_types_1.ExpiredCredentialsError();
|
|
56
57
|
}
|
|
57
58
|
let credentialsChanged = false;
|
|
58
|
-
if (
|
|
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
|
|
112
|
-
if (
|
|
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 (
|
|
119
|
+
else if (workspaceTokenStatus === lib_1.TokenStatus.EXPIRED) {
|
|
116
120
|
throw new command_types_1.ExpiredCredentialsError();
|
|
117
121
|
}
|
|
118
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -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>;
|
package/dist/utils/cli.utils.js
CHANGED
|
@@ -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
|
|
198
|
-
message
|
|
200
|
+
if (typeof errorData.message === 'string' && errorData.message?.trim?.().length > 0) {
|
|
201
|
+
message += ' [' + errorData.message + ']';
|
|
199
202
|
}
|
|
200
|
-
|
|
201
|
-
|
|
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(
|
|
223
|
+
CLIUtils.consoleLog(JSON.stringify(jsonLog));
|
|
212
224
|
}
|
|
213
225
|
else {
|
|
214
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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(`
|
|
103
|
+
throw new Error(`Unsatisfiable Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
104
104
|
}
|
|
105
105
|
else if (parsed === -2) {
|
|
106
|
-
throw new Error(`
|
|
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,
|
|
20
|
-
|
|
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:
|
|
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')]: '
|
|
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
|
};
|