@internxt/cli 1.6.7 → 1.6.9
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 +36 -231
- package/bin/dev.js +10 -0
- package/bin/run.js +10 -0
- package/dist/commands/create-folder.js +2 -6
- package/dist/commands/delete-permanently-file.js +1 -1
- package/dist/commands/delete-permanently-folder.js +1 -1
- package/dist/commands/download-file.js +3 -3
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +1 -1
- package/dist/commands/login.d.ts +0 -1
- package/dist/commands/login.js +1 -11
- package/dist/commands/move-file.d.ts +1 -0
- package/dist/commands/move-file.js +1 -1
- package/dist/commands/move-folder.js +1 -1
- package/dist/commands/rename-file.js +1 -1
- package/dist/commands/rename-folder.js +1 -1
- package/dist/commands/trash-file.js +1 -1
- package/dist/commands/trash-folder.js +1 -1
- package/dist/commands/trash-list.d.ts +2 -0
- package/dist/commands/trash-restore-file.d.ts +1 -0
- package/dist/commands/trash-restore-file.js +1 -1
- package/dist/commands/trash-restore-folder.js +1 -1
- package/dist/commands/upload-file.d.ts +1 -0
- package/dist/commands/upload-file.js +22 -13
- package/dist/hooks/prerun/auth_check.js +2 -2
- package/dist/services/auth.service.d.ts +3 -3
- package/dist/services/auth.service.js +30 -40
- package/dist/services/crypto.service.d.ts +0 -4
- package/dist/services/crypto.service.js +1 -32
- package/dist/services/database/drive-item/drive-item.model.js +1 -0
- package/dist/services/database/drive-item/drive-item.repository.js +12 -2
- package/dist/services/drive/drive-file.service.d.ts +2 -0
- package/dist/services/drive/drive-file.service.js +48 -18
- package/dist/services/drive/drive-folder.service.d.ts +2 -0
- package/dist/services/drive/drive-folder.service.js +18 -48
- package/dist/services/drive/trash.service.d.ts +2 -0
- package/dist/services/drive/trash.service.js +14 -41
- package/dist/services/network/upload/upload-file.service.js +5 -9
- package/dist/services/network/upload/upload.types.d.ts +2 -1
- package/dist/services/sdk-manager.service.d.ts +0 -2
- package/dist/services/sdk-manager.service.js +0 -7
- package/dist/services/thumbnail.service.d.ts +4 -13
- package/dist/services/thumbnail.service.js +7 -21
- package/dist/services/universal-link.service.d.ts +1 -1
- package/dist/services/universal-link.service.js +2 -2
- package/dist/services/validation.service.d.ts +1 -1
- package/dist/services/validation.service.js +3 -2
- package/dist/types/command.types.d.ts +0 -29
- package/dist/types/command.types.js +1 -15
- package/dist/types/network.types.d.ts +3 -11
- package/dist/types/webdav.types.d.ts +0 -3
- package/dist/utils/cli.utils.js +9 -22
- package/dist/utils/errors.utils.d.ts +8 -0
- package/dist/utils/errors.utils.js +19 -1
- package/dist/utils/logger.utils.js +9 -2
- package/dist/utils/network.utils.js +7 -6
- package/dist/utils/stream.utils.d.ts +2 -2
- package/dist/utils/stream.utils.js +7 -6
- package/dist/utils/thumbnail.utils.d.ts +1 -3
- package/dist/utils/thumbnail.utils.js +3 -13
- package/dist/utils/upload.utils.d.ts +1 -1
- package/dist/utils/upload.utils.js +2 -2
- package/dist/utils/webdav.utils.d.ts +2 -0
- package/dist/utils/webdav.utils.js +16 -0
- package/dist/webdav/handlers/DELETE.handler.js +0 -2
- package/dist/webdav/handlers/GET.handler.js +18 -7
- package/dist/webdav/handlers/HEAD.handler.js +18 -7
- package/dist/webdav/handlers/LOCK.handler.js +5 -1
- package/dist/webdav/handlers/MOVE.handler.js +13 -1
- package/dist/webdav/handlers/OPTIONS.handler.js +3 -3
- package/dist/webdav/handlers/PROPFIND.handler.js +5 -6
- package/dist/webdav/handlers/PROPPATCH.handler.d.ts +3 -1
- package/dist/webdav/handlers/PROPPATCH.handler.js +53 -3
- package/dist/webdav/handlers/PUT.handler.js +19 -10
- package/dist/webdav/index.js +2 -2
- package/dist/webdav/middewares/auth.middleware.js +1 -1
- package/dist/webdav/middewares/errors.middleware.js +32 -6
- package/oclif.manifest.json +9 -198
- package/package.json +26 -25
- package/dist/commands/workspaces-list.d.ts +0 -20
- package/dist/commands/workspaces-list.js +0 -68
- package/dist/commands/workspaces-unset.d.ts +0 -23
- package/dist/commands/workspaces-unset.js +0 -46
- package/dist/commands/workspaces-use.d.ts +0 -27
- package/dist/commands/workspaces-use.js +0 -114
- package/dist/services/drive/workspace.service.d.ts +0 -7
- package/dist/services/drive/workspace.service.js +0 -30
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NotImplementedError = exports.MethodNotAllowed = exports.UnsupportedMediaTypeError = exports.BadRequestError = exports.NotFoundError = exports.ConflictError = exports.ErrorUtils = void 0;
|
|
3
|
+
exports.RangeNotSatisfiableError = exports.PreconditionFailedError = exports.NotImplementedError = exports.MethodNotAllowed = exports.UnsupportedMediaTypeError = exports.BadRequestError = exports.NotFoundError = exports.ConflictError = exports.ErrorUtils = void 0;
|
|
4
4
|
const logger_utils_1 = require("./logger.utils");
|
|
5
5
|
class ErrorUtils {
|
|
6
6
|
static isError = (error) => {
|
|
@@ -80,3 +80,21 @@ class NotImplementedError extends Error {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
exports.NotImplementedError = NotImplementedError;
|
|
83
|
+
class PreconditionFailedError extends Error {
|
|
84
|
+
statusCode = 412;
|
|
85
|
+
constructor(message) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.name = 'PreconditionFailedError';
|
|
88
|
+
Object.setPrototypeOf(this, PreconditionFailedError.prototype);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.PreconditionFailedError = PreconditionFailedError;
|
|
92
|
+
class RangeNotSatisfiableError extends Error {
|
|
93
|
+
statusCode = 416;
|
|
94
|
+
constructor(message) {
|
|
95
|
+
super(message);
|
|
96
|
+
this.name = 'RangeNotSatisfiableError';
|
|
97
|
+
Object.setPrototypeOf(this, RangeNotSatisfiableError.prototype);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.RangeNotSatisfiableError = RangeNotSatisfiableError;
|
|
@@ -4,14 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.webdavLogger = exports.logger = void 0;
|
|
7
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
7
8
|
const winston_1 = __importDefault(require("winston"));
|
|
8
9
|
const configs_1 = require("../constants/configs");
|
|
10
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
9
11
|
const maxLogSize = 40 * 1024 * 1024;
|
|
10
12
|
const maxLogsFiles = 5;
|
|
13
|
+
const environmentMeta = {
|
|
14
|
+
version: package_json_1.default.version,
|
|
15
|
+
os: `${node_os_1.default.platform()} ${node_os_1.default.release()} (${node_os_1.default.arch()})`,
|
|
16
|
+
node: process.version,
|
|
17
|
+
};
|
|
11
18
|
exports.logger = winston_1.default.createLogger({
|
|
12
19
|
level: 'info',
|
|
13
20
|
format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.json()),
|
|
14
|
-
defaultMeta: { service: 'internxt-cli' },
|
|
21
|
+
defaultMeta: { service: 'internxt-cli', ...environmentMeta },
|
|
15
22
|
transports: [
|
|
16
23
|
new winston_1.default.transports.File({
|
|
17
24
|
filename: 'internxt-cli-error.log',
|
|
@@ -33,7 +40,7 @@ exports.logger = winston_1.default.createLogger({
|
|
|
33
40
|
exports.webdavLogger = winston_1.default.createLogger({
|
|
34
41
|
level: 'info',
|
|
35
42
|
format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.json()),
|
|
36
|
-
defaultMeta: { service: 'internxt-webdav' },
|
|
43
|
+
defaultMeta: { service: 'internxt-webdav', ...environmentMeta },
|
|
37
44
|
transports: [
|
|
38
45
|
new winston_1.default.transports.File({
|
|
39
46
|
filename: 'internxt-webdav-error.log',
|
|
@@ -10,6 +10,7 @@ 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
12
|
const configs_1 = require("../constants/configs");
|
|
13
|
+
const errors_utils_1 = require("./errors.utils");
|
|
13
14
|
class NetworkUtils {
|
|
14
15
|
static getAuthFromCredentials(creds) {
|
|
15
16
|
return {
|
|
@@ -81,10 +82,10 @@ class NetworkUtils {
|
|
|
81
82
|
const parsed = (0, range_parser_1.default)(rangeOptions.totalFileSize, rangeOptions.range);
|
|
82
83
|
if (Array.isArray(parsed)) {
|
|
83
84
|
if (parsed.length > 1) {
|
|
84
|
-
throw new
|
|
85
|
+
throw new errors_utils_1.BadRequestError(`Multi Range-Requests functionality is not implemented. ${JSON.stringify(rangeOptions)}`);
|
|
85
86
|
}
|
|
86
87
|
else if (parsed.length <= 0) {
|
|
87
|
-
throw new
|
|
88
|
+
throw new errors_utils_1.RangeNotSatisfiableError(`Empty Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
88
89
|
}
|
|
89
90
|
if (parsed.type === 'bytes') {
|
|
90
91
|
const rangeSize = parsed[0].end - parsed[0].start + 1;
|
|
@@ -96,17 +97,17 @@ class NetworkUtils {
|
|
|
96
97
|
};
|
|
97
98
|
}
|
|
98
99
|
else {
|
|
99
|
-
throw new
|
|
100
|
+
throw new errors_utils_1.BadRequestError(`Unkwnown Range-Request type "${parsed.type}". ${JSON.stringify(rangeOptions)}`);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
else if (parsed === -1) {
|
|
103
|
-
throw new
|
|
104
|
+
throw new errors_utils_1.RangeNotSatisfiableError(`Unsatisfiable Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
104
105
|
}
|
|
105
106
|
else if (parsed === -2) {
|
|
106
|
-
throw new
|
|
107
|
+
throw new errors_utils_1.BadRequestError(`Malformed Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
107
108
|
}
|
|
108
109
|
else {
|
|
109
|
-
throw new
|
|
110
|
+
throw new errors_utils_1.BadRequestError(`Unknown error from Range-Request. ${JSON.stringify(rangeOptions)}`);
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
}
|
|
@@ -6,10 +6,10 @@ export declare class StreamUtils {
|
|
|
6
6
|
static joinReadableBinaryStreams(streams: ReadableStream<Uint8Array>[]): ReadableStream<Uint8Array>;
|
|
7
7
|
}
|
|
8
8
|
export declare class BufferStream extends Transform {
|
|
9
|
-
|
|
9
|
+
private chunks;
|
|
10
10
|
constructor(opts?: TransformOptions);
|
|
11
11
|
_transform(chunk: Buffer, _: BufferEncoding, callback: TransformCallback): void;
|
|
12
12
|
_flush(callback: TransformCallback): void;
|
|
13
13
|
reset(): void;
|
|
14
|
-
getBuffer(): Buffer |
|
|
14
|
+
getBuffer(): Buffer | undefined;
|
|
15
15
|
}
|
|
@@ -56,24 +56,25 @@ class StreamUtils {
|
|
|
56
56
|
}
|
|
57
57
|
exports.StreamUtils = StreamUtils;
|
|
58
58
|
class BufferStream extends node_stream_1.Transform {
|
|
59
|
-
|
|
59
|
+
chunks;
|
|
60
60
|
constructor(opts) {
|
|
61
61
|
super(opts);
|
|
62
|
-
this.
|
|
62
|
+
this.chunks = [];
|
|
63
63
|
}
|
|
64
64
|
_transform(chunk, _, callback) {
|
|
65
|
-
|
|
66
|
-
this.buffer = Buffer.concat([currentBuffer, chunk]);
|
|
65
|
+
this.chunks.push(chunk);
|
|
67
66
|
callback(null, chunk);
|
|
68
67
|
}
|
|
69
68
|
_flush(callback) {
|
|
70
69
|
callback();
|
|
71
70
|
}
|
|
72
71
|
reset() {
|
|
73
|
-
this.
|
|
72
|
+
this.chunks = [];
|
|
74
73
|
}
|
|
75
74
|
getBuffer() {
|
|
76
|
-
|
|
75
|
+
if (this.chunks.length === 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
return Buffer.concat(this.chunks);
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
exports.BufferStream = BufferStream;
|
|
@@ -5,8 +5,6 @@ export declare const ThumbnailConfig: {
|
|
|
5
5
|
readonly Type: "png";
|
|
6
6
|
};
|
|
7
7
|
export declare class ThumbnailUtils {
|
|
8
|
-
static readonly
|
|
9
|
-
static readonly isFileThumbnailable: (fileType: string) => boolean;
|
|
10
|
-
static readonly isPDFThumbnailable: (fileType: string) => boolean;
|
|
8
|
+
static readonly MAX_IMAGE_THUMBNAILABLE_SIZE_IN_BYTES: number;
|
|
11
9
|
static readonly isImageThumbnailable: (fileType: string, size: number) => boolean;
|
|
12
10
|
}
|
|
@@ -18,9 +18,6 @@ const imageExtensions = {
|
|
|
18
18
|
raw: ['raw', 'cr2', 'nef', 'orf', 'sr2'],
|
|
19
19
|
webp: ['webp'],
|
|
20
20
|
};
|
|
21
|
-
const pdfExtensions = {
|
|
22
|
-
pdf: ['pdf'],
|
|
23
|
-
};
|
|
24
21
|
const thumbnailableImageExtension = new Set([
|
|
25
22
|
...imageExtensions['jpg'],
|
|
26
23
|
...imageExtensions['png'],
|
|
@@ -28,19 +25,12 @@ const thumbnailableImageExtension = new Set([
|
|
|
28
25
|
...imageExtensions['gif'],
|
|
29
26
|
...imageExtensions['tiff'],
|
|
30
27
|
]);
|
|
31
|
-
const thumbnailablePdfExtension = new Set(pdfExtensions['pdf']);
|
|
32
|
-
const thumbnailableExtension = new Set(thumbnailableImageExtension);
|
|
33
28
|
class ThumbnailUtils {
|
|
34
|
-
static
|
|
35
|
-
static isFileThumbnailable = (fileType) => {
|
|
36
|
-
return fileType.trim().length > 0 && thumbnailableExtension.has(fileType.trim().toLowerCase());
|
|
37
|
-
};
|
|
38
|
-
static isPDFThumbnailable = (fileType) => {
|
|
39
|
-
return fileType.trim().length > 0 && thumbnailablePdfExtension.has(fileType.trim().toLowerCase());
|
|
40
|
-
};
|
|
29
|
+
static MAX_IMAGE_THUMBNAILABLE_SIZE_IN_BYTES = 128 * 1024 * 1024;
|
|
41
30
|
static isImageThumbnailable = (fileType, size) => {
|
|
42
|
-
if (size > ThumbnailUtils.
|
|
31
|
+
if (size <= 0 || size > ThumbnailUtils.MAX_IMAGE_THUMBNAILABLE_SIZE_IN_BYTES) {
|
|
43
32
|
return false;
|
|
33
|
+
}
|
|
44
34
|
return fileType.trim().length > 0 && thumbnailableImageExtension.has(fileType.trim().toLowerCase());
|
|
45
35
|
};
|
|
46
36
|
}
|
|
@@ -2,7 +2,7 @@ import { Readable } from 'node:stream';
|
|
|
2
2
|
import { BufferStream } from './stream.utils';
|
|
3
3
|
export declare class UploadUtils {
|
|
4
4
|
static readonly checkUploadSizeLimits: (size: number) => Promise<void>;
|
|
5
|
-
static readonly prepareUploadStreams: (readable: Readable, fileType: string) => {
|
|
5
|
+
static readonly prepareUploadStreams: (readable: Readable, fileType: string, size: number) => {
|
|
6
6
|
fileStream: Readable;
|
|
7
7
|
thumbnailStream: BufferStream | undefined;
|
|
8
8
|
isThumbnailable: boolean;
|
|
@@ -19,8 +19,8 @@ class UploadUtils {
|
|
|
19
19
|
throw new Error('File is too big (more than 100 GB)');
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
static prepareUploadStreams = (readable, fileType) => {
|
|
23
|
-
const isThumbnailable = thumbnail_utils_1.ThumbnailUtils.
|
|
22
|
+
static prepareUploadStreams = (readable, fileType, size) => {
|
|
23
|
+
const isThumbnailable = thumbnail_utils_1.ThumbnailUtils.isImageThumbnailable(fileType, size);
|
|
24
24
|
if (!isThumbnailable) {
|
|
25
25
|
return { fileStream: readable, thumbnailStream: undefined, isThumbnailable };
|
|
26
26
|
}
|
|
@@ -13,4 +13,6 @@ export declare class WebDavUtils {
|
|
|
13
13
|
itemType: 'file' | 'folder';
|
|
14
14
|
uuid: string;
|
|
15
15
|
}>(driveItem: T): Promise<void>;
|
|
16
|
+
static generateETag(parts: Array<string | number | null | undefined>): string;
|
|
17
|
+
static getItemETag(driveItem: DriveFileItem | DriveFolderItem): string;
|
|
16
18
|
}
|
|
@@ -5,6 +5,7 @@ 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 node_crypto_1 = require("node:crypto");
|
|
8
9
|
const drive_item_service_1 = require("../services/drive/drive-item.service");
|
|
9
10
|
const logger_utils_1 = require("./logger.utils");
|
|
10
11
|
const config_service_1 = require("../services/config.service");
|
|
@@ -100,5 +101,20 @@ class WebDavUtils {
|
|
|
100
101
|
}
|
|
101
102
|
await drive_item_repository_1.DriveItemRepository.instance.delete([driveItem.uuid]);
|
|
102
103
|
}
|
|
104
|
+
static generateETag(parts) {
|
|
105
|
+
const normalized = parts.map((part) => part ?? '-');
|
|
106
|
+
const hash = (0, node_crypto_1.createHash)('sha256').update(normalized.join('|')).digest('hex');
|
|
107
|
+
return `"${hash}"`;
|
|
108
|
+
}
|
|
109
|
+
static getItemETag(driveItem) {
|
|
110
|
+
return this.generateETag([
|
|
111
|
+
driveItem.uuid,
|
|
112
|
+
driveItem.itemType === 'file' ? driveItem.size : undefined,
|
|
113
|
+
driveItem.createdAt.getTime(),
|
|
114
|
+
driveItem.updatedAt.getTime(),
|
|
115
|
+
driveItem.creationTime.getTime(),
|
|
116
|
+
driveItem.modificationTime.getTime(),
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
103
119
|
}
|
|
104
120
|
exports.WebDavUtils = WebDavUtils;
|
|
@@ -1,7 +1,6 @@
|
|
|
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");
|
|
5
4
|
const webdav_utils_1 = require("../../utils/webdav.utils");
|
|
6
5
|
const logger_utils_1 = require("../../utils/logger.utils");
|
|
7
6
|
const errors_utils_1 = require("../../utils/errors.utils");
|
|
@@ -14,7 +13,6 @@ class DELETERequestHandler {
|
|
|
14
13
|
throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
|
|
15
14
|
}
|
|
16
15
|
await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveItem);
|
|
17
|
-
await drive_item_repository_1.DriveItemRepository.instance.delete([driveItem.uuid]);
|
|
18
16
|
res.status(204).send();
|
|
19
17
|
};
|
|
20
18
|
}
|
|
@@ -21,17 +21,28 @@ class GETRequestHandler {
|
|
|
21
21
|
logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] Network ready for download`);
|
|
22
22
|
res.header('Content-Type', 'application/octet-stream');
|
|
23
23
|
res.header('Accept-Ranges', 'bytes');
|
|
24
|
+
res.header('ETag', webdav_utils_1.WebDavUtils.getItemETag(driveFile));
|
|
24
25
|
const fileSize = driveFile.size ?? 0;
|
|
25
26
|
if (fileSize > 0) {
|
|
26
27
|
const range = req.headers['range'];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
let rangeOptions;
|
|
29
|
+
try {
|
|
30
|
+
rangeOptions = network_utils_1.NetworkUtils.parseRangeHeader({
|
|
31
|
+
range,
|
|
32
|
+
totalFileSize: fileSize,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof errors_utils_1.RangeNotSatisfiableError) {
|
|
37
|
+
res.header('Content-Range', `bytes */${fileSize}`);
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
31
41
|
let contentLength = fileSize;
|
|
32
42
|
if (rangeOptions) {
|
|
33
43
|
logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] Range request received:`, { rangeOptions });
|
|
34
44
|
contentLength = rangeOptions.rangeSize;
|
|
45
|
+
res.header('Content-Range', `bytes ${rangeOptions.parsed.start}-${rangeOptions.parsed.end}/${fileSize}`);
|
|
35
46
|
}
|
|
36
47
|
res.header('Content-length', contentLength.toString());
|
|
37
48
|
const writable = new WritableStream({
|
|
@@ -45,10 +56,10 @@ class GETRequestHandler {
|
|
|
45
56
|
if (!driveFile.fileId) {
|
|
46
57
|
throw new command_types_1.NotValidFileIdError();
|
|
47
58
|
}
|
|
48
|
-
const { networkFacade,
|
|
49
|
-
const [executeDownload] = await networkFacade.downloadToStream(bucket, mnemonic, driveFile.fileId, contentLength, writable, rangeOptions);
|
|
59
|
+
const { networkFacade, mnemonic } = await cli_utils_1.CLIUtils.prepareNetwork(user);
|
|
60
|
+
const [executeDownload] = await networkFacade.downloadToStream(driveFile.bucket, mnemonic, driveFile.fileId, contentLength, writable, rangeOptions);
|
|
50
61
|
logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] Download prepared, executing...`);
|
|
51
|
-
res.status(200);
|
|
62
|
+
res.status(rangeOptions ? 206 : 200);
|
|
52
63
|
await executeDownload;
|
|
53
64
|
logger_utils_1.webdavLogger.info(`[GET] [${driveFile.uuid}] ✅ Download ready, replying to client`);
|
|
54
65
|
}
|
|
@@ -14,22 +14,33 @@ class HEADRequestHandler {
|
|
|
14
14
|
throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
|
|
15
15
|
}
|
|
16
16
|
logger_utils_1.webdavLogger.info(`[HEAD] [${driveItem.uuid}] Found Drive item`);
|
|
17
|
+
res.header('ETag', webdav_utils_1.WebDavUtils.getItemETag(driveItem));
|
|
18
|
+
let rangeOptions;
|
|
17
19
|
if (driveItem.itemType === 'file') {
|
|
18
20
|
const range = req.headers['range'];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
try {
|
|
22
|
+
rangeOptions = network_utils_1.NetworkUtils.parseRangeHeader({
|
|
23
|
+
range,
|
|
24
|
+
totalFileSize: driveItem.size,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof errors_utils_1.RangeNotSatisfiableError) {
|
|
29
|
+
res.header('Content-Range', `bytes */${driveItem.size}`);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
23
33
|
let contentLength = driveItem.size;
|
|
34
|
+
res.header('Content-Type', 'application/octet-stream');
|
|
35
|
+
res.header('Accept-Ranges', 'bytes');
|
|
24
36
|
if (rangeOptions) {
|
|
25
37
|
logger_utils_1.webdavLogger.info(`[HEAD] [${driveItem.uuid}] Range request received:`, { rangeOptions });
|
|
26
38
|
contentLength = rangeOptions.rangeSize;
|
|
39
|
+
res.header('Content-Range', `bytes ${rangeOptions.parsed.start}-${rangeOptions.parsed.end}/${driveItem.size}`);
|
|
27
40
|
}
|
|
28
|
-
res.header('Content-Type', 'application/octet-stream');
|
|
29
|
-
res.header('Accept-Ranges', 'bytes');
|
|
30
41
|
res.header('Content-length', contentLength.toString());
|
|
31
42
|
}
|
|
32
|
-
res.status(200).send();
|
|
43
|
+
res.status(rangeOptions ? 206 : 200).send();
|
|
33
44
|
};
|
|
34
45
|
}
|
|
35
46
|
exports.HEADRequestHandler = HEADRequestHandler;
|
|
@@ -4,6 +4,7 @@ exports.LOCKRequestHandler = void 0;
|
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const logger_utils_1 = require("../../utils/logger.utils");
|
|
6
6
|
const xml_utils_1 = require("../../utils/xml.utils");
|
|
7
|
+
const webdav_utils_1 = require("../../utils/webdav.utils");
|
|
7
8
|
const LOCK_TIMEOUT_SECONDS = 300;
|
|
8
9
|
class LOCKRequestHandler {
|
|
9
10
|
handle = async (req, res) => {
|
|
@@ -42,9 +43,12 @@ class LOCKRequestHandler {
|
|
|
42
43
|
},
|
|
43
44
|
},
|
|
44
45
|
}, { suppressEmptyNode: true }, 'prop');
|
|
46
|
+
const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
|
|
47
|
+
const driveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(resource);
|
|
48
|
+
const statusCode = driveItem ? 200 : 201;
|
|
45
49
|
res.set('Lock-Token', `<${lockToken}>`);
|
|
46
50
|
res.set('Content-Type', 'application/xml; charset="utf-8"');
|
|
47
|
-
res.status(
|
|
51
|
+
res.status(statusCode).send(lockDiscoveryXml);
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
exports.LOCKRequestHandler = LOCKRequestHandler;
|
|
@@ -23,6 +23,17 @@ class MOVERequestHandler {
|
|
|
23
23
|
if (!originalDriveItem) {
|
|
24
24
|
throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
|
|
25
25
|
}
|
|
26
|
+
let isOverwrite = false;
|
|
27
|
+
const destinationDriveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(destinationResource);
|
|
28
|
+
if (destinationDriveItem && destinationDriveItem.uuid !== originalDriveItem.uuid) {
|
|
29
|
+
const overwrite = (req.header('overwrite') ?? 'T').toUpperCase();
|
|
30
|
+
if (overwrite === 'F') {
|
|
31
|
+
throw new errors_utils_1.PreconditionFailedError(`Destination '${destinationResource.url}' already exists`);
|
|
32
|
+
}
|
|
33
|
+
isOverwrite = true;
|
|
34
|
+
logger_utils_1.webdavLogger.info(`[MOVE] Destination '${destinationResource.url}' already exists, overwriting it`);
|
|
35
|
+
await webdav_utils_1.WebDavUtils.deleteOrTrashItem(destinationDriveItem);
|
|
36
|
+
}
|
|
26
37
|
if (destinationResource.path.dir === resource.path.dir) {
|
|
27
38
|
logger_utils_1.webdavLogger.info(`[MOVE] Renaming ${originalDriveItem.itemType}
|
|
28
39
|
with UUID ${originalDriveItem.uuid} to ${destinationResource.name}`);
|
|
@@ -75,7 +86,8 @@ class MOVERequestHandler {
|
|
|
75
86
|
updatedAt: new Date(),
|
|
76
87
|
},
|
|
77
88
|
]);
|
|
78
|
-
|
|
89
|
+
const statusCode = isOverwrite ? 204 : 201;
|
|
90
|
+
res.status(statusCode).send();
|
|
79
91
|
};
|
|
80
92
|
}
|
|
81
93
|
exports.MOVERequestHandler = MOVERequestHandler;
|
|
@@ -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, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PUT, UNLOCK';
|
|
11
|
+
const allowedMethods = 'DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PROPPATCH, PUT, UNLOCK';
|
|
12
12
|
logger_utils_1.webdavLogger.info(`[OPTIONS] Returning Allowed Options: ${allowedMethods}`);
|
|
13
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, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, UNLOCK';
|
|
18
|
+
const allowedMethods = 'DELETE, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PROPFIND, PROPPATCH, 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, LOCK, MOVE, OPTIONS, PROPFIND, PUT, UNLOCK';
|
|
25
|
+
const allowedMethods = 'DELETE, GET, HEAD, LOCK, MOVE, OPTIONS, PROPFIND, PROPPATCH, 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');
|
|
@@ -10,19 +10,18 @@ const drive_item_repository_1 = require("../../services/database/drive-item/driv
|
|
|
10
10
|
const drive_folder_service_1 = require("../../services/drive/drive-folder.service");
|
|
11
11
|
const drive_utils_1 = require("../../utils/drive.utils");
|
|
12
12
|
const format_utils_1 = require("../../utils/format.utils");
|
|
13
|
-
const node_crypto_1 = require("node:crypto");
|
|
14
13
|
const mime_types_1 = __importDefault(require("mime-types"));
|
|
15
14
|
const webdav_utils_1 = require("../../utils/webdav.utils");
|
|
16
15
|
const logger_utils_1 = require("../../utils/logger.utils");
|
|
17
16
|
const usage_service_1 = require("../../services/usage.service");
|
|
17
|
+
const errors_utils_1 = require("../../utils/errors.utils");
|
|
18
18
|
class PROPFINDRequestHandler {
|
|
19
19
|
handle = async (req, res) => {
|
|
20
20
|
const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
|
|
21
21
|
logger_utils_1.webdavLogger.info(`[PROPFIND] Request received for item at ${resource.url}`);
|
|
22
22
|
const driveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(resource);
|
|
23
23
|
if (!driveItem) {
|
|
24
|
-
|
|
25
|
-
return;
|
|
24
|
+
throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
|
|
26
25
|
}
|
|
27
26
|
res.set('Content-Type', 'application/xml; charset="utf-8"');
|
|
28
27
|
switch (driveItem.itemType) {
|
|
@@ -143,7 +142,7 @@ class PROPFINDRequestHandler {
|
|
|
143
142
|
[xml_utils_1.XMLUtils.addDefaultNamespace('status')]: 'HTTP/1.1 200 OK',
|
|
144
143
|
[xml_utils_1.XMLUtils.addDefaultNamespace('prop')]: {
|
|
145
144
|
[xml_utils_1.XMLUtils.addDefaultNamespace('getcontenttype')]: 'httpd/unix-directory',
|
|
146
|
-
[xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]:
|
|
145
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]: webdav_utils_1.WebDavUtils.getItemETag(driveFolderItem),
|
|
147
146
|
'x1:lastmodified': {
|
|
148
147
|
'#text': format_utils_1.FormatUtils.formatDateForWebDav(driveFolderItem.updatedAt),
|
|
149
148
|
'@_xmlns:x1': 'SAR:',
|
|
@@ -177,7 +176,7 @@ class PROPFINDRequestHandler {
|
|
|
177
176
|
[xml_utils_1.XMLUtils.addDefaultNamespace('getlastmodified')]: format_utils_1.FormatUtils.formatDateForWebDav(driveFolderItem.updatedAt),
|
|
178
177
|
[xml_utils_1.XMLUtils.addDefaultNamespace('getcontentlength')]: 0,
|
|
179
178
|
[xml_utils_1.XMLUtils.addDefaultNamespace('getcontenttype')]: 'httpd/unix-directory',
|
|
180
|
-
[xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]:
|
|
179
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]: webdav_utils_1.WebDavUtils.getItemETag(driveFolderItem),
|
|
181
180
|
[xml_utils_1.XMLUtils.addDefaultNamespace('resourcetype')]: {
|
|
182
181
|
[xml_utils_1.XMLUtils.addDefaultNamespace('collection')]: '',
|
|
183
182
|
},
|
|
@@ -195,7 +194,7 @@ class PROPFINDRequestHandler {
|
|
|
195
194
|
[xml_utils_1.XMLUtils.addDefaultNamespace('status')]: 'HTTP/1.1 200 OK',
|
|
196
195
|
[xml_utils_1.XMLUtils.addDefaultNamespace('prop')]: {
|
|
197
196
|
[xml_utils_1.XMLUtils.addDefaultNamespace('resourcetype')]: '',
|
|
198
|
-
[xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]:
|
|
197
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('getetag')]: webdav_utils_1.WebDavUtils.getItemETag(driveFileItem),
|
|
199
198
|
[xml_utils_1.XMLUtils.addDefaultNamespace('displayname')]: displayName,
|
|
200
199
|
[xml_utils_1.XMLUtils.addDefaultNamespace('getcontenttype')]: mime_types_1.default.lookup(displayName) || 'application/octet-stream',
|
|
201
200
|
[xml_utils_1.XMLUtils.addDefaultNamespace('getlastmodified')]: lastModified,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
1
2
|
import { WebDavMethodHandler } from '../../types/webdav.types';
|
|
2
3
|
export declare class PROPPATCHRequestHandler implements WebDavMethodHandler {
|
|
3
|
-
handle(
|
|
4
|
+
handle: (req: Request, res: Response) => Promise<void>;
|
|
5
|
+
private readonly getRequestedPropertyNames;
|
|
4
6
|
}
|
|
@@ -1,10 +1,60 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PROPPATCHRequestHandler = void 0;
|
|
4
|
+
const webdav_utils_1 = require("../../utils/webdav.utils");
|
|
5
|
+
const logger_utils_1 = require("../../utils/logger.utils");
|
|
6
|
+
const xml_utils_1 = require("../../utils/xml.utils");
|
|
4
7
|
const errors_utils_1 = require("../../utils/errors.utils");
|
|
5
8
|
class PROPPATCHRequestHandler {
|
|
6
|
-
async
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
handle = async (req, res) => {
|
|
10
|
+
const resource = await webdav_utils_1.WebDavUtils.getRequestedResource(req.url);
|
|
11
|
+
logger_utils_1.webdavLogger.info(`[PROPPATCH] Request received for item at ${resource.url}`);
|
|
12
|
+
const driveItem = await webdav_utils_1.WebDavUtils.getDriveItemFromResource(resource);
|
|
13
|
+
if (!driveItem) {
|
|
14
|
+
throw new errors_utils_1.NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
|
|
15
|
+
}
|
|
16
|
+
const propertyNames = this.getRequestedPropertyNames(req.body);
|
|
17
|
+
const propNode = Object.fromEntries(propertyNames.map((name) => [name, '']));
|
|
18
|
+
const responseXML = xml_utils_1.XMLUtils.toWebDavXML({
|
|
19
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('response')]: {
|
|
20
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('href')]: xml_utils_1.XMLUtils.encodeWebDavUri(resource.url),
|
|
21
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('propstat')]: {
|
|
22
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('prop')]: propNode,
|
|
23
|
+
[xml_utils_1.XMLUtils.addDefaultNamespace('status')]: 'HTTP/1.1 403 Forbidden',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}, { suppressEmptyNode: true });
|
|
27
|
+
res.set('Content-Type', 'application/xml; charset="utf-8"');
|
|
28
|
+
res.status(207).send(responseXML);
|
|
29
|
+
};
|
|
30
|
+
getRequestedPropertyNames = (body) => {
|
|
31
|
+
if (typeof body !== 'string' || body.trim().length === 0) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const parsed = xml_utils_1.XMLUtils.toJSON(body, { ignoreAttributes: false });
|
|
36
|
+
const propertyUpdate = parsed?.['D:propertyupdate'] ?? parsed?.propertyupdate;
|
|
37
|
+
const sections = [
|
|
38
|
+
propertyUpdate?.['D:set'] ?? propertyUpdate?.set,
|
|
39
|
+
propertyUpdate?.['D:remove'] ?? propertyUpdate?.remove,
|
|
40
|
+
];
|
|
41
|
+
const names = new Set();
|
|
42
|
+
for (const section of sections) {
|
|
43
|
+
const prop = section?.['D:prop'] ?? section?.prop;
|
|
44
|
+
if (prop && typeof prop === 'object') {
|
|
45
|
+
for (const key of Object.keys(prop)) {
|
|
46
|
+
if (!key.startsWith('@_')) {
|
|
47
|
+
names.add(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return Array.from(names);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger_utils_1.webdavLogger.warn(`[PROPPATCH] Failed to parse request body: ${error instanceof Error ? error.message : String(error)}`);
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
};
|
|
9
59
|
}
|
|
10
60
|
exports.PROPPATCHRequestHandler = PROPPATCHRequestHandler;
|
|
@@ -39,16 +39,10 @@ class PUTRequestHandler {
|
|
|
39
39
|
}
|
|
40
40
|
isReplacement = true;
|
|
41
41
|
logger_utils_1.webdavLogger.info(`[PUT] File '${resource.name}' already exists in '${resource.path.dir}', it will be replaced...`);
|
|
42
|
-
try {
|
|
43
|
-
await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveFileItem);
|
|
44
|
-
await drive_item_repository_1.DriveItemRepository.instance.delete([driveFileItem.uuid]);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
}
|
|
48
42
|
}
|
|
49
43
|
const { user } = await auth_service_1.AuthService.instance.getAuthDetails();
|
|
50
44
|
const fileType = resource.path.ext.replace('.', '');
|
|
51
|
-
const { fileStream, thumbnailStream } = upload_utils_1.UploadUtils.prepareUploadStreams(req, fileType);
|
|
45
|
+
const { fileStream, thumbnailStream } = upload_utils_1.UploadUtils.prepareUploadStreams(req, fileType, contentLength);
|
|
52
46
|
const { networkFacade, bucket } = await cli_utils_1.CLIUtils.prepareNetwork(user);
|
|
53
47
|
let fileId;
|
|
54
48
|
if (contentLength > 0) {
|
|
@@ -79,7 +73,7 @@ class PUTRequestHandler {
|
|
|
79
73
|
logger_utils_1.webdavLogger.info('[PUT] ✅ File uploaded to network');
|
|
80
74
|
}
|
|
81
75
|
const driveTimer = cli_utils_1.CLIUtils.timer();
|
|
82
|
-
const
|
|
76
|
+
const filePayload = {
|
|
83
77
|
plainName: resource.path.name,
|
|
84
78
|
type: fileType,
|
|
85
79
|
size: contentLength,
|
|
@@ -87,7 +81,21 @@ class PUTRequestHandler {
|
|
|
87
81
|
fileId,
|
|
88
82
|
bucket,
|
|
89
83
|
encryptVersion: types_1.EncryptionVersion.Aes03,
|
|
90
|
-
}
|
|
84
|
+
};
|
|
85
|
+
let file;
|
|
86
|
+
if (driveFileItem?.itemType === 'file') {
|
|
87
|
+
try {
|
|
88
|
+
file = await drive_file_service_1.DriveFileService.instance.replaceFile(driveFileItem.uuid, filePayload);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
logger_utils_1.webdavLogger.warn(`[PUT] File replace failed for '${resource.url}', falling back to delete and create: ${error instanceof Error ? error.message : String(error)}`);
|
|
92
|
+
await webdav_utils_1.WebDavUtils.deleteOrTrashItem(driveFileItem);
|
|
93
|
+
file = await drive_file_service_1.DriveFileService.instance.createFile(filePayload);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
file = await drive_file_service_1.DriveFileService.instance.createFile(filePayload);
|
|
98
|
+
}
|
|
91
99
|
timings.driveUpload = driveTimer.stop();
|
|
92
100
|
await drive_item_repository_1.DriveItemRepository.instance.createOrUpdate([
|
|
93
101
|
{
|
|
@@ -101,7 +109,7 @@ class PUTRequestHandler {
|
|
|
101
109
|
const thumbnailTimer = cli_utils_1.CLIUtils.timer();
|
|
102
110
|
await thumbnail_service_1.ThumbnailService.instance.tryUploadThumbnail({
|
|
103
111
|
fileUuid: file.uuid,
|
|
104
|
-
|
|
112
|
+
input: thumbnailStream?.getBuffer(),
|
|
105
113
|
fileType,
|
|
106
114
|
bucket,
|
|
107
115
|
networkFacade,
|
|
@@ -115,6 +123,7 @@ class PUTRequestHandler {
|
|
|
115
123
|
const statusCode = isReplacement ? 204 : 201;
|
|
116
124
|
logger_utils_1.webdavLogger.info(`[PUT] [RESPONSE-${statusCode}] ${resource.url} - Returning ${statusCode} ` +
|
|
117
125
|
`after ${cli_utils_1.CLIUtils.formatDuration(totalTime)}`);
|
|
126
|
+
res.header('ETag', webdav_utils_1.WebDavUtils.getItemETag(file));
|
|
118
127
|
res.status(statusCode).send();
|
|
119
128
|
};
|
|
120
129
|
}
|
package/dist/webdav/index.js
CHANGED
|
@@ -18,8 +18,8 @@ const init = async () => {
|
|
|
18
18
|
await config_service_1.ConfigService.instance.ensureInternxtLogsDirExists();
|
|
19
19
|
await database_service_1.DatabaseService.instance.initialize();
|
|
20
20
|
await database_service_1.DatabaseService.instance.clear();
|
|
21
|
-
const { token
|
|
22
|
-
sdk_manager_service_1.SdkManager.init({ token
|
|
21
|
+
const { token } = await auth_service_1.AuthService.instance.getAuthDetails();
|
|
22
|
+
sdk_manager_service_1.SdkManager.init({ token });
|
|
23
23
|
new webdav_server_1.WebDavServer((0, express_1.default)())
|
|
24
24
|
.start()
|
|
25
25
|
.then()
|