@internxt/cli 1.6.1 → 1.6.2
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 +27 -27
- package/dist/commands/add-cert.js +2 -2
- package/dist/commands/logs.js +3 -3
- package/dist/commands/upload-file.d.ts +1 -1
- package/dist/commands/upload-file.js +55 -41
- package/dist/constants/configs.d.ts +12 -0
- package/dist/constants/configs.js +20 -0
- package/dist/services/auth.service.js +11 -6
- package/dist/services/config.service.d.ts +0 -12
- package/dist/services/config.service.js +33 -38
- package/dist/services/network/upload/upload-facade.service.js +1 -1
- package/dist/services/network/upload/upload-file.service.d.ts +5 -4
- package/dist/services/network/upload/upload-file.service.js +51 -29
- package/dist/services/network/upload/upload.types.d.ts +2 -2
- package/dist/services/network/upload/upload.types.js +1 -1
- package/dist/services/sdk-manager.service.d.ts +2 -2
- package/dist/services/sdk-manager.service.js +2 -2
- package/dist/services/validation.service.d.ts +5 -0
- package/dist/services/validation.service.js +22 -18
- package/dist/utils/cli.utils.d.ts +3 -0
- package/dist/utils/cli.utils.js +37 -3
- package/dist/utils/errors.utils.d.ts +1 -0
- package/dist/utils/errors.utils.js +8 -2
- package/dist/utils/logger.utils.js +5 -5
- package/dist/utils/network.utils.js +17 -18
- package/dist/utils/thumbnail.utils.d.ts +1 -1
- package/dist/utils/thumbnail.utils.js +8 -8
- package/dist/utils/xml.utils.d.ts +1 -1
- package/dist/utils/xml.utils.js +1 -1
- package/dist/webdav/handlers/GET.handler.js +0 -2
- package/dist/webdav/handlers/PUT.handler.js +56 -41
- package/dist/webdav/middewares/errors.middleware.js +1 -1
- package/dist/webdav/webdav-server.js +4 -4
- package/oclif.manifest.json +1 -1
- package/package.json +10 -10
|
@@ -4,23 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ConfigService = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
-
const os_1 = __importDefault(require("os"));
|
|
9
7
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
8
|
const crypto_service_1 = require("./crypto.service");
|
|
9
|
+
const errors_utils_1 = require("../utils/errors.utils");
|
|
10
|
+
const configs_1 = require("../constants/configs");
|
|
11
11
|
class ConfigService {
|
|
12
|
-
static INTERNXT_CLI_DATA_DIR = node_path_1.default.join(os_1.default.homedir(), '.internxt-cli');
|
|
13
|
-
static INTERNXT_CLI_LOGS_DIR = node_path_1.default.join(this.INTERNXT_CLI_DATA_DIR, 'logs');
|
|
14
|
-
static INTERNXT_TMP_DIR = os_1.default.tmpdir();
|
|
15
|
-
static CREDENTIALS_FILE = node_path_1.default.join(this.INTERNXT_CLI_DATA_DIR, '.inxtcli');
|
|
16
|
-
static DRIVE_SQLITE_FILE = node_path_1.default.join(this.INTERNXT_CLI_DATA_DIR, 'internxt-cli-drive.sqlite');
|
|
17
|
-
static WEBDAV_SSL_CERTS_DIR = node_path_1.default.join(this.INTERNXT_CLI_DATA_DIR, 'certs');
|
|
18
|
-
static WEBDAV_CONFIGS_FILE = node_path_1.default.join(this.INTERNXT_CLI_DATA_DIR, 'config.webdav.inxt');
|
|
19
|
-
static WEBDAV_DEFAULT_HOST = '127.0.0.1';
|
|
20
|
-
static WEBDAV_DEFAULT_PORT = '3005';
|
|
21
|
-
static WEBDAV_DEFAULT_PROTOCOL = 'https';
|
|
22
|
-
static WEBDAV_DEFAULT_TIMEOUT = 0;
|
|
23
|
-
static WEBDAV_DEFAULT_CREATE_FULL_PATH = true;
|
|
24
12
|
static instance = new ConfigService();
|
|
25
13
|
get = (key) => {
|
|
26
14
|
const value = process.env[key];
|
|
@@ -32,17 +20,24 @@ class ConfigService {
|
|
|
32
20
|
await this.ensureInternxtCliDataDirExists();
|
|
33
21
|
const credentialsString = JSON.stringify(loginCredentials);
|
|
34
22
|
const encryptedCredentials = crypto_service_1.CryptoService.instance.encryptText(credentialsString);
|
|
35
|
-
await promises_1.default.writeFile(
|
|
23
|
+
await promises_1.default.writeFile(configs_1.CREDENTIALS_FILE, encryptedCredentials, 'utf8');
|
|
36
24
|
};
|
|
37
25
|
clearUser = async () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
try {
|
|
27
|
+
const stat = await promises_1.default.stat(configs_1.CREDENTIALS_FILE);
|
|
28
|
+
if (stat.size === 0)
|
|
29
|
+
return;
|
|
30
|
+
await promises_1.default.writeFile(configs_1.CREDENTIALS_FILE, '', 'utf8');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (!(0, errors_utils_1.isFileNotFoundError)(error)) {
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
42
37
|
};
|
|
43
38
|
readUser = async () => {
|
|
44
39
|
try {
|
|
45
|
-
const encryptedCredentials = await promises_1.default.readFile(
|
|
40
|
+
const encryptedCredentials = await promises_1.default.readFile(configs_1.CREDENTIALS_FILE, 'utf8');
|
|
46
41
|
const credentialsString = crypto_service_1.CryptoService.instance.decryptText(encryptedCredentials);
|
|
47
42
|
const loginCredentials = JSON.parse(credentialsString);
|
|
48
43
|
return loginCredentials;
|
|
@@ -54,52 +49,52 @@ class ConfigService {
|
|
|
54
49
|
saveWebdavConfig = async (webdavConfig) => {
|
|
55
50
|
await this.ensureInternxtCliDataDirExists();
|
|
56
51
|
const configs = JSON.stringify(webdavConfig);
|
|
57
|
-
await promises_1.default.writeFile(
|
|
52
|
+
await promises_1.default.writeFile(configs_1.WEBDAV_CONFIGS_FILE, configs, 'utf8');
|
|
58
53
|
};
|
|
59
54
|
readWebdavConfig = async () => {
|
|
60
55
|
try {
|
|
61
|
-
const configsData = await promises_1.default.readFile(
|
|
56
|
+
const configsData = await promises_1.default.readFile(configs_1.WEBDAV_CONFIGS_FILE, 'utf8');
|
|
62
57
|
const configs = JSON.parse(configsData);
|
|
63
58
|
return {
|
|
64
|
-
host: configs?.host ??
|
|
65
|
-
port: configs?.port ??
|
|
66
|
-
protocol: configs?.protocol ??
|
|
67
|
-
timeoutMinutes: configs?.timeoutMinutes ??
|
|
68
|
-
createFullPath: configs?.createFullPath ??
|
|
59
|
+
host: configs?.host ?? configs_1.WEBDAV_DEFAULT_HOST,
|
|
60
|
+
port: configs?.port ?? configs_1.WEBDAV_DEFAULT_PORT,
|
|
61
|
+
protocol: configs?.protocol ?? configs_1.WEBDAV_DEFAULT_PROTOCOL,
|
|
62
|
+
timeoutMinutes: configs?.timeoutMinutes ?? configs_1.WEBDAV_DEFAULT_TIMEOUT,
|
|
63
|
+
createFullPath: configs?.createFullPath ?? configs_1.WEBDAV_DEFAULT_CREATE_FULL_PATH,
|
|
69
64
|
};
|
|
70
65
|
}
|
|
71
66
|
catch {
|
|
72
67
|
return {
|
|
73
|
-
host:
|
|
74
|
-
port:
|
|
75
|
-
protocol:
|
|
76
|
-
timeoutMinutes:
|
|
77
|
-
createFullPath:
|
|
68
|
+
host: configs_1.WEBDAV_DEFAULT_HOST,
|
|
69
|
+
port: configs_1.WEBDAV_DEFAULT_PORT,
|
|
70
|
+
protocol: configs_1.WEBDAV_DEFAULT_PROTOCOL,
|
|
71
|
+
timeoutMinutes: configs_1.WEBDAV_DEFAULT_TIMEOUT,
|
|
72
|
+
createFullPath: configs_1.WEBDAV_DEFAULT_CREATE_FULL_PATH,
|
|
78
73
|
};
|
|
79
74
|
}
|
|
80
75
|
};
|
|
81
76
|
ensureInternxtCliDataDirExists = async () => {
|
|
82
77
|
try {
|
|
83
|
-
await promises_1.default.access(
|
|
78
|
+
await promises_1.default.access(configs_1.INTERNXT_CLI_DATA_DIR);
|
|
84
79
|
}
|
|
85
80
|
catch {
|
|
86
|
-
await promises_1.default.mkdir(
|
|
81
|
+
await promises_1.default.mkdir(configs_1.INTERNXT_CLI_DATA_DIR);
|
|
87
82
|
}
|
|
88
83
|
};
|
|
89
84
|
ensureWebdavCertsDirExists = async () => {
|
|
90
85
|
try {
|
|
91
|
-
await promises_1.default.access(
|
|
86
|
+
await promises_1.default.access(configs_1.WEBDAV_SSL_CERTS_DIR);
|
|
92
87
|
}
|
|
93
88
|
catch {
|
|
94
|
-
await promises_1.default.mkdir(
|
|
89
|
+
await promises_1.default.mkdir(configs_1.WEBDAV_SSL_CERTS_DIR);
|
|
95
90
|
}
|
|
96
91
|
};
|
|
97
92
|
ensureInternxtLogsDirExists = async () => {
|
|
98
93
|
try {
|
|
99
|
-
await promises_1.default.access(
|
|
94
|
+
await promises_1.default.access(configs_1.INTERNXT_CLI_LOGS_DIR);
|
|
100
95
|
}
|
|
101
96
|
catch {
|
|
102
|
-
await promises_1.default.mkdir(
|
|
97
|
+
await promises_1.default.mkdir(configs_1.INTERNXT_CLI_LOGS_DIR);
|
|
103
98
|
}
|
|
104
99
|
};
|
|
105
100
|
}
|
|
@@ -32,7 +32,7 @@ class UploadFacade {
|
|
|
32
32
|
throw new Error('Failed to create folders, cannot upload files');
|
|
33
33
|
}
|
|
34
34
|
await async_utils_1.AsyncUtils.sleep(500);
|
|
35
|
-
const totalBytes = await upload_file_service_1.UploadFileService.instance.
|
|
35
|
+
const totalBytes = await upload_file_service_1.UploadFileService.instance.uploadFilesConcurrently({
|
|
36
36
|
network,
|
|
37
37
|
filesToUpload: scanResult.files,
|
|
38
38
|
folderMap,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UploadFilesConcurrentlyParams, UploadFileWithRetryParams } from './upload.types';
|
|
2
|
+
import { DriveFileItem } from '../../../types/drive.types';
|
|
2
3
|
export declare class UploadFileService {
|
|
3
4
|
static readonly instance: UploadFileService;
|
|
4
|
-
|
|
5
|
-
uploadFileWithRetry({ file, network, bucket, parentFolderUuid, }: UploadFileWithRetryParams): Promise<
|
|
6
|
-
private
|
|
5
|
+
uploadFilesConcurrently({ network, filesToUpload, folderMap, bucket, destinationFolderUuid, currentProgress, emitProgress, }: UploadFilesConcurrentlyParams): Promise<number>;
|
|
6
|
+
uploadFileWithRetry({ file, network, bucket, parentFolderUuid, }: UploadFileWithRetryParams): Promise<DriveFileItem | null>;
|
|
7
|
+
private concurrencyArray;
|
|
7
8
|
}
|
|
@@ -9,13 +9,14 @@ const errors_utils_1 = require("../../../utils/errors.utils");
|
|
|
9
9
|
const promises_1 = require("node:fs/promises");
|
|
10
10
|
const types_1 = require("@internxt/sdk/dist/drive/storage/types");
|
|
11
11
|
const thumbnail_utils_1 = require("../../../utils/thumbnail.utils");
|
|
12
|
+
const cli_utils_1 = require("../../../utils/cli.utils");
|
|
12
13
|
class UploadFileService {
|
|
13
14
|
static instance = new UploadFileService();
|
|
14
|
-
async
|
|
15
|
+
async uploadFilesConcurrently({ network, filesToUpload, folderMap, bucket, destinationFolderUuid, currentProgress, emitProgress, }) {
|
|
15
16
|
let bytesUploaded = 0;
|
|
16
|
-
const
|
|
17
|
-
for (const
|
|
18
|
-
await Promise.allSettled(
|
|
17
|
+
const concurrentFiles = this.concurrencyArray(filesToUpload, upload_types_1.MAX_CONCURRENT_UPLOADS);
|
|
18
|
+
for (const fileArray of concurrentFiles) {
|
|
19
|
+
await Promise.allSettled(fileArray.map(async (file) => {
|
|
19
20
|
const parentPath = (0, node_path_1.dirname)(file.relativePath);
|
|
20
21
|
const parentFolderUuid = parentPath === '.' || parentPath === '' ? destinationFolderUuid : folderMap.get(parentPath);
|
|
21
22
|
if (!parentFolderUuid) {
|
|
@@ -42,27 +43,37 @@ class UploadFileService {
|
|
|
42
43
|
for (let attempt = 0; attempt <= upload_types_1.MAX_RETRIES; attempt++) {
|
|
43
44
|
try {
|
|
44
45
|
const stats = await (0, promises_1.stat)(file.absolutePath);
|
|
45
|
-
|
|
46
|
-
logger_utils_1.logger.warn(`Skipping empty file: ${file.relativePath}`);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
46
|
+
const fileSize = stats.size ?? 0;
|
|
49
47
|
const fileType = (0, node_path_1.extname)(file.absolutePath).replaceAll('.', '');
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
let fileId;
|
|
49
|
+
let thumbnailStream;
|
|
50
|
+
const timings = {
|
|
51
|
+
networkUpload: 0,
|
|
52
|
+
driveUpload: 0,
|
|
53
|
+
thumbnailUpload: 0,
|
|
54
|
+
};
|
|
55
|
+
if (fileSize > 0) {
|
|
56
|
+
const { fileStream, bufferStream } = (0, thumbnail_utils_1.createFileStreamWithBuffer)({
|
|
57
|
+
path: file.absolutePath,
|
|
58
|
+
fileType,
|
|
59
|
+
});
|
|
60
|
+
const uploadTimer = cli_utils_1.CLIUtils.timer();
|
|
61
|
+
thumbnailStream = bufferStream;
|
|
62
|
+
fileId = await new Promise((resolve, reject) => {
|
|
63
|
+
network.uploadFile(fileStream, fileSize, bucket, (err, res) => {
|
|
64
|
+
if (err) {
|
|
65
|
+
return reject(err);
|
|
66
|
+
}
|
|
67
|
+
resolve(res);
|
|
68
|
+
}, () => { });
|
|
69
|
+
});
|
|
70
|
+
timings.networkUpload = uploadTimer.stop();
|
|
71
|
+
}
|
|
72
|
+
const driveTimer = cli_utils_1.CLIUtils.timer();
|
|
62
73
|
const createdDriveFile = await drive_file_service_1.DriveFileService.instance.createFile({
|
|
63
74
|
plainName: file.name,
|
|
64
75
|
type: fileType,
|
|
65
|
-
size:
|
|
76
|
+
size: fileSize,
|
|
66
77
|
folderUuid: parentFolderUuid,
|
|
67
78
|
fileId,
|
|
68
79
|
bucket,
|
|
@@ -70,16 +81,27 @@ class UploadFileService {
|
|
|
70
81
|
creationTime: stats.birthtime?.toISOString(),
|
|
71
82
|
modificationTime: stats.mtime?.toISOString(),
|
|
72
83
|
});
|
|
73
|
-
|
|
84
|
+
timings.driveUpload = driveTimer.stop();
|
|
85
|
+
const thumbnailTimer = cli_utils_1.CLIUtils.timer();
|
|
86
|
+
if (thumbnailStream && fileSize > 0) {
|
|
74
87
|
void (0, thumbnail_utils_1.tryUploadThumbnail)({
|
|
75
|
-
bufferStream,
|
|
88
|
+
bufferStream: thumbnailStream,
|
|
76
89
|
fileType,
|
|
77
90
|
userBucket: bucket,
|
|
78
91
|
fileUuid: createdDriveFile.uuid,
|
|
79
92
|
networkFacade: network,
|
|
80
93
|
});
|
|
81
94
|
}
|
|
82
|
-
|
|
95
|
+
timings.thumbnailUpload = thumbnailTimer.stop();
|
|
96
|
+
const totalTime = Object.values(timings).reduce((sum, time) => sum + time, 0);
|
|
97
|
+
const throughputMBps = cli_utils_1.CLIUtils.calculateThroughputMBps(stats.size, timings.networkUpload);
|
|
98
|
+
logger_utils_1.logger.info(`Uploaded '${file.name}' (${cli_utils_1.CLIUtils.formatBytesToString(stats.size)})`);
|
|
99
|
+
logger_utils_1.logger.info(`Timing breakdown:\n
|
|
100
|
+
Network upload: ${cli_utils_1.CLIUtils.formatDuration(timings.networkUpload)} (${throughputMBps.toFixed(2)} MB/s)\n
|
|
101
|
+
Drive upload: ${cli_utils_1.CLIUtils.formatDuration(timings.driveUpload)}\n
|
|
102
|
+
Thumbnail: ${cli_utils_1.CLIUtils.formatDuration(timings.thumbnailUpload)}\n
|
|
103
|
+
Total: ${cli_utils_1.CLIUtils.formatDuration(totalTime)}\n`);
|
|
104
|
+
return createdDriveFile;
|
|
83
105
|
}
|
|
84
106
|
catch (error) {
|
|
85
107
|
if ((0, errors_utils_1.isAlreadyExistsError)(error)) {
|
|
@@ -101,12 +123,12 @@ class UploadFileService {
|
|
|
101
123
|
}
|
|
102
124
|
return null;
|
|
103
125
|
}
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
for (let i = 0; i < array.length; i +=
|
|
107
|
-
|
|
126
|
+
concurrencyArray(array, arraySize) {
|
|
127
|
+
const arrays = [];
|
|
128
|
+
for (let i = 0; i < array.length; i += arraySize) {
|
|
129
|
+
arrays.push(array.slice(i, i + arraySize));
|
|
108
130
|
}
|
|
109
|
-
return
|
|
131
|
+
return arrays;
|
|
110
132
|
}
|
|
111
133
|
}
|
|
112
134
|
exports.UploadFileService = UploadFileService;
|
|
@@ -30,7 +30,7 @@ export interface CreateFolderWithRetryParams {
|
|
|
30
30
|
folderName: string;
|
|
31
31
|
parentFolderUuid: string;
|
|
32
32
|
}
|
|
33
|
-
export interface
|
|
33
|
+
export interface UploadFilesConcurrentlyParams {
|
|
34
34
|
network: NetworkFacade;
|
|
35
35
|
filesToUpload: FileSystemNode[];
|
|
36
36
|
folderMap: Map<string, string>;
|
|
@@ -48,6 +48,6 @@ export interface UploadFileWithRetryParams {
|
|
|
48
48
|
bucket: string;
|
|
49
49
|
parentFolderUuid: string;
|
|
50
50
|
}
|
|
51
|
-
export declare const MAX_CONCURRENT_UPLOADS =
|
|
51
|
+
export declare const MAX_CONCURRENT_UPLOADS = 10;
|
|
52
52
|
export declare const DELAYS_MS: number[];
|
|
53
53
|
export declare const MAX_RETRIES = 2;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MAX_RETRIES = exports.DELAYS_MS = exports.MAX_CONCURRENT_UPLOADS = void 0;
|
|
4
|
-
exports.MAX_CONCURRENT_UPLOADS =
|
|
4
|
+
exports.MAX_CONCURRENT_UPLOADS = 10;
|
|
5
5
|
exports.DELAYS_MS = [500, 1000, 2000];
|
|
6
6
|
exports.MAX_RETRIES = 2;
|
|
@@ -6,8 +6,8 @@ export declare class SdkManager {
|
|
|
6
6
|
private static apiSecurity?;
|
|
7
7
|
static readonly init: (apiSecurity: SdkManagerApiSecurity) => void;
|
|
8
8
|
static readonly clean: () => void;
|
|
9
|
-
static readonly getApiSecurity: (
|
|
10
|
-
throwErrorOnMissingCredentials
|
|
9
|
+
static readonly getApiSecurity: ({ throwErrorOnMissingCredentials }?: {
|
|
10
|
+
throwErrorOnMissingCredentials?: boolean | undefined;
|
|
11
11
|
}) => SdkManagerApiSecurity;
|
|
12
12
|
static readonly getAppDetails: () => AppDetails;
|
|
13
13
|
getAuth(): Auth;
|
|
@@ -18,8 +18,8 @@ class SdkManager {
|
|
|
18
18
|
static clean = () => {
|
|
19
19
|
SdkManager.apiSecurity = undefined;
|
|
20
20
|
};
|
|
21
|
-
static getApiSecurity = (
|
|
22
|
-
if (!SdkManager.apiSecurity &&
|
|
21
|
+
static getApiSecurity = ({ throwErrorOnMissingCredentials = true } = {}) => {
|
|
22
|
+
if (!SdkManager.apiSecurity && throwErrorOnMissingCredentials)
|
|
23
23
|
throw new Error('Api security properties not found in SdkManager');
|
|
24
24
|
return SdkManager.apiSecurity;
|
|
25
25
|
};
|
|
@@ -9,6 +9,11 @@ export declare class ValidationService {
|
|
|
9
9
|
validateStringIsNotEmpty: (str: string) => boolean;
|
|
10
10
|
validateDirectoryExists: (path: string) => Promise<boolean>;
|
|
11
11
|
validateFileExists: (path: string) => Promise<boolean>;
|
|
12
|
+
validateJwtAndCheckExpiration: (token?: string) => number | null;
|
|
13
|
+
checkTokenExpiration: (expirationTimestamp: number) => {
|
|
14
|
+
expired: boolean;
|
|
15
|
+
refreshRequired: boolean;
|
|
16
|
+
};
|
|
12
17
|
validateTokenAndCheckExpiration: (token?: string) => {
|
|
13
18
|
isValid: boolean;
|
|
14
19
|
expiration: {
|
|
@@ -38,29 +38,33 @@ class ValidationService {
|
|
|
38
38
|
const fileStat = await promises_1.default.stat(path);
|
|
39
39
|
return fileStat.isFile();
|
|
40
40
|
};
|
|
41
|
-
|
|
42
|
-
if (!token || typeof token !== 'string') {
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
const parts = token.split('.');
|
|
46
|
-
if (parts.length !== 3) {
|
|
47
|
-
return { isValid: false, expiration: { expired: true, refreshRequired: false } };
|
|
41
|
+
validateJwtAndCheckExpiration = (token) => {
|
|
42
|
+
if (!token || typeof token !== 'string' || token.split('.').length !== 3) {
|
|
43
|
+
return null;
|
|
48
44
|
}
|
|
49
45
|
try {
|
|
50
|
-
const payload = JSON.parse(atob(
|
|
51
|
-
|
|
52
|
-
return { isValid: false, expiration: { expired: true, refreshRequired: false } };
|
|
53
|
-
}
|
|
54
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
55
|
-
const twoDaysInSeconds = 2 * 24 * 60 * 60;
|
|
56
|
-
const remainingSeconds = payload.exp - currentTime;
|
|
57
|
-
const expired = remainingSeconds <= 0;
|
|
58
|
-
const refreshRequired = remainingSeconds > 0 && remainingSeconds <= twoDaysInSeconds;
|
|
59
|
-
return { isValid: true, expiration: { expired, refreshRequired } };
|
|
46
|
+
const payload = JSON.parse(atob(token.split('.')[1]));
|
|
47
|
+
return typeof payload.exp === 'number' ? payload.exp : null;
|
|
60
48
|
}
|
|
61
49
|
catch {
|
|
62
|
-
return
|
|
50
|
+
return null;
|
|
63
51
|
}
|
|
64
52
|
};
|
|
53
|
+
checkTokenExpiration = (expirationTimestamp) => {
|
|
54
|
+
const TWO_DAYS_IN_SECONDS = 2 * 24 * 60 * 60;
|
|
55
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
56
|
+
const remainingSeconds = expirationTimestamp - currentTime;
|
|
57
|
+
return {
|
|
58
|
+
expired: remainingSeconds <= 0,
|
|
59
|
+
refreshRequired: remainingSeconds > 0 && remainingSeconds <= TWO_DAYS_IN_SECONDS,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
validateTokenAndCheckExpiration = (token) => {
|
|
63
|
+
const expiration = this.validateJwtAndCheckExpiration(token);
|
|
64
|
+
return {
|
|
65
|
+
isValid: expiration !== null,
|
|
66
|
+
expiration: expiration ? this.checkTokenExpiration(expiration) : { expired: true, refreshRequired: false },
|
|
67
|
+
};
|
|
68
|
+
};
|
|
65
69
|
}
|
|
66
70
|
exports.ValidationService = ValidationService;
|
|
@@ -43,6 +43,9 @@ export declare class CLIUtils {
|
|
|
43
43
|
static readonly timer: () => {
|
|
44
44
|
stop: () => number;
|
|
45
45
|
};
|
|
46
|
+
static readonly formatDuration: (milliseconds: number) => string;
|
|
47
|
+
static readonly formatBytesToString: (bytes: number) => string;
|
|
48
|
+
static readonly calculateThroughputMBps: (bytes: number, milliseconds: number) => number;
|
|
46
49
|
static readonly catchError: ({ error, logReporter, command, jsonFlag, }: {
|
|
47
50
|
error: Error;
|
|
48
51
|
command?: string;
|
package/dist/utils/cli.utils.js
CHANGED
|
@@ -143,11 +143,11 @@ class CLIUtils {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
} while (!isValid && currentAttempts < maxAttempts);
|
|
146
|
-
if (
|
|
147
|
-
|
|
146
|
+
if (isValid) {
|
|
147
|
+
return promptValue;
|
|
148
148
|
}
|
|
149
149
|
else {
|
|
150
|
-
|
|
150
|
+
throw validation.error;
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
static timer = () => {
|
|
@@ -160,6 +160,40 @@ class CLIUtils {
|
|
|
160
160
|
},
|
|
161
161
|
};
|
|
162
162
|
};
|
|
163
|
+
static formatDuration = (milliseconds) => {
|
|
164
|
+
if (milliseconds <= 0) {
|
|
165
|
+
return '00:00:00.000';
|
|
166
|
+
}
|
|
167
|
+
const totalSeconds = Math.floor(milliseconds / 1000);
|
|
168
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
169
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
170
|
+
const seconds = totalSeconds % 60;
|
|
171
|
+
const ms = Math.floor(milliseconds % 1000);
|
|
172
|
+
const hoursFormated = hours.toString().padStart(2, '0');
|
|
173
|
+
const minutesFormated = minutes.toString().padStart(2, '0');
|
|
174
|
+
const secondsFormated = seconds.toString().padStart(2, '0');
|
|
175
|
+
const msFormated = ms.toString().padStart(3, '0');
|
|
176
|
+
return `${hoursFormated}:${minutesFormated}:${secondsFormated}.${msFormated}`;
|
|
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
|
+
static calculateThroughputMBps = (bytes, milliseconds) => {
|
|
190
|
+
if (bytes <= 0 || milliseconds <= 0) {
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
const megabytes = bytes / 1024 / 1024;
|
|
194
|
+
const seconds = milliseconds / 1000;
|
|
195
|
+
return megabytes / seconds;
|
|
196
|
+
};
|
|
163
197
|
static catchError = ({ error, logReporter, command, jsonFlag, }) => {
|
|
164
198
|
let message;
|
|
165
199
|
if ('message' in error && error.message.trim().length > 0) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare function isError(error: unknown): error is Error;
|
|
2
2
|
export declare function isAlreadyExistsError(error: unknown): error is Error;
|
|
3
|
+
export declare function isFileNotFoundError(error: unknown): error is NodeJS.ErrnoException;
|
|
3
4
|
export declare class ErrorUtils {
|
|
4
5
|
static report(error: unknown, props?: Record<string, unknown>): void;
|
|
5
6
|
}
|
|
@@ -3,15 +3,21 @@ 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
4
|
exports.isError = isError;
|
|
5
5
|
exports.isAlreadyExistsError = isAlreadyExistsError;
|
|
6
|
+
exports.isFileNotFoundError = isFileNotFoundError;
|
|
6
7
|
const logger_utils_1 = require("./logger.utils");
|
|
7
|
-
const node_util_1 = require("node:util");
|
|
8
8
|
function isError(error) {
|
|
9
|
-
return
|
|
9
|
+
return typeof Error.isError === 'function'
|
|
10
|
+
? Error.isError(error)
|
|
11
|
+
: error instanceof Error ||
|
|
12
|
+
(typeof error === 'object' && error !== null && 'message' in error && ('stack' in error || 'name' in error));
|
|
10
13
|
}
|
|
11
14
|
function isAlreadyExistsError(error) {
|
|
12
15
|
return ((isError(error) && error.message.includes('already exists')) ||
|
|
13
16
|
(typeof error === 'object' && error !== null && 'status' in error && error.status === 409));
|
|
14
17
|
}
|
|
18
|
+
function isFileNotFoundError(error) {
|
|
19
|
+
return isError(error) && 'code' in error && error.code === 'ENOENT';
|
|
20
|
+
}
|
|
15
21
|
class ErrorUtils {
|
|
16
22
|
static report(error, props = {}) {
|
|
17
23
|
if (isError(error)) {
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.webdavLogger = exports.logger = void 0;
|
|
7
7
|
const winston_1 = __importDefault(require("winston"));
|
|
8
|
-
const
|
|
8
|
+
const configs_1 = require("../constants/configs");
|
|
9
9
|
const maxLogSize = 40 * 1024 * 1024;
|
|
10
10
|
const maxLogsFiles = 5;
|
|
11
11
|
exports.logger = winston_1.default.createLogger({
|
|
@@ -16,14 +16,14 @@ exports.logger = winston_1.default.createLogger({
|
|
|
16
16
|
new winston_1.default.transports.File({
|
|
17
17
|
filename: 'internxt-cli-error.log',
|
|
18
18
|
level: 'error',
|
|
19
|
-
dirname:
|
|
19
|
+
dirname: configs_1.INTERNXT_CLI_LOGS_DIR,
|
|
20
20
|
maxsize: maxLogSize,
|
|
21
21
|
maxFiles: maxLogsFiles,
|
|
22
22
|
tailable: true,
|
|
23
23
|
}),
|
|
24
24
|
new winston_1.default.transports.File({
|
|
25
25
|
filename: 'internxt-cli-combined.log',
|
|
26
|
-
dirname:
|
|
26
|
+
dirname: configs_1.INTERNXT_CLI_LOGS_DIR,
|
|
27
27
|
maxsize: maxLogSize,
|
|
28
28
|
maxFiles: maxLogsFiles,
|
|
29
29
|
tailable: true,
|
|
@@ -38,14 +38,14 @@ exports.webdavLogger = winston_1.default.createLogger({
|
|
|
38
38
|
new winston_1.default.transports.File({
|
|
39
39
|
filename: 'internxt-webdav-error.log',
|
|
40
40
|
level: 'error',
|
|
41
|
-
dirname:
|
|
41
|
+
dirname: configs_1.INTERNXT_CLI_LOGS_DIR,
|
|
42
42
|
maxsize: maxLogSize,
|
|
43
43
|
maxFiles: maxLogsFiles,
|
|
44
44
|
tailable: true,
|
|
45
45
|
}),
|
|
46
46
|
new winston_1.default.transports.File({
|
|
47
47
|
filename: 'internxt-webdav-combined.log',
|
|
48
|
-
dirname:
|
|
48
|
+
dirname: configs_1.INTERNXT_CLI_LOGS_DIR,
|
|
49
49
|
maxsize: maxLogSize,
|
|
50
50
|
maxFiles: maxLogsFiles,
|
|
51
51
|
tailable: true,
|
|
@@ -9,7 +9,7 @@ const promises_1 = require("node:fs/promises");
|
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const selfsigned_1 = __importDefault(require("selfsigned"));
|
|
11
11
|
const range_parser_1 = __importDefault(require("range-parser"));
|
|
12
|
-
const
|
|
12
|
+
const configs_1 = require("../constants/configs");
|
|
13
13
|
class NetworkUtils {
|
|
14
14
|
static getAuthFromCredentials(creds) {
|
|
15
15
|
return {
|
|
@@ -18,8 +18,8 @@ class NetworkUtils {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
static WEBDAV_SSL_CERTS_PATH = {
|
|
21
|
-
cert: node_path_1.default.join(
|
|
22
|
-
privateKey: node_path_1.default.join(
|
|
21
|
+
cert: node_path_1.default.join(configs_1.WEBDAV_SSL_CERTS_DIR, 'cert.crt'),
|
|
22
|
+
privateKey: node_path_1.default.join(configs_1.WEBDAV_SSL_CERTS_DIR, 'priv.key'),
|
|
23
23
|
};
|
|
24
24
|
static async generateNewSelfsignedCerts(configs) {
|
|
25
25
|
const newCerts = await this.generateSelfSignedSSLCerts(configs);
|
|
@@ -59,17 +59,16 @@ class NetworkUtils {
|
|
|
59
59
|
}
|
|
60
60
|
static async generateSelfSignedSSLCerts(configs) {
|
|
61
61
|
const attrs = [{ name: 'commonName', value: configs.host }];
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
];
|
|
62
|
+
const extension = {
|
|
63
|
+
name: 'subjectAltName',
|
|
64
|
+
altNames: [
|
|
65
|
+
{
|
|
66
|
+
type: 2,
|
|
67
|
+
value: configs.host,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
const extensions = [extension];
|
|
73
72
|
const notAfterDate = new Date();
|
|
74
73
|
notAfterDate.setDate(notAfterDate.getDate() + 365);
|
|
75
74
|
const pems = await selfsigned_1.default.generate(attrs, { notAfterDate, algorithm: 'sha256', keySize: 2048, extensions });
|
|
@@ -87,10 +86,7 @@ class NetworkUtils {
|
|
|
87
86
|
else if (parsed.length <= 0) {
|
|
88
87
|
throw new Error(`Empty Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
throw new Error(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
89
|
+
if (parsed.type === 'bytes') {
|
|
94
90
|
const rangeSize = parsed[0].end - parsed[0].start + 1;
|
|
95
91
|
return {
|
|
96
92
|
range: rangeOptions.range,
|
|
@@ -99,6 +95,9 @@ class NetworkUtils {
|
|
|
99
95
|
parsed: parsed[0],
|
|
100
96
|
};
|
|
101
97
|
}
|
|
98
|
+
else {
|
|
99
|
+
throw new Error(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
|
|
100
|
+
}
|
|
102
101
|
}
|
|
103
102
|
else if (parsed === -1) {
|
|
104
103
|
throw new Error(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
@@ -11,7 +11,7 @@ export declare const isFileThumbnailable: (fileType: string) => boolean;
|
|
|
11
11
|
export declare const isPDFThumbnailable: (fileType: string) => boolean;
|
|
12
12
|
export declare const isImageThumbnailable: (fileType: string) => boolean;
|
|
13
13
|
export declare const tryUploadThumbnail: ({ bufferStream, fileType, userBucket, fileUuid, networkFacade, }: {
|
|
14
|
-
bufferStream
|
|
14
|
+
bufferStream?: BufferStream;
|
|
15
15
|
fileType: string;
|
|
16
16
|
userBucket: string;
|
|
17
17
|
fileUuid: string;
|