Skip to content

Commit bccbb6f

Browse files
authored
Merge pull request #517 from internxt/fix/auth-check-hook
[_]: fix/auth-check-hook
2 parents 213e153 + 7ce4260 commit bccbb6f

13 files changed

Lines changed: 124 additions & 38 deletions

File tree

src/commands/config.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { CLIUtils } from '../utils/cli.utils';
44
import { UsageService } from '../services/usage.service';
55
import { FormatUtils } from '../utils/format.utils';
66
import { Header } from 'tty-table';
7+
import { AsyncUtils } from '../utils/async.utils';
8+
9+
const MAX_USAGE_TIMEOUT = 15_000;
710

811
export default class Config extends Command {
912
static readonly args = {};
@@ -25,8 +28,7 @@ export default class Config extends Command {
2528

2629
const userCredentials = await ConfigService.instance.readUser();
2730
if (userCredentials?.user) {
28-
const usedSpace = FormatUtils.humanFileSize(await UsageService.instance.fetchUsage());
29-
const availableSpace = FormatUtils.formatLimit(await UsageService.instance.fetchSpaceLimit());
31+
const [usedSpace, availableSpace] = await Promise.all([this.tryGetUsage(), this.tryGetSpaceLimit()]);
3032

3133
const configList = [
3234
{ key: 'Email', value: userCredentials.user.email },
@@ -65,4 +67,30 @@ export default class Config extends Command {
6567
});
6668
this.exit(1);
6769
};
70+
71+
private readonly tryGetUsage = async () => {
72+
try {
73+
const usage = await AsyncUtils.withTimeout(
74+
UsageService.instance.fetchUsage(),
75+
MAX_USAGE_TIMEOUT,
76+
'Usage fetch timeout',
77+
);
78+
return FormatUtils.humanFileSize(usage);
79+
} catch {
80+
return '-';
81+
}
82+
};
83+
84+
private readonly tryGetSpaceLimit = async () => {
85+
try {
86+
const limit = await AsyncUtils.withTimeout(
87+
UsageService.instance.fetchSpaceLimit(),
88+
MAX_USAGE_TIMEOUT,
89+
'Space limit timeout',
90+
);
91+
return FormatUtils.formatLimit(limit);
92+
} catch {
93+
return '-';
94+
}
95+
};
6896
}

src/commands/webdav.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PM2Utils } from '../utils/pm2.utils';
33
import { CLIUtils } from '../utils/cli.utils';
44
import { ConfigService } from '../services/config.service';
55
import { AuthService } from '../services/auth.service';
6+
import { WebDavServer } from '../webdav/webdav-server';
67

78
export default class Webdav extends Command {
89
static readonly args = {
@@ -28,10 +29,14 @@ export default class Webdav extends Command {
2829
let message = '';
2930
let success = true;
3031
await PM2Utils.connect();
32+
33+
const configs = await ConfigService.instance.readWebdavConfig();
34+
3135
switch (args.action) {
3236
case 'start':
3337
case 'enable': {
3438
await AuthService.instance.getAuthDetails();
39+
WebDavServer.checkwebDAVCredentials(configs);
3540
message = await this.enableWebDav(flags['json']);
3641
break;
3742
}
@@ -44,6 +49,7 @@ export default class Webdav extends Command {
4449

4550
case 'restart': {
4651
await AuthService.instance.getAuthDetails();
52+
WebDavServer.checkwebDAVCredentials(configs);
4753
message = await this.restartWebDav(flags['json']);
4854
break;
4955
}

src/hooks/prerun/auth_check.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ const hook: Hook<'prerun'> = async function (opts) {
2121
const { Command, argv } = opts;
2222
const jsonFlag = argv.includes('--json');
2323

24+
const commandsToSkipNames = CommandsToSkip.map((command) => command.name?.toLowerCase()).filter(Boolean);
25+
const commandsToSkipIds = CommandsToSkip.map((command) => command.id?.toLowerCase()).filter(Boolean);
26+
2427
if (
25-
!CommandsToSkip.map((command) => command.name.toLowerCase()).includes(Command.name.toLowerCase()) &&
26-
!CommandsToSkip.map((command) => command.id.toLowerCase()).includes(Command.id.toLowerCase())
28+
!commandsToSkipNames.includes(Command.name?.toLowerCase()) &&
29+
!commandsToSkipIds.includes(Command.id?.toLowerCase())
2730
) {
2831
CLIUtils.doing('Checking credentials', jsonFlag);
2932
try {

src/services/drive/drive-file.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { DriveFolderService } from './drive-folder.service';
88
import { NotFoundError } from '../../utils/errors.utils';
99
import { PathUtils } from '../../utils/path.utils';
1010
import { logger } from '../../utils/logger.utils';
11+
import { FileStatus } from '@internxt/sdk/dist/drive/storage/types';
1112

1213
export class DriveFileService {
1314
static readonly instance = new DriveFileService();
@@ -67,6 +68,9 @@ export class DriveFileService {
6768
const [getFileMetadata] = storageClient.getFile(uuid);
6869

6970
const fileMetadata = await getFileMetadata;
71+
if (fileMetadata?.status !== FileStatus.EXISTS) {
72+
throw new NotFoundError(`File with uuid ${uuid} not found`);
73+
}
7074
const driveFileItem = DriveUtils.driveFileMetaToItem(fileMetadata);
7175

7276
await FileRepository.instance.createOrUpdate([driveFileItem]);
@@ -102,7 +106,7 @@ export class DriveFileService {
102106
const fileMeta = subFiles.find(
103107
(file) => (file.plainName === name || file.name === name) && (file.type ?? null) === type,
104108
);
105-
if (!fileMeta) {
109+
if (fileMeta?.status !== FileStatus.EXISTS) {
106110
throw new NotFoundError('File not found');
107111
}
108112
return DriveUtils.driveFileMetaToItem(fileMeta);

src/services/drive/drive-folder.service.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ export class DriveFolderService {
2121
const storageClient = SdkManager.instance.getStorage();
2222
const folderMeta = await storageClient.getFolderMeta(uuid);
2323
const folderItem = DriveUtils.driveFolderMetaToItem(folderMeta);
24+
if (folderItem?.status !== FileStatus.EXISTS) {
25+
throw new NotFoundError(`Folder with uuid ${uuid} not found`);
26+
}
2427
await FolderRepository.instance.createOrUpdate([folderItem]);
28+
2529
return folderItem;
2630
};
2731

@@ -83,6 +87,8 @@ export class DriveFolderService {
8387
folders = (await personalFolderContentPromise).folders;
8488
}
8589

90+
folders = folders.filter((folder) => folder.status === FileStatus.EXISTS);
91+
8692
await FolderRepository.instance.createOrUpdate(
8793
folders.map(
8894
(folder) =>
@@ -130,6 +136,8 @@ export class DriveFolderService {
130136
files = (await folderContentPromise).files;
131137
}
132138

139+
files = files.filter((file) => file.status === FileStatus.EXISTS);
140+
133141
if (files.length > 0) {
134142
await FileRepository.instance.deleteByParentUuid(folderUuid);
135143
await FileRepository.instance.createOrUpdate(
@@ -202,7 +210,7 @@ export class DriveFolderService {
202210
public getByParentUuidAndName = async (parentUuid: string, name: string): Promise<DriveFolderItem> => {
203211
const subFolders = await this.getFolderSubfolders(parentUuid);
204212
const folderMeta = subFolders.find((folder) => folder.plainName === name || folder.name === name);
205-
if (!folderMeta) {
213+
if (folderMeta?.status !== FileStatus.EXISTS) {
206214
throw new NotFoundError('Folder not found');
207215
}
208216

src/services/thumbnail.service.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { NetworkFacade } from './network/network-facade.service';
66
import { ThumbnailConfig, ThumbnailUtils } from '../utils/thumbnail.utils';
77
import { BufferStream } from '../utils/stream.utils';
88
import { ErrorUtils } from '../utils/errors.utils';
9+
import { AsyncUtils } from '../utils/async.utils';
910

1011
let sharpDependency: typeof import('sharp') | null = null;
1112

@@ -102,19 +103,11 @@ export class ThumbnailService {
102103
try {
103104
const thumbnailBuffer = bufferStream?.getBuffer();
104105
if (thumbnailBuffer) {
105-
let timeoutId: NodeJS.Timeout;
106-
const timeoutPromise = new Promise<never>((_, reject) => {
107-
timeoutId = setTimeout(() => {
108-
reject(new Error('Thumbnail upload timeout'));
109-
}, ThumbnailService.MAX_THUMBNAIL_TIMEOUT);
110-
});
111-
112-
await Promise.race([
106+
await AsyncUtils.withTimeout(
113107
ThumbnailService.instance.uploadThumbnail(thumbnailBuffer, fileType, bucket, fileUuid, networkFacade, size),
114-
timeoutPromise,
115-
]).finally(() => {
116-
clearTimeout(timeoutId);
117-
});
108+
ThumbnailService.MAX_THUMBNAIL_TIMEOUT,
109+
'Thumbnail upload timeout',
110+
);
118111
}
119112
} catch (error) {
120113
ErrorUtils.report(error);

src/utils/async.utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,22 @@ export class AsyncUtils {
88
static readonly sleep = (ms: number): Promise<void> => {
99
return new Promise((resolve) => setTimeout(resolve, ms));
1010
};
11+
12+
static readonly withTimeout = async <T>(
13+
promise: Promise<T>,
14+
timeoutMs: number,
15+
errorMessage = 'Operation timed out',
16+
): Promise<T> => {
17+
let timeoutId: NodeJS.Timeout;
18+
19+
const timeoutPromise = new Promise<never>((_, reject) => {
20+
timeoutId = setTimeout(() => {
21+
reject(new Error(errorMessage));
22+
}, timeoutMs);
23+
});
24+
25+
return Promise.race([promise, timeoutPromise]).finally(() => {
26+
clearTimeout(timeoutId);
27+
}) as Promise<T>;
28+
};
1129
}

src/utils/database.utils.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export class DatabaseUtils {
2727

2828
const folder = await getByParentAndName(parentUuid, currentFolder);
2929

30-
// If no intermediate folder is found, return onNotFound callback
3130
if (!folder) {
3231
return;
3332
}

src/utils/drive.utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FileMeta, FolderMeta, CreateFolderResponse } from '@internxt/sdk/dist/drive/storage/types';
1+
import { FileMeta, FolderMeta, CreateFolderResponse, FileStatus } from '@internxt/sdk/dist/drive/storage/types';
22
import { DriveFileItem, DriveFolderItem } from '../types/drive.types';
33

44
export class DriveUtils {
@@ -25,7 +25,7 @@ export class DriveUtils {
2525
itemType: 'folder',
2626
uuid: folderMeta.uuid,
2727
bucket: folderMeta.bucket,
28-
status: folderMeta.deleted || folderMeta.removed ? 'TRASHED' : 'EXISTS',
28+
status: folderMeta.deleted || folderMeta.removed ? FileStatus.TRASHED : FileStatus.EXISTS,
2929
name: folderMeta.plainName ?? folderMeta.name,
3030
parentUuid: folderMeta.parentUuid,
3131
createdAt: new Date(folderMeta.createdAt),
@@ -40,7 +40,7 @@ export class DriveUtils {
4040
itemType: 'folder',
4141
uuid: folderResponse.uuid,
4242
bucket: folderResponse.bucket,
43-
status: folderResponse.deleted || folderResponse.removed ? 'TRASHED' : 'EXISTS',
43+
status: folderResponse.deleted || folderResponse.removed ? FileStatus.TRASHED : FileStatus.EXISTS,
4444
name: folderResponse.plainName ?? folderResponse.name,
4545
parentUuid: folderResponse.parentUuid,
4646
createdAt: new Date(folderResponse.createdAt),

src/webdav/handlers/HEAD.handler.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,32 @@ export class HEADRequestHandler implements WebDavMethodHandler {
99
handle = async (req: Request, res: Response) => {
1010
const resource = await WebDavUtils.getRequestedResource(req.url);
1111

12-
webdavLogger.info(`[HEAD] Request received for file at ${resource.url}`);
12+
webdavLogger.info(`[HEAD] Request received for item at ${resource.url}`);
1313

14-
try {
15-
const driveFile = await WebDavUtils.getDriveFileFromResource(resource.url);
14+
const driveItem = await WebDavUtils.getDriveItemFromResource(resource);
1615

17-
if (!driveFile) {
18-
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
19-
}
16+
if (!driveItem) {
17+
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
18+
}
2019

21-
webdavLogger.info(`[HEAD] [${driveFile.uuid}] Found Drive File`);
20+
webdavLogger.info(`[HEAD] [${driveItem.uuid}] Found Drive item`);
2221

22+
if (driveItem.itemType === 'file') {
2323
const range = req.headers['range'];
2424
const rangeOptions = NetworkUtils.parseRangeHeader({
2525
range,
26-
totalFileSize: driveFile.size,
26+
totalFileSize: driveItem.size,
2727
});
28-
let contentLength = driveFile.size;
28+
let contentLength = driveItem.size;
2929
if (rangeOptions) {
30-
webdavLogger.info(`[HEAD] [${driveFile.uuid}] Range request received:`, { rangeOptions });
30+
webdavLogger.info(`[HEAD] [${driveItem.uuid}] Range request received:`, { rangeOptions });
3131
contentLength = rangeOptions.rangeSize;
3232
}
3333

3434
res.header('Content-Type', 'application/octet-stream');
3535
res.header('Content-length', contentLength.toString());
36-
res.status(200).send();
37-
} catch {
38-
res.header('Content-Type', 'application/octet-stream');
39-
res.status(200).send();
4036
}
37+
38+
res.status(200).send();
4139
};
4240
}

0 commit comments

Comments
 (0)