add category and global meta (#438)

This commit is contained in:
Aria Moradi
2022-11-07 21:04:34 +03:30
committed by GitHub
parent 907adea73f
commit e850049e8e
15 changed files with 244 additions and 16 deletions

View File

@@ -8,11 +8,17 @@ package suwayomi.tachidesk.global
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.apibuilder.ApiBuilder.get import io.javalin.apibuilder.ApiBuilder.get
import io.javalin.apibuilder.ApiBuilder.patch
import io.javalin.apibuilder.ApiBuilder.path import io.javalin.apibuilder.ApiBuilder.path
import suwayomi.tachidesk.global.controller.GlobalMetaController
import suwayomi.tachidesk.global.controller.SettingsController import suwayomi.tachidesk.global.controller.SettingsController
object GlobalAPI { object GlobalAPI {
fun defineEndpoints() { fun defineEndpoints() {
path("meta") {
get("", GlobalMetaController.getMeta)
patch("", GlobalMetaController.modifyMeta)
}
path("settings") { path("settings") {
get("about", SettingsController.about) get("about", SettingsController.about)
get("check-update", SettingsController.checkUpdate) get("check-update", SettingsController.checkUpdate)

View File

@@ -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<String>("key"),
formParam<String>("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)
}
)
}

View File

@@ -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<String, String> {
return transaction {
GlobalMetaTable.selectAll()
.associate { it[GlobalMetaTable.key] to it[GlobalMetaTable.value] }
}
}
}

View File

@@ -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)
}

View File

@@ -86,6 +86,8 @@ object MangaAPI {
get("{categoryId}", CategoryController.categoryMangas) get("{categoryId}", CategoryController.categoryMangas)
patch("{categoryId}", CategoryController.categoryModify) patch("{categoryId}", CategoryController.categoryModify)
delete("{categoryId}", CategoryController.categoryDelete) delete("{categoryId}", CategoryController.categoryDelete)
patch("{categoryId}/meta", CategoryController.meta)
} }
path("backup") { path("backup") {

View File

@@ -129,4 +129,25 @@ object CategoryController {
httpCode(HttpCode.OK) httpCode(HttpCode.OK)
} }
) )
/** used to modify a category's meta parameters */
val meta = handler(
pathParam<Int>("categoryId"),
formParam<String>("key"),
formParam<String>("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)
}
)
} }

View File

@@ -11,6 +11,7 @@ import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll 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.impl.util.lang.isNotEmpty
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable 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.CategoryTable
import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.toDataClass import suwayomi.tachidesk.manga.model.table.toDataClass
@@ -120,4 +122,31 @@ object Category {
} }
} }
} }
fun getCategoryMetaMap(categoryId: Int): Map<String, String> {
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
}
}
}
}
} }

View File

@@ -219,9 +219,9 @@ object Chapter {
val chapterId = val chapterId =
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) } ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id].value .first()[ChapterTable.id].value
val meta = transaction { val meta =
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
}.firstOrNull() .firstOrNull()
if (meta == null) { if (meta == null) {
ChapterMetaTable.insert { ChapterMetaTable.insert {

View File

@@ -152,29 +152,27 @@ object Manga {
false false
) )
fun getMangaMetaMap(manga: Int): Map<String, String> { fun getMangaMetaMap(mangaId: Int): Map<String, String> {
return transaction { return transaction {
MangaMetaTable.select { MangaMetaTable.ref eq manga } MangaMetaTable.select { MangaMetaTable.ref eq mangaId }
.associate { it[MangaMetaTable.key] to it[MangaMetaTable.value] } .associate { it[MangaMetaTable.key] to it[MangaMetaTable.value] }
} }
} }
fun modifyMangaMeta(mangaId: Int, key: String, value: String) { fun modifyMangaMeta(mangaId: Int, key: String, value: String) {
transaction { transaction {
val manga = MangaTable.select { MangaTable.id eq mangaId } val meta =
.first()[MangaTable.id] MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
val meta = transaction { .firstOrNull()
MangaMetaTable.select { (MangaMetaTable.ref eq manga) and (MangaMetaTable.key eq key) }
}.firstOrNull()
if (meta == null) { if (meta == null) {
MangaMetaTable.insert { MangaMetaTable.insert {
it[MangaMetaTable.key] = key it[MangaMetaTable.key] = key
it[MangaMetaTable.value] = value it[MangaMetaTable.value] = value
it[MangaMetaTable.ref] = manga it[MangaMetaTable.ref] = mangaId
} }
} else { } 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 it[MangaMetaTable.value] = value
} }
} }

View File

@@ -11,5 +11,6 @@ data class CategoryDataClass(
val id: Int, val id: Int,
val order: Int, val order: Int,
val name: String, val name: String,
val default: Boolean val default: Boolean,
val meta: Map<String, String> = emptyMap()
) )

View File

@@ -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)
}

View File

@@ -9,6 +9,7 @@ package suwayomi.tachidesk.manga.model.table
import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
object CategoryTable : IntIdTable() { object CategoryTable : IntIdTable() {
@@ -18,8 +19,9 @@ object CategoryTable : IntIdTable() {
} }
fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass( fun CategoryTable.toDataClass(categoryEntry: ResultRow) = CategoryDataClass(
categoryEntry[this.id].value, categoryEntry[id].value,
categoryEntry[order], categoryEntry[order],
categoryEntry[name], categoryEntry[name],
categoryEntry[isDefault] categoryEntry[isDefault],
Category.getCategoryMetaMap(categoryEntry[id].value)
) )

View File

@@ -12,7 +12,7 @@ import org.jetbrains.exposed.sql.ReferenceOption
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref 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, * 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 * this is what will show up when you request that manga from the api again

View File

@@ -12,7 +12,7 @@ import org.jetbrains.exposed.sql.ReferenceOption
import suwayomi.tachidesk.manga.model.table.MangaMetaTable.ref 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, * 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 * this is what will show up when you request that manga from the api again

View File

@@ -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<Table>
get() = arrayOf(
GlobalMetaTable(),
CategoryMetaTable()
)
}