diff --git a/client/components/modals/collections/AddCreateModal.vue b/client/components/modals/collections/AddCreateModal.vue index 7c22525f..94a7f0e7 100644 --- a/client/components/modals/collections/AddCreateModal.vue +++ b/client/components/modals/collections/AddCreateModal.vue @@ -138,7 +138,6 @@ export default { .$post(`/api/collections/${collection.id}/batch/remove`, { books: this.selectedBookIds }) .then((updatedCollection) => { console.log(`Books removed from collection`, updatedCollection) - this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess) this.processing = false }) .catch((error) => { @@ -152,7 +151,6 @@ export default { .$delete(`/api/collections/${collection.id}/book/${this.selectedLibraryItemId}`) .then((updatedCollection) => { console.log(`Book removed from collection`, updatedCollection) - this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess) this.processing = false }) .catch((error) => { @@ -167,12 +165,11 @@ export default { this.processing = true if (this.showBatchCollectionModal) { - // BATCH Remove books + // BATCH Add books this.$axios .$post(`/api/collections/${collection.id}/batch/add`, { books: this.selectedBookIds }) .then((updatedCollection) => { console.log(`Books added to collection`, updatedCollection) - this.$toast.success(this.$strings.ToastCollectionItemsAddSuccess) this.processing = false }) .catch((error) => { @@ -187,7 +184,6 @@ export default { .$post(`/api/collections/${collection.id}/book`, { id: this.selectedLibraryItemId }) .then((updatedCollection) => { console.log(`Book added to collection`, updatedCollection) - this.$toast.success(this.$strings.ToastCollectionItemsAddSuccess) this.processing = false }) .catch((error) => { @@ -214,7 +210,6 @@ export default { .$post('/api/collections', newCollection) .then((data) => { console.log('New Collection Created', data) - this.$toast.success(`Collection "${data.name}" created`) this.processing = false this.newCollectionName = '' }) diff --git a/client/strings/bg.json b/client/strings/bg.json index 8e124d06..bc4db4f6 100644 --- a/client/strings/bg.json +++ b/client/strings/bg.json @@ -729,7 +729,6 @@ "ToastBookmarkUpdateSuccess": "Отметката е обновена", "ToastChaptersHaveErrors": "Главите имат грешки", "ToastChaptersMustHaveTitles": "Главите трябва да имат заглавия", - "ToastCollectionItemsRemoveSuccess": "Елемент(и) премахнати от колекция", "ToastCollectionRemoveSuccess": "Колекцията е премахната", "ToastCollectionUpdateSuccess": "Колекцията е обновена", "ToastItemCoverUpdateSuccess": "Корицата на елемента е обновена", diff --git a/client/strings/bn.json b/client/strings/bn.json index 16f8a447..e88db2e5 100644 --- a/client/strings/bn.json +++ b/client/strings/bn.json @@ -951,8 +951,6 @@ "ToastChaptersRemoved": "অধ্যায়গুলো মুছে ফেলা হয়েছে", "ToastChaptersUpdated": "অধ্যায় আপডেট করা হয়েছে", "ToastCollectionItemsAddFailed": "আইটেম(গুলি) সংগ্রহে যোগ করা ব্যর্থ হয়েছে", - "ToastCollectionItemsAddSuccess": "আইটেম(গুলি) সংগ্রহে যোগ করা সফল হয়েছে", - "ToastCollectionItemsRemoveSuccess": "আইটেম(গুলি) সংগ্রহ থেকে সরানো হয়েছে", "ToastCollectionRemoveSuccess": "সংগ্রহ সরানো হয়েছে", "ToastCollectionUpdateSuccess": "সংগ্রহ আপডেট করা হয়েছে", "ToastCoverUpdateFailed": "কভার আপডেট ব্যর্থ হয়েছে", diff --git a/client/strings/ca.json b/client/strings/ca.json index f7e85ae2..bebb17e9 100644 --- a/client/strings/ca.json +++ b/client/strings/ca.json @@ -904,8 +904,6 @@ "ToastChaptersRemoved": "Capítols eliminats", "ToastChaptersUpdated": "Capítols actualitzats", "ToastCollectionItemsAddFailed": "Error en afegir elements a la col·lecció", - "ToastCollectionItemsAddSuccess": "Elements afegits a la col·lecció", - "ToastCollectionItemsRemoveSuccess": "Elements eliminats de la col·lecció", "ToastCollectionRemoveSuccess": "Col·lecció eliminada", "ToastCollectionUpdateSuccess": "Col·lecció actualitzada", "ToastCoverUpdateFailed": "Error en actualitzar la portada", diff --git a/client/strings/cs.json b/client/strings/cs.json index 684a25e1..3db3ddaa 100644 --- a/client/strings/cs.json +++ b/client/strings/cs.json @@ -943,7 +943,6 @@ "ToastChaptersHaveErrors": "Kapitoly obsahují chyby", "ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy", "ToastChaptersRemoved": "Kapitoly odstraněny", - "ToastCollectionItemsRemoveSuccess": "Položky odstraněny z kolekce", "ToastCollectionRemoveSuccess": "Kolekce odstraněna", "ToastCollectionUpdateSuccess": "Kolekce aktualizována", "ToastCoverUpdateFailed": "Aktualizace obálky selhala", diff --git a/client/strings/da.json b/client/strings/da.json index 0f7af2c4..0f7b1eed 100644 --- a/client/strings/da.json +++ b/client/strings/da.json @@ -640,7 +640,6 @@ "ToastBookmarkUpdateSuccess": "Bogmærke opdateret", "ToastChaptersHaveErrors": "Kapitler har fejl", "ToastChaptersMustHaveTitles": "Kapitler skal have titler", - "ToastCollectionItemsRemoveSuccess": "Element(er) fjernet fra samlingen", "ToastCollectionRemoveSuccess": "Samling fjernet", "ToastCollectionUpdateSuccess": "Samling opdateret", "ToastItemCoverUpdateSuccess": "Varens omslag opdateret", diff --git a/client/strings/de.json b/client/strings/de.json index db298c75..6ac20fab 100644 --- a/client/strings/de.json +++ b/client/strings/de.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "Kapitel entfernt", "ToastChaptersUpdated": "Kapitel aktualisiert", "ToastCollectionItemsAddFailed": "Das Hinzufügen von Element(en) zur Sammlung ist fehlgeschlagen", - "ToastCollectionItemsAddSuccess": "Element(e) erfolgreich zur Sammlung hinzugefügt", - "ToastCollectionItemsRemoveSuccess": "Medien aus der Sammlung entfernt", "ToastCollectionRemoveSuccess": "Sammlung entfernt", "ToastCollectionUpdateSuccess": "Sammlung aktualisiert", "ToastCoverUpdateFailed": "Cover-Update fehlgeschlagen", diff --git a/client/strings/en-us.json b/client/strings/en-us.json index eee94abf..e94e5197 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -961,8 +961,6 @@ "ToastChaptersRemoved": "Chapters removed", "ToastChaptersUpdated": "Chapters updated", "ToastCollectionItemsAddFailed": "Item(s) added to collection failed", - "ToastCollectionItemsAddSuccess": "Item(s) added to collection success", - "ToastCollectionItemsRemoveSuccess": "Item(s) removed from collection", "ToastCollectionRemoveSuccess": "Collection removed", "ToastCollectionUpdateSuccess": "Collection updated", "ToastCoverUpdateFailed": "Cover update failed", diff --git a/client/strings/es.json b/client/strings/es.json index fa4f8c45..4196b6dd 100644 --- a/client/strings/es.json +++ b/client/strings/es.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "Capítulos eliminados", "ToastChaptersUpdated": "Capítulos actualizados", "ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)", - "ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente", - "ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección", "ToastCollectionRemoveSuccess": "Colección removida", "ToastCollectionUpdateSuccess": "Colección actualizada", "ToastCoverUpdateFailed": "Error al actualizar la cubierta", diff --git a/client/strings/et.json b/client/strings/et.json index 8d256c01..835666f8 100644 --- a/client/strings/et.json +++ b/client/strings/et.json @@ -713,7 +713,6 @@ "ToastBookmarkUpdateSuccess": "Järjehoidja värskendatud", "ToastChaptersHaveErrors": "Peatükkidel on vigu", "ToastChaptersMustHaveTitles": "Peatükkidel peab olema pealkiri", - "ToastCollectionItemsRemoveSuccess": "Üksus(ed) eemaldatud kogumist", "ToastCollectionRemoveSuccess": "Kogum eemaldatud", "ToastCollectionUpdateSuccess": "Kogum värskendatud", "ToastItemCoverUpdateSuccess": "Üksuse kaas värskendatud", diff --git a/client/strings/fr.json b/client/strings/fr.json index 2c92bf7c..2dfe5b0b 100644 --- a/client/strings/fr.json +++ b/client/strings/fr.json @@ -953,8 +953,6 @@ "ToastChaptersRemoved": "Chapitres supprimés", "ToastChaptersUpdated": "Chapitres mis à jour", "ToastCollectionItemsAddFailed": "Échec de l’ajout de(s) élément(s) à la collection", - "ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi", - "ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection", "ToastCollectionRemoveSuccess": "Collection supprimée", "ToastCollectionUpdateSuccess": "Collection mise à jour", "ToastCoverUpdateFailed": "Échec de la mise à jour de la couverture", diff --git a/client/strings/he.json b/client/strings/he.json index a93d3f05..71a6df9e 100644 --- a/client/strings/he.json +++ b/client/strings/he.json @@ -744,7 +744,6 @@ "ToastBookmarkUpdateSuccess": "הסימניה עודכנה בהצלחה", "ToastChaptersHaveErrors": "פרקים מכילים שגיאות", "ToastChaptersMustHaveTitles": "פרקים חייבים לכלול כותרות", - "ToastCollectionItemsRemoveSuccess": "הפריט(ים) הוסרו מהאוסף בהצלחה", "ToastCollectionRemoveSuccess": "האוסף הוסר בהצלחה", "ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה", "ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה", diff --git a/client/strings/hr.json b/client/strings/hr.json index bc66b8a5..8b824128 100644 --- a/client/strings/hr.json +++ b/client/strings/hr.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "Poglavlja uklonjena", "ToastChaptersUpdated": "Poglavlja su ažurirana", "ToastCollectionItemsAddFailed": "Neuspješno dodavanje stavki u zbirku", - "ToastCollectionItemsAddSuccess": "Uspješno dodavanje stavki u zbirku", - "ToastCollectionItemsRemoveSuccess": "Stavke izbrisane iz zbirke", "ToastCollectionRemoveSuccess": "Zbirka izbrisana", "ToastCollectionUpdateSuccess": "Zbirka ažurirana", "ToastCoverUpdateFailed": "Ažuriranje naslovnice nije uspjelo", diff --git a/client/strings/hu.json b/client/strings/hu.json index 10807375..25f9a9be 100644 --- a/client/strings/hu.json +++ b/client/strings/hu.json @@ -945,7 +945,6 @@ "ToastChaptersMustHaveTitles": "A fejezeteknek címekkel kell rendelkezniük", "ToastChaptersRemoved": "Fejezetek eltávolítva", "ToastChaptersUpdated": "Fejezetek frissítve", - "ToastCollectionItemsRemoveSuccess": "Elem(ek) eltávolítva a gyűjteményből", "ToastCollectionRemoveSuccess": "Gyűjtemény eltávolítva", "ToastCollectionUpdateSuccess": "Gyűjtemény frissítve", "ToastCoverUpdateFailed": "A borító frissítése nem sikerült", diff --git a/client/strings/it.json b/client/strings/it.json index 70490e3b..633dea89 100644 --- a/client/strings/it.json +++ b/client/strings/it.json @@ -950,8 +950,6 @@ "ToastChaptersRemoved": "Capitoli rimossi", "ToastChaptersUpdated": "Capitoli aggiornati", "ToastCollectionItemsAddFailed": "l'aggiunta dell'elemento(i) alla raccolta non è riuscito", - "ToastCollectionItemsAddSuccess": "L'aggiunta dell'elemento(i) alla raccolta è riuscito", - "ToastCollectionItemsRemoveSuccess": "Oggetto(i) rimossi dalla Raccolta", "ToastCollectionRemoveSuccess": "Collezione rimossa", "ToastCollectionUpdateSuccess": "Raccolta aggiornata", "ToastCoverUpdateFailed": "Aggiornamento cover fallito", diff --git a/client/strings/lt.json b/client/strings/lt.json index e1a63510..8c902f5a 100644 --- a/client/strings/lt.json +++ b/client/strings/lt.json @@ -666,8 +666,6 @@ "ToastChaptersMustHaveTitles": "Skyriai turi turėti pavadinimus", "ToastChaptersRemoved": "Skyriai pašalinti", "ToastCollectionItemsAddFailed": "Nepavyko pridėti į kolekciją", - "ToastCollectionItemsAddSuccess": "Pridėta į kolekciją", - "ToastCollectionItemsRemoveSuccess": "Elementai pašalinti iš kolekcijos", "ToastCollectionRemoveSuccess": "Kolekcija pašalinta", "ToastCollectionUpdateSuccess": "Kolekcija atnaujinta", "ToastCoverUpdateFailed": "Viršelio atnaujinimas nepavyko", diff --git a/client/strings/nl.json b/client/strings/nl.json index bc5a40ca..97bb1d01 100644 --- a/client/strings/nl.json +++ b/client/strings/nl.json @@ -946,8 +946,6 @@ "ToastChaptersRemoved": "Hoofdstukken verwijderd", "ToastChaptersUpdated": "Hoofdstukken bijgewerkt", "ToastCollectionItemsAddFailed": "Item(s) toegevoegd aan collectie mislukt", - "ToastCollectionItemsAddSuccess": "Item(s) toegevoegd aan collectie gelukt", - "ToastCollectionItemsRemoveSuccess": "Onderdeel (of onderdelen) verwijderd uit collectie", "ToastCollectionRemoveSuccess": "Collectie verwijderd", "ToastCollectionUpdateSuccess": "Collectie bijgewerkt", "ToastCoverUpdateFailed": "Cover update mislukt", diff --git a/client/strings/no.json b/client/strings/no.json index ddfc2489..553a852e 100644 --- a/client/strings/no.json +++ b/client/strings/no.json @@ -882,8 +882,6 @@ "ToastChaptersRemoved": "Kapitler fjernet", "ToastChaptersUpdated": "Kapitler oppdatert", "ToastCollectionItemsAddFailed": "Feil med å legge til element(er)", - "ToastCollectionItemsAddSuccess": "Element(er) lagt til samlingen", - "ToastCollectionItemsRemoveSuccess": "Gjenstand(er) fjernet fra samling", "ToastCollectionRemoveSuccess": "Samling fjernet", "ToastCollectionUpdateSuccess": "samlingupdated", "ToastCoverUpdateFailed": "Oppdatering av bilde feilet", diff --git a/client/strings/pl.json b/client/strings/pl.json index 85c7b769..d8cf9848 100644 --- a/client/strings/pl.json +++ b/client/strings/pl.json @@ -772,7 +772,6 @@ "ToastBookmarkCreateSuccess": "Dodano zakładkę", "ToastBookmarkRemoveSuccess": "Zakładka została usunięta", "ToastBookmarkUpdateSuccess": "Zaktualizowano zakładkę", - "ToastCollectionItemsRemoveSuccess": "Przedmiot(y) zostały usunięte z kolekcji", "ToastCollectionRemoveSuccess": "Kolekcja usunięta", "ToastCollectionUpdateSuccess": "Zaktualizowano kolekcję", "ToastItemCoverUpdateSuccess": "Zaktualizowano okładkę", diff --git a/client/strings/pt-br.json b/client/strings/pt-br.json index 7df7c47d..b6d7e7e3 100644 --- a/client/strings/pt-br.json +++ b/client/strings/pt-br.json @@ -735,7 +735,6 @@ "ToastCachePurgeSuccess": "Cache apagado com sucesso", "ToastChaptersHaveErrors": "Capítulos com erro", "ToastChaptersMustHaveTitles": "Capítulos precisam ter títulos", - "ToastCollectionItemsRemoveSuccess": "Item(ns) removidos da coleção", "ToastCollectionRemoveSuccess": "Coleção removida", "ToastCollectionUpdateSuccess": "Coleção atualizada", "ToastDeleteFileFailed": "Falha ao apagar arquivo", diff --git a/client/strings/ru.json b/client/strings/ru.json index 3e07ea02..41129bde 100644 --- a/client/strings/ru.json +++ b/client/strings/ru.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "Удалены главы", "ToastChaptersUpdated": "Обновленные главы", "ToastCollectionItemsAddFailed": "Не удалось добавить элемент(ы) в коллекцию", - "ToastCollectionItemsAddSuccess": "Элемент(ы) добавлены в коллекцию", - "ToastCollectionItemsRemoveSuccess": "Элемент(ы), удалены из коллекции", "ToastCollectionRemoveSuccess": "Коллекция удалена", "ToastCollectionUpdateSuccess": "Коллекция обновлена", "ToastCoverUpdateFailed": "Не удалось обновить обложку", diff --git a/client/strings/sl.json b/client/strings/sl.json index a12643f4..3c304eaa 100644 --- a/client/strings/sl.json +++ b/client/strings/sl.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "Poglavja so odstranjena", "ToastChaptersUpdated": "Poglavja so posodobljena", "ToastCollectionItemsAddFailed": "Dodajanje elementov v zbirko ni uspelo", - "ToastCollectionItemsAddSuccess": "Dodajanje elementov v zbirko je bilo uspešno", - "ToastCollectionItemsRemoveSuccess": "Elementi so bili odstranjeni iz zbirke", "ToastCollectionRemoveSuccess": "Zbirka je bila odstranjena", "ToastCollectionUpdateSuccess": "Zbirka je bila posodobljena", "ToastCoverUpdateFailed": "Posodobitev naslovnice ni uspela", diff --git a/client/strings/sv.json b/client/strings/sv.json index 7513e8db..8e60e2cd 100644 --- a/client/strings/sv.json +++ b/client/strings/sv.json @@ -705,7 +705,6 @@ "ToastBookmarkUpdateSuccess": "Bokmärket har uppdaterats", "ToastChaptersHaveErrors": "Kapitlen har fel", "ToastChaptersMustHaveTitles": "Kapitel måste ha titlar", - "ToastCollectionItemsRemoveSuccess": "Objekt borttagna från samlingen", "ToastCollectionRemoveSuccess": "Samlingen har raderats", "ToastCollectionUpdateSuccess": "Samlingen har uppdaterats", "ToastItemCoverUpdateSuccess": "Objektets omslag uppdaterat", diff --git a/client/strings/uk.json b/client/strings/uk.json index 3d0150f5..4b745264 100644 --- a/client/strings/uk.json +++ b/client/strings/uk.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "Розділи видалені", "ToastChaptersUpdated": "Розділи оновлені", "ToastCollectionItemsAddFailed": "Не вдалося додати елемент(и) до колекції", - "ToastCollectionItemsAddSuccess": "Елемент(и) успішно додано до колекції", - "ToastCollectionItemsRemoveSuccess": "Елемент(и) видалено з добірки", "ToastCollectionRemoveSuccess": "Добірку видалено", "ToastCollectionUpdateSuccess": "Добірку оновлено", "ToastCoverUpdateFailed": "Не вдалося оновити обкладинку", diff --git a/client/strings/vi-vn.json b/client/strings/vi-vn.json index 8b0ca165..a176dba3 100644 --- a/client/strings/vi-vn.json +++ b/client/strings/vi-vn.json @@ -683,7 +683,6 @@ "ToastBookmarkUpdateSuccess": "Đánh dấu đã được cập nhật", "ToastChaptersHaveErrors": "Các chương có lỗi", "ToastChaptersMustHaveTitles": "Các chương phải có tiêu đề", - "ToastCollectionItemsRemoveSuccess": "Mục đã được xóa khỏi bộ sưu tập", "ToastCollectionRemoveSuccess": "Bộ sưu tập đã được xóa", "ToastCollectionUpdateSuccess": "Bộ sưu tập đã được cập nhật", "ToastItemCoverUpdateSuccess": "Ảnh bìa mục đã được cập nhật", diff --git a/client/strings/zh-cn.json b/client/strings/zh-cn.json index 3a8b432f..472ef84e 100644 --- a/client/strings/zh-cn.json +++ b/client/strings/zh-cn.json @@ -959,8 +959,6 @@ "ToastChaptersRemoved": "已删除章节", "ToastChaptersUpdated": "章节已更新", "ToastCollectionItemsAddFailed": "项目添加到收藏夹失败", - "ToastCollectionItemsAddSuccess": "项目添加到收藏夹成功", - "ToastCollectionItemsRemoveSuccess": "项目从收藏夹移除", "ToastCollectionRemoveSuccess": "收藏夹已删除", "ToastCollectionUpdateSuccess": "收藏夹已更新", "ToastCoverUpdateFailed": "封面更新失败", diff --git a/client/strings/zh-tw.json b/client/strings/zh-tw.json index 080d1bac..96f878c2 100644 --- a/client/strings/zh-tw.json +++ b/client/strings/zh-tw.json @@ -727,7 +727,6 @@ "ToastBookmarkUpdateSuccess": "書籤已更新", "ToastChaptersHaveErrors": "章節有錯誤", "ToastChaptersMustHaveTitles": "章節必須有標題", - "ToastCollectionItemsRemoveSuccess": "項目從收藏夾移除", "ToastCollectionRemoveSuccess": "收藏夾已刪除", "ToastCollectionUpdateSuccess": "收藏夾已更新", "ToastItemCoverUpdateSuccess": "項目封面已更新", diff --git a/server/Database.js b/server/Database.js index afb09dae..070c89d7 100644 --- a/server/Database.js +++ b/server/Database.js @@ -406,11 +406,6 @@ class Database { return Promise.all(oldBooks.map((oldBook) => this.models.book.saveFromOld(oldBook))) } - createBulkCollectionBooks(collectionBooks) { - if (!this.sequelize) return false - return this.models.collectionBook.bulkCreate(collectionBooks) - } - createPlaylistMediaItem(playlistMediaItem) { if (!this.sequelize) return false return this.models.playlistMediaItem.create(playlistMediaItem) diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 3e35c08b..23f8796f 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -5,13 +5,17 @@ const SocketAuthority = require('../SocketAuthority') const Database = require('../Database') const RssFeedManager = require('../managers/RssFeedManager') -const Collection = require('../objects/Collection') /** * @typedef RequestUserObject * @property {import('../models/User')} user * * @typedef {Request & RequestUserObject} RequestWithUser + * + * @typedef RequestEntityObject + * @property {import('../models/Collection')} collection + * + * @typedef {RequestWithUser & RequestEntityObject} CollectionControllerRequest */ class CollectionController { @@ -25,36 +29,68 @@ class CollectionController { * @param {Response} res */ async create(req, res) { - const newCollection = new Collection() - req.body.userId = req.user.id - if (!newCollection.setData(req.body)) { + const reqBody = req.body || {} + + // Validation + if (!reqBody.name || !reqBody.libraryId) { return res.status(400).send('Invalid collection data') } + const libraryItemIds = (reqBody.books || []).filter((b) => !!b && typeof b == 'string') + if (!libraryItemIds.length) { + return res.status(400).send('Invalid collection data. No books') + } - // Create collection record - await Database.collectionModel.createFromOld(newCollection) - - // Get library items in collection - const libraryItemsInCollection = await Database.libraryItemModel.getForCollection(newCollection) - - // Create collectionBook records - let order = 1 - const collectionBooksToAdd = [] - for (const libraryItemId of newCollection.books) { - const libraryItem = libraryItemsInCollection.find((li) => li.id === libraryItemId) - if (libraryItem) { - collectionBooksToAdd.push({ - collectionId: newCollection.id, - bookId: libraryItem.media.id, - order: order++ - }) + // Load library items + const libraryItems = await Database.libraryItemModel.findAll({ + attributes: ['id', 'mediaId', 'mediaType', 'libraryId'], + where: { + id: libraryItemIds, + libraryId: reqBody.libraryId, + mediaType: 'book' } - } - if (collectionBooksToAdd.length) { - await Database.createBulkCollectionBooks(collectionBooksToAdd) + }) + if (libraryItems.length !== libraryItemIds.length) { + return res.status(400).send('Invalid collection data. Invalid books') } - const jsonExpanded = newCollection.toJSONExpanded(libraryItemsInCollection) + /** @type {import('../models/Collection')} */ + let newCollection = null + + const transaction = await Database.sequelize.transaction() + try { + // Create collection + newCollection = await Database.collectionModel.create( + { + libraryId: reqBody.libraryId, + name: reqBody.name, + description: reqBody.description || null + }, + { transaction } + ) + + // Create collectionBooks + const collectionBookPayloads = libraryItemIds.map((llid, index) => { + const libraryItem = libraryItems.find((li) => li.id === llid) + return { + collectionId: newCollection.id, + bookId: libraryItem.mediaId, + order: index + 1 + } + }) + await Database.collectionBookModel.bulkCreate(collectionBookPayloads, { transaction }) + + await transaction.commit() + } catch (error) { + await transaction.rollback() + Logger.error('[CollectionController] create:', error) + return res.status(500).send('Failed to create collection') + } + + // Load books expanded + newCollection.books = await newCollection.getBooksExpandedWithLibraryItem() + + // Note: The old collection model stores expanded libraryItems in the books property + const jsonExpanded = newCollection.toOldJSONExpanded() SocketAuthority.emitter('collection_added', jsonExpanded) res.json(jsonExpanded) } @@ -75,7 +111,7 @@ class CollectionController { /** * GET: /api/collections/:id * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async findOne(req, res) { @@ -94,7 +130,7 @@ class CollectionController { * PATCH: /api/collections/:id * Update collection * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async update(req, res) { @@ -158,7 +194,7 @@ class CollectionController { * * @this {import('../routers/ApiRouter')} * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async delete(req, res) { @@ -178,7 +214,7 @@ class CollectionController { * Add a single book to a collection * Req.body { id: } * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async addBook(req, res) { @@ -212,7 +248,7 @@ class CollectionController { * Remove a single book from a collection. Re-order books * TODO: bookId is actually libraryItemId. Clients need updating to use bookId * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async removeBook(req, res) { @@ -257,29 +293,31 @@ class CollectionController { * Add multiple books to collection * Req.body { books: } * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async addBatch(req, res) { // filter out invalid libraryItemIds const bookIdsToAdd = (req.body.books || []).filter((b) => !!b && typeof b == 'string') if (!bookIdsToAdd.length) { - return res.status(500).send('Invalid request body') + return res.status(400).send('Invalid request body') } // Get library items associated with ids const libraryItems = await Database.libraryItemModel.findAll({ + attributes: ['id', 'mediaId', 'mediaType', 'libraryId'], where: { - id: { - [Sequelize.Op.in]: bookIdsToAdd - } - }, - include: { - model: Database.bookModel + id: bookIdsToAdd, + libraryId: req.collection.libraryId, + mediaType: 'book' } }) + if (!libraryItems.length) { + return res.status(400).send('Invalid request body. No valid books') + } // Get collection books already in collection + /** @type {import('../models/CollectionBook')[]} */ const collectionBooks = await req.collection.getCollectionBooks() let order = collectionBooks.length + 1 @@ -288,10 +326,10 @@ 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.mediaId)) { collectionBooksToAdd.push({ collectionId: req.collection.id, - bookId: libraryItem.media.id, + bookId: libraryItem.mediaId, order: order++ }) hasUpdated = true @@ -302,7 +340,8 @@ class CollectionController { let jsonExpanded = null if (hasUpdated) { - await Database.createBulkCollectionBooks(collectionBooksToAdd) + await Database.collectionBookModel.bulkCreate(collectionBooksToAdd) + jsonExpanded = await req.collection.getOldJsonExpanded() SocketAuthority.emitter('collection_updated', jsonExpanded) } else { @@ -316,7 +355,7 @@ class CollectionController { * Remove multiple books from collection * Req.body { books: } * - * @param {RequestWithUser} req + * @param {CollectionControllerRequest} req * @param {Response} res */ async removeBatch(req, res) { @@ -329,9 +368,7 @@ class CollectionController { // Get library items associated with ids const libraryItems = await Database.libraryItemModel.findAll({ where: { - id: { - [Sequelize.Op.in]: bookIdsToRemove - } + id: bookIdsToRemove }, include: { model: Database.bookModel @@ -339,6 +376,7 @@ class CollectionController { }) // Get collection books already in collection + /** @type {import('../models/CollectionBook')[]} */ const collectionBooks = await req.collection.getCollectionBooks({ order: [['order', 'ASC']] }) diff --git a/server/models/Collection.js b/server/models/Collection.js index e01ad90a..c8f62e69 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -1,7 +1,5 @@ const { DataTypes, Model, Sequelize } = require('sequelize') -const oldCollection = require('../objects/Collection') - class Collection extends Model { constructor(values, options) { super(values, options) @@ -26,12 +24,12 @@ class Collection extends Model { } /** - * Get all old collections toJSONExpanded, items filtered for user permissions + * Get all toOldJSONExpanded, items filtered for user permissions * * @param {import('./User')} user * @param {string} [libraryId] * @param {string[]} [include] - * @returns {Promise} oldCollection.toJSONExpanded + * @async */ static async getOldCollectionsJsonExpanded(user, libraryId, include) { let collectionWhere = null @@ -79,8 +77,6 @@ class Collection extends Model { // TODO: Handle user permission restrictions on initial query return collections .map((c) => { - const oldCollection = this.getOldCollection(c) - // Filter books using user permissions const books = c.books?.filter((b) => { @@ -95,20 +91,14 @@ class Collection extends Model { return true }) || [] - // Map to library items - const libraryItems = books.map((b) => { - const libraryItem = b.libraryItem - delete b.libraryItem - libraryItem.media = b - return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem) - }) - // Users with restricted permissions will not see this collection - if (!books.length && oldCollection.books.length) { + if (!books.length && c.books.length) { return null } - const collectionExpanded = oldCollection.toJSONExpanded(libraryItems) + this.books = books + + const collectionExpanded = c.toOldJSONExpanded() // Map feed if found if (c.feeds?.length) { @@ -153,69 +143,6 @@ class Collection extends Model { }) } - /** - * Get old collection from Collection - * @param {Collection} collectionExpanded - * @returns {oldCollection} - */ - static getOldCollection(collectionExpanded) { - const libraryItemIds = collectionExpanded.books?.map((b) => b.libraryItem?.id || null).filter((lid) => lid) || [] - return new oldCollection({ - id: collectionExpanded.id, - libraryId: collectionExpanded.libraryId, - name: collectionExpanded.name, - description: collectionExpanded.description, - books: libraryItemIds, - lastUpdate: collectionExpanded.updatedAt.valueOf(), - createdAt: collectionExpanded.createdAt.valueOf() - }) - } - - /** - * - * @param {oldCollection} oldCollection - * @returns {Promise} - */ - static createFromOld(oldCollection) { - const collection = this.getFromOld(oldCollection) - return this.create(collection) - } - - static getFromOld(oldCollection) { - return { - id: oldCollection.id, - name: oldCollection.name, - description: oldCollection.description, - libraryId: oldCollection.libraryId - } - } - - static removeById(collectionId) { - return this.destroy({ - where: { - id: collectionId - } - }) - } - - /** - * Get old collection by id - * @param {string} collectionId - * @returns {Promise} returns null if not found - */ - static async getOldById(collectionId) { - if (!collectionId) return null - const collection = await this.findByPk(collectionId, { - include: { - model: this.sequelize.models.book, - include: this.sequelize.models.libraryItem - }, - order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']] - }) - if (!collection) return null - return this.getOldCollection(collection) - } - /** * Remove all collections belonging to library * @param {string} libraryId @@ -286,64 +213,37 @@ class Collection extends Model { } /** - * Get old collection toJSONExpanded, items filtered for user permissions + * Get toOldJSONExpanded, items filtered for user permissions * * @param {import('./User')|null} user * @param {string[]} [include] - * @returns {Promise} oldCollection.toJSONExpanded + * @async */ async getOldJsonExpanded(user, include) { - this.books = - (await this.getBooks({ - include: [ - { - model: this.sequelize.models.libraryItem - }, - { - model: this.sequelize.models.author, - through: { - attributes: [] - } - }, - { - model: this.sequelize.models.series, - through: { - attributes: ['sequence'] - } - } - ], - order: [Sequelize.literal('`collectionBook.order` ASC')] - })) || [] + this.books = await this.getBooksExpandedWithLibraryItem() // Filter books using user permissions // TODO: Handle user permission restrictions on initial query - const books = - this.books?.filter((b) => { - if (user) { - if (b.tags?.length && !user.checkCanAccessLibraryItemWithTags(b.tags)) { - return false - } - if (b.explicit === true && !user.canAccessExplicitContent) { - return false - } + if (user) { + const books = this.books.filter((b) => { + if (b.tags?.length && !user.checkCanAccessLibraryItemWithTags(b.tags)) { + return false + } + if (b.explicit === true && !user.canAccessExplicitContent) { + return false } return true - }) || [] + }) - // Map to library items - const libraryItems = books.map((b) => { - const libraryItem = b.libraryItem - delete b.libraryItem - libraryItem.media = b - return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem) - }) + // Users with restricted permissions will not see this collection + if (!books.length && this.books.length) { + return null + } - // Users with restricted permissions will not see this collection - if (!books.length && this.books.length) { - return null + this.books = books } - const collectionExpanded = this.toOldJSONExpanded(libraryItems) + const collectionExpanded = this.toOldJSONExpanded() if (include?.includes('rssfeed')) { const feeds = await this.getFeeds() @@ -357,10 +257,10 @@ class Collection extends Model { /** * - * @param {string[]} libraryItemIds + * @param {string[]} [libraryItemIds=[]] * @returns */ - toOldJSON(libraryItemIds) { + toOldJSON(libraryItemIds = []) { return { id: this.id, libraryId: this.libraryId, @@ -372,19 +272,19 @@ class Collection extends Model { } } - /** - * - * @param {import('../objects/LibraryItem')} oldLibraryItems - * @returns - */ - toOldJSONExpanded(oldLibraryItems) { - const json = this.toOldJSON(oldLibraryItems.map((li) => li.id)) - json.books = json.books - .map((libraryItemId) => { - const book = oldLibraryItems.find((li) => li.id === libraryItemId) - return book ? book.toJSONExpanded() : null - }) - .filter((b) => !!b) + toOldJSONExpanded() { + if (!this.books) { + throw new Error('Books are required to expand Collection') + } + + const json = this.toOldJSON() + json.books = this.books.map((book) => { + const libraryItem = book.libraryItem + delete book.libraryItem + libraryItem.media = book + return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem).toJSONExpanded() + }) + return json } } diff --git a/server/models/CollectionBook.js b/server/models/CollectionBook.js index e04da3b2..e706d68c 100644 --- a/server/models/CollectionBook.js +++ b/server/models/CollectionBook.js @@ -16,15 +16,6 @@ class CollectionBook extends Model { this.createdAt } - static removeByIds(collectionId, bookId) { - return this.destroy({ - where: { - bookId, - collectionId - } - }) - } - static init(sequelize) { super.init( { diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index 8ebed1d5..bed96631 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -123,7 +123,7 @@ class LibraryItem extends Model { } /** - * Currently unused because this is too slow and uses too much mem + * * @param {import('sequelize').WhereOptions} [where] * @returns {Array} old library items */ diff --git a/server/objects/Collection.js b/server/objects/Collection.js deleted file mode 100644 index 970d714b..00000000 --- a/server/objects/Collection.js +++ /dev/null @@ -1,115 +0,0 @@ -const uuidv4 = require("uuid").v4 - -class Collection { - constructor(collection) { - this.id = null - this.libraryId = null - - this.name = null - this.description = null - - this.cover = null - this.coverFullPath = null - this.books = [] - - this.lastUpdate = null - this.createdAt = null - - if (collection) { - this.construct(collection) - } - } - - toJSON() { - return { - id: this.id, - libraryId: this.libraryId, - name: this.name, - description: this.description, - cover: this.cover, - coverFullPath: this.coverFullPath, - books: [...this.books], - lastUpdate: this.lastUpdate, - createdAt: this.createdAt - } - } - - toJSONExpanded(libraryItems, minifiedBooks = false) { - const json = this.toJSON() - json.books = json.books.map(bookId => { - const book = libraryItems.find(li => li.id === bookId) - return book ? minifiedBooks ? book.toJSONMinified() : book.toJSONExpanded() : null - }).filter(b => !!b) - return json - } - - // Expanded and filtered out items not accessible to user - toJSONExpandedForUser(user, libraryItems) { - const json = this.toJSON() - json.books = json.books.map(libraryItemId => { - const libraryItem = libraryItems.find(li => li.id === libraryItemId) - return libraryItem ? libraryItem.toJSONExpanded() : null - }).filter(li => { - return li && user.checkCanAccessLibraryItem(li) - }) - return json - } - - construct(collection) { - this.id = collection.id - this.libraryId = collection.libraryId - this.name = collection.name - this.description = collection.description || null - this.cover = collection.cover || null - this.coverFullPath = collection.coverFullPath || null - this.books = collection.books ? [...collection.books] : [] - this.lastUpdate = collection.lastUpdate || null - this.createdAt = collection.createdAt || null - } - - setData(data) { - if (!data.libraryId || !data.name) { - return false - } - this.id = uuidv4() - this.libraryId = data.libraryId - this.name = data.name - this.description = data.description || null - this.cover = data.cover || null - this.coverFullPath = data.coverFullPath || null - this.books = data.books ? [...data.books] : [] - this.lastUpdate = Date.now() - this.createdAt = Date.now() - return true - } - - addBook(bookId) { - this.books.push(bookId) - this.lastUpdate = Date.now() - } - - removeBook(bookId) { - this.books = this.books.filter(bid => bid !== bookId) - this.lastUpdate = Date.now() - } - - update(payload) { - let hasUpdates = false - for (const key in payload) { - if (key === 'books') { - if (payload.books && this.books.join(',') !== payload.books.join(',')) { - this.books = [...payload.books] - hasUpdates = true - } - } else if (this[key] !== undefined && this[key] !== payload[key]) { - hasUpdates = true - this[key] = payload[key] - } - } - if (hasUpdates) { - this.lastUpdate = Date.now() - } - return hasUpdates - } -} -module.exports = Collection \ No newline at end of file