Add new options and fix path

This commit is contained in:
Vito0912 2025-06-11 23:08:41 +02:00
parent 7a33a412fc
commit d2d558754f
No known key found for this signature in database
GPG Key ID: 29A3D509FE70B237

View File

@ -88,7 +88,10 @@ class FileSystemController {
return res.sendStatus(403) return res.sendStatus(403)
} }
const { directory, folderPath } = req.body // fileName - If fileName is provided, the check only returns true if the actual file exists, not just the directory
// allowBookFiles - If true, allows containing other book related files (e.g. .pdf, .epub, etc.)
// allowAudioFiles - If true, allows containing other audio related files (e.g. .mp3, .m4b, etc.)
const { directory, folderPath, fileName, allowBookFiles, allowAudioFiles } = req.body
if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') { if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') {
Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`) Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`)
return res.status(400).json({ return res.status(400).json({
@ -96,6 +99,20 @@ class FileSystemController {
}) })
} }
if (fileName && typeof fileName !== 'string') {
Logger.error(`[FileSystemController] Invalid fileName in request body: ${JSON.stringify(req.body)}`)
return res.status(400).json({
error: 'Invalid fileName'
})
}
if (allowBookFiles && typeof allowBookFiles !== 'boolean' || allowAudioFiles && typeof allowAudioFiles !== 'boolean' || (allowBookFiles && allowAudioFiles)) {
Logger.error(`[FileSystemController] Invalid allowBookFiles or allowAudioFiles in request body: ${JSON.stringify(req.body)}`)
return res.status(400).json({
error: 'Invalid allowBookFiles or allowAudioFiles'
})
}
// Check that library folder exists // Check that library folder exists
const libraryFolder = await Database.libraryFolderModel.findOne({ const libraryFolder = await Database.libraryFolderModel.findOne({
where: { where: {
@ -110,20 +127,52 @@ class FileSystemController {
const filepath = Path.join(libraryFolder.path, directory) const filepath = Path.join(libraryFolder.path, directory)
// Ensure filepath is inside library folder (prevents directory traversal) // Ensure filepath is inside library folder (prevents directory traversal) (And convert libraryFolder to Path to normalize)
if (!filepath.startsWith(libraryFolder.path)) { if (!filepath.startsWith(Path.join(libraryFolder.path))) {
Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`) Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`)
return res.sendStatus(400) return res.sendStatus(400)
} }
if (await fs.pathExists(filepath)) { if (await fs.pathExists(filepath)) {
return res.json({ if (fileName) {
exists: true // Check if a specific file exists
}) const filePath = Path.join(filepath, fileName)
if (await fs.pathExists(filePath)) {
return res.json({
exists: true,
})
}
} else if(allowBookFiles || allowAudioFiles) {
let allowedExtensions = []
if (allowBookFiles && !allowAudioFiles) {
allowedExtensions = ['epub', 'pdf', 'mobi', 'azw3', 'cbr', 'cbz']
} else if (allowAudioFiles && !allowBookFiles) {
allowedExtensions = ['m4b', 'mp3', 'm4a', 'flac', 'opus', 'ogg', 'oga', 'mp4', 'aac', 'wma', 'aiff', 'aif', 'wav', 'webm', 'webma', 'mka', 'awb', 'caf', 'mpeg', 'mpg']
} else {
allowedExtensions = []
}
const files = await fs.readdir(filepath)
const exists = allowedExtensions.length === 0
? files.length > 0
: files.some((file) => {
const ext = Path.extname(file).toLowerCase().replace(/^\./, '')
return allowedExtensions.includes(ext)
})
// To let the sub dir check run
if(exists) return res.json({
exists: exists
})
} else {
return res.json({
exists: true
})
}
} }
// Check if a library item exists in a subdirectory // Check if a library item exists in a subdirectory
// See: https://github.com/advplyr/audiobookshelf/issues/4146 // See: https://github.com/advplyr/audiobookshelf/issues/4146
// For filenames it does not matter if the file is in a subdirectory or not because the file is not allowed to be created
const cleanedDirectory = directory.split('/').filter(Boolean).join('/') const cleanedDirectory = directory.split('/').filter(Boolean).join('/')
if (cleanedDirectory.includes('/')) { if (cleanedDirectory.includes('/')) {
// Can only be 2 levels deep // Can only be 2 levels deep