From 3bc29414456b60d0653d32b9a3fc72c88c3b2400 Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 3 Nov 2024 08:44:57 +0200 Subject: [PATCH 1/3] No db access for author image if in disk cache --- server/controllers/AuthorController.js | 21 ++++++++++++++------- server/managers/CacheManager.js | 17 +++++++++++------ server/routers/ApiRouter.js | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js index 54a64185..45bbdf84 100644 --- a/server/controllers/AuthorController.js +++ b/server/controllers/AuthorController.js @@ -381,16 +381,23 @@ class AuthorController { */ async getImage(req, res) { const { - query: { width, height, format, raw }, - author + query: { width, height, format, raw } } = req - if (!author.imagePath || !(await fs.pathExists(author.imagePath))) { - Logger.warn(`[AuthorController] Author "${author.name}" has invalid imagePath: ${author.imagePath}`) - return res.sendStatus(404) - } + const authorId = req.params.id if (raw) { + const author = await Database.authorModel.findByPk(authorId) + if (!author) { + Logger.warn(`[AuthorController] Author "${authorId}" not found`) + return res.sendStatus(404) + } + + if (!author.imagePath || !(await fs.pathExists(author.imagePath))) { + Logger.warn(`[AuthorController] Author "${author.name}" has invalid imagePath: ${author.imagePath}`) + return res.sendStatus(404) + } + return res.sendFile(author.imagePath) } @@ -399,7 +406,7 @@ class AuthorController { height: height ? parseInt(height) : null, width: width ? parseInt(width) : null } - return CacheManager.handleAuthorCache(res, author, options) + return CacheManager.handleAuthorCache(res, authorId, options) } /** diff --git a/server/managers/CacheManager.js b/server/managers/CacheManager.js index 83efae90..f0375691 100644 --- a/server/managers/CacheManager.js +++ b/server/managers/CacheManager.js @@ -134,22 +134,22 @@ class CacheManager { /** * * @param {import('express').Response} res - * @param {import('../models/Author')} author + * @param {String} authorId * @param {{ format?: string, width?: number, height?: number }} options * @returns */ - async handleAuthorCache(res, author, options = {}) { + async handleAuthorCache(res, authorId, options = {}) { const format = options.format || 'webp' const width = options.width || 400 const height = options.height || null res.type(`image/${format}`) - var path = Path.join(this.ImageCachePath, `${author.id}_${width}${height ? `x${height}` : ''}`) + '.' + format + var cachePath = Path.join(this.ImageCachePath, `${authorId}_${width}${height ? `x${height}` : ''}`) + '.' + format // Cache exists - if (await fs.pathExists(path)) { - const r = fs.createReadStream(path) + if (await fs.pathExists(cachePath)) { + const r = fs.createReadStream(cachePath) const ps = new stream.PassThrough() stream.pipeline(r, ps, (err) => { if (err) { @@ -160,7 +160,12 @@ class CacheManager { return ps.pipe(res) } - let writtenFile = await resizeImage(author.imagePath, path, width, height) + const author = await Database.authorModel.findByPk(authorId) + if (!author || !author.imagePath || !(await fs.pathExists(author.imagePath))) { + return res.sendStatus(404) + } + + let writtenFile = await resizeImage(author.imagePath, cachePath, width, height) if (!writtenFile) return res.sendStatus(500) var readStream = fs.createReadStream(writtenFile) diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index f81bc26d..c9399d79 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -216,7 +216,7 @@ class ApiRouter { this.router.patch('/authors/:id', AuthorController.middleware.bind(this), AuthorController.update.bind(this)) this.router.delete('/authors/:id', AuthorController.middleware.bind(this), AuthorController.delete.bind(this)) this.router.post('/authors/:id/match', AuthorController.middleware.bind(this), AuthorController.match.bind(this)) - this.router.get('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.getImage.bind(this)) + this.router.get('/authors/:id/image', AuthorController.getImage.bind(this)) this.router.post('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.uploadImage.bind(this)) this.router.delete('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.deleteImage.bind(this)) From bf8407274e3ee300af1927ee660d078a7a801e1c Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 3 Nov 2024 08:45:43 +0200 Subject: [PATCH 2/3] No auth for author images --- server/Auth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/Auth.js b/server/Auth.js index da124b72..5b2d8bcd 100644 --- a/server/Auth.js +++ b/server/Auth.js @@ -18,7 +18,7 @@ class Auth { constructor() { // Map of openId sessions indexed by oauth2 state-variable this.openIdAuthSession = new Map() - this.ignorePattern = /\/api\/items\/[^/]+\/cover/ + this.ignorePatterns = [/\/api\/items\/[^/]+\/cover/, /\/api\/authors\/[^/]+\/image/] } /** @@ -28,7 +28,7 @@ class Auth { * @private */ authNotNeeded(req) { - return req.method === 'GET' && this.ignorePattern.test(req.originalUrl) + return req.method === 'GET' && this.ignorePatterns.some((pattern) => pattern.test(req.originalUrl)) } ifAuthNeeded(middleware) { From 68fd1d67cb867c1deeb0cac772dede2935a63527 Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 3 Nov 2024 08:46:09 +0200 Subject: [PATCH 3/3] Remove token from author image URLs --- client/components/covers/AuthorImage.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/covers/AuthorImage.vue b/client/components/covers/AuthorImage.vue index 01926363..e320e552 100644 --- a/client/components/covers/AuthorImage.vue +++ b/client/components/covers/AuthorImage.vue @@ -56,7 +56,7 @@ export default { }, imgSrc() { if (!this.imagePath) return null - return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}` + return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?ts=${this.updatedAt}` } }, methods: {