From e850049e8ee6743285ddb3a06732f0b7ea4b7d3b Mon Sep 17 00:00:00 2001 From: Aria Moradi Date: Mon, 7 Nov 2022 21:04:34 +0330 Subject: [PATCH] add category and global meta (#438) --- .../suwayomi/tachidesk/global/GlobalAPI.kt | 6 +++ .../global/controller/GlobalMetaController.kt | 53 +++++++++++++++++++ .../tachidesk/global/impl/GlobalMeta.kt | 43 +++++++++++++++ .../global/model/table/GlobalMetaTable.kt | 18 +++++++ .../suwayomi/tachidesk/manga/MangaAPI.kt | 2 + .../manga/controller/CategoryController.kt | 21 ++++++++ .../suwayomi/tachidesk/manga/impl/Category.kt | 29 ++++++++++ .../suwayomi/tachidesk/manga/impl/Chapter.kt | 4 +- .../suwayomi/tachidesk/manga/impl/Manga.kt | 16 +++--- .../model/dataclass/CategoryDataClass.kt | 3 +- .../manga/model/table/CategoryMetaTable.kt | 21 ++++++++ .../manga/model/table/CategoryTable.kt | 6 ++- .../manga/model/table/ChapterMetaTable.kt | 2 +- .../manga/model/table/MangaMetaTable.kt | 2 +- .../migration/M0021_GlobalAndCategoryMeta.kt | 34 ++++++++++++ 15 files changed, 244 insertions(+), 16 deletions(-) create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/global/controller/GlobalMetaController.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/GlobalAPI.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/GlobalAPI.kt index 97b57a55..174eb78f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/global/GlobalAPI.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/GlobalAPI.kt @@ -8,11 +8,17 @@ package suwayomi.tachidesk.global * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import io.javalin.apibuilder.ApiBuilder.get +import io.javalin.apibuilder.ApiBuilder.patch import io.javalin.apibuilder.ApiBuilder.path +import suwayomi.tachidesk.global.controller.GlobalMetaController import suwayomi.tachidesk.global.controller.SettingsController object GlobalAPI { fun defineEndpoints() { + path("meta") { + get("", GlobalMetaController.getMeta) + patch("", GlobalMetaController.modifyMeta) + } path("settings") { get("about", SettingsController.about) get("check-update", SettingsController.checkUpdate) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/controller/GlobalMetaController.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/controller/GlobalMetaController.kt new file mode 100644 index 00000000..178a8ed3 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/controller/GlobalMetaController.kt @@ -0,0 +1,53 @@ +package suwayomi.tachidesk.global.controller + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import io.javalin.http.HttpCode +import suwayomi.tachidesk.global.impl.GlobalMeta +import suwayomi.tachidesk.server.util.formParam +import suwayomi.tachidesk.server.util.handler +import suwayomi.tachidesk.server.util.withOperation + +object GlobalMetaController { + /** used to modify a category's meta parameters */ + val getMeta = handler( + documentWith = { + withOperation { + summary("Server level meta mapping") + description("Get a list of globally stored key-value mapping, you can set values for whatever you want inside it.") + } + }, + behaviorOf = { ctx -> + ctx.json(GlobalMeta.getMetaMap()) + ctx.status(200) + }, + withResults = { + httpCode(HttpCode.OK) + } + ) + + /** used to modify global meta parameters */ + val modifyMeta = handler( + formParam("key"), + formParam("value"), + documentWith = { + withOperation { + summary("Add meta data to the global meta mapping") + description("A simple Key-Value stored at server global level, you can set values for whatever you want inside it.") + } + }, + behaviorOf = { ctx, key, value -> + GlobalMeta.modifyMeta(key, value) + ctx.status(200) + }, + withResults = { + httpCode(HttpCode.OK) + httpCode(HttpCode.NOT_FOUND) + } + ) +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt new file mode 100644 index 00000000..fe300f84 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt @@ -0,0 +1,43 @@ +package suwayomi.tachidesk.global.impl + +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update +import suwayomi.tachidesk.global.model.table.GlobalMetaTable + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +object GlobalMeta { + fun modifyMeta(key: String, value: String) { + transaction { + val meta = transaction { + GlobalMetaTable.select { GlobalMetaTable.key eq key } + }.firstOrNull() + + if (meta == null) { + GlobalMetaTable.insert { + it[GlobalMetaTable.key] = key + it[GlobalMetaTable.value] = value + } + } else { + GlobalMetaTable.update({ GlobalMetaTable.key eq key }) { + it[GlobalMetaTable.value] = value + } + } + } + } + + fun getMetaMap(): Map { + return transaction { + GlobalMetaTable.selectAll() + .associate { it[GlobalMetaTable.key] to it[GlobalMetaTable.value] } + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt new file mode 100644 index 00000000..4872f5c1 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt @@ -0,0 +1,18 @@ +package suwayomi.tachidesk.global.model.table + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import org.jetbrains.exposed.dao.id.IntIdTable + +/** + * Metadata storage for clients, server/global level. + */ +object GlobalMetaTable : IntIdTable() { + val key = varchar("key", 256) + val value = varchar("value", 4096) +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt index 3c593f44..1b63c38f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt @@ -86,6 +86,8 @@ object MangaAPI { get("{categoryId}", CategoryController.categoryMangas) patch("{categoryId}", CategoryController.categoryModify) delete("{categoryId}", CategoryController.categoryDelete) + + patch("{categoryId}/meta", CategoryController.meta) } path("backup") { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt index a4ff4c05..96b56b5a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt @@ -129,4 +129,25 @@ object CategoryController { httpCode(HttpCode.OK) } ) + + /** used to modify a category's meta parameters */ + val meta = handler( + pathParam("categoryId"), + formParam("key"), + formParam("value"), + documentWith = { + withOperation { + summary("Add meta data to category") + description("A simple Key-Value storage in the manga object, you can set values for whatever you want inside it.") + } + }, + behaviorOf = { ctx, categoryId, key, value -> + Category.modifyMeta(categoryId, key, value) + ctx.status(200) + }, + withResults = { + httpCode(HttpCode.OK) + httpCode(HttpCode.NOT_FOUND) + } + ) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt index eb04cbc3..ad684c04 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt @@ -11,6 +11,7 @@ import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll @@ -20,6 +21,7 @@ import suwayomi.tachidesk.manga.impl.CategoryManga.removeMangaFromCategory import suwayomi.tachidesk.manga.impl.util.lang.isNotEmpty import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.table.CategoryMangaTable +import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.toDataClass @@ -120,4 +122,31 @@ object Category { } } } + + fun getCategoryMetaMap(categoryId: Int): Map { + return transaction { + CategoryMetaTable.select { CategoryMetaTable.ref eq categoryId } + .associate { it[CategoryMetaTable.key] to it[CategoryMetaTable.value] } + } + } + + fun modifyMeta(categoryId: Int, key: String, value: String) { + transaction { + val meta = transaction { + CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } + }.firstOrNull() + + if (meta == null) { + CategoryMetaTable.insert { + it[CategoryMetaTable.key] = key + it[CategoryMetaTable.value] = value + it[CategoryMetaTable.ref] = categoryId + } + } else { + CategoryMetaTable.update({ (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }) { + it[CategoryMetaTable.value] = value + } + } + } + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt index 0d3597d3..c9cb89b9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -219,9 +219,9 @@ object Chapter { val chapterId = ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) } .first()[ChapterTable.id].value - val meta = transaction { + val meta = ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } - }.firstOrNull() + .firstOrNull() if (meta == null) { ChapterMetaTable.insert { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt index 6a6ae4ca..b92ff295 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt @@ -152,29 +152,27 @@ object Manga { false ) - fun getMangaMetaMap(manga: Int): Map { + fun getMangaMetaMap(mangaId: Int): Map { return transaction { - MangaMetaTable.select { MangaMetaTable.ref eq manga } + MangaMetaTable.select { MangaMetaTable.ref eq mangaId } .associate { it[MangaMetaTable.key] to it[MangaMetaTable.value] } } } fun modifyMangaMeta(mangaId: Int, key: String, value: String) { transaction { - val manga = MangaTable.select { MangaTable.id eq mangaId } - .first()[MangaTable.id] - val meta = transaction { - MangaMetaTable.select { (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) } - }.firstOrNull() + val meta = + MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } + .firstOrNull() if (meta == null) { MangaMetaTable.insert { it[MangaMetaTable.key] = key it[MangaMetaTable.value] = value - it[MangaMetaTable.ref] = manga + it[MangaMetaTable.ref] = mangaId } } else { - MangaMetaTable.update({ (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) }) { + MangaMetaTable.update({ (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }) { it[MangaMetaTable.value] = value } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt index a3d95cd4..3e7fd1fb 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt @@ -11,5 +11,6 @@ data class CategoryDataClass( val id: Int, val order: Int, val name: String, - val default: Boolean + val default: Boolean, + val meta: Map = emptyMap() ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt new file mode 100644 index 00000000..8792e745 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt @@ -0,0 +1,21 @@ +package suwayomi.tachidesk.manga.model.table + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.ReferenceOption +import suwayomi.tachidesk.manga.model.table.CategoryMetaTable.ref + +/** + * Metadata storage for clients, about Chapter with id == [ref]. + */ +object CategoryMetaTable : IntIdTable() { + val key = varchar("key", 256) + val value = varchar("value", 4096) + val ref = reference("category_ref", CategoryTable, ReferenceOption.CASCADE) +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt index 0bcc0fd2..8d8a08ab 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt @@ -9,6 +9,7 @@ package suwayomi.tachidesk.manga.model.table import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ResultRow +import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass object CategoryTable : IntIdTable() { @@ -18,8 +19,9 @@ object CategoryTable : IntIdTable() { } fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass( - categoryEntry[this.id].value, + categoryEntry[id].value, categoryEntry[order], categoryEntry[name], - categoryEntry[isDefault] + categoryEntry[isDefault], + Category.getCategoryMetaMap(categoryEntry[id].value) ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt index b290e8ff..6584679f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt @@ -12,7 +12,7 @@ import org.jetbrains.exposed.sql.ReferenceOption import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref /** - * Meta data storage for clients, about Chapter with id == [ref]. + * Metadata storage for clients, about Chapter with id == [ref]. * * For example, if you added reader mode(with the key juiReaderMode) such as webtoon to a manga object, * this is what will show up when you request that manga from the api again diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt index ed661e33..b4a7098a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt @@ -12,7 +12,7 @@ import org.jetbrains.exposed.sql.ReferenceOption import suwayomi.tachidesk.manga.model.table.MangaMetaTable.ref /** - * Meta data storage for clients, about Manga with id == [ref]. + * Metadata storage for clients, about Manga with id == [ref]. * * For example, if you added reader mode(with the key juiReaderMode) such as webtoon to a manga object, * this is what will show up when you request that manga from the api again diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt new file mode 100644 index 00000000..a268f4cc --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt @@ -0,0 +1,34 @@ +package suwayomi.tachidesk.server.database.migration + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.helpers.AddTableMigration +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.sql.Table +import suwayomi.tachidesk.manga.model.table.ChapterTable + +@Suppress("ClassName", "unused") +class M0021_GlobalAndCategoryMeta : AddTableMigration() { + private class GlobalMetaTable : IntIdTable() { + val key = varchar("key", 256) + val value = varchar("value", 4096) + } + + private class CategoryMetaTable : IntIdTable() { + val key = varchar("key", 256) + val value = varchar("value", 4096) + val ref = reference("category_ref", ChapterTable, ReferenceOption.CASCADE) + } + + override val tables: Array + get() = arrayOf( + GlobalMetaTable(), + CategoryMetaTable() + ) +}