From 68ef3a07a7e09948a04773059a7ca05febf27e2d Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 10 Aug 2024 17:15:21 -0500 Subject: [PATCH] Update controllers to use new user model --- server/controllers/AuthorController.js | 12 +- server/controllers/BackupController.js | 4 +- server/controllers/CacheController.js | 8 +- server/controllers/CollectionController.js | 58 ++++----- .../CustomMetadataProviderController.js | 33 +++--- server/controllers/EmailController.js | 18 +-- server/controllers/FileSystemController.js | 26 ++-- server/controllers/LibraryController.js | 58 ++++----- server/controllers/LibraryItemController.js | 66 +++++------ server/controllers/PlaylistController.js | 72 +++++------ server/controllers/SeriesController.js | 25 ++-- server/models/Collection.js | 6 +- server/models/LibraryItem.js | 6 +- server/models/User.js | 77 ++++++++++++ server/utils/libraryHelpers.js | 112 ++++++++++-------- server/utils/queries/libraryFilters.js | 22 ++-- server/utils/queries/libraryItemFilters.js | 36 +++--- .../utils/queries/libraryItemsBookFilters.js | 26 ++-- .../queries/libraryItemsPodcastFilters.js | 31 ++--- server/utils/queries/seriesFilters.js | 4 +- 20 files changed, 396 insertions(+), 304 deletions(-) diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js index 57eebb43..ec1d648e 100644 --- a/server/controllers/AuthorController.js +++ b/server/controllers/AuthorController.js @@ -24,7 +24,7 @@ class AuthorController { // Used on author landing page to include library items and items grouped in series if (include.includes('items')) { - authorJson.libraryItems = await Database.libraryItemModel.getForAuthor(req.author, req.user) + authorJson.libraryItems = await Database.libraryItemModel.getForAuthor(req.author, req.userNew) if (include.includes('series')) { const seriesMap = {} @@ -222,8 +222,8 @@ class AuthorController { * @param {import('express').Response} res */ async uploadImage(req, res) { - if (!req.user.canUpload) { - Logger.warn('User attempted to upload an image without permission', req.user) + if (!req.userNew.canUpload) { + Logger.warn(`User "${req.userNew.username}" attempted to upload an image without permission`) return res.sendStatus(403) } if (!req.body.url) { @@ -362,11 +362,11 @@ class AuthorController { const author = await Database.authorModel.getOldById(req.params.id) if (!author) return res.sendStatus(404) - if (req.method == 'DELETE' && !req.user.canDelete) { - Logger.warn(`[AuthorController] User attempted to delete without permission`, req.user) + if (req.method == 'DELETE' && !req.userNew.canDelete) { + Logger.warn(`[AuthorController] User "${req.userNew.username}" attempted to delete without permission`) return res.sendStatus(403) } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { - Logger.warn('[AuthorController] User attempted to update without permission', req.user) + Logger.warn(`[AuthorController] User "${req.userNew.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/BackupController.js b/server/controllers/BackupController.js index df33aa1d..86c4f4e7 100644 --- a/server/controllers/BackupController.js +++ b/server/controllers/BackupController.js @@ -113,8 +113,8 @@ class BackupController { } middleware(req, res, next) { - if (!req.user.isAdminOrUp) { - Logger.error(`[BackupController] Non-admin user attempting to access backups`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[BackupController] Non-admin user "${req.userNew.username}" attempting to access backups`) return res.sendStatus(403) } diff --git a/server/controllers/CacheController.js b/server/controllers/CacheController.js index 95c5fe0c..3a06d203 100644 --- a/server/controllers/CacheController.js +++ b/server/controllers/CacheController.js @@ -1,11 +1,11 @@ const CacheManager = require('../managers/CacheManager') class CacheController { - constructor() { } + constructor() {} // POST: api/cache/purge async purgeCache(req, res) { - if (!req.user.isAdminOrUp) { + if (!req.userNew.isAdminOrUp) { return res.sendStatus(403) } await CacheManager.purgeAll() @@ -14,11 +14,11 @@ class CacheController { // POST: api/cache/items/purge async purgeItemsCache(req, res) { - if (!req.user.isAdminOrUp) { + if (!req.userNew.isAdminOrUp) { return res.sendStatus(403) } await CacheManager.purgeItems() res.sendStatus(200) } } -module.exports = new CacheController() \ No newline at end of file +module.exports = new CacheController() diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 5357a5dc..09525957 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -6,17 +6,17 @@ const Database = require('../Database') const Collection = require('../objects/Collection') class CollectionController { - constructor() { } + constructor() {} /** * POST: /api/collections * Create new collection - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async create(req, res) { const newCollection = new Collection() - req.body.userId = req.user.id + req.body.userId = req.userNew.id if (!newCollection.setData(req.body)) { return res.status(400).send('Invalid collection data') } @@ -31,7 +31,7 @@ class CollectionController { let order = 1 const collectionBooksToAdd = [] for (const libraryItemId of newCollection.books) { - const libraryItem = libraryItemsInCollection.find(li => li.id === libraryItemId) + const libraryItem = libraryItemsInCollection.find((li) => li.id === libraryItemId) if (libraryItem) { collectionBooksToAdd.push({ collectionId: newCollection.id, @@ -50,7 +50,7 @@ class CollectionController { } async findAll(req, res) { - const collectionsExpanded = await Database.collectionModel.getOldCollectionsJsonExpanded(req.user) + const collectionsExpanded = await Database.collectionModel.getOldCollectionsJsonExpanded(req.userNew) res.json({ collections: collectionsExpanded }) @@ -59,7 +59,7 @@ class CollectionController { async findOne(req, res) { const includeEntities = (req.query.include || '').split(',') - const collectionExpanded = await req.collection.getOldJsonExpanded(req.user, includeEntities) + const collectionExpanded = await req.collection.getOldJsonExpanded(req.userNew, includeEntities) if (!collectionExpanded) { // This may happen if the user is restricted from all books return res.sendStatus(404) @@ -71,8 +71,8 @@ class CollectionController { /** * PATCH: /api/collections/:id * Update collection - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async update(req, res) { let wasUpdated = false @@ -102,8 +102,8 @@ class CollectionController { order: [['order', 'ASC']] }) collectionBooks.sort((a, b) => { - const aIndex = req.body.books.findIndex(lid => lid === a.book.libraryItem.id) - const bIndex = req.body.books.findIndex(lid => lid === b.book.libraryItem.id) + const aIndex = req.body.books.findIndex((lid) => lid === a.book.libraryItem.id) + const bIndex = req.body.books.findIndex((lid) => lid === b.book.libraryItem.id) return aIndex - bIndex }) for (let i = 0; i < collectionBooks.length; i++) { @@ -139,8 +139,8 @@ class CollectionController { * POST: /api/collections/:id/book * Add a single book to a collection * Req.body { id: } - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async addBook(req, res) { const libraryItem = await Database.libraryItemModel.getOldById(req.body.id) @@ -153,7 +153,7 @@ class CollectionController { // Check if book is already in collection const collectionBooks = await req.collection.getCollectionBooks() - if (collectionBooks.some(cb => cb.bookId === libraryItem.media.id)) { + if (collectionBooks.some((cb) => cb.bookId === libraryItem.media.id)) { return res.status(400).send('Book already in collection') } @@ -172,8 +172,8 @@ class CollectionController { * DELETE: /api/collections/:id/book/:bookId * Remove a single book from a collection. Re-order books * TODO: bookId is actually libraryItemId. Clients need updating to use bookId - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async removeBook(req, res) { const libraryItem = await Database.libraryItemModel.getOldById(req.params.bookId) @@ -187,7 +187,7 @@ class CollectionController { }) let jsonExpanded = null - const collectionBookToRemove = collectionBooks.find(cb => cb.bookId === libraryItem.media.id) + const collectionBookToRemove = collectionBooks.find((cb) => cb.bookId === libraryItem.media.id) if (collectionBookToRemove) { // Remove collection book record await collectionBookToRemove.destroy() @@ -216,12 +216,12 @@ class CollectionController { * POST: /api/collections/:id/batch/add * Add multiple books to collection * Req.body { books: } - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async addBatch(req, res) { // filter out invalid libraryItemIds - const bookIdsToAdd = (req.body.books || []).filter(b => !!b && typeof b == 'string') + const bookIdsToAdd = (req.body.books || []).filter((b) => !!b && typeof b == 'string') if (!bookIdsToAdd.length) { return res.status(500).send('Invalid request body') } @@ -247,7 +247,7 @@ class CollectionController { // Check and set new collection books to add for (const libraryItem of libraryItems) { - if (!collectionBooks.some(cb => cb.bookId === libraryItem.media.id)) { + if (!collectionBooks.some((cb) => cb.bookId === libraryItem.media.id)) { collectionBooksToAdd.push({ collectionId: req.collection.id, bookId: libraryItem.media.id, @@ -274,12 +274,12 @@ class CollectionController { * POST: /api/collections/:id/batch/remove * Remove multiple books from collection * Req.body { books: } - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async removeBatch(req, res) { // filter out invalid libraryItemIds - const bookIdsToRemove = (req.body.books || []).filter(b => !!b && typeof b == 'string') + const bookIdsToRemove = (req.body.books || []).filter((b) => !!b && typeof b == 'string') if (!bookIdsToRemove.length) { return res.status(500).send('Invalid request body') } @@ -305,7 +305,7 @@ class CollectionController { let order = 1 let hasUpdated = false for (const collectionBook of collectionBooks) { - if (libraryItems.some(li => li.media.id === collectionBook.bookId)) { + if (libraryItems.some((li) => li.media.id === collectionBook.bookId)) { await collectionBook.destroy() hasUpdated = true continue @@ -334,15 +334,15 @@ class CollectionController { req.collection = collection } - if (req.method == 'DELETE' && !req.user.canDelete) { - Logger.warn(`[CollectionController] User attempted to delete without permission`, req.user.username) + if (req.method == 'DELETE' && !req.userNew.canDelete) { + Logger.warn(`[CollectionController] User "${req.userNew.username}" attempted to delete without permission`) return res.sendStatus(403) } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { - Logger.warn('[CollectionController] User attempted to update without permission', req.user.username) + Logger.warn(`[CollectionController] User "${req.userNew.username}" attempted to update without permission`) return res.sendStatus(403) } next() } } -module.exports = new CollectionController() \ No newline at end of file +module.exports = new CollectionController() diff --git a/server/controllers/CustomMetadataProviderController.js b/server/controllers/CustomMetadataProviderController.js index fdb4df2d..fd31b5f4 100644 --- a/server/controllers/CustomMetadataProviderController.js +++ b/server/controllers/CustomMetadataProviderController.js @@ -8,7 +8,7 @@ const { validateUrl } = require('../utils/index') // This is a controller for routes that don't have a home yet :( // class CustomMetadataProviderController { - constructor() { } + constructor() {} /** * GET: /api/custom-metadata-providers @@ -47,7 +47,7 @@ class CustomMetadataProviderController { name, mediaType, url, - authHeaderValue: !authHeaderValue ? null : authHeaderValue, + authHeaderValue: !authHeaderValue ? null : authHeaderValue }) // TODO: Necessary to emit to all clients? @@ -60,7 +60,7 @@ class CustomMetadataProviderController { /** * DELETE: /api/custom-metadata-providers/:id - * + * * @param {import('express').Request} req * @param {import('express').Response} res */ @@ -76,13 +76,16 @@ class CustomMetadataProviderController { await provider.destroy() // Libraries using this provider fallback to default provider - await Database.libraryModel.update({ - provider: fallbackProvider - }, { - where: { - provider: slug + await Database.libraryModel.update( + { + provider: fallbackProvider + }, + { + where: { + provider: slug + } } - }) + ) // TODO: Necessary to emit to all clients? SocketAuthority.emitter('custom_metadata_provider_removed', providerClientJson) @@ -92,14 +95,14 @@ class CustomMetadataProviderController { /** * Middleware that requires admin or up - * - * @param {import('express').Request} req - * @param {import('express').Response} res - * @param {import('express').NextFunction} next + * + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next */ async middleware(req, res, next) { - if (!req.user.isAdminOrUp) { - Logger.warn(`[CustomMetadataProviderController] Non-admin user "${req.user.username}" attempted access route "${req.path}"`) + if (!req.userNew.isAdminOrUp) { + Logger.warn(`[CustomMetadataProviderController] Non-admin user "${req.userNew.username}" attempted access route "${req.path}"`) return res.sendStatus(403) } diff --git a/server/controllers/EmailController.js b/server/controllers/EmailController.js index fcbc4905..42acbefd 100644 --- a/server/controllers/EmailController.js +++ b/server/controllers/EmailController.js @@ -3,7 +3,7 @@ const SocketAuthority = require('../SocketAuthority') const Database = require('../Database') class EmailController { - constructor() { } + constructor() {} getSettings(req, res) { res.json({ @@ -54,12 +54,12 @@ class EmailController { /** * Send ebook to device * User must have access to device and library item - * - * @param {import('express').Request} req - * @param {import('express').Response} res + * + * @param {import('express').Request} req + * @param {import('express').Response} res */ async sendEBookToDevice(req, res) { - Logger.debug(`[EmailController] Send ebook to device requested by user "${req.user.username}" for libraryItemId=${req.body.libraryItemId}, deviceName=${req.body.deviceName}`) + Logger.debug(`[EmailController] Send ebook to device requested by user "${req.userNew.username}" for libraryItemId=${req.body.libraryItemId}, deviceName=${req.body.deviceName}`) const device = Database.emailSettings.getEReaderDevice(req.body.deviceName) if (!device) { @@ -67,7 +67,7 @@ class EmailController { } // Check user has access to device - if (!Database.emailSettings.checkUserCanAccessDevice(device, req.user)) { + if (!Database.emailSettings.checkUserCanAccessDevice(device, req.userNew)) { return res.sendStatus(403) } @@ -77,7 +77,7 @@ class EmailController { } // Check user has access to library item - if (!req.user.checkCanAccessLibraryItem(libraryItem)) { + if (!req.userNew.checkCanAccessLibraryItem(libraryItem)) { return res.sendStatus(403) } @@ -90,11 +90,11 @@ class EmailController { } adminMiddleware(req, res, next) { - if (!req.user.isAdminOrUp) { + if (!req.userNew.isAdminOrUp) { return res.sendStatus(404) } next() } } -module.exports = new EmailController() \ No newline at end of file +module.exports = new EmailController() diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js index 88459e51..367215b2 100644 --- a/server/controllers/FileSystemController.js +++ b/server/controllers/FileSystemController.js @@ -5,16 +5,16 @@ const { toNumber } = require('../utils/index') const fileUtils = require('../utils/fileUtils') class FileSystemController { - constructor() { } + constructor() {} /** - * - * @param {import('express').Request} req - * @param {import('express').Response} res + * + * @param {import('express').Request} req + * @param {import('express').Response} res */ async getPaths(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[FileSystemController] Non-admin user attempting to get filesystem paths`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[FileSystemController] Non-admin user "${req.userNew.username}" attempting to get filesystem paths`) return res.sendStatus(403) } @@ -22,7 +22,7 @@ class FileSystemController { const level = toNumber(req.query.level, 0) // Validate path. Must be absolute - if (relpath && (!Path.isAbsolute(relpath) || !await fs.pathExists(relpath))) { + if (relpath && (!Path.isAbsolute(relpath) || !(await fs.pathExists(relpath)))) { Logger.error(`[FileSystemController] Invalid path in query string "${relpath}"`) return res.status(400).send('Invalid "path" query string') } @@ -40,7 +40,7 @@ class FileSystemController { return [] }) if (drives.length) { - directories = drives.map(d => { + directories = drives.map((d) => { return { path: d, dirname: d, @@ -54,10 +54,10 @@ class FileSystemController { } // Exclude some dirs from this project to be cleaner in Docker - const excludedDirs = ['node_modules', 'client', 'server', '.git', 'static', 'build', 'dist', 'metadata', 'config', 'sys', 'proc', '.devcontainer', '.nyc_output', '.github', '.vscode'].map(dirname => { + const excludedDirs = ['node_modules', 'client', 'server', '.git', 'static', 'build', 'dist', 'metadata', 'config', 'sys', 'proc', '.devcontainer', '.nyc_output', '.github', '.vscode'].map((dirname) => { return fileUtils.filePathToPOSIX(Path.join(global.appRoot, dirname)) }) - directories = directories.filter(dir => { + directories = directories.filter((dir) => { return !excludedDirs.includes(dir.path) }) @@ -69,8 +69,8 @@ class FileSystemController { // POST: api/filesystem/pathexists async checkPathExists(req, res) { - if (!req.user.canUpload) { - Logger.error(`[FileSystemController] Non-admin user attempting to check path exists`, req.user) + if (!req.userNew.canUpload) { + Logger.error(`[FileSystemController] Non-admin user "${req.userNew.username}" attempting to check path exists`) return res.sendStatus(403) } @@ -85,4 +85,4 @@ class FileSystemController { }) } } -module.exports = new FileSystemController() \ No newline at end of file +module.exports = new FileSystemController() diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index c468cb75..bd0882a7 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -83,7 +83,7 @@ class LibraryController { async findAll(req, res) { const libraries = await Database.libraryModel.getAllOldLibraries() - const librariesAccessible = req.user.librariesAccessible || [] + const librariesAccessible = req.userNew.permissions?.librariesAccessible || [] if (librariesAccessible.length) { return res.json({ libraries: libraries.filter((lib) => librariesAccessible.includes(lib.id)).map((lib) => lib.toJSON()) @@ -110,7 +110,7 @@ class LibraryController { return res.json({ filterdata, issues: filterdata.numIssues, - numUserPlaylists: await Database.playlistModel.getNumPlaylistsForUserAndLibrary(req.user.id, req.library.id), + numUserPlaylists: await Database.playlistModel.getNumPlaylistsForUserAndLibrary(req.userNew.id, req.library.id), customMetadataProviders, library: req.library }) @@ -327,9 +327,9 @@ class LibraryController { const filterByValue = filterByGroup ? libraryFilters.decode(payload.filterBy.replace(`${filterByGroup}.`, '')) : null if (filterByGroup === 'series' && filterByValue !== 'no-series' && payload.collapseseries) { const seriesId = libraryFilters.decode(payload.filterBy.split('.')[1]) - payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, req.library) + payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.userNew, req.library) } else { - const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.user, payload) + const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.userNew, payload) payload.results = libraryItems payload.total = count } @@ -420,7 +420,7 @@ class LibraryController { } const offset = payload.page * payload.limit - const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset) + const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.userNew, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset) payload.total = count payload.results = series @@ -447,11 +447,11 @@ class LibraryController { if (!series) return res.sendStatus(404) const oldSeries = series.getOldSeries() - const libraryItemsInSeries = await libraryItemsBookFilters.getLibraryItemsForSeries(oldSeries, req.user) + const libraryItemsInSeries = await libraryItemsBookFilters.getLibraryItemsForSeries(oldSeries, req.userNew) const seriesJson = oldSeries.toJSON() if (include.includes('progress')) { - const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.user.getMediaProgress(li.id)?.isFinished) + const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.userNew.getMediaProgress(li.media.id)?.isFinished) seriesJson.progress = { libraryItemIds: libraryItemsInSeries.map((li) => li.id), libraryItemIdsFinished: libraryItemsFinished.map((li) => li.id), @@ -492,7 +492,7 @@ class LibraryController { } // TODO: Create paginated queries - let collections = await Database.collectionModel.getOldCollectionsJsonExpanded(req.user, req.library.id, include) + let collections = await Database.collectionModel.getOldCollectionsJsonExpanded(req.userNew, req.library.id, include) payload.total = collections.length @@ -512,7 +512,7 @@ class LibraryController { * @param {*} res */ async getUserPlaylistsForLibrary(req, res) { - let playlistsForUser = await Database.playlistModel.getOldPlaylistsForUserAndLibrary(req.user.id, req.library.id) + let playlistsForUser = await Database.playlistModel.getOldPlaylistsForUserAndLibrary(req.userNew.id, req.library.id) const payload = { results: [], @@ -552,7 +552,7 @@ class LibraryController { .split(',') .map((v) => v.trim().toLowerCase()) .filter((v) => !!v) - const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.user, include, limitPerShelf) + const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.userNew, include, limitPerShelf) res.json(shelves) } @@ -563,8 +563,8 @@ class LibraryController { * @param {import('express').Response} res */ async reorder(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error('[LibraryController] ReorderLibraries invalid user', req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.userNew}" attempted to reorder libraries`) return res.sendStatus(403) } const libraries = await Database.libraryModel.getAllOldLibraries() @@ -609,7 +609,7 @@ class LibraryController { const limit = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12 const query = asciiOnlyToLowerCase(req.query.q.trim()) - const matches = await libraryItemFilters.search(req.user, req.library, query, limit) + const matches = await libraryItemFilters.search(req.userNew, req.library, query, limit) res.json(matches) } @@ -662,7 +662,7 @@ class LibraryController { * @param {import('express').Response} res */ async getAuthors(req, res) { - const { bookWhere, replacements } = libraryItemsBookFilters.getUserPermissionBookWhereQuery(req.user) + const { bookWhere, replacements } = libraryItemsBookFilters.getUserPermissionBookWhereQuery(req.userNew) const authors = await Database.authorModel.findAll({ where: { libraryId: req.library.id @@ -672,7 +672,7 @@ class LibraryController { model: Database.bookModel, attributes: ['id', 'tags', 'explicit'], where: bookWhere, - required: !req.user.isAdminOrUp, // Only show authors with 0 books for admin users or up + required: !req.userNew.isAdminOrUp, // Only show authors with 0 books for admin users or up through: { attributes: [] } @@ -746,8 +746,8 @@ class LibraryController { * @param {*} res */ async updateNarrator(req, res) { - if (!req.user.canUpdate) { - Logger.error(`[LibraryController] Unauthorized user "${req.user.username}" attempted to update narrator`) + if (!req.userNew.canUpdate) { + Logger.error(`[LibraryController] Unauthorized user "${req.userNew.username}" attempted to update narrator`) return res.sendStatus(403) } @@ -796,8 +796,8 @@ class LibraryController { * @param {*} res */ async removeNarrator(req, res) { - if (!req.user.canUpdate) { - Logger.error(`[LibraryController] Unauthorized user "${req.user.username}" attempted to remove narrator`) + if (!req.userNew.canUpdate) { + Logger.error(`[LibraryController] Unauthorized user "${req.userNew.username}" attempted to remove narrator`) return res.sendStatus(403) } @@ -839,8 +839,8 @@ class LibraryController { * @param {import('express').Response} res */ async matchAll(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryController] Non-root user attempted to match library items`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryController] Non-root user "${req.userNew.username}" attempted to match library items`) return res.sendStatus(403) } Scanner.matchLibraryItems(req.library) @@ -856,8 +856,8 @@ class LibraryController { * @param {import('express').Response} res */ async scan(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryController] Non-root user attempted to scan library`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.userNew.username}" attempted to scan library`) return res.sendStatus(403) } res.sendStatus(200) @@ -887,7 +887,7 @@ class LibraryController { } const offset = payload.page * payload.limit - payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, req.library, payload.limit, offset) + payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.userNew, req.library, payload.limit, offset) res.json(payload) } @@ -898,7 +898,7 @@ class LibraryController { * @param {import('express').Response} res */ async getOPMLFile(req, res) { - const userPermissionPodcastWhere = libraryItemsPodcastFilters.getUserPermissionPodcastWhereQuery(req.user) + const userPermissionPodcastWhere = libraryItemsPodcastFilters.getUserPermissionPodcastWhereQuery(req.userNew) const podcasts = await Database.podcastModel.findAll({ attributes: ['id', 'feedURL', 'title', 'description', 'itunesPageURL', 'language'], where: userPermissionPodcastWhere.podcastWhere, @@ -924,8 +924,8 @@ class LibraryController { * @param {import('express').Response} res */ async removeAllMetadataFiles(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryController] Non-admin user attempted to remove all metadata files`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.userNew.username}" attempted to remove all metadata files`) return res.sendStatus(403) } @@ -974,8 +974,8 @@ class LibraryController { * @param {import('express').NextFunction} next */ async middleware(req, res, next) { - if (!req.user.checkCanAccessLibrary(req.params.id)) { - Logger.warn(`[LibraryController] Library ${req.params.id} not accessible to user ${req.user.username}`) + if (!req.userNew.checkCanAccessLibrary(req.params.id)) { + Logger.warn(`[LibraryController] Library ${req.params.id} not accessible to user ${req.userNew.username}`) return res.sendStatus(403) } diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index d354d236..c73bddf6 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -35,7 +35,7 @@ class LibraryItemController { // Include users media progress if (includeEntities.includes('progress')) { var episodeId = req.query.episode || null - item.userMediaProgress = req.user.getMediaProgress(item.id, episodeId) + item.userMediaProgress = req.userNew.getOldMediaProgress(item.id, episodeId) } if (includeEntities.includes('rssfeed')) { @@ -43,7 +43,7 @@ class LibraryItemController { item.rssFeed = feedData?.toJSONMinified() || null } - if (item.mediaType === 'book' && req.user.isAdminOrUp && includeEntities.includes('share')) { + if (item.mediaType === 'book' && req.userNew.isAdminOrUp && includeEntities.includes('share')) { item.mediaItemShare = ShareManager.findByMediaItemId(item.media.id) } @@ -109,8 +109,8 @@ class LibraryItemController { * @param {import('express').Response} res */ download(req, res) { - if (!req.user.canDownload) { - Logger.warn('User attempted to download without permission', req.user) + if (!req.userNew.canDownload) { + Logger.warn(`User "${req.userNew.username}" attempted to download without permission`) return res.sendStatus(403) } const libraryItemPath = req.libraryItem.path @@ -123,12 +123,12 @@ class LibraryItemController { if (audioMimeType) { res.setHeader('Content-Type', audioMimeType) } - Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) + Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) res.download(libraryItemPath, req.libraryItem.relPath) return } - Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) + Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) const filename = `${itemTitle}.zip` zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res) } @@ -200,8 +200,8 @@ class LibraryItemController { // POST: api/items/:id/cover async uploadCover(req, res, updateAndReturnJson = true) { - if (!req.user.canUpload) { - Logger.warn('User attempted to upload a cover without permission', req.user) + if (!req.userNew.canUpload) { + Logger.warn(`User "${req.userNew.username}" attempted to upload a cover without permission`) return res.sendStatus(403) } @@ -299,7 +299,7 @@ class LibraryItemController { } // Check if user can access this library item - if (!req.user.checkCanAccessLibraryItemWithData(libraryItem.libraryId, libraryItem.media.explicit, libraryItem.media.tags)) { + if (!req.userNew.checkCanAccessLibraryItem(libraryItem)) { return res.sendStatus(403) } @@ -387,8 +387,8 @@ class LibraryItemController { * @param {import('express').Response} res */ async batchDelete(req, res) { - if (!req.user.canDelete) { - Logger.warn(`[LibraryItemController] User attempted to delete without permission`, req.user) + if (!req.userNew.canDelete) { + Logger.warn(`[LibraryItemController] User "${req.userNew.username}" attempted to delete without permission`) return res.sendStatus(403) } const hardDelete = req.query.hard == 1 // Delete files from filesystem @@ -486,8 +486,8 @@ class LibraryItemController { // POST: api/items/batch/quickmatch async batchQuickMatch(req, res) { - if (!req.user.isAdminOrUp) { - Logger.warn('User other than admin attempted to batch quick match library items', req.user) + if (!req.userNew.isAdminOrUp) { + Logger.warn(`Non-admin user "${req.userNew.username}" other than admin attempted to batch quick match library items`) return res.sendStatus(403) } @@ -522,13 +522,13 @@ class LibraryItemController { updates: itemsUpdated, unmatched: itemsUnmatched } - SocketAuthority.clientEmitter(req.user.id, 'batch_quickmatch_complete', result) + SocketAuthority.clientEmitter(req.userNew.id, 'batch_quickmatch_complete', result) } // POST: api/items/batch/scan async batchScan(req, res) { - if (!req.user.isAdminOrUp) { - Logger.warn('User other than admin attempted to batch scan library items', req.user) + if (!req.userNew.isAdminOrUp) { + Logger.warn(`Non-admin user "${req.userNew.username}" other than admin attempted to batch scan library items`) return res.sendStatus(403) } @@ -562,8 +562,8 @@ class LibraryItemController { // POST: api/items/:id/scan async scan(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-admin user attempted to scan library item`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-admin user "${req.userNew.username}" attempted to scan library item`) return res.sendStatus(403) } @@ -580,8 +580,8 @@ class LibraryItemController { } getMetadataObject(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-admin user attempted to get metadata object`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-admin user "${req.userNew.username}" attempted to get metadata object`) return res.sendStatus(403) } @@ -595,8 +595,8 @@ class LibraryItemController { // POST: api/items/:id/chapters async updateMediaChapters(req, res) { - if (!req.user.canUpdate) { - Logger.error(`[LibraryItemController] User attempted to update chapters with invalid permissions`, req.user.username) + if (!req.userNew.canUpdate) { + Logger.error(`[LibraryItemController] User "${req.userNew.username}" attempted to update chapters with invalid permissions`) return res.sendStatus(403) } @@ -631,8 +631,8 @@ class LibraryItemController { * @param {express.Response} res */ async getFFprobeData(req, res) { - if (!req.user.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-admin user attempted to get ffprobe data`, req.user) + if (!req.userNew.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-admin user "${req.userNew.username}" attempted to get ffprobe data`) return res.sendStatus(403) } if (req.libraryFile.fileType !== 'audio') { @@ -682,7 +682,7 @@ class LibraryItemController { async deleteLibraryFile(req, res) { const libraryFile = req.libraryFile - Logger.info(`[LibraryItemController] User "${req.user.username}" requested file delete at "${libraryFile.metadata.path}"`) + Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested file delete at "${libraryFile.metadata.path}"`) await fs.remove(libraryFile.metadata.path).catch((error) => { Logger.error(`[LibraryItemController] Failed to delete library file at "${libraryFile.metadata.path}"`, error) @@ -710,12 +710,12 @@ class LibraryItemController { async downloadLibraryFile(req, res) { const libraryFile = req.libraryFile - if (!req.user.canDownload) { - Logger.error(`[LibraryItemController] User without download permission attempted to download file "${libraryFile.metadata.path}"`, req.user) + if (!req.userNew.canDownload) { + Logger.error(`[LibraryItemController] User "${req.userNew.username}" without download permission attempted to download file "${libraryFile.metadata.path}"`) return res.sendStatus(403) } - Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${req.libraryItem.media.metadata.title}" file at "${libraryFile.metadata.path}"`) + Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${req.libraryItem.media.metadata.title}" file at "${libraryFile.metadata.path}"`) if (global.XAccel) { const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path) @@ -759,7 +759,7 @@ class LibraryItemController { } const ebookFilePath = ebookFile.metadata.path - Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${req.libraryItem.media.metadata.title}" ebook at "${ebookFilePath}"`) + Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${req.libraryItem.media.metadata.title}" ebook at "${ebookFilePath}"`) if (global.XAccel) { const encodedURI = encodeUriPath(global.XAccel + ebookFilePath) @@ -812,7 +812,7 @@ class LibraryItemController { if (!req.libraryItem?.media) return res.sendStatus(404) // Check user can access this library item - if (!req.user.checkCanAccessLibraryItem(req.libraryItem)) { + if (!req.userNew.checkCanAccessLibraryItem(req.libraryItem)) { return res.sendStatus(403) } @@ -827,11 +827,11 @@ class LibraryItemController { if (req.path.includes('/play')) { // allow POST requests using /play and /play/:episodeId - } else if (req.method == 'DELETE' && !req.user.canDelete) { - Logger.warn(`[LibraryItemController] User attempted to delete without permission`, req.user) + } else if (req.method == 'DELETE' && !req.userNew.canDelete) { + Logger.warn(`[LibraryItemController] User "${req.userNew.username}" attempted to delete without permission`) return res.sendStatus(403) } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { - Logger.warn('[LibraryItemController] User attempted to update without permission', req.user.username) + Logger.warn(`[LibraryItemController] User "${req.userNew.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/PlaylistController.js b/server/controllers/PlaylistController.js index c501f287..94c769b1 100644 --- a/server/controllers/PlaylistController.js +++ b/server/controllers/PlaylistController.js @@ -5,13 +5,13 @@ const Database = require('../Database') const Playlist = require('../objects/Playlist') class PlaylistController { - constructor() { } + constructor() {} /** * POST: /api/playlists * Create playlist - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async create(req, res) { const oldPlaylist = new Playlist() @@ -25,7 +25,7 @@ class PlaylistController { const newPlaylist = await Database.playlistModel.createFromOld(oldPlaylist) // Lookup all library items in playlist - const libraryItemIds = oldPlaylist.items.map(i => i.libraryItemId).filter(i => i) + const libraryItemIds = oldPlaylist.items.map((i) => i.libraryItemId).filter((i) => i) const libraryItemsInPlaylist = await Database.libraryItemModel.findAll({ where: { id: libraryItemIds @@ -36,7 +36,7 @@ class PlaylistController { const mediaItemsToAdd = [] let order = 1 for (const mediaItemObj of oldPlaylist.items) { - const libraryItem = libraryItemsInPlaylist.find(li => li.id === mediaItemObj.libraryItemId) + const libraryItem = libraryItemsInPlaylist.find((li) => li.id === mediaItemObj.libraryItemId) if (!libraryItem) continue mediaItemsToAdd.push({ @@ -58,8 +58,8 @@ class PlaylistController { /** * GET: /api/playlists * Get all playlists for user - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async findAllForUser(req, res) { const playlistsForUser = await Database.playlistModel.findAll({ @@ -79,8 +79,8 @@ class PlaylistController { /** * GET: /api/playlists/:id - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async findOne(req, res) { const jsonExpanded = await req.playlist.getOldJsonExpanded() @@ -90,8 +90,8 @@ class PlaylistController { /** * PATCH: /api/playlists/:id * Update playlist - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async update(req, res) { const updatedPlaylist = req.playlist.set(req.body) @@ -104,7 +104,7 @@ class PlaylistController { } // If array of items is passed in then update order of playlist media items - const libraryItemIds = req.body.items?.map(i => i.libraryItemId).filter(i => i) || [] + const libraryItemIds = req.body.items?.map((i) => i.libraryItemId).filter((i) => i) || [] if (libraryItemIds.length) { const libraryItems = await Database.libraryItemModel.findAll({ where: { @@ -118,7 +118,7 @@ class PlaylistController { // Set an array of mediaItemId const newMediaItemIdOrder = [] for (const item of req.body.items) { - const libraryItem = libraryItems.find(li => li.id === item.libraryItemId) + const libraryItem = libraryItems.find((li) => li.id === item.libraryItemId) if (!libraryItem) { continue } @@ -128,8 +128,8 @@ class PlaylistController { // Sort existing playlist media items into new order existingPlaylistMediaItems.sort((a, b) => { - const aIndex = newMediaItemIdOrder.findIndex(i => i === a.mediaItemId) - const bIndex = newMediaItemIdOrder.findIndex(i => i === b.mediaItemId) + const aIndex = newMediaItemIdOrder.findIndex((i) => i === a.mediaItemId) + const bIndex = newMediaItemIdOrder.findIndex((i) => i === b.mediaItemId) return aIndex - bIndex }) @@ -156,8 +156,8 @@ class PlaylistController { /** * DELETE: /api/playlists/:id * Remove playlist - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async delete(req, res) { const jsonExpanded = await req.playlist.getOldJsonExpanded() @@ -169,8 +169,8 @@ class PlaylistController { /** * POST: /api/playlists/:id/item * Add item to playlist - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async addItem(req, res) { const oldPlaylist = await Database.playlistModel.getById(req.playlist.id) @@ -213,8 +213,8 @@ class PlaylistController { /** * DELETE: /api/playlists/:id/item/:libraryItemId/:episodeId? * Remove item from playlist - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async removeItem(req, res) { const oldLibraryItem = await Database.libraryItemModel.getOldById(req.params.libraryItemId) @@ -229,7 +229,7 @@ class PlaylistController { }) // Check if media item to delete is in playlist - const mediaItemToRemove = playlistMediaItems.find(pmi => pmi.mediaItemId === mediaItemId) + const mediaItemToRemove = playlistMediaItems.find((pmi) => pmi.mediaItemId === mediaItemId) if (!mediaItemToRemove) { return res.status(404).send('Media item not found in playlist') } @@ -266,8 +266,8 @@ class PlaylistController { /** * POST: /api/playlists/:id/batch/add * Batch add playlist items - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async addBatch(req, res) { if (!req.body.items?.length) { @@ -275,7 +275,7 @@ class PlaylistController { } const itemsToAdd = req.body.items - const libraryItemIds = itemsToAdd.map(i => i.libraryItemId).filter(i => i) + const libraryItemIds = itemsToAdd.map((i) => i.libraryItemId).filter((i) => i) if (!libraryItemIds.length) { return res.status(400).send('Invalid request body') } @@ -297,12 +297,12 @@ class PlaylistController { // Setup array of playlistMediaItem records to add let order = existingPlaylistMediaItems.length + 1 for (const item of itemsToAdd) { - const libraryItem = libraryItems.find(li => li.id === item.libraryItemId) + const libraryItem = libraryItems.find((li) => li.id === item.libraryItemId) if (!libraryItem) { return res.status(404).send('Item not found with id ' + item.libraryItemId) } else { const mediaItemId = item.episodeId || libraryItem.mediaId - if (existingPlaylistMediaItems.some(pmi => pmi.mediaItemId === mediaItemId)) { + if (existingPlaylistMediaItems.some((pmi) => pmi.mediaItemId === mediaItemId)) { // Already exists in playlist continue } else { @@ -330,8 +330,8 @@ class PlaylistController { /** * POST: /api/playlists/:id/batch/remove * Batch remove playlist items - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async removeBatch(req, res) { if (!req.body.items?.length) { @@ -339,7 +339,7 @@ class PlaylistController { } const itemsToRemove = req.body.items - const libraryItemIds = itemsToRemove.map(i => i.libraryItemId).filter(i => i) + const libraryItemIds = itemsToRemove.map((i) => i.libraryItemId).filter((i) => i) if (!libraryItemIds.length) { return res.status(400).send('Invalid request body') } @@ -360,10 +360,10 @@ class PlaylistController { // Remove playlist media items let hasUpdated = false for (const item of itemsToRemove) { - const libraryItem = libraryItems.find(li => li.id === item.libraryItemId) + const libraryItem = libraryItems.find((li) => li.id === item.libraryItemId) if (!libraryItem) continue const mediaItemId = item.episodeId || libraryItem.mediaId - const existingMediaItem = existingPlaylistMediaItems.find(pmi => pmi.mediaItemId === mediaItemId) + const existingMediaItem = existingPlaylistMediaItems.find((pmi) => pmi.mediaItemId === mediaItemId) if (!existingMediaItem) continue await existingMediaItem.destroy() hasUpdated = true @@ -387,8 +387,8 @@ class PlaylistController { /** * POST: /api/playlists/collection/:collectionId * Create a playlist from a collection - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res */ async createFromCollection(req, res) { const collection = await Database.collectionModel.findByPk(req.params.collectionId) @@ -396,7 +396,7 @@ class PlaylistController { return res.status(404).send('Collection not found') } // Expand collection to get library items - const collectionExpanded = await collection.getOldJsonExpanded(req.user) + const collectionExpanded = await collection.getOldJsonExpanded(req.userNew) if (!collectionExpanded) { // This can happen if the user has no access to all items in collection return res.status(404).send('Collection not found') @@ -452,4 +452,4 @@ class PlaylistController { next() } } -module.exports = new PlaylistController() \ No newline at end of file +module.exports = new PlaylistController() diff --git a/server/controllers/SeriesController.js b/server/controllers/SeriesController.js index 38ab3da9..b3adec4b 100644 --- a/server/controllers/SeriesController.js +++ b/server/controllers/SeriesController.js @@ -4,33 +4,36 @@ const Database = require('../Database') const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters') class SeriesController { - constructor() { } + constructor() {} /** * @deprecated * /api/series/:id - * + * * TODO: Update mobile app to use /api/libraries/:id/series/:seriesId API route instead * Series are not library specific so we need to know what the library id is - * - * @param {*} req - * @param {*} res + * + * @param {*} req + * @param {*} res */ async findOne(req, res) { - const include = (req.query.include || '').split(',').map(v => v.trim()).filter(v => !!v) + const include = (req.query.include || '') + .split(',') + .map((v) => v.trim()) + .filter((v) => !!v) const seriesJson = req.series.toJSON() // Add progress map with isFinished flag if (include.includes('progress')) { const libraryItemsInSeries = req.libraryItemsInSeries - const libraryItemsFinished = libraryItemsInSeries.filter(li => { + const libraryItemsFinished = libraryItemsInSeries.filter((li) => { const mediaProgress = req.user.getMediaProgress(li.id) return mediaProgress?.isFinished }) seriesJson.progress = { - libraryItemIds: libraryItemsInSeries.map(li => li.id), - libraryItemIdsFinished: libraryItemsFinished.map(li => li.id), + libraryItemIds: libraryItemsInSeries.map((li) => li.id), + libraryItemIdsFinished: libraryItemsFinished.map((li) => li.id), isFinished: libraryItemsFinished.length === libraryItemsInSeries.length } } @@ -59,7 +62,7 @@ class SeriesController { /** * Filter out any library items not accessible to user */ - const libraryItems = await libraryItemsBookFilters.getLibraryItemsForSeries(series, req.user) + const libraryItems = await libraryItemsBookFilters.getLibraryItemsForSeries(series, req.userNew) if (!libraryItems.length) { Logger.warn(`[SeriesController] User attempted to access series "${series.id}" with no accessible books`, req.user) return res.sendStatus(404) @@ -78,4 +81,4 @@ class SeriesController { next() } } -module.exports = new SeriesController() \ No newline at end of file +module.exports = new SeriesController() diff --git a/server/models/Collection.js b/server/models/Collection.js index 5fa0310d..dcc86e5a 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -22,7 +22,8 @@ class Collection extends Model { /** * Get all old collections toJSONExpanded, items filtered for user permissions - * @param {oldUser} [user] + * + * @param {import('./User')} user * @param {string} [libraryId] * @param {string[]} [include] * @returns {Promise} oldCollection.toJSONExpanded @@ -116,7 +117,8 @@ class Collection extends Model { /** * Get old collection toJSONExpanded, items filtered for user permissions - * @param {oldUser} [user] + * + * @param {import('./User')|null} user * @param {string[]} [include] * @returns {Promise} oldCollection.toJSONExpanded */ diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index bf7e26a0..847ff650 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -543,7 +543,7 @@ class LibraryItem extends Model { /** * Get library items using filter and sort * @param {oldLibrary} library - * @param {oldUser} user + * @param {import('./User')} user * @param {object} options * @returns {{ libraryItems:oldLibraryItem[], count:number }} */ @@ -586,7 +586,7 @@ class LibraryItem extends Model { /** * Get home page data personalized shelves * @param {oldLibrary} library - * @param {oldUser} user + * @param {import('./User')} user * @param {string[]} include * @param {number} limit * @returns {object[]} array of shelf objects @@ -759,7 +759,7 @@ class LibraryItem extends Model { /** * Get book library items for author, optional use user permissions * @param {oldAuthor} author - * @param {[oldUser]} user + * @param {import('./User')} user * @returns {Promise} */ static async getForAuthor(author, user = null) { diff --git a/server/models/User.js b/server/models/User.js index 4755967a..075276d4 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -414,6 +414,21 @@ class User extends Model { get isUser() { return this.type === 'user' } + get canAccessExplicitContent() { + return !!this.permissions?.accessExplicitContent && this.isActive + } + get canDelete() { + return !!this.permissions?.delete && this.isActive + } + get canUpdate() { + return !!this.permissions?.update && this.isActive + } + get canDownload() { + return !!this.permissions?.download && this.isActive + } + get canUpload() { + return !!this.permissions?.upload && this.isActive + } /** @type {string|null} */ get authOpenIDSub() { return this.extraData?.authOpenIDSub || null @@ -490,6 +505,40 @@ class User extends Model { return this.permissions.librariesAccessible.includes(libraryId) } + /** + * Check user has access to library item with tags + * + * @param {string[]} tags + * @returns {boolean} + */ + checkCanAccessLibraryItemWithTags(tags) { + if (this.permissions.accessAllTags) return true + const itemTagsSelected = this.permissions?.itemTagsSelected || [] + if (this.permissions.selectedTagsNotAccessible) { + if (!tags?.length) return true + return tags.every((tag) => !itemTagsSelected?.includes(tag)) + } + if (!tags?.length) return false + return itemTagsSelected.some((tag) => tags.includes(tag)) + } + + /** + * Check user can access library item + * TODO: Currently supports both old and new library item models + * + * @param {import('../objects/LibraryItem')|import('./LibraryItem')} libraryItem + * @returns {boolean} + */ + checkCanAccessLibraryItem(libraryItem) { + if (!this.checkCanAccessLibrary(libraryItem.libraryId)) return false + + const libraryItemExplicit = !!libraryItem.media.explicit || !!libraryItem.media.metadata?.explicit + + if (libraryItemExplicit && !this.canAccessExplicitContent) return false + + return this.checkCanAccessLibraryItemWithTags(libraryItem.media.tags) + } + /** * Get first available library id for user * @@ -500,6 +549,34 @@ class User extends Model { // Libraries should already be in ascending display order, find first accessible return libraryIds.find((lid) => this.checkCanAccessLibrary(lid)) || null } + + /** + * Get media progress by media item id + * + * @param {string} libraryItemId + * @param {string|null} [episodeId] + * @returns {import('./MediaProgress')|null} + */ + getMediaProgress(mediaItemId) { + if (!this.mediaProgresses?.length) return null + return this.mediaProgresses.find((mp) => mp.mediaItemId === mediaItemId) + } + + /** + * Get old media progress + * TODO: Update to new model + * + * @param {string} libraryItemId + * @param {string} [episodeId] + * @returns + */ + getOldMediaProgress(libraryItemId, episodeId = null) { + const mediaProgress = this.mediaProgresses?.find((mp) => { + if (episodeId && mp.mediaItemId === episodeId) return true + return mp.extraData?.libraryItemId === libraryItemId + }) + return mediaProgress?.getOldMediaProgress() || null + } } module.exports = User diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js index 8517660d..ad71ee3f 100644 --- a/server/utils/libraryHelpers.js +++ b/server/utils/libraryHelpers.js @@ -11,7 +11,7 @@ module.exports = { const seriesToFilterOut = {} books.forEach((libraryItem) => { // get all book series for item that is not already filtered out - const bookSeries = (libraryItem.media.metadata.series || []).filter(se => !seriesToFilterOut[se.id]) + const bookSeries = (libraryItem.media.metadata.series || []).filter((se) => !seriesToFilterOut[se.id]) if (!bookSeries.length) return bookSeries.forEach((bookSeriesObj) => { @@ -43,11 +43,11 @@ module.exports = { // Library setting to hide series with only 1 book if (hideSingleBookSeries) { - seriesItems = seriesItems.filter(se => se.books.length > 1) + seriesItems = seriesItems.filter((se) => se.books.length > 1) } return seriesItems.map((series) => { - series.books = naturalSort(series.books).asc(li => li.sequence) + series.books = naturalSort(series.books).asc((li) => li.sequence) return series }) }, @@ -55,9 +55,7 @@ module.exports = { collapseBookSeries(libraryItems, filterSeries, hideSingleBookSeries) { // Get series from the library items. If this list is being collapsed after filtering for a series, // don't collapse that series, only books that are in other series. - const seriesObjects = this - .getSeriesFromBooks(libraryItems, filterSeries, hideSingleBookSeries) - .filter(s => s.id != filterSeries) + const seriesObjects = this.getSeriesFromBooks(libraryItems, filterSeries, hideSingleBookSeries).filter((s) => s.id != filterSeries) const filteredLibraryItems = [] @@ -65,22 +63,29 @@ module.exports = { if (li.mediaType != 'book') return // Handle when this is the first book in a series - seriesObjects.filter(s => s.books[0].id == li.id).forEach(series => { - // Clone the library item as we need to attach data to it, but don't - // want to change the global copy of the library item - filteredLibraryItems.push(Object.assign( - Object.create(Object.getPrototypeOf(li)), - li, { collapsedSeries: series })) - }) + seriesObjects + .filter((s) => s.books[0].id == li.id) + .forEach((series) => { + // Clone the library item as we need to attach data to it, but don't + // want to change the global copy of the library item + filteredLibraryItems.push(Object.assign(Object.create(Object.getPrototypeOf(li)), li, { collapsedSeries: series })) + }) // Only included books not contained in series - if (!seriesObjects.some(s => s.books.some(b => b.id == li.id))) - filteredLibraryItems.push(li) + if (!seriesObjects.some((s) => s.books.some((b) => b.id == li.id))) filteredLibraryItems.push(li) }) return filteredLibraryItems }, + /** + * + * @param {*} payload + * @param {string} seriesId + * @param {import('../models/User')} user + * @param {import('../objects/Library')} library + * @returns {Object[]} + */ async handleCollapseSubseries(payload, seriesId, user, library) { const seriesWithBooks = await Database.seriesModel.findByPk(seriesId, { include: { @@ -112,17 +117,18 @@ module.exports = { return [] } - const books = seriesWithBooks.books payload.total = books.length - let libraryItems = books.map((book) => { - const libraryItem = book.libraryItem - libraryItem.media = book - return Database.libraryItemModel.getOldLibraryItem(libraryItem) - }).filter(li => { - return user.checkCanAccessLibraryItem(li) - }) + let libraryItems = books + .map((book) => { + const libraryItem = book.libraryItem + libraryItem.media = book + return Database.libraryItemModel.getOldLibraryItem(libraryItem) + }) + .filter((li) => { + return user.checkCanAccessLibraryItem(li) + }) const collapsedItems = this.collapseBookSeries(libraryItems, seriesId, library.settings.hideSingleBookSeries) if (!(collapsedItems.length == 1 && collapsedItems[0].collapsedSeries)) { @@ -139,7 +145,8 @@ module.exports = { { [direction]: (li) => li.media.metadata.getSeries(seriesId).sequence }, - { // If no series sequence then fallback to sorting by title (or collapsed series name for sub-series) + { + // If no series sequence then fallback to sorting by title (or collapsed series name for sub-series) [direction]: (li) => { if (sortingIgnorePrefix) { return li.collapsedSeries?.nameIgnorePrefix || li.media.metadata.titleIgnorePrefix @@ -150,7 +157,7 @@ module.exports = { } ] } else { - // If series are collapsed and not sorting by title or sequence, + // If series are collapsed and not sorting by title or sequence, // sort all collapsed series to the end in alphabetical order if (payload.sortBy !== 'media.metadata.title') { sortArray.push({ @@ -185,47 +192,48 @@ module.exports = { libraryItems = libraryItems.slice(startIndex, startIndex + payload.limit) } - return Promise.all(libraryItems.map(async li => { - const filteredSeries = li.media.metadata.getSeries(seriesId) - const json = li.toJSONMinified() - json.media.metadata.series = { - id: filteredSeries.id, - name: filteredSeries.name, - sequence: filteredSeries.sequence - } - - if (li.collapsedSeries) { - json.collapsedSeries = { - id: li.collapsedSeries.id, - name: li.collapsedSeries.name, - nameIgnorePrefix: li.collapsedSeries.nameIgnorePrefix, - libraryItemIds: li.collapsedSeries.books.map(b => b.id), - numBooks: li.collapsedSeries.books.length + return Promise.all( + libraryItems.map(async (li) => { + const filteredSeries = li.media.metadata.getSeries(seriesId) + const json = li.toJSONMinified() + json.media.metadata.series = { + id: filteredSeries.id, + name: filteredSeries.name, + sequence: filteredSeries.sequence } - // If collapsing by series and filtering by a series, generate the list of sequences the collapsed - // series represents in the filtered series - json.collapsedSeries.seriesSequenceList = - naturalSort(li.collapsedSeries.books.filter(b => b.filterSeriesSequence).map(b => b.filterSeriesSequence)).asc() + if (li.collapsedSeries) { + json.collapsedSeries = { + id: li.collapsedSeries.id, + name: li.collapsedSeries.name, + nameIgnorePrefix: li.collapsedSeries.nameIgnorePrefix, + libraryItemIds: li.collapsedSeries.books.map((b) => b.id), + numBooks: li.collapsedSeries.books.length + } + + // If collapsing by series and filtering by a series, generate the list of sequences the collapsed + // series represents in the filtered series + json.collapsedSeries.seriesSequenceList = naturalSort(li.collapsedSeries.books.filter((b) => b.filterSeriesSequence).map((b) => b.filterSeriesSequence)) + .asc() .reduce((ranges, currentSequence) => { let lastRange = ranges.at(-1) let isNumber = /^(\d+|\d+\.\d*|\d*\.\d+)$/.test(currentSequence) if (isNumber) currentSequence = parseFloat(currentSequence) - if (lastRange && isNumber && lastRange.isNumber && ((lastRange.end + 1) == currentSequence)) { + if (lastRange && isNumber && lastRange.isNumber && lastRange.end + 1 == currentSequence) { lastRange.end = currentSequence - } - else { + } else { ranges.push({ start: currentSequence, end: currentSequence, isNumber: isNumber }) } return ranges }, []) - .map(r => r.start == r.end ? r.start : `${r.start}-${r.end}`) + .map((r) => (r.start == r.end ? r.start : `${r.start}-${r.end}`)) .join(', ') - } + } - return json - })) + return json + }) + ) } } diff --git a/server/utils/queries/libraryFilters.js b/server/utils/queries/libraryFilters.js index ffcbd83e..471a1c0b 100644 --- a/server/utils/queries/libraryFilters.js +++ b/server/utils/queries/libraryFilters.js @@ -16,7 +16,7 @@ module.exports = { /** * Get library items using filter and sort * @param {import('../../objects/Library')} library - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @param {object} options * @returns {object} { libraryItems:LibraryItem[], count:number } */ @@ -42,7 +42,7 @@ module.exports = { /** * Get library items for continue listening & continue reading shelves * @param {import('../../objects/Library')} library - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {Promise<{ items:import('../../models/LibraryItem')[], count:number }>} @@ -79,7 +79,7 @@ module.exports = { /** * Get library items for most recently added shelf * @param {import('../../objects/Library')} library - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {object} { libraryItems:LibraryItem[], count:number } @@ -127,7 +127,7 @@ module.exports = { /** * Get library items for continue series shelf * @param {import('../../objects/Library')} library - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {object} { libraryItems:LibraryItem[], count:number } @@ -155,7 +155,7 @@ module.exports = { /** * Get library items or podcast episodes for the "Listen Again" and "Read Again" shelf * @param {import('../../objects/Library')} library - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {object} { items:object[], count:number } @@ -192,7 +192,7 @@ module.exports = { /** * Get series for recent series shelf * @param {import('../../objects/Library')} library - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {{ series:import('../../objects/entities/Series')[], count:number}} @@ -235,7 +235,7 @@ module.exports = { if (!user.canAccessExplicitContent) { attrQuery += ' AND b.explicit = 0' } - if (!user.permissions.accessAllTags && user.itemTagsSelected.length) { + if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) { if (user.permissions.selectedTagsNotAccessible) { attrQuery += ' AND (SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected)) = 0' } else { @@ -317,7 +317,7 @@ module.exports = { * Get most recently created authors for "Newest Authors" shelf * Author must be linked to at least 1 book * @param {oldLibrary} library - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {number} limit * @returns {object} { authors:oldAuthor[], count:number } */ @@ -360,7 +360,7 @@ module.exports = { /** * Get book library items for the "Discover" shelf * @param {oldLibrary} library - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {object} {libraryItems:oldLibraryItem[], count:number} @@ -387,7 +387,7 @@ module.exports = { /** * Get podcast episodes most recently added * @param {oldLibrary} library - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {number} limit * @returns {object} {libraryItems:oldLibraryItem[], count:number} */ @@ -408,7 +408,7 @@ module.exports = { /** * Get library items for an author, optional use user permissions * @param {oldAuthor} author - * @param {[oldUser]} user + * @param {import('../../models/User')} user * @param {number} limit * @param {number} offset * @returns {Promise} { libraryItems:LibraryItem[], count:number } diff --git a/server/utils/queries/libraryItemFilters.js b/server/utils/queries/libraryItemFilters.js index 677b11c7..128df6fd 100644 --- a/server/utils/queries/libraryItemFilters.js +++ b/server/utils/queries/libraryItemFilters.js @@ -6,7 +6,7 @@ const libraryItemsPodcastFilters = require('./libraryItemsPodcastFilters') module.exports = { /** * Get all library items that have tags - * @param {string[]} tags + * @param {string[]} tags * @returns {Promise} */ async getAllLibraryItemsWithTags(tags) { @@ -71,7 +71,7 @@ module.exports = { /** * Get all library items that have genres - * @param {string[]} genres + * @param {string[]} genres * @returns {Promise} */ async getAllLibraryItemsWithGenres(genres) { @@ -131,10 +131,10 @@ module.exports = { }, /** - * Get all library items that have narrators - * @param {string[]} narrators - * @returns {Promise} - */ + * Get all library items that have narrators + * @param {string[]} narrators + * @returns {Promise} + */ async getAllLibraryItemsWithNarrators(narrators) { const libraryItems = [] const booksWithGenre = await Database.bookModel.findAll({ @@ -172,24 +172,24 @@ module.exports = { /** * Search library items - * @param {import('../../objects/user/User')} oldUser - * @param {import('../../objects/Library')} oldLibrary + * @param {import('../../models/User')} user + * @param {import('../../objects/Library')} oldLibrary * @param {string} query - * @param {number} limit + * @param {number} limit * @returns {{book:object[], narrators:object[], authors:object[], tags:object[], series:object[], podcast:object[]}} */ - search(oldUser, oldLibrary, query, limit) { + search(user, oldLibrary, query, limit) { if (oldLibrary.isBook) { - return libraryItemsBookFilters.search(oldUser, oldLibrary, query, limit, 0) + return libraryItemsBookFilters.search(user, oldLibrary, query, limit, 0) } else { - return libraryItemsPodcastFilters.search(oldUser, oldLibrary, query, limit, 0) + return libraryItemsPodcastFilters.search(user, oldLibrary, query, limit, 0) } }, /** * Get largest items in library - * @param {string} libraryId - * @param {number} limit + * @param {string} libraryId + * @param {number} limit * @returns {Promise<{ id:string, title:string, size:number }[]>} */ async getLargestItems(libraryId, limit) { @@ -208,12 +208,10 @@ module.exports = { attributes: ['id', 'title'] } ], - order: [ - ['size', 'DESC'] - ], + order: [['size', 'DESC']], limit }) - return libraryItems.map(libraryItem => { + return libraryItems.map((libraryItem) => { return { id: libraryItem.id, title: libraryItem.media.title, @@ -221,4 +219,4 @@ module.exports = { } }) } -} \ No newline at end of file +} diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index 57f7f554..da356b3e 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -8,7 +8,7 @@ const ShareManager = require('../../managers/ShareManager') module.exports = { /** * User permissions to restrict books for explicit content & tags - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @returns {{ bookWhere:Sequelize.WhereOptions, replacements:object }} */ getUserPermissionBookWhereQuery(user) { @@ -21,8 +21,8 @@ module.exports = { explicit: false }) } - if (!user.permissions.accessAllTags && user.itemTagsSelected.length) { - replacements['userTagsSelected'] = user.itemTagsSelected + if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) { + replacements['userTagsSelected'] = user.permissions.itemTagsSelected if (user.permissions.selectedTagsNotAccessible) { bookWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0)) } else { @@ -333,7 +333,7 @@ module.exports = { /** * Get library items for book media type using filter and sort * @param {string} libraryId - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @param {string|null} filterGroup * @param {string|null} filterValue * @param {string} sortBy @@ -637,7 +637,7 @@ module.exports = { * 3. Has at least 1 unfinished book * TODO: Reduce queries * @param {import('../../objects/Library')} library - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @param {number} offset @@ -672,7 +672,7 @@ module.exports = { where: [ { id: { - [Sequelize.Op.notIn]: user.seriesHideFromContinueListening + [Sequelize.Op.notIn]: user.extraData?.seriesHideFromContinueListening || [] }, libraryId }, @@ -780,7 +780,7 @@ module.exports = { * Random selection of books that are not started * - only includes the first book of a not-started series * @param {string} libraryId - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {string[]} include * @param {number} limit * @returns {object} {libraryItems:LibraryItem, count:number} @@ -955,25 +955,25 @@ module.exports = { /** * Get library items for series * @param {import('../../objects/entities/Series')} oldSeries - * @param {import('../../objects/user/User')} [oldUser] + * @param {import('../../models/User')} [user] * @returns {Promise} */ - async getLibraryItemsForSeries(oldSeries, oldUser) { - const { libraryItems } = await this.getFilteredLibraryItems(oldSeries.libraryId, oldUser, 'series', oldSeries.id, null, null, false, [], null, null) + async getLibraryItemsForSeries(oldSeries, user) { + const { libraryItems } = await this.getFilteredLibraryItems(oldSeries.libraryId, user, 'series', oldSeries.id, null, null, false, [], null, null) return libraryItems.map((li) => Database.libraryItemModel.getOldLibraryItem(li)) }, /** * Search books, authors, series - * @param {import('../../objects/user/User')} oldUser + * @param {import('../../models/User')} user * @param {import('../../objects/Library')} oldLibrary * @param {string} query * @param {number} limit * @param {number} offset * @returns {{book:object[], narrators:object[], authors:object[], tags:object[], series:object[]}} */ - async search(oldUser, oldLibrary, query, limit, offset) { - const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(oldUser) + async search(user, oldLibrary, query, limit, offset) { + const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(user) const normalizedQuery = query diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js index 85ac74f6..f629b689 100644 --- a/server/utils/queries/libraryItemsPodcastFilters.js +++ b/server/utils/queries/libraryItemsPodcastFilters.js @@ -1,12 +1,11 @@ const Sequelize = require('sequelize') const Database = require('../../Database') const Logger = require('../../Logger') -const { asciiOnlyToLowerCase } = require('../index') module.exports = { /** * User permissions to restrict podcasts for explicit content & tags - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @returns {{ podcastWhere:Sequelize.WhereOptions, replacements:object }} */ getUserPermissionPodcastWhereQuery(user) { @@ -17,18 +16,20 @@ module.exports = { explicit: false }) } - if (!user.permissions.accessAllTags && user.itemTagsSelected.length) { - replacements['userTagsSelected'] = user.itemTagsSelected + + if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) { + replacements['userTagsSelected'] = user.permissions.itemTagsSelected if (user.permissions.selectedTagsNotAccessible) { - podcastWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0)) + bookWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0)) } else { - podcastWhere.push( + bookWhere.push( Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), { [Sequelize.Op.gte]: 1 }) ) } } + return { podcastWhere, replacements @@ -98,7 +99,7 @@ module.exports = { /** * Get library items for podcast media type using filter and sort * @param {string} libraryId - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {[string]} filterGroup * @param {[string]} filterValue * @param {string} sortBy @@ -200,7 +201,7 @@ module.exports = { /** * Get podcast episodes filtered and sorted * @param {string} libraryId - * @param {oldUser} user + * @param {import('../../models/User')} user * @param {[string]} filterGroup * @param {[string]} filterValue * @param {string} sortBy @@ -304,15 +305,15 @@ module.exports = { /** * Search podcasts - * @param {import('../../objects/user/User')} oldUser + * @param {import('../../models/User')} user * @param {import('../../objects/Library')} oldLibrary * @param {string} query * @param {number} limit * @param {number} offset * @returns {{podcast:object[], tags:object[]}} */ - async search(oldUser, oldLibrary, query, limit, offset) { - const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(oldUser) + async search(user, oldLibrary, query, limit, offset) { + const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(user) const normalizedQuery = query const matchTitle = Database.matchExpression('title', normalizedQuery) @@ -410,14 +411,14 @@ module.exports = { /** * Most recent podcast episodes not finished - * @param {import('../../objects/user/User')} oldUser + * @param {import('../../models/User')} user * @param {import('../../objects/Library')} oldLibrary * @param {number} limit * @param {number} offset * @returns {Promise} */ - async getRecentEpisodes(oldUser, oldLibrary, limit, offset) { - const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(oldUser) + async getRecentEpisodes(user, oldLibrary, limit, offset) { + const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(user) const episodes = await Database.podcastEpisodeModel.findAll({ where: { @@ -441,7 +442,7 @@ module.exports = { { model: Database.mediaProgressModel, where: { - userId: oldUser.id + userId: user.id }, required: false } diff --git a/server/utils/queries/seriesFilters.js b/server/utils/queries/seriesFilters.js index 1c384085..c03c13bf 100644 --- a/server/utils/queries/seriesFilters.js +++ b/server/utils/queries/seriesFilters.js @@ -12,7 +12,7 @@ module.exports = { * Get series filtered and sorted * * @param {import('../../objects/Library')} library - * @param {import('../../objects/user/User')} user + * @param {import('../../models/User')} user * @param {string} filterBy * @param {string} sortBy * @param {boolean} sortDesc @@ -93,7 +93,7 @@ module.exports = { if (!user.canAccessExplicitContent) { attrQuery += ' AND b.explicit = 0' } - if (!user.permissions.accessAllTags && user.itemTagsSelected.length) { + if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) { if (user.permissions.selectedTagsNotAccessible) { attrQuery += ' AND (SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected)) = 0' } else {