document all endpoints (#350)

* Document all endpoints

* Forgot about global endpoints
This commit is contained in:
Mitchell Syer
2022-04-27 07:31:39 -04:00
committed by GitHub
parent 84f701c4ab
commit fe17176b31
9 changed files with 787 additions and 307 deletions

View File

@@ -1,11 +1,14 @@
package suwayomi.tachidesk.manga.controller
import io.javalin.http.Context
import io.javalin.http.HttpCode
import suwayomi.tachidesk.manga.impl.backup.AbstractBackupValidator
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.future
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.withOperation
import java.text.SimpleDateFormat
import java.util.Date
@@ -19,78 +22,156 @@ import java.util.Date
object BackupController {
/** expects a Tachiyomi protobuf backup in the body */
fun protobufImport(ctx: Context) {
ctx.future(
future {
ProtoBackupImport.performRestore(ctx.bodyAsInputStream())
val protobufImport = handler(
documentWith = {
withOperation {
summary("Restore a backup")
description("Expects a Tachiyomi protobuf backup in the body")
}
)
}
},
behaviorOf = { ctx ->
ctx.future(
future {
ProtoBackupImport.performRestore(ctx.bodyAsInputStream())
}
)
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** 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.future(
future {
ProtoBackupImport.performRestore(ctx.uploadedFile("backup.proto.gz")!!.content)
val protobufImportFile = handler(
documentWith = {
withOperation {
summary("Restore a backup file")
description("Expects a Tachiyomi protobuf backup as a file upload, the file must be named \"backup.proto.gz\"")
}
)
}
uploadedFile("backup.proto.gz") {
it.description("Protobuf backup")
it.required(true)
}
},
behaviorOf = { ctx ->
// TODO: rewrite this with ctx.uploadedFiles(), don't call the multipart field "backup.proto.gz"
ctx.future(
future {
ProtoBackupImport.performRestore(ctx.uploadedFile("backup.proto.gz")!!.content)
}
)
},
withResults = {
httpCode(HttpCode.OK)
httpCode(HttpCode.NOT_FOUND)
}
)
/** returns a Tachiyomi protobuf backup created from the current database as a body */
fun protobufExport(ctx: Context) {
ctx.contentType("application/octet-stream")
ctx.future(
future {
ProtoBackupExport.createBackup(
BackupFlags(
includeManga = true,
includeCategories = true,
includeChapters = true,
includeTracking = true,
includeHistory = true,
)
)
val protobufExport = handler(
documentWith = {
withOperation {
summary("Create a backup")
description("Returns a Tachiyomi protobuf backup created from the current database as a body")
}
)
}
},
behaviorOf = { ctx ->
ctx.contentType("application/octet-stream")
ctx.future(
future {
ProtoBackupExport.createBackup(
BackupFlags(
includeManga = true,
includeCategories = true,
includeChapters = true,
includeTracking = true,
includeHistory = true,
)
)
}
)
},
withResults = {
mime(HttpCode.OK, "application/octet-stream")
}
)
/** returns a Tachiyomi protobuf backup created from the current database as a file */
fun protobufExportFile(ctx: Context) {
ctx.contentType("application/octet-stream")
val currentDate = SimpleDateFormat("yyyy-MM-dd_HH-mm").format(Date())
ctx.header("Content-Disposition", """attachment; filename="tachidesk_$currentDate.proto.gz"""")
ctx.future(
future {
ProtoBackupExport.createBackup(
BackupFlags(
includeManga = true,
includeCategories = true,
includeChapters = true,
includeTracking = true,
includeHistory = true,
)
)
val protobufExportFile = handler(
documentWith = {
withOperation {
summary("Create a backup file")
description("Returns a Tachiyomi protobuf backup created from the current database as a file")
}
)
}
},
behaviorOf = { ctx ->
ctx.contentType("application/octet-stream")
val currentDate = SimpleDateFormat("yyyy-MM-dd_HH-mm").format(Date())
ctx.header("Content-Disposition", """attachment; filename="tachidesk_$currentDate.proto.gz"""")
ctx.future(
future {
ProtoBackupExport.createBackup(
BackupFlags(
includeManga = true,
includeCategories = true,
includeChapters = true,
includeTracking = true,
includeHistory = true,
)
)
}
)
},
withResults = {
mime(HttpCode.OK, "application/octet-stream")
}
)
/** Reports missing sources and trackers, expects a Tachiyomi protobuf backup in the body */
fun protobufValidate(ctx: Context) {
ctx.future(
future {
ProtoBackupValidator.validate(ctx.bodyAsInputStream())
val protobufValidate = handler(
documentWith = {
withOperation {
summary("Validate a backup")
description("Reports missing sources and trackers, expects a Tachiyomi protobuf backup in the body")
}
)
}
body<ByteArray>("") {
}
},
behaviorOf = { ctx ->
ctx.future(
future {
ProtoBackupValidator.validate(ctx.bodyAsInputStream())
}
)
},
withResults = {
json<AbstractBackupValidator.ValidationResult>(HttpCode.OK)
}
)
/** 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.future(
future {
ProtoBackupValidator.validate(ctx.uploadedFile("backup.proto.gz")!!.content)
val protobufValidateFile = handler(
documentWith = {
withOperation {
summary("Validate a backup file")
description("Reports missing sources and trackers, expects a Tachiyomi protobuf backup as a file upload, the file must be named \"backup.proto.gz\"")
}
)
}
uploadedFile("backup.proto.gz") {
it.description("Protobuf backup")
it.required(true)
}
},
behaviorOf = { ctx ->
ctx.future(
future {
ProtoBackupValidator.validate(ctx.uploadedFile("backup.proto.gz")!!.content)
}
)
},
withResults = {
json<AbstractBackupValidator.ValidationResult>(HttpCode.OK)
}
)
}

View File

@@ -7,50 +7,127 @@ package suwayomi.tachidesk.manga.controller
* 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.Context
import io.javalin.http.HttpCode
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.impl.CategoryManga
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.server.util.formParam
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.withOperation
object CategoryController {
/** category list */
fun categoryList(ctx: Context) {
ctx.json(Category.getCategoryList())
}
val categoryList = handler(
documentWith = {
withOperation {
summary("Category list")
description("get a list of categories")
}
},
behaviorOf = { ctx ->
ctx.json(Category.getCategoryList())
},
withResults = {
json<List<CategoryDataClass>>(HttpCode.OK)
}
)
/** category create */
fun categoryCreate(ctx: Context) {
val name = ctx.formParam("name")!!
Category.createCategory(name)
ctx.status(200)
}
val categoryCreate = handler(
formParam<String>("name"),
documentWith = {
withOperation {
summary("Category create")
description("Create a category")
}
},
behaviorOf = { ctx, name ->
if (Category.createCategory(name) != -1) {
ctx.status(200)
} else {
ctx.status(HttpCode.BAD_REQUEST)
}
},
withResults = {
httpCode(HttpCode.OK)
httpCode(HttpCode.BAD_REQUEST)
}
)
/** category modification */
fun categoryModify(ctx: Context) {
val categoryId = ctx.pathParam("categoryId").toInt()
val name = ctx.formParam("name")
val isDefault = ctx.formParam("default")?.toBoolean()
Category.updateCategory(categoryId, name, isDefault)
ctx.status(200)
}
val categoryModify = handler(
pathParam<Int>("categoryId"),
formParam<String?>("name"),
formParam<Boolean?>("default"),
documentWith = {
withOperation {
summary("Category modify")
description("Modify a category")
}
},
behaviorOf = { ctx, categoryId, name, isDefault ->
Category.updateCategory(categoryId, name, isDefault)
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** category delete */
fun categoryDelete(ctx: Context) {
val categoryId = ctx.pathParam("categoryId").toInt()
Category.removeCategory(categoryId)
ctx.status(200)
}
val categoryDelete = handler(
pathParam<Int>("categoryId"),
documentWith = {
withOperation {
summary("Category delete")
description("Delete a category")
}
},
behaviorOf = { ctx, categoryId ->
Category.removeCategory(categoryId)
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** returns the manga list associated with a category */
fun categoryMangas(ctx: Context) {
val categoryId = ctx.pathParam("categoryId").toInt()
ctx.json(CategoryManga.getCategoryMangaList(categoryId))
}
val categoryMangas = handler(
pathParam<Int>("categoryId"),
documentWith = {
withOperation {
summary("Category manga")
description("Returns the manga list associated with a category")
}
},
behaviorOf = { ctx, categoryId ->
ctx.json(CategoryManga.getCategoryMangaList(categoryId))
},
withResults = {
json<List<MangaDataClass>>(HttpCode.OK)
}
)
/** category re-ordering */
fun categoryReorder(ctx: Context) {
val from = ctx.formParam("from")!!.toInt()
val to = ctx.formParam("to")!!.toInt()
Category.reorderCategory(from, to)
ctx.status(200)
}
val categoryReorder = handler(
formParam<Int>("from"),
formParam<Int>("to"),
documentWith = {
withOperation {
summary("Category re-ordering")
description("Re-order a category")
}
},
behaviorOf = { ctx, from, to ->
Category.reorderCategory(from, to)
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
}

View File

@@ -7,10 +7,13 @@ package suwayomi.tachidesk.manga.controller
* 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.Context
import io.javalin.http.HttpCode
import io.javalin.websocket.WsConfig
import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.server.JavalinSetup.future
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.withOperation
object DownloadController {
/** Download queue stats */
@@ -28,45 +31,100 @@ object DownloadController {
}
/** Start the downloader */
fun start(ctx: Context) {
DownloadManager.start()
val start = handler(
documentWith = {
withOperation {
summary("Downloader start")
description("Start the downloader")
}
},
behaviorOf = { ctx ->
DownloadManager.start()
ctx.status(200)
}
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** Stop the downloader */
fun stop(ctx: Context) {
DownloadManager.stop()
val stop = handler(
documentWith = {
withOperation {
summary("Downloader stop")
description("Stop the downloader")
}
},
behaviorOf = { ctx ->
DownloadManager.stop()
ctx.status(200)
}
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** clear download queue */
fun clear(ctx: Context) {
DownloadManager.clear()
val clear = handler(
documentWith = {
withOperation {
summary("Downloader clear")
description("Clear download queue")
}
},
behaviorOf = { ctx ->
DownloadManager.clear()
ctx.status(200)
}
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** Queue chapter for download */
fun queueChapter(ctx: Context) {
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
val mangaId = ctx.pathParam("mangaId").toInt()
ctx.future(
future {
DownloadManager.enqueue(chapterIndex, mangaId)
val queueChapter = handler(
pathParam<Int>("chapterIndex"),
pathParam<Int>("mangaId"),
documentWith = {
withOperation {
summary("Downloader add chapter")
description("Queue chapter for download")
}
)
}
},
behaviorOf = { ctx, chapterIndex, mangaId ->
ctx.future(
future {
DownloadManager.enqueue(chapterIndex, mangaId)
}
)
},
withResults = {
httpCode(HttpCode.OK)
httpCode(HttpCode.NOT_FOUND)
}
)
/** delete chapter from download queue */
fun unqueueChapter(ctx: Context) {
val chapterIndex = ctx.pathParam("chapterIndex").toInt()
val mangaId = ctx.pathParam("mangaId").toInt()
val unqueueChapter = handler(
pathParam<Int>("chapterIndex"),
pathParam<Int>("mangaId"),
documentWith = {
withOperation {
summary("Downloader remove chapter")
description("Delete chapter from download queue")
}
},
behaviorOf = { ctx, chapterIndex, mangaId ->
DownloadManager.unqueue(chapterIndex, mangaId)
DownloadManager.unqueue(chapterIndex, mangaId)
ctx.status(200)
}
ctx.status(200)
},
withResults = {
httpCode(HttpCode.OK)
}
)
}

View File

@@ -7,78 +7,160 @@ package suwayomi.tachidesk.manga.controller
* 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.Context
import io.javalin.http.HttpCode
import mu.KotlinLogging
import suwayomi.tachidesk.manga.impl.extension.Extension
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
import suwayomi.tachidesk.server.JavalinSetup.future
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.queryParam
import suwayomi.tachidesk.server.util.withOperation
object ExtensionController {
private val logger = KotlinLogging.logger {}
/** list all extensions */
fun list(ctx: Context) {
ctx.future(
future {
ExtensionsList.getExtensionList()
val list = handler(
documentWith = {
withOperation {
summary("Extension list")
description("List all extensions")
}
)
}
},
behaviorOf = { ctx ->
ctx.future(
future {
ExtensionsList.getExtensionList()
}
)
},
withResults = {
json<List<ExtensionDataClass>>(HttpCode.OK)
}
)
/** install extension identified with "pkgName" */
fun install(ctx: Context) {
val pkgName = ctx.pathParam("pkgName")
ctx.future(
future {
Extension.installExtension(pkgName)
val install = handler(
pathParam<String>("pkgName"),
documentWith = {
withOperation {
summary("Extension install")
description("install extension identified with \"pkgName\"")
}
)
}
},
behaviorOf = { ctx, pkgName ->
ctx.future(
future {
Extension.installExtension(pkgName)
}
)
},
withResults = {
httpCode(HttpCode.CREATED)
httpCode(HttpCode.FOUND)
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
}
)
/** install the uploaded apk file */
fun installFile(ctx: Context) {
val uploadedFile = ctx.uploadedFile("file")!!
logger.debug { "Uploaded extension file name: " + uploadedFile.filename }
ctx.future(
future {
Extension.installExternalExtension(uploadedFile.content, uploadedFile.filename)
val installFile = handler(
documentWith = {
withOperation {
summary("Extension install apk")
description("Install the uploaded apk file")
}
)
}
uploadedFile("file") {
it.description("Extension apk")
it.required(true)
}
},
behaviorOf = { ctx ->
val uploadedFile = ctx.uploadedFile("file")!!
logger.debug { "Uploaded extension file name: " + uploadedFile.filename }
ctx.future(
future {
Extension.installExternalExtension(uploadedFile.content, uploadedFile.filename)
}
)
},
withResults = {
httpCode(HttpCode.CREATED)
httpCode(HttpCode.FOUND)
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
}
)
/** update extension identified with "pkgName" */
fun update(ctx: Context) {
val pkgName = ctx.pathParam("pkgName")
ctx.future(
future {
Extension.updateExtension(pkgName)
val update = handler(
pathParam<String>("pkgName"),
documentWith = {
withOperation {
summary("Extension update")
description("Update extension identified with \"pkgName\"")
}
)
}
},
behaviorOf = { ctx, pkgName ->
ctx.future(
future {
Extension.updateExtension(pkgName)
}
)
},
withResults = {
httpCode(HttpCode.CREATED)
httpCode(HttpCode.FOUND)
httpCode(HttpCode.NOT_FOUND)
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
}
)
/** uninstall extension identified with "pkgName" */
fun uninstall(ctx: Context) {
val pkgName = ctx.pathParam("pkgName")
Extension.uninstallExtension(pkgName)
ctx.status(200)
}
val uninstall = handler(
pathParam<String>("pkgName"),
documentWith = {
withOperation {
summary("Extension uninstall")
description("Uninstall extension identified with \"pkgName\"")
}
},
behaviorOf = { ctx, pkgName ->
Extension.uninstallExtension(pkgName)
ctx.status(200)
},
withResults = {
httpCode(HttpCode.CREATED)
httpCode(HttpCode.FOUND)
httpCode(HttpCode.NOT_FOUND)
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
}
)
/** icon for extension named `apkName` */
fun icon(ctx: Context) {
val apkName = ctx.pathParam("apkName")
val useCache = ctx.queryParam("useCache")?.toBoolean() ?: true
ctx.future(
future { Extension.getExtensionIcon(apkName, useCache) }
.thenApply {
ctx.header("content-type", it.second)
it.first
}
)
}
val icon = handler(
pathParam<String>("apkName"),
queryParam("useCache", true),
documentWith = {
withOperation {
summary("Extension icon")
description("Icon for extension named `apkName`")
}
},
behaviorOf = { ctx, apkName, useCache ->
ctx.future(
future { Extension.getExtensionIcon(apkName, useCache) }
.thenApply {
ctx.header("content-type", it.second)
it.first
}
)
},
withResults = {
httpCode(HttpCode.OK)
httpCode(HttpCode.NOT_FOUND)
}
)
}

View File

@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
* 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.Context
import io.javalin.http.HttpCode
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.kodein.di.DI
@@ -18,87 +18,205 @@ import suwayomi.tachidesk.manga.impl.Search
import suwayomi.tachidesk.manga.impl.Search.FilterChange
import suwayomi.tachidesk.manga.impl.Source
import suwayomi.tachidesk.manga.impl.Source.SourcePreferenceChange
import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
import suwayomi.tachidesk.server.JavalinSetup.future
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.queryParam
import suwayomi.tachidesk.server.util.withOperation
import javax.sound.sampled.SourceDataLine
object SourceController {
/** list of sources */
fun list(ctx: Context) {
ctx.json(Source.getSourceList())
}
val list = handler(
documentWith = {
withOperation {
summary("Sources list")
description("List of sources")
}
},
behaviorOf = { ctx ->
ctx.json(Source.getSourceList())
},
withResults = {
json<List<SourceDataLine>>(HttpCode.OK)
}
)
/** fetch source with id `sourceId` */
fun retrieve(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
ctx.json(Source.getSource(sourceId)!!)
}
val retrieve = handler(
pathParam<Long>("sourceId"),
documentWith = {
withOperation {
summary("Source fetch")
description("Fetch source with id `sourceId`")
}
},
behaviorOf = { ctx, sourceId ->
ctx.json(Source.getSource(sourceId)!!)
},
withResults = {
json<SourceDataLine>(HttpCode.OK)
httpCode(HttpCode.NOT_FOUND)
}
)
/** popular mangas from source with id `sourceId` */
fun popular(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
val pageNum = ctx.pathParam("pageNum").toInt()
ctx.future(
future {
MangaList.getMangaList(sourceId, pageNum, popular = true)
val popular = handler(
pathParam<Long>("sourceId"),
pathParam<Int>("pageNum"),
documentWith = {
withOperation {
summary("Source popular manga")
description("Popular mangas from source with id `sourceId`")
}
)
}
},
behaviorOf = { ctx, sourceId, pageNum ->
ctx.future(
future {
MangaList.getMangaList(sourceId, pageNum, popular = true)
}
)
},
withResults = {
json<PagedMangaListDataClass>(HttpCode.OK)
}
)
/** latest mangas from source with id `sourceId` */
fun latest(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
val pageNum = ctx.pathParam("pageNum").toInt()
ctx.future(
future {
MangaList.getMangaList(sourceId, pageNum, popular = false)
val latest = handler(
pathParam<Long>("sourceId"),
pathParam<Int>("pageNum"),
documentWith = {
withOperation {
summary("Source latest manga")
description("Latest mangas from source with id `sourceId`")
}
)
}
},
behaviorOf = { ctx, sourceId, pageNum ->
ctx.future(
future {
MangaList.getMangaList(sourceId, pageNum, popular = false)
}
)
},
withResults = {
json<PagedMangaListDataClass>(HttpCode.OK)
}
)
/** fetch preferences of source with id `sourceId` */
fun getPreferences(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
ctx.json(Source.getSourcePreferences(sourceId))
}
val getPreferences = handler(
pathParam<Long>("sourceId"),
documentWith = {
withOperation {
summary("Source preferences")
description("Fetch preferences of source with id `sourceId`")
}
},
behaviorOf = { ctx, sourceId ->
ctx.json(Source.getSourcePreferences(sourceId))
},
withResults = {
json<List<Source.PreferenceObject>>(HttpCode.OK)
}
)
/** set one preference of source with id `sourceId` */
fun setPreference(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
val preferenceChange = ctx.bodyAsClass(SourcePreferenceChange::class.java)
ctx.json(Source.setSourcePreference(sourceId, preferenceChange))
}
val setPreference = handler(
pathParam<Long>("sourceId"),
documentWith = {
withOperation {
summary("Source preference set")
description("Set one preference of source with id `sourceId`")
}
},
behaviorOf = { ctx, sourceId ->
val preferenceChange = ctx.bodyAsClass(SourcePreferenceChange::class.java)
ctx.json(Source.setSourcePreference(sourceId, preferenceChange))
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** fetch filters of source with id `sourceId` */
fun getFilters(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
val reset = ctx.queryParam("reset")?.toBoolean() ?: false
ctx.json(Search.getFilterList(sourceId, reset))
}
val getFilters = handler(
pathParam<Long>("sourceId"),
queryParam("reset", false),
documentWith = {
withOperation {
summary("Source filters")
description("Fetch filters of source with id `sourceId`")
}
},
behaviorOf = { ctx, sourceId, reset ->
ctx.json(Search.getFilterList(sourceId, reset))
},
withResults = {
json<List<Search.FilterObject>>(HttpCode.OK)
}
)
private val json by DI.global.instance<Json>()
/** change filters of source with id `sourceId` */
fun setFilters(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
val filterChange = try {
json.decodeFromString<List<FilterChange>>(ctx.body())
} catch (e: Exception) {
listOf(json.decodeFromString<FilterChange>(ctx.body()))
}
val setFilters = handler(
pathParam<Long>("sourceId"),
documentWith = {
withOperation {
summary("Source filters set")
description("Change filters of source with id `sourceId`")
}
},
behaviorOf = { ctx, sourceId ->
val filterChange = try {
json.decodeFromString<List<FilterChange>>(ctx.body())
} catch (e: Exception) {
listOf(json.decodeFromString<FilterChange>(ctx.body()))
}
ctx.json(Search.setFilter(sourceId, filterChange))
}
ctx.json(Search.setFilter(sourceId, filterChange))
},
withResults = {
httpCode(HttpCode.OK)
}
)
/** single source search */
fun searchSingle(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong()
val searchTerm = ctx.queryParam("searchTerm") ?: ""
val pageNum = ctx.queryParam("pageNum")?.toInt() ?: 1
ctx.future(future { Search.sourceSearch(sourceId, searchTerm, pageNum) })
}
val searchSingle = handler(
pathParam<Long>("sourceId"),
queryParam("searchTerm", ""),
queryParam("pageNum", 1),
documentWith = {
withOperation {
summary("Source search")
description("Single source search")
}
},
behaviorOf = { ctx, sourceId, searchTerm, pageNum ->
ctx.future(future { Search.sourceSearch(sourceId, searchTerm, pageNum) })
},
withResults = {
json<PagedMangaListDataClass>(HttpCode.OK)
}
)
/** all source search */
fun searchAll(ctx: Context) { // TODO
val searchTerm = ctx.pathParam("searchTerm")
ctx.json(Search.sourceGlobalSearch(searchTerm))
}
val searchAll = handler(
pathParam<String>("searchTerm"),
documentWith = {
withOperation {
summary("Source global search")
description("All source search")
}
},
behaviorOf = { ctx, searchTerm -> // TODO
ctx.json(Search.sourceGlobalSearch(searchTerm))
},
withResults = {
httpCode(HttpCode.OK)
}
)
}

View File

@@ -1,6 +1,5 @@
package suwayomi.tachidesk.manga.controller
import io.javalin.http.Context
import io.javalin.http.HttpCode
import io.javalin.websocket.WsConfig
import kotlinx.coroutines.runBlocking
@@ -12,10 +11,15 @@ import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.impl.CategoryManga
import suwayomi.tachidesk.manga.impl.Chapter
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.impl.update.UpdateStatus
import suwayomi.tachidesk.manga.impl.update.UpdaterSocket
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.dataclass.PaginatedList
import suwayomi.tachidesk.server.JavalinSetup.future
import suwayomi.tachidesk.server.util.formParam
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.withOperation
/*
@@ -29,35 +33,57 @@ object UpdateController {
private val logger = KotlinLogging.logger { }
/** get recently updated manga chapters */
fun recentChapters(ctx: Context) {
val pageNum = ctx.pathParam("pageNum").toInt()
ctx.future(
future {
Chapter.getRecentChapters(pageNum)
}
)
}
fun categoryUpdate(ctx: Context) {
val categoryId = ctx.formParam("category")?.toIntOrNull()
val categoriesForUpdate = ArrayList<CategoryDataClass>()
if (categoryId == null) {
logger.info { "Adding Library to Update Queue" }
categoriesForUpdate.addAll(Category.getCategoryList())
} else {
val category = Category.getCategoryById(categoryId)
if (category != null) {
categoriesForUpdate.add(category)
} else {
logger.info { "No Category found" }
ctx.status(HttpCode.BAD_REQUEST)
return
val recentChapters = handler(
pathParam<Int>("pageNum"),
documentWith = {
withOperation {
summary("Updates fetch")
description("Get recently updated manga chapters")
}
},
behaviorOf = { ctx, pageNum ->
ctx.future(
future {
Chapter.getRecentChapters(pageNum)
}
)
},
withResults = {
json<PaginatedList<MangaDataClass>>(HttpCode.OK)
}
addCategoriesToUpdateQueue(categoriesForUpdate, true)
ctx.status(HttpCode.OK)
}
)
val categoryUpdate = handler(
formParam<Int?>("categoryId"),
documentWith = {
withOperation {
summary("Updater start")
description("Starts the updater")
}
},
behaviorOf = { ctx, categoryId ->
val categoriesForUpdate = ArrayList<CategoryDataClass>()
if (categoryId == null) {
logger.info { "Adding Library to Update Queue" }
categoriesForUpdate.addAll(Category.getCategoryList())
} else {
val category = Category.getCategoryById(categoryId)
if (category != null) {
categoriesForUpdate.add(category)
} else {
logger.info { "No Category found" }
ctx.status(HttpCode.BAD_REQUEST)
return@handler
}
}
addCategoriesToUpdateQueue(categoriesForUpdate, true)
ctx.status(HttpCode.OK)
},
withResults = {
httpCode(HttpCode.OK)
httpCode(HttpCode.BAD_REQUEST)
}
)
private fun addCategoriesToUpdateQueue(categories: List<CategoryDataClass>, clear: Boolean = false) {
val updater by DI.global.instance<IUpdater>()
@@ -84,15 +110,27 @@ object UpdateController {
}
}
fun updateSummary(ctx: Context) {
val updater by DI.global.instance<IUpdater>()
ctx.json(updater.getStatus().value.getJsonSummary())
}
val updateSummary = handler(
documentWith = {
withOperation {
summary("Updater summary")
description("Gets the latest updater summary")
}
},
behaviorOf = { ctx ->
val updater by DI.global.instance<IUpdater>()
ctx.json(updater.getStatus().value.getJsonSummary())
},
withResults = {
json<UpdateStatus>(HttpCode.OK)
}
)
val reset = handler(
documentWith = {
withOperation {
summary("Stops and resets the Updater")
summary("Updater reset")
description("Stops and resets the Updater")
}
},
behaviorOf = { ctx ->