perf: mediainfo router should use async/await to get file info (#656)

This commit is contained in:
Trim21
2023-11-29 15:31:08 +08:00
committed by GitHub
parent 572ce1ed3a
commit 713b1d95c9
3 changed files with 66 additions and 3 deletions

View File

@@ -35,9 +35,17 @@ import {
reannounceTorrentsSchema,
setTorrentsTagsSchema,
} from '../../../shared/schema/api/torrents';
import {accessDeniedError, fileNotFoundError, isAllowedPath, sanitizePath} from '../../util/fileUtil';
import {
accessDeniedError,
existAsync,
fileNotFoundError,
isAllowedPath,
isAllowedPathAsync,
sanitizePath,
} from '../../util/fileUtil';
import {getTempPath} from '../../models/TemporaryStorage';
import {getToken} from '../../util/authUtil';
import {asyncFilter} from '../../util/async';
const getDestination = async (
services: Express.Request['services'],
@@ -891,13 +899,13 @@ router.get<{hash: string}>(
sanitizePath(path.join(torrentDirectory, content.path)),
);
torrentContentPaths = torrentContentPaths.filter((contentPath) => isAllowedPath(contentPath));
torrentContentPaths = await asyncFilter(torrentContentPaths, (contentPath) => isAllowedPathAsync(contentPath));
if (torrentContentPaths.length < 1) {
const {code, message} = accessDeniedError();
return res.status(403).json({code, message});
}
torrentContentPaths = torrentContentPaths.filter((contentPath) => fs.existsSync(contentPath));
torrentContentPaths = await asyncFilter(torrentContentPaths, (contentPath) => existAsync(contentPath));
if (torrentContentPaths.length < 1) {
const {code, message} = fileNotFoundError();
return res.status(404).json({code, message});

14
server/util/async.ts Normal file
View File

@@ -0,0 +1,14 @@
export async function asyncFilter<T>(
array: Array<T>,
predicate: (item: T, index: number) => Promise<boolean>,
): Promise<Array<T>> {
const results: T[] = [];
for (const [index, item] of array.entries()) {
if (await predicate(item, index)) {
results.push(item);
}
}
return results;
}

View File

@@ -1,4 +1,5 @@
import fs from 'fs';
import {promises as fsp} from 'fs';
import {homedir} from 'os';
import path from 'path';
@@ -43,6 +44,46 @@ export const isAllowedPath = (resolvedPath: string) => {
});
};
export async function isAllowedPathAsync(resolvedPath: string) {
if (config.allowedPaths == null) {
return true;
}
let realPath: string | null = null;
let parentPath: string = resolvedPath;
while (realPath == null) {
try {
realPath = await fsp.realpath(parentPath);
} catch (e) {
if ((e as NodeJS.ErrnoException).code === 'ENOENT') {
parentPath = path.resolve(parentPath, '..');
} else {
return false;
}
}
}
return config.allowedPaths.some((allowedPath) => {
if (realPath?.startsWith(allowedPath)) {
return true;
}
return false;
});
}
export async function existAsync(path: string): Promise<boolean> {
try {
await fsp.stat(path);
} catch (err: unknown) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
return false;
}
throw err;
}
return true;
}
export const sanitizePath = (input?: string): string => {
if (typeof input !== 'string') {
throw accessDeniedError();