From e378b79fbc58e4360debec01162c4656ace6c577 Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 7 Jul 2023 17:59:17 -0500 Subject: [PATCH] Fix:Access series that are in multiple libraries and user does not have access to all #1899, new libraries/series endpoint --- client/pages/library/_library/series/_id.vue | 2 +- server/controllers/LibraryController.js | 46 +++++++++++++++++++- server/controllers/SeriesController.js | 24 +++++++--- server/routers/ApiRouter.js | 1 + 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/client/pages/library/_library/series/_id.vue b/client/pages/library/_library/series/_id.vue index c6ad6ad2..865648f8 100644 --- a/client/pages/library/_library/series/_id.vue +++ b/client/pages/library/_library/series/_id.vue @@ -19,7 +19,7 @@ export default { return redirect(`/library/${libraryId}`) } - const series = await app.$axios.$get(`/api/series/${params.id}?include=progress,rssfeed`).catch((error) => { + const series = await app.$axios.$get(`/api/libraries/${library.id}/series/${params.id}?include=progress,rssfeed`).catch((error) => { console.error('Failed', error) return false }) diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index 55f2c79f..af4adb1c 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -387,7 +387,13 @@ class LibraryController { res.sendStatus(200) } - // api/libraries/:id/series + /** + * api/libraries/:id/series + * Optional query string: `?include=rssfeed` that adds `rssFeed` to series if a feed is open + * + * @param {*} req + * @param {*} res + */ async getAllSeriesForLibrary(req, res) { const libraryItems = req.libraryItems @@ -448,6 +454,42 @@ class LibraryController { res.json(payload) } + /** + * api/libraries/:id/series/:seriesId + * + * Optional includes (e.g. `?include=rssfeed,progress`) + * rssfeed: adds `rssFeed` to series object if a feed is open + * progress: adds `progress` to series object with { libraryItemIds:Array, libraryItemIdsFinished:Array, isFinished:boolean } + * + * @param {*} req + * @param {*} res - Series + */ + async getSeriesForLibrary(req, res) { + const include = (req.query.include || '').split(',').map(v => v.trim().toLowerCase()).filter(v => !!v) + + const series = this.db.series.find(se => se.id === req.params.seriesId) + if (!series) return res.sendStatus(404) + + const libraryItemsInSeries = req.libraryItems.filter(li => li.media.metadata.hasSeries?.(series.id)) + + const seriesJson = series.toJSON() + if (include.includes('progress')) { + const libraryItemsFinished = libraryItemsInSeries.filter(li => !!req.user.getMediaProgress(li.id)?.isFinished) + seriesJson.progress = { + libraryItemIds: libraryItemsInSeries.map(li => li.id), + libraryItemIdsFinished: libraryItemsFinished.map(li => li.id), + isFinished: libraryItemsFinished.length >= libraryItemsInSeries.length + } + } + + if (include.includes('rssfeed')) { + const feedObj = this.rssFeedManager.findFeedForEntityId(seriesJson.id) + seriesJson.rssFeed = feedObj?.toJSONMinified() || null + } + + res.json(seriesJson) + } + // api/libraries/:id/collections async getCollectionsForLibrary(req, res) { const libraryItems = req.libraryItems @@ -855,7 +897,7 @@ class LibraryController { 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}`) - return res.sendStatus(404) + return res.sendStatus(403) } const library = this.db.libraries.find(lib => lib.id === req.params.id) diff --git a/server/controllers/SeriesController.js b/server/controllers/SeriesController.js index 970191e7..0152c336 100644 --- a/server/controllers/SeriesController.js +++ b/server/controllers/SeriesController.js @@ -4,6 +4,16 @@ const SocketAuthority = require('../SocketAuthority') class SeriesController { 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 + */ async findOne(req, res) { const include = (req.query.include || '').split(',').map(v => v.trim()).filter(v => !!v) @@ -28,7 +38,7 @@ class SeriesController { seriesJson.rssFeed = feedObj?.toJSONMinified() || null } - return res.json(seriesJson) + res.json(seriesJson) } async search(req, res) { @@ -55,9 +65,13 @@ class SeriesController { const series = this.db.series.find(se => se.id === req.params.id) if (!series) return res.sendStatus(404) - const libraryItemsInSeries = this.db.libraryItems.filter(li => li.media.metadata.hasSeries?.(series.id)) - if (libraryItemsInSeries.some(li => !req.user.checkCanAccessLibrary(li.libraryId))) { - Logger.warn(`[SeriesController] User attempted to access series "${series.id}" without access to the library`, req.user) + /** + * Filter out any library items not accessible to user + */ + const libraryItems = this.db.libraryItems.filter(li => li.media.metadata.hasSeries?.(series.id)) + const libraryItemsAccessible = libraryItems.filter(req.user.checkCanAccessLibraryItem) + if (libraryItems.length && !libraryItemsAccessible.length) { + Logger.warn(`[SeriesController] User attempted to access series "${series.id}" without access to any of the books`, req.user) return res.sendStatus(403) } @@ -70,7 +84,7 @@ class SeriesController { } req.series = series - req.libraryItemsInSeries = libraryItemsInSeries + req.libraryItemsInSeries = libraryItemsAccessible next() } } diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index 9277f4c3..a2a256c7 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -78,6 +78,7 @@ class ApiRouter { this.router.delete('/libraries/:id/issues', LibraryController.middleware.bind(this), LibraryController.removeLibraryItemsWithIssues.bind(this)) this.router.get('/libraries/:id/episode-downloads', LibraryController.middleware.bind(this), LibraryController.getEpisodeDownloadQueue.bind(this)) this.router.get('/libraries/:id/series', LibraryController.middleware.bind(this), LibraryController.getAllSeriesForLibrary.bind(this)) + this.router.get('/libraries/:id/series/:seriesId', LibraryController.middleware.bind(this), LibraryController.getSeriesForLibrary.bind(this)) this.router.get('/libraries/:id/collections', LibraryController.middleware.bind(this), LibraryController.getCollectionsForLibrary.bind(this)) this.router.get('/libraries/:id/playlists', LibraryController.middleware.bind(this), LibraryController.getUserPlaylistsForLibrary.bind(this)) this.router.get('/libraries/:id/albums', LibraryController.middleware.bind(this), LibraryController.getAlbumsForLibrary.bind(this))