mirror of
https://github.com/zoriya/flood.git
synced 2025-12-05 23:06:20 +00:00
perf: mediainfo router should use async/await to get file info (#656)
This commit is contained in:
@@ -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
14
server/util/async.ts
Normal 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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user