diff --git a/server/routes/api/torrents.test.ts b/server/routes/api/torrents.test.ts index 444d0c5f..769d62aa 100644 --- a/server/routes/api/torrents.test.ts +++ b/server/routes/api/torrents.test.ts @@ -252,7 +252,7 @@ describe('POST /api/torrents/add-files', () => { await Promise.all( addedTorrents.map(async (torrent) => { - expect(torrent.directory.startsWith(addTorrentByFileOptions.destination)).toBe(true); + expect(torrent.directory.startsWith(addTorrentByFileOptions.destination as string)).toBe(true); }), ); diff --git a/server/routes/api/torrents.ts b/server/routes/api/torrents.ts index e43521eb..32fc8762 100644 --- a/server/routes/api/torrents.ts +++ b/server/routes/api/torrents.ts @@ -25,10 +25,33 @@ import {accessDeniedError, isAllowedPath, sanitizePath} from '../../util/fileUti import {getResponseFn, validationError} from '../../util/ajaxUtil'; import {getTempPath} from '../../models/TemporaryStorage'; -const getDestination = (destination: string): string | undefined => { +const getDestination = async ( + services: Express.Request['services'], + {destination}: {destination?: string}, +): Promise => { + let autoDestination = destination === '' ? undefined : destination; + + // Use default destination of torrent client + if (autoDestination == null) { + const {directoryDefault} = (await services?.clientGatewayService?.getClientSettings().catch(() => undefined)) || {}; + autoDestination = directoryDefault; + } + + // Use last download destination + if (autoDestination == null) { + await services?.settingService.get('torrentDestination').then( + ({torrentDestination}) => { + if (torrentDestination != null) { + autoDestination = torrentDestination; + } + }, + () => undefined, + ); + } + let sanitizedPath: string | null = null; try { - sanitizedPath = sanitizePath(destination); + sanitizedPath = sanitizePath(autoDestination); if (!isAllowedPath(sanitizedPath)) { return undefined; } @@ -75,7 +98,7 @@ router.get('/', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/add-urls', (req, res) => { +router.post('/add-urls', async (req, res) => { const callback = getResponseFn(res); const parsedResult = addTorrentByURLSchema.safeParse(req.body); @@ -87,7 +110,9 @@ router.post('/add-urls', (req, res) => const {urls, cookies, destination, tags, isBasePath, isCompleted, start} = parsedResult.data; - const finalDestination = getDestination(destination); + const finalDestination = await getDestination(req.services, { + destination, + }); if (finalDestination == null) { callback(null, accessDeniedError()); @@ -123,7 +148,7 @@ router.post('/add-urls', (req, res) => * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/add-files', (req, res) => { +router.post('/add-files', async (req, res) => { const callback = getResponseFn(res); const parsedResult = addTorrentByFileSchema.safeParse(req.body); @@ -135,7 +160,9 @@ router.post('/add-files', (req, res) const {files, destination, tags, isBasePath, isCompleted, start} = parsedResult.data; - const finalDestination = getDestination(destination); + const finalDestination = await getDestination(req.services, { + destination, + }); if (finalDestination == null) { callback(null, accessDeniedError()); diff --git a/server/services/settingService.ts b/server/services/settingService.ts index 0674111c..68022043 100644 --- a/server/services/settingService.ts +++ b/server/services/settingService.ts @@ -29,7 +29,7 @@ class SettingService extends BaseService { return database; } - async get(property: string | null): Promise> { + async get(property: keyof FloodSettings | null): Promise> { return new Promise((resolve, reject) => { this.db .find( diff --git a/server/util/fileUtil.ts b/server/util/fileUtil.ts index 31c501ad..39603bb0 100644 --- a/server/util/fileUtil.ts +++ b/server/util/fileUtil.ts @@ -43,7 +43,7 @@ export const isAllowedPath = (resolvedPath: string) => { }); }; -export const sanitizePath = (input: string): string => { +export const sanitizePath = (input?: string): string => { if (typeof input !== 'string') { throw accessDeniedError(); } diff --git a/shared/schema/api/torrents.ts b/shared/schema/api/torrents.ts index 4c6d6a22..f1bc7b4f 100644 --- a/shared/schema/api/torrents.ts +++ b/shared/schema/api/torrents.ts @@ -9,7 +9,7 @@ export const addTorrentByURLSchema = object({ // Cookies to attach to requests, arrays of strings in the format "name=value" with domain as key cookies: record(array(string())).optional(), // Path of destination - destination: string(), + destination: string().optional(), // Tags tags: array(string()).optional(), // Whether destination is the base path [default: false] @@ -27,7 +27,7 @@ export const addTorrentByFileSchema = object({ // Torrent files in base64 files: array(string()).nonempty(), // Path of destination - destination: string(), + destination: string().optional(), // Tags tags: array(string()).optional(), // Whether destination is the base path [default: false]