From dad12537b626cb198f0f38ff506e1c7383e104a2 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 13 Mar 2022 06:42:43 -0500 Subject: [PATCH] New data model authors routes --- client/components/cards/AuthorCard.vue | 14 +++++- server/ApiController.js | 68 ++------------------------ server/controllers/AuthorController.js | 56 +++++++++++++++++++++ server/finders/AuthorFinder.js | 21 ++++++++ server/objects/entities/Author.js | 4 ++ server/objects/entities/Series.js | 4 ++ 6 files changed, 102 insertions(+), 65 deletions(-) create mode 100644 server/controllers/AuthorController.js diff --git a/client/components/cards/AuthorCard.vue b/client/components/cards/AuthorCard.vue index fddb9b55..430c5a86 100644 --- a/client/components/cards/AuthorCard.vue +++ b/client/components/cards/AuthorCard.vue @@ -20,6 +20,10 @@

{{ name }}

{{ numBooks }} Book{{ numBooks === 1 ? '' : 's' }}

+ +
+ search +
@@ -74,7 +78,15 @@ export default { return url.href + `?token=${this.userToken}&ts=${this.lastUpdate}` } }, - methods: {}, + methods: { + async searchAuthor() { + var author = await this.$axios.$post(`/api/authors/${this.authorId}/match`, { q: this.name }).catch((error) => { + console.error('Failed', error) + return null + }) + console.log('Got author', author) + } + }, mounted() {} } \ No newline at end of file diff --git a/server/ApiController.js b/server/ApiController.js index 89c19d4a..68adf139 100644 --- a/server/ApiController.js +++ b/server/ApiController.js @@ -16,6 +16,7 @@ const MeController = require('./controllers/MeController') const BackupController = require('./controllers/BackupController') const LibraryItemController = require('./controllers/LibraryItemController') const SeriesController = require('./controllers/SeriesController') +const AuthorController = require('./controllers/AuthorController') const BookFinder = require('./finders/BookFinder') const AuthorFinder = require('./finders/AuthorFinder') @@ -161,12 +162,9 @@ class ApiController { // // Author Routes // - this.router.get('/authors', this.getAuthors.bind(this)) - this.router.get('/authors/search', this.searchAuthors.bind(this)) - this.router.get('/authors/:id', this.getAuthor.bind(this)) - this.router.post('/authors', this.createAuthor.bind(this)) - this.router.patch('/authors/:id', this.updateAuthor.bind(this)) - this.router.delete('/authors/:id', this.deleteAuthor.bind(this)) + this.router.get('/authors/:id', AuthorController.middleware.bind(this), AuthorController.findOne.bind(this)) + this.router.post('/authors/:id/match', AuthorController.middleware.bind(this), AuthorController.match.bind(this)) + this.router.get('/authors/search', AuthorController.search.bind(this)) // // Series Routes @@ -230,64 +228,6 @@ class ApiController { res.json({ user: req.user }) } - async getAuthors(req, res) { - res.json(this.db.authors) - } - - searchAuthors(req, res) { - var query = req.query.q || '' - var limit = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 100 - var authors = this.db.authors.filter(au => au.name.toLowerCase().includes(query.toLowerCase())) - authors = authors.slice(0, limit) - res.json(authors) - } - - async getAuthor(req, res) { - var author = this.db.authors.find(p => p.id === req.params.id) - if (!author) { - return res.status(404).send('Author not found') - } - res.json(author.toJSON()) - } - - async createAuthor(req, res) { - var author = await this.authorFinder.createAuthor(req.body) - if (!author) { - return res.status(500).send('Failed to create author') - } - - await this.db.insertEntity('author', author) - this.emitter('author_added', author.toJSON()) - res.json(author) - } - - async updateAuthor(req, res) { - var author = this.db.authors.find(p => p.id === req.params.id) - if (!author) { - return res.status(404).send('Author not found') - } - - var wasUpdated = author.update(req.body) - if (wasUpdated) { - await this.db.updateEntity('author', author) - this.emitter('author_updated', author.toJSON()) - } - res.json(author) - } - - async deleteAuthor(req, res) { - var author = this.db.authors.find(p => p.id === req.params.id) - if (!author) { - return res.status(404).send('Author not found') - } - - var authorJson = author.toJSON() - - await this.db.removeEntity('author', author.id) - this.emitter('author_removed', authorJson) - res.sendStatus(200) - } - async updateServerSettings(req, res) { if (!req.user.isRoot) { Logger.error('User other than root attempting to update server settings', req.user) diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js new file mode 100644 index 00000000..52004f62 --- /dev/null +++ b/server/controllers/AuthorController.js @@ -0,0 +1,56 @@ +const Logger = require('../Logger') + +class AuthorController { + constructor() { } + + async findOne(req, res) { + return res.json(req.author) + } + + async search(req, res) { + var q = (req.query.q || '').toLowerCase() + if (!q) return res.json([]) + var limit = (req.query.limit && !isNaN(req.query.limit)) ? Number(req.query.limit) : 25 + var authors = this.db.authors.filter(au => au.name.toLowerCase().includes(q)) + authors = authors.slice(0, limit) + res.json(authors) + } + + async match(req, res) { + var authorData = await this.authorFinder.findAuthorByName(req.body.q) + if (!authorData) { + return res.status(404).send('Author not found') + } + req.author.asin = authorData.asin + if (authorData.image) { + var imageData = await this.authorFinder.saveAuthorImage(req.author.id, authorData.image) + if (imageData) { + req.author.imagePath = imageData.path + req.author.relImagePath = imageData.relPath + } + } + if (authorData.description) { + req.author.description = authorData.description + } + await this.db.updateEntity('author', req.author) + this.emitter('author_updated', req.author) + res.json(req.author) + } + + middleware(req, res, next) { + var author = this.db.authors.find(au => au.id === 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) + 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) + return res.sendStatus(403) + } + + req.author = author + next() + } +} +module.exports = new AuthorController() \ No newline at end of file diff --git a/server/finders/AuthorFinder.js b/server/finders/AuthorFinder.js index d4936a04..9504b698 100644 --- a/server/finders/AuthorFinder.js +++ b/server/finders/AuthorFinder.js @@ -31,6 +31,27 @@ class AuthorFinder { return author } + async saveAuthorImage(authorId, url) { + var authorDir = this.AuthorPath + var relAuthorDir = Path.posix.join('/metadata', 'authors') + await fs.ensureDir(authorDir) + + var imageExtension = url.toLowerCase().split('.').pop() + var ext = imageExtension === 'png' ? 'png' : 'jpg' + var filename = authorId + '.' + ext + var outputPath = Path.posix.join(authorDir, filename) + var relPath = Path.posix.join(relAuthorDir, filename) + + var success = await this.downloadImage(url, outputPath) + if (!success) { + return null + } + return { + path: outputPath, + relPath + } + } + async createAuthor(payload) { if (!payload || !payload.name) return null diff --git a/server/objects/entities/Author.js b/server/objects/entities/Author.js index 5f60d88f..4c1d4358 100644 --- a/server/objects/entities/Author.js +++ b/server/objects/entities/Author.js @@ -5,6 +5,7 @@ class Author { this.id = null this.asin = null this.name = null + this.description = null this.imagePath = null this.relImagePath = null this.addedAt = null @@ -19,6 +20,7 @@ class Author { this.id = author.id this.asin = author.asin this.name = author.name + this.description = author.description || null this.imagePath = author.imagePath this.relImagePath = author.relImagePath this.addedAt = author.addedAt @@ -30,6 +32,7 @@ class Author { id: this.id, asin: this.asin, name: this.name, + description: this.description, imagePath: this.imagePath, relImagePath: this.relImagePath, addedAt: this.addedAt, @@ -47,6 +50,7 @@ class Author { setData(data) { this.id = getId('aut') this.name = data.name + this.description = data.description || null this.asin = data.asin || null this.imagePath = data.imagePath || null this.relImagePath = data.relImagePath || null diff --git a/server/objects/entities/Series.js b/server/objects/entities/Series.js index cf4450ce..18f3b4a3 100644 --- a/server/objects/entities/Series.js +++ b/server/objects/entities/Series.js @@ -4,6 +4,7 @@ class Series { constructor(series) { this.id = null this.name = null + this.description = null this.addedAt = null this.updatedAt = null @@ -15,6 +16,7 @@ class Series { construct(series) { this.id = series.id this.name = series.name + this.description = series.description || null this.addedAt = series.addedAt this.updatedAt = series.updatedAt } @@ -23,6 +25,7 @@ class Series { return { id: this.id, name: this.name, + description: this.description, addedAt: this.addedAt, updatedAt: this.updatedAt } @@ -39,6 +42,7 @@ class Series { setData(data) { this.id = getId('ser') this.name = data.name + this.description = data.description || null this.addedAt = Date.now() this.updatedAt = Date.now() }