diff --git a/build.gradle.kts b/build.gradle.kts index e7152164..e47ed1e1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -89,6 +89,6 @@ configure(projects) { // dependency both in AndroidCompat and server, version locked by javalin - implementation("com.fasterxml.jackson.core:jackson-annotations:2.10.3") + implementation("com.fasterxml.jackson.core:jackson-annotations:2.12.4") } } \ No newline at end of file diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 50509cea..35306623 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -31,10 +31,10 @@ dependencies { implementation("com.squareup.okio:okio:2.10.0") // Javalin api - implementation("io.javalin:javalin:3.13.11") + implementation("io.javalin:javalin:4.0.0") // jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency` - implementation("com.fasterxml.jackson.core:jackson-databind:2.10.3") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.10.3") + implementation("com.fasterxml.jackson.core:jackson-databind:2.12.4") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.4") // Exposed ORM val exposedVersion = "0.34.1" diff --git a/server/src/main/kotlin/suwayomi/tachidesk/anime/AnimeAPI.kt b/server/src/main/kotlin/suwayomi/tachidesk/anime/AnimeAPI.kt index 665c1f22..49018516 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/anime/AnimeAPI.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/anime/AnimeAPI.kt @@ -28,7 +28,7 @@ object AnimeAPI { fun defineEndpoints(app: Javalin) { // list all extensions app.get("/api/v1/anime/extension/list") { ctx -> - ctx.json( + ctx.future( future { getExtensionList() } @@ -36,10 +36,10 @@ object AnimeAPI { } // install extension identified with "pkgName" - app.get("/api/v1/anime/extension/install/:pkgName") { ctx -> + app.get("/api/v1/anime/extension/install/{pkgName}") { ctx -> val pkgName = ctx.pathParam("pkgName") - ctx.json( + ctx.future( future { installExtension(pkgName) } @@ -47,10 +47,10 @@ object AnimeAPI { } // update extension identified with "pkgName" - app.get("/api/v1/anime/extension/update/:pkgName") { ctx -> + app.get("/api/v1/anime/extension/update/{pkgName}") { ctx -> val pkgName = ctx.pathParam("pkgName") - ctx.json( + ctx.future( future { updateExtension(pkgName) } @@ -58,7 +58,7 @@ object AnimeAPI { } // uninstall extension identified with "pkgName" - app.get("/api/v1/anime/extension/uninstall/:pkgName") { ctx -> + app.get("/api/v1/anime/extension/uninstall/{pkgName}") { ctx -> val pkgName = ctx.pathParam("pkgName") uninstallExtension(pkgName) @@ -66,10 +66,10 @@ object AnimeAPI { } // icon for extension named `apkName` - app.get("/api/v1/anime/extension/icon/:apkName") { ctx -> // TODO: move to pkgName + app.get("/api/v1/anime/extension/icon/{apkName}") { ctx -> // TODO: move to pkgName val apkName = ctx.pathParam("apkName") - ctx.result( + ctx.future( future { getExtensionIcon(apkName) } .thenApply { ctx.header("content-type", it.second) @@ -84,16 +84,16 @@ object AnimeAPI { } // fetch source with id `sourceId` - app.get("/api/v1/anime/source/:sourceId") { ctx -> + app.get("/api/v1/anime/source/{sourceId}") { ctx -> val sourceId = ctx.pathParam("sourceId").toLong() ctx.json(getAnimeSource(sourceId)) } // popular animes from source with id `sourceId` - app.get("/api/v1/anime/source/:sourceId/popular/:pageNum") { ctx -> + app.get("/api/v1/anime/source/{sourceId}/popular/{pageNum}") { ctx -> val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() - ctx.json( + ctx.future( future { getAnimeList(sourceId, pageNum, popular = true) } @@ -101,10 +101,10 @@ object AnimeAPI { } // latest animes from source with id `sourceId` - app.get("/api/v1/anime/source/:sourceId/latest/:pageNum") { ctx -> + app.get("/api/v1/anime/source/{sourceId}/latest/{pageNum}") { ctx -> val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() - ctx.json( + ctx.future( future { getAnimeList(sourceId, pageNum, popular = false) } @@ -112,11 +112,11 @@ object AnimeAPI { } // get anime info - app.get("/api/v1/anime/anime/:animeId/") { ctx -> + app.get("/api/v1/anime/anime/{animeId}/") { ctx -> val animeId = ctx.pathParam("animeId").toInt() - val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean() + val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean() ?: false - ctx.json( + ctx.future( future { getAnime(animeId, onlineFetch) } @@ -124,10 +124,10 @@ object AnimeAPI { } // anime thumbnail - app.get("api/v1/anime/anime/:animeId/thumbnail") { ctx -> + app.get("api/v1/anime/anime/{animeId}/thumbnail") { ctx -> val animeId = ctx.pathParam("animeId").toInt() - ctx.result( + ctx.future( future { getAnimeThumbnail(animeId) } .thenApply { ctx.header("content-type", it.second) @@ -137,13 +137,13 @@ object AnimeAPI { } // // // list manga's categories -// app.get("api/v1/manga/:mangaId/category/") { ctx -> +// app.get("api/v1/manga/{mangaId}/category/") { ctx -> // val mangaId = ctx.pathParam("mangaId").toInt() // ctx.json(getMangaCategories(mangaId)) // } // // // adds the manga to category -// app.get("api/v1/manga/:mangaId/category/:categoryId") { ctx -> +// app.get("api/v1/manga/{mangaId}/category/{categoryId}") { ctx -> // val mangaId = ctx.pathParam("mangaId").toInt() // val categoryId = ctx.pathParam("categoryId").toInt() // addMangaToCategory(mangaId, categoryId) @@ -151,7 +151,7 @@ object AnimeAPI { // } // // // removes the manga from the category -// app.delete("api/v1/manga/:mangaId/category/:categoryId") { ctx -> +// app.delete("api/v1/manga/{mangaId}/category/{categoryId}") { ctx -> // val mangaId = ctx.pathParam("mangaId").toInt() // val categoryId = ctx.pathParam("categoryId").toInt() // removeMangaFromCategory(mangaId, categoryId) @@ -159,23 +159,23 @@ object AnimeAPI { // } // // get episode list when showing a anime - app.get("/api/v1/anime/anime/:animeId/episodes") { ctx -> + app.get("/api/v1/anime/anime/{animeId}/episodes") { ctx -> val animeId = ctx.pathParam("animeId").toInt() val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean() - ctx.json(future { getEpisodeList(animeId, onlineFetch) }) + ctx.future(future { getEpisodeList(animeId, onlineFetch) }) } // used to display a episode, get a episode in order to show it's - app.get("/api/v1/anime/anime/:animeId/episode/:episodeIndex") { ctx -> + app.get("/api/v1/anime/anime/{animeId}/episode/{episodeIndex}") { ctx -> val episodeIndex = ctx.pathParam("episodeIndex").toInt() val animeId = ctx.pathParam("animeId").toInt() - ctx.json(future { getEpisode(episodeIndex, animeId) }) + ctx.future(future { getEpisode(episodeIndex, animeId) }) } // used to modify a episode's parameters - app.patch("/api/v1/anime/anime/:animeId/episode/:episodeIndex") { ctx -> + app.patch("/api/v1/anime/anime/{animeId}/episode/{episodeIndex}") { ctx -> val episodeIndex = ctx.pathParam("episodeIndex").toInt() val animeId = ctx.pathParam("animeId").toInt() @@ -190,7 +190,7 @@ object AnimeAPI { } // // // get page at index "index" -// app.get("/api/v1/manga/:mangaId/chapter/:chapterIndex/page/:index") { ctx -> +// app.get("/api/v1/manga/{mangaId}/chapter/{chapterIndex}/page/{index}") { ctx -> // val mangaId = ctx.pathParam("mangaId").toInt() // val chapterIndex = ctx.pathParam("chapterIndex").toInt() // val index = ctx.pathParam("index").toInt() @@ -205,49 +205,49 @@ object AnimeAPI { // } // // // submit a chapter for download -// app.put("/api/v1/manga/:mangaId/chapter/:chapterIndex/download") { ctx -> +// app.put("/api/v1/manga/{mangaId}/chapter/{chapterIndex}/download") { ctx -> // // TODO // } // // // cancel a chapter download -// app.delete("/api/v1/manga/:mangaId/chapter/:chapterIndex/download") { ctx -> +// app.delete("/api/v1/manga/{mangaId}/chapter/{chapterIndex}/download") { ctx -> // // TODO // } // // // global search, Not implemented yet -// app.get("/api/v1/search/:searchTerm") { ctx -> +// app.get("/api/v1/search/{searchTerm}") { ctx -> // val searchTerm = ctx.pathParam("searchTerm") // ctx.json(sourceGlobalSearch(searchTerm)) // } // // single source search - app.get("/api/v1/anime/source/:sourceId/search/:searchTerm/:pageNum") { ctx -> + app.get("/api/v1/anime/source/{sourceId}/search/{searchTerm}/{pageNum}") { ctx -> val sourceId = ctx.pathParam("sourceId").toLong() val searchTerm = ctx.pathParam("searchTerm") val pageNum = ctx.pathParam("pageNum").toInt() - ctx.json(future { sourceSearch(sourceId, searchTerm, pageNum) }) + ctx.future(future { sourceSearch(sourceId, searchTerm, pageNum) }) } // // // source filter list -// app.get("/api/v1/source/:sourceId/filters/") { ctx -> +// app.get("/api/v1/source/{sourceId}/filters/") { ctx -> // val sourceId = ctx.pathParam("sourceId").toLong() // ctx.json(sourceFilters(sourceId)) // } // // // adds the manga to library -// app.get("api/v1/manga/:mangaId/library") { ctx -> +// app.get("api/v1/manga/{mangaId}/library") { ctx -> // val mangaId = ctx.pathParam("mangaId").toInt() // -// ctx.result( +// ctx.future( // JavalinSetup.future { addMangaToLibrary(mangaId) } // ) // } // // // removes the manga from the library -// app.delete("api/v1/manga/:mangaId/library") { ctx -> +// app.delete("api/v1/manga/{mangaId}/library") { ctx -> // val mangaId = ctx.pathParam("mangaId").toInt() // -// ctx.result( +// ctx.future( // JavalinSetup.future { removeMangaFromLibrary(mangaId) } // ) // } @@ -275,7 +275,7 @@ object AnimeAPI { // } // // // category modification -// app.patch("/api/v1/category/:categoryId") { ctx -> +// app.patch("/api/v1/category/{categoryId}") { ctx -> // val categoryId = ctx.pathParam("categoryId").toInt() // val name = ctx.formParam("name") // val isDefault = ctx.formParam("default")?.toBoolean() @@ -284,7 +284,7 @@ object AnimeAPI { // } // // // category re-ordering -// app.patch("/api/v1/category/:categoryId/reorder") { ctx -> +// app.patch("/api/v1/category/{categoryId}/reorder") { ctx -> // val categoryId = ctx.pathParam("categoryId").toInt() // val from = ctx.formParam("from")!!.toInt() // val to = ctx.formParam("to")!!.toInt() @@ -293,21 +293,21 @@ object AnimeAPI { // } // // // category delete -// app.delete("/api/v1/category/:categoryId") { ctx -> +// app.delete("/api/v1/category/{categoryId}") { ctx -> // val categoryId = ctx.pathParam("categoryId").toInt() // Category.removeCategory(categoryId) // ctx.status(200) // } // // // returns the manga list associated with a category -// app.get("/api/v1/category/:categoryId") { ctx -> +// app.get("/api/v1/category/{categoryId}") { ctx -> // val categoryId = ctx.pathParam("categoryId").toInt() // ctx.json(getCategoryMangaList(categoryId)) // } // // // expects a Tachiyomi legacy backup json in the body // app.post("/api/v1/backup/legacy/import") { ctx -> -// ctx.result( +// ctx.future( // future { // restoreLegacyBackup(ctx.bodyAsInputStream()) // } @@ -316,7 +316,7 @@ object AnimeAPI { // // // expects a Tachiyomi legacy backup json as a file upload, the file must be named "backup.json" // app.post("/api/v1/backup/legacy/import/file") { ctx -> -// ctx.result( +// ctx.future( // JavalinSetup.future { // restoreLegacyBackup(ctx.uploadedFile("backup.json")!!.content) // } @@ -326,7 +326,7 @@ object AnimeAPI { // // returns a Tachiyomi legacy backup json created from the current database as a json body // app.get("/api/v1/backup/legacy/export") { ctx -> // ctx.contentType("application/json") -// ctx.result( +// ctx.future( // JavalinSetup.future { // createLegacyBackup( // BackupFlags( @@ -348,7 +348,7 @@ object AnimeAPI { // val currentDate = sdf.format(Date()) // // ctx.header("Content-Disposition", "attachment; filename=\"tachidesk_$currentDate.json\"") -// ctx.result( +// ctx.future( // JavalinSetup.future { // createLegacyBackup( // BackupFlags( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt index c24c19e1..922ceb23 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt @@ -25,59 +25,59 @@ object MangaAPI { path("extension") { get("list", ExtensionController::list) - get("install/:pkgName", ExtensionController::install) + get("install/{pkgName}", ExtensionController::install) post("install", ExtensionController::installFile) - get("update/:pkgName", ExtensionController::update) - get("uninstall/:pkgName", ExtensionController::uninstall) + get("update/{pkgName}", ExtensionController::update) + get("uninstall/{pkgName}", ExtensionController::uninstall) - get("icon/:apkName", ExtensionController::icon) + get("icon/{apkName}", ExtensionController::icon) } path("source") { get("list", SourceController::list) - get(":sourceId", SourceController::retrieve) + get("{sourceId}", SourceController::retrieve) - get(":sourceId/popular/:pageNum", SourceController::popular) - get(":sourceId/latest/:pageNum", SourceController::latest) + get("{sourceId}/popular/{pageNum}", SourceController::popular) + get("{sourceId}/latest/{pageNum}", SourceController::latest) - get(":sourceId/preferences", SourceController::getPreferences) - post(":sourceId/preferences", SourceController::setPreference) + get("{sourceId}/preferences", SourceController::getPreferences) + post("{sourceId}/preferences", SourceController::setPreference) - get(":sourceId/filters", SourceController::filters) + get("{sourceId}/filters", SourceController::filters) - get(":sourceId/search/:searchTerm/:pageNum", SourceController::searchSingle) -// get("search/:searchTerm/:pageNum", SourceController::searchGlobal) + get("{sourceId}/search/{searchTerm}/{pageNum}", SourceController::searchSingle) +// get("search/{searchTerm}/{pageNum}", SourceController::searchGlobal) } path("manga") { - get(":mangaId", MangaController::retrieve) - get(":mangaId/thumbnail", MangaController::thumbnail) + get("{mangaId}", MangaController::retrieve) + get("{mangaId}/thumbnail", MangaController::thumbnail) - get(":mangaId/category", MangaController::categoryList) - get(":mangaId/category/:categoryId", MangaController::addToCategory) - delete(":mangaId/category/:categoryId", MangaController::removeFromCategory) + get("{mangaId}/category", MangaController::categoryList) + get("{mangaId}/category/{categoryId}", MangaController::addToCategory) + delete("{mangaId}/category/{categoryId}", MangaController::removeFromCategory) - get(":mangaId/library", MangaController::addToLibrary) - delete(":mangaId/library", MangaController::removeFromLibrary) + get("{mangaId}/library", MangaController::addToLibrary) + delete("{mangaId}/library", MangaController::removeFromLibrary) - patch(":mangaId/meta", MangaController::meta) + patch("{mangaId}/meta", MangaController::meta) - get(":mangaId/chapters", MangaController::chapterList) - get(":mangaId/chapter/:chapterIndex", MangaController::chapterRetrieve) - patch(":mangaId/chapter/:chapterIndex", MangaController::chapterModify) + get("{mangaId}/chapters", MangaController::chapterList) + get("{mangaId}/chapter/{chapterIndex}", MangaController::chapterRetrieve) + patch("{mangaId}/chapter/{chapterIndex}", MangaController::chapterModify) - patch(":mangaId/chapter/:chapterIndex/meta", MangaController::chapterMeta) + patch("{mangaId}/chapter/{chapterIndex}/meta", MangaController::chapterMeta) - get(":mangaId/chapter/:chapterIndex/page/:index", MangaController::pageRetrieve) + get("{mangaId}/chapter/{chapterIndex}/page/{index}", MangaController::pageRetrieve) } path("category") { get("", CategoryController::categoryList) post("", CategoryController::categoryCreate) - get(":categoryId", CategoryController::categoryMangas) - patch(":categoryId", CategoryController::categoryModify) - delete(":categoryId", CategoryController::categoryDelete) + get("{categoryId}", CategoryController::categoryMangas) + patch("{categoryId}", CategoryController::categoryModify) + delete("{categoryId}", CategoryController::categoryDelete) patch("reorder", CategoryController::categoryReorder) } @@ -102,8 +102,8 @@ object MangaAPI { } path("download") { - get(":mangaId/chapter/:chapterIndex", DownloadController::queueChapter) - delete(":mangaId/chapter/:chapterIndex", DownloadController::unqueueChapter) + get("{mangaId}/chapter/{chapterIndex}", DownloadController::queueChapter) + delete("{mangaId}/chapter/{chapterIndex}", DownloadController::unqueueChapter) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt index f8f3a8bd..362387af 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt @@ -5,7 +5,7 @@ import suwayomi.tachidesk.manga.impl.backup.BackupFlags import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupImport import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupValidator -import suwayomi.tachidesk.server.JavalinSetup +import suwayomi.tachidesk.server.JavalinSetup.future import java.text.SimpleDateFormat import java.util.Date @@ -20,8 +20,8 @@ object BackupController { /** expects a Tachiyomi protobuf backup in the body */ fun protobufImport(ctx: Context) { - ctx.json( - JavalinSetup.future { + ctx.future( + future { ProtoBackupImport.performRestore(ctx.bodyAsInputStream()) } ) @@ -30,8 +30,8 @@ object BackupController { /** expects a Tachiyomi protobuf backup as a file upload, the file must be named "backup.proto.gz" */ fun protobufImportFile(ctx: Context) { // TODO: rewrite this with ctx.uploadedFiles(), don't call the multipart field "backup.proto.gz" - ctx.json( - JavalinSetup.future { + ctx.future( + future { ProtoBackupImport.performRestore(ctx.uploadedFile("backup.proto.gz")!!.content) } ) @@ -40,8 +40,8 @@ object BackupController { /** returns a Tachiyomi protobuf backup created from the current database as a body */ fun protobufExport(ctx: Context) { ctx.contentType("application/octet-stream") - ctx.result( - JavalinSetup.future { + ctx.future( + future { ProtoBackupExport.createBackup( BackupFlags( includeManga = true, @@ -61,8 +61,8 @@ object BackupController { val currentDate = SimpleDateFormat("yyyy-MM-dd_HH-mm").format(Date()) ctx.header("Content-Disposition", """attachment; filename="tachidesk_$currentDate.proto.gz"""") - ctx.result( - JavalinSetup.future { + ctx.future( + future { ProtoBackupExport.createBackup( BackupFlags( includeManga = true, @@ -78,8 +78,8 @@ object BackupController { /** Reports missing sources and trackers, expects a Tachiyomi protobuf backup in the body */ fun protobufValidate(ctx: Context) { - ctx.json( - JavalinSetup.future { + ctx.future( + future { ProtoBackupValidator.validate(ctx.bodyAsInputStream()) } ) @@ -87,8 +87,8 @@ object BackupController { /** Reports missing sources and trackers, expects a Tachiyomi protobuf backup as a file upload, the file must be named "backup.proto.gz" */ fun protobufValidateFile(ctx: Context) { - ctx.json( - JavalinSetup.future { + ctx.future( + future { ProtoBackupValidator.validate(ctx.uploadedFile("backup.proto.gz")!!.content) } ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt index 0a51134d..c8fcf967 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt @@ -8,12 +8,12 @@ package suwayomi.tachidesk.manga.controller * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import io.javalin.http.Context -import io.javalin.websocket.WsHandler +import io.javalin.websocket.WsConfig import suwayomi.tachidesk.manga.impl.download.DownloadManager object DownloadController { /** Download queue stats */ - fun downloadsWS(ws: WsHandler) { + fun downloadsWS(ws: WsConfig) { ws.onConnect { ctx -> DownloadManager.addClient(ctx) DownloadManager.notifyClient(ctx) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/ExtensionController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/ExtensionController.kt index 70d82a1d..7e1501ab 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/ExtensionController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/ExtensionController.kt @@ -18,7 +18,7 @@ object ExtensionController { /** list all extensions */ fun list(ctx: Context) { - ctx.json( + ctx.future( future { ExtensionsList.getExtensionList() } @@ -29,7 +29,7 @@ object ExtensionController { fun install(ctx: Context) { val pkgName = ctx.pathParam("pkgName") - ctx.json( + ctx.future( future { Extension.installExtension(pkgName) } @@ -42,7 +42,7 @@ object ExtensionController { val uploadedFile = ctx.uploadedFile("file")!! logger.debug { "Uploaded extension file name: " + uploadedFile.filename } - ctx.json( + ctx.future( future { Extension.installExternalExtension(uploadedFile.content, uploadedFile.filename) } @@ -53,7 +53,7 @@ object ExtensionController { fun update(ctx: Context) { val pkgName = ctx.pathParam("pkgName") - ctx.json( + ctx.future( future { Extension.updateExtension(pkgName) } @@ -72,7 +72,7 @@ object ExtensionController { fun icon(ctx: Context) { val apkName = ctx.pathParam("apkName") - ctx.result( + ctx.future( future { Extension.getExtensionIcon(apkName) } .thenApply { ctx.header("content-type", it.second) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt index a143b212..007db1ba 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt @@ -19,9 +19,9 @@ object MangaController { /** get manga info */ fun retrieve(ctx: Context) { val mangaId = ctx.pathParam("mangaId").toInt() - val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean() + val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean() ?: false - ctx.json( + ctx.future( future { Manga.getManga(mangaId, onlineFetch) } @@ -32,7 +32,7 @@ object MangaController { fun thumbnail(ctx: Context) { val mangaId = ctx.pathParam("mangaId").toInt() - ctx.result( + ctx.future( future { Manga.getMangaThumbnail(mangaId) } .thenApply { ctx.header("content-type", it.second) @@ -45,7 +45,7 @@ object MangaController { fun addToLibrary(ctx: Context) { val mangaId = ctx.pathParam("mangaId").toInt() - ctx.result( + ctx.future( future { Library.addMangaToLibrary(mangaId) } ) } @@ -54,7 +54,7 @@ object MangaController { fun removeFromLibrary(ctx: Context) { val mangaId = ctx.pathParam("mangaId").toInt() - ctx.result( + ctx.future( future { Library.removeMangaFromLibrary(mangaId) } ) } @@ -97,16 +97,16 @@ object MangaController { fun chapterList(ctx: Context) { val mangaId = ctx.pathParam("mangaId").toInt() - val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean() + val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean() ?: false - ctx.json(future { Chapter.getChapterList(mangaId, onlineFetch) }) + ctx.future(future { Chapter.getChapterList(mangaId, onlineFetch) }) } /** used to display a chapter, get a chapter in order to show its pages */ fun chapterRetrieve(ctx: Context) { val chapterIndex = ctx.pathParam("chapterIndex").toInt() val mangaId = ctx.pathParam("mangaId").toInt() - ctx.json(future { Chapter.getChapter(chapterIndex, mangaId) }) + ctx.future(future { Chapter.getChapter(chapterIndex, mangaId) }) } /** used to modify a chapter's parameters */ @@ -143,7 +143,7 @@ object MangaController { val chapterIndex = ctx.pathParam("chapterIndex").toInt() val index = ctx.pathParam("index").toInt() - ctx.result( + ctx.future( future { Page.getPageImage(mangaId, chapterIndex, index) } .thenApply { ctx.header("content-type", it.second) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt index 13a6e1db..9d52dce3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt @@ -30,7 +30,7 @@ object SourceController { fun popular(ctx: Context) { val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() - ctx.json( + ctx.future( future { MangaList.getMangaList(sourceId, pageNum, popular = true) } @@ -41,7 +41,7 @@ object SourceController { fun latest(ctx: Context) { val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() - ctx.json( + ctx.future( future { MangaList.getMangaList(sourceId, pageNum, popular = false) } @@ -64,7 +64,7 @@ object SourceController { /** fetch filters of source with id `sourceId` */ fun filters(ctx: Context) { val sourceId = ctx.pathParam("sourceId").toLong() - val reset = ctx.queryParam("reset", "false").toBoolean() + val reset = ctx.queryParam("reset")?.toBoolean() ?: false ctx.json(Search.getInitialFilterList(sourceId, reset)) } @@ -74,7 +74,7 @@ object SourceController { val sourceId = ctx.pathParam("sourceId").toLong() val searchTerm = ctx.pathParam("searchTerm") val pageNum = ctx.pathParam("pageNum").toInt() - ctx.json(future { Search.sourceSearch(sourceId, searchTerm, pageNum) }) + ctx.future(future { Search.sourceSearch(sourceId, searchTerm, pageNum) }) } /** all source search */ diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt index 66933b2b..47cd7bc3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt @@ -9,6 +9,7 @@ package suwayomi.tachidesk.server import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.path +import io.javalin.core.security.RouteRole import io.javalin.http.staticfiles.Location import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -86,4 +87,8 @@ object JavalinSetup { } } } + + object Auth { + enum class Role : RouteRole { ANYONE, USER_READ, USER_WRITE } + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt index 15adda57..5e30d3f8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt @@ -46,7 +46,7 @@ object AppMutex { } return try { - JavalinJackson.fromJson(response, AboutDataClass::class.java) + JavalinJackson().fromJsonString(response, AboutDataClass::class.java) AppMutexState.TachideskInstanceRunning } catch (e: IOException) { AppMutexState.OtherApplicationRunning