mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-23 13:02:34 +01:00
Update dependency io.javalin:javalin to v6 (#1152)
* Update dependency io.javalin:javalin to v6 * Simple compile fixes * Simple compile fixes pass 2 * Add results to futures * Setup jetty server and api routes * Setup Cors * Setup basic auth * Documentation stubs * Replace chapter mutex cache * Fix compile * Disable Jetty Logging --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Syer10 <syer10@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ kotlin = "2.0.21"
|
|||||||
coroutines = "1.9.0"
|
coroutines = "1.9.0"
|
||||||
serialization = "1.7.3"
|
serialization = "1.7.3"
|
||||||
okhttp = "5.0.0-alpha.14" # Major version is locked by Tachiyomi extensions
|
okhttp = "5.0.0-alpha.14" # Major version is locked by Tachiyomi extensions
|
||||||
javalin = "4.6.8" # Javalin 5.0.0+ requires Java 11
|
javalin = "6.3.0" # Javalin 5.0.0+ requires Java 11
|
||||||
jackson = "2.13.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
jackson = "2.13.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
||||||
exposed = "0.40.1"
|
exposed = "0.40.1"
|
||||||
dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed
|
dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed
|
||||||
@@ -104,6 +104,7 @@ xmlpull = "xmlpull:xmlpull:1.1.3.4a"
|
|||||||
|
|
||||||
# Disk & File
|
# Disk & File
|
||||||
appdirs = "net.harawata:appdirs:1.2.2"
|
appdirs = "net.harawata:appdirs:1.2.2"
|
||||||
|
cache4k = "io.github.reactivecircus.cache4k:cache4k:0.13.0"
|
||||||
zip4j = "net.lingala.zip4j:zip4j:2.11.5"
|
zip4j = "net.lingala.zip4j:zip4j:2.11.5"
|
||||||
commonscompress = "org.apache.commons:commons-compress:1.27.1"
|
commonscompress = "org.apache.commons:commons-compress:1.27.1"
|
||||||
junrar = "com.github.junrar:junrar:7.5.5"
|
junrar = "com.github.junrar:junrar:7.5.5"
|
||||||
@@ -200,7 +201,7 @@ okhttp = [
|
|||||||
]
|
]
|
||||||
javalin = [
|
javalin = [
|
||||||
"javalin-core",
|
"javalin-core",
|
||||||
"javalin-openapi",
|
#"javalin-openapi",
|
||||||
]
|
]
|
||||||
jackson = [
|
jackson = [
|
||||||
"jackson-databind",
|
"jackson-databind",
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ dependencies {
|
|||||||
implementation(libs.asm)
|
implementation(libs.asm)
|
||||||
|
|
||||||
// Disk & File
|
// Disk & File
|
||||||
|
implementation(libs.cache4k)
|
||||||
implementation(libs.zip4j)
|
implementation(libs.zip4j)
|
||||||
implementation(libs.commonscompress)
|
implementation(libs.commonscompress)
|
||||||
implementation(libs.junrar)
|
implementation(libs.junrar)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.global.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import suwayomi.tachidesk.global.impl.GlobalMeta
|
import suwayomi.tachidesk.global.impl.GlobalMeta
|
||||||
import suwayomi.tachidesk.server.util.formParam
|
import suwayomi.tachidesk.server.util.formParam
|
||||||
import suwayomi.tachidesk.server.util.handler
|
import suwayomi.tachidesk.server.util.handler
|
||||||
@@ -28,7 +28,7 @@ object GlobalMetaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,8 +48,8 @@ object GlobalMetaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.global.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import suwayomi.tachidesk.global.impl.About
|
import suwayomi.tachidesk.global.impl.About
|
||||||
import suwayomi.tachidesk.global.impl.AboutDataClass
|
import suwayomi.tachidesk.global.impl.AboutDataClass
|
||||||
import suwayomi.tachidesk.global.impl.AppUpdate
|
import suwayomi.tachidesk.global.impl.AppUpdate
|
||||||
@@ -31,7 +31,7 @@ object SettingsController {
|
|||||||
ctx.json(About.getAbout())
|
ctx.json(About.getAbout())
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<AboutDataClass>(HttpCode.OK)
|
json<AboutDataClass>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,12 +45,13 @@ object SettingsController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { AppUpdate.checkUpdate() },
|
future { AppUpdate.checkUpdate() }
|
||||||
)
|
.thenApply { ctx.json(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<UpdateDataClass>>(HttpCode.OK)
|
json<Array<UpdateDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ package suwayomi.tachidesk.graphql.controller
|
|||||||
|
|
||||||
import io.javalin.http.ContentType
|
import io.javalin.http.ContentType
|
||||||
import io.javalin.http.Context
|
import io.javalin.http.Context
|
||||||
|
import io.javalin.http.HttpStatus
|
||||||
import io.javalin.websocket.WsConfig
|
import io.javalin.websocket.WsConfig
|
||||||
import suwayomi.tachidesk.graphql.server.TachideskGraphQLServer
|
import suwayomi.tachidesk.graphql.server.TachideskGraphQLServer
|
||||||
import suwayomi.tachidesk.graphql.server.TemporaryFileStorage
|
import suwayomi.tachidesk.graphql.server.TemporaryFileStorage
|
||||||
@@ -20,11 +21,17 @@ object GraphQLController {
|
|||||||
|
|
||||||
/** execute graphql query */
|
/** execute graphql query */
|
||||||
fun execute(ctx: Context) {
|
fun execute(ctx: Context) {
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
server.execute(ctx)
|
server.execute(ctx)
|
||||||
},
|
}.thenApply {
|
||||||
)
|
if (it != null) {
|
||||||
|
ctx.json(it)
|
||||||
|
} else {
|
||||||
|
ctx.status(HttpStatus.BAD_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playground(ctx: Context) {
|
fun playground(ctx: Context) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class BackupMutation {
|
|||||||
val (clientMutationId, backup) = input
|
val (clientMutationId, backup) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
val restoreId = ProtoBackupImport.restore(backup.content)
|
val restoreId = ProtoBackupImport.restore(backup.content())
|
||||||
|
|
||||||
withTimeout(10.seconds) {
|
withTimeout(10.seconds) {
|
||||||
ProtoBackupImport.notifyFlow.first {
|
ProtoBackupImport.notifyFlow.first {
|
||||||
|
|||||||
@@ -165,9 +165,9 @@ class ExtensionMutation {
|
|||||||
|
|
||||||
return future {
|
return future {
|
||||||
asDataFetcherResult {
|
asDataFetcherResult {
|
||||||
Extension.installExternalExtension(extensionFile.content, extensionFile.filename)
|
Extension.installExternalExtension(extensionFile.content(), extensionFile.filename())
|
||||||
|
|
||||||
val dbExtension = transaction { ExtensionTable.select { ExtensionTable.apkName eq extensionFile.filename }.first() }
|
val dbExtension = transaction { ExtensionTable.select { ExtensionTable.apkName eq extensionFile.filename() }.first() }
|
||||||
|
|
||||||
InstallExternalExtensionPayload(
|
InstallExternalExtensionPayload(
|
||||||
clientMutationId,
|
clientMutationId,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class BackupQuery {
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun validateBackup(input: ValidateBackupInput): ValidateBackupResult {
|
fun validateBackup(input: ValidateBackupInput): ValidateBackupResult {
|
||||||
val result = ProtoBackupValidator.validate(input.backup.content)
|
val result = ProtoBackupValidator.validate(input.backup.content())
|
||||||
return ValidateBackupResult(
|
return ValidateBackupResult(
|
||||||
result.missingSourceIds.map { ValidateBackupSource(it.first, it.second) },
|
result.missingSourceIds.map { ValidateBackupSource(it.first, it.second) },
|
||||||
result.missingTrackers.map { ValidateBackupTracker(it) },
|
result.missingTrackers.map { ValidateBackupTracker(it) },
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import com.expediagroup.graphql.server.types.GraphQLRequest
|
|||||||
import com.expediagroup.graphql.server.types.GraphQLServerRequest
|
import com.expediagroup.graphql.server.types.GraphQLServerRequest
|
||||||
import io.javalin.http.Context
|
import io.javalin.http.Context
|
||||||
import io.javalin.http.UploadedFile
|
import io.javalin.http.UploadedFile
|
||||||
import io.javalin.plugin.json.jsonMapper
|
import io.javalin.json.fromJsonString
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
||||||
@@ -33,20 +33,13 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val request =
|
val request =
|
||||||
context.jsonMapper().fromJsonString(
|
context.jsonMapper().fromJsonString<GraphQLServerRequest>(formParam)
|
||||||
formParam,
|
|
||||||
GraphQLServerRequest::class.java,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val map =
|
val map =
|
||||||
context
|
context
|
||||||
.formParam("map")
|
.formParam("map")
|
||||||
?.let {
|
?.let {
|
||||||
context.jsonMapper().fromJsonString(
|
context.jsonMapper().fromJsonString<Map<String, List<String>>>(it)
|
||||||
it,
|
|
||||||
Map::class.java as Class<Map<String, List<String>>>,
|
|
||||||
)
|
|
||||||
}.orEmpty()
|
}.orEmpty()
|
||||||
|
|
||||||
val mapItems =
|
val mapItems =
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class ApolloSubscriptionProtocolHandler(
|
|||||||
|
|
||||||
if (operationMessage.type != GQL_PING.type) {
|
if (operationMessage.type != GQL_PING.type) {
|
||||||
logger.debug {
|
logger.debug {
|
||||||
"GraphQL subscription client message, sessionId=${context.sessionId} type=${operationMessage.type} operationName=${
|
"GraphQL subscription client message, sessionId=${context.sessionId()} type=${operationMessage.type} operationName=${
|
||||||
getOperationName(operationMessage.payload)
|
getOperationName(operationMessage.payload)
|
||||||
} ${
|
} ${
|
||||||
if (serverConfig.gqlDebugLogsEnabled.value) {
|
if (serverConfig.gqlDebugLogsEnabled.value) {
|
||||||
@@ -118,7 +118,7 @@ class ApolloSubscriptionProtocolHandler(
|
|||||||
|
|
||||||
if (sessionState.doesOperationExist(operationMessage)) {
|
if (sessionState.doesOperationExist(operationMessage)) {
|
||||||
sessionState.terminateSession(context, CloseStatus(4409, "Subscriber for ${operationMessage.id} already exists"))
|
sessionState.terminateSession(context, CloseStatus(4409, "Subscriber for ${operationMessage.id} already exists"))
|
||||||
logger.info("Already subscribed to operation ${operationMessage.id} for session ${context.sessionId}")
|
logger.info("Already subscribed to operation ${operationMessage.id} for session ${context.sessionId()}")
|
||||||
return emptyFlow()
|
return emptyFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ class ApolloSubscriptionProtocolHandler(
|
|||||||
private fun onPing(): Flow<SubscriptionOperationMessage> = flowOf(pongMessage)
|
private fun onPing(): Flow<SubscriptionOperationMessage> = flowOf(pongMessage)
|
||||||
|
|
||||||
private fun onDisconnect(context: WsContext): Flow<SubscriptionOperationMessage> {
|
private fun onDisconnect(context: WsContext): Flow<SubscriptionOperationMessage> {
|
||||||
logger.debug("Session \"${context.sessionId}\" disconnected")
|
logger.debug("Session \"${context.sessionId()}\" disconnected")
|
||||||
sessionState.terminateSession(context, CloseStatus(1000, "Normal Closure"))
|
sessionState.terminateSession(context, CloseStatus(1000, "Normal Closure"))
|
||||||
return emptyFlow()
|
return emptyFlow()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ internal class ApolloSubscriptionSessionState {
|
|||||||
context: WsContext,
|
context: WsContext,
|
||||||
graphQLContext: GraphQLContext,
|
graphQLContext: GraphQLContext,
|
||||||
) {
|
) {
|
||||||
cachedGraphQLContext[context.sessionId] = graphQLContext
|
cachedGraphQLContext[context.sessionId()] = graphQLContext
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the graphQL context for this session.
|
* Return the graphQL context for this session.
|
||||||
*/
|
*/
|
||||||
fun getGraphQLContext(context: WsContext): GraphQLContext =
|
fun getGraphQLContext(context: WsContext): GraphQLContext =
|
||||||
cachedGraphQLContext[context.sessionId] ?: emptyMap<Any, Any>().toGraphQLContext()
|
cachedGraphQLContext[context.sessionId()] ?: emptyMap<Any, Any>().toGraphQLContext()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the operation that is sending data to the client.
|
* Save the operation that is sending data to the client.
|
||||||
@@ -58,7 +58,7 @@ internal class ApolloSubscriptionSessionState {
|
|||||||
val id = operationMessage.id
|
val id = operationMessage.id
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
activeOperations[id] = subscription
|
activeOperations[id] = subscription
|
||||||
sessionToOperationId.getOrPut(context.sessionId) { CopyOnWriteArrayList() } += id
|
sessionToOperationId.getOrPut(context.sessionId()) { CopyOnWriteArrayList() } += id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,10 +87,10 @@ internal class ApolloSubscriptionSessionState {
|
|||||||
context: WsContext,
|
context: WsContext,
|
||||||
code: CloseStatus,
|
code: CloseStatus,
|
||||||
) {
|
) {
|
||||||
sessionToOperationId.remove(context.sessionId)?.forEach {
|
sessionToOperationId.remove(context.sessionId())?.forEach {
|
||||||
removeActiveOperation(it)
|
removeActiveOperation(it)
|
||||||
}
|
}
|
||||||
cachedGraphQLContext.remove(context.sessionId)
|
cachedGraphQLContext.remove(context.sessionId())
|
||||||
context.closeSession(code)
|
context.closeSession(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package suwayomi.tachidesk.manga.controller
|
package suwayomi.tachidesk.manga.controller
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
|
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
|
||||||
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport
|
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport
|
||||||
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupImport
|
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupImport
|
||||||
@@ -28,14 +28,16 @@ object BackupController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ProtoBackupImport.restoreLegacy(ctx.bodyAsInputStream())
|
ProtoBackupImport.restoreLegacy(ctx.bodyInputStream())
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.json(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,15 +56,17 @@ object BackupController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
// TODO: rewrite this with ctx.uploadedFiles(), don't call the multipart field "backup.proto.gz"
|
// TODO: rewrite this with ctx.uploadedFiles(), don't call the multipart field "backup.proto.gz"
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ProtoBackupImport.restoreLegacy(ctx.uploadedFile("backup.proto.gz")!!.content)
|
ProtoBackupImport.restoreLegacy(ctx.uploadedFile("backup.proto.gz")!!.content())
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.json(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +81,7 @@ object BackupController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.contentType("application/octet-stream")
|
ctx.contentType("application/octet-stream")
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ProtoBackupExport.createBackup(
|
ProtoBackupExport.createBackup(
|
||||||
BackupFlags(
|
BackupFlags(
|
||||||
@@ -88,11 +92,11 @@ object BackupController {
|
|||||||
includeHistory = true,
|
includeHistory = true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
}.thenApply { ctx.result(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
stream(HttpCode.OK)
|
stream(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,7 +113,7 @@ object BackupController {
|
|||||||
ctx.contentType("application/octet-stream")
|
ctx.contentType("application/octet-stream")
|
||||||
|
|
||||||
ctx.header("Content-Disposition", """attachment; filename="${Backup.getFilename()}"""")
|
ctx.header("Content-Disposition", """attachment; filename="${Backup.getFilename()}"""")
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ProtoBackupExport.createBackup(
|
ProtoBackupExport.createBackup(
|
||||||
BackupFlags(
|
BackupFlags(
|
||||||
@@ -120,11 +124,11 @@ object BackupController {
|
|||||||
includeHistory = true,
|
includeHistory = true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
}.thenApply { ctx.result(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
stream(HttpCode.OK)
|
stream(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -138,14 +142,16 @@ object BackupController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ProtoBackupValidator.validate(ctx.bodyAsInputStream())
|
ProtoBackupValidator.validate(ctx.bodyInputStream())
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.json(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<ProtoBackupValidator.ValidationResult>(HttpCode.OK)
|
json<ProtoBackupValidator.ValidationResult>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -167,14 +173,16 @@ object BackupController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ProtoBackupValidator.validate(ctx.uploadedFile("backup.proto.gz")!!.content)
|
ProtoBackupValidator.validate(ctx.uploadedFile("backup.proto.gz")!!.content())
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.json(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<ProtoBackupValidator.ValidationResult>(HttpCode.OK)
|
json<ProtoBackupValidator.ValidationResult>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import suwayomi.tachidesk.manga.impl.Category
|
import suwayomi.tachidesk.manga.impl.Category
|
||||||
import suwayomi.tachidesk.manga.impl.CategoryManga
|
import suwayomi.tachidesk.manga.impl.CategoryManga
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
||||||
@@ -31,7 +31,7 @@ object CategoryController {
|
|||||||
ctx.json(Category.getCategoryList())
|
ctx.json(Category.getCategoryList())
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<CategoryDataClass>>(HttpCode.OK)
|
json<Array<CategoryDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,12 +49,12 @@ object CategoryController {
|
|||||||
if (Category.createCategory(name) != -1) {
|
if (Category.createCategory(name) != -1) {
|
||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
} else {
|
} else {
|
||||||
ctx.status(HttpCode.BAD_REQUEST)
|
ctx.status(HttpStatus.BAD_REQUEST)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.BAD_REQUEST)
|
httpCode(HttpStatus.BAD_REQUEST)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ object CategoryController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ object CategoryController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ object CategoryController {
|
|||||||
ctx.json(CategoryManga.getCategoryMangaList(categoryId))
|
ctx.json(CategoryManga.getCategoryMangaList(categoryId))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<MangaDataClass>>(HttpCode.OK)
|
json<Array<MangaDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ object CategoryController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -155,8 +155,8 @@ object CategoryController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import io.javalin.websocket.WsConfig
|
import io.javalin.websocket.WsConfig
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import suwayomi.tachidesk.manga.impl.download.DownloadManager
|
import suwayomi.tachidesk.manga.impl.download.DownloadManager
|
||||||
@@ -48,7 +48,7 @@ object DownloadController {
|
|||||||
DownloadManager.start()
|
DownloadManager.start()
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,12 +62,13 @@ object DownloadController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { DownloadManager.stop() },
|
future { DownloadManager.stop() }
|
||||||
)
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,12 +82,13 @@ object DownloadController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { DownloadManager.clear() },
|
future { DownloadManager.clear() }
|
||||||
)
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,15 +104,15 @@ object DownloadController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, chapterIndex, mangaId ->
|
behaviorOf = { ctx, chapterIndex, mangaId ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
DownloadManager.enqueueWithChapterIndex(mangaId, chapterIndex)
|
DownloadManager.enqueueWithChapterIndex(mangaId, chapterIndex)
|
||||||
},
|
}.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -125,14 +127,14 @@ object DownloadController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val inputs = json.decodeFromString<EnqueueInput>(ctx.body())
|
val inputs = json.decodeFromString<EnqueueInput>(ctx.body())
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
DownloadManager.enqueue(inputs)
|
DownloadManager.enqueue(inputs)
|
||||||
},
|
}.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -148,14 +150,14 @@ object DownloadController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val input = json.decodeFromString<EnqueueInput>(ctx.body())
|
val input = json.decodeFromString<EnqueueInput>(ctx.body())
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
DownloadManager.dequeue(input)
|
DownloadManager.dequeue(input)
|
||||||
},
|
}.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -176,7 +178,7 @@ object DownloadController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -196,7 +198,7 @@ object DownloadController {
|
|||||||
DownloadManager.reorder(chapterIndex, mangaId, to)
|
DownloadManager.reorder(chapterIndex, mangaId, to)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import suwayomi.tachidesk.manga.impl.extension.Extension
|
import suwayomi.tachidesk.manga.impl.extension.Extension
|
||||||
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList
|
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList
|
||||||
@@ -31,14 +31,16 @@ object ExtensionController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
ExtensionsList.getExtensionList()
|
ExtensionsList.getExtensionList()
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.json(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<ExtensionDataClass>>(HttpCode.OK)
|
json<Array<ExtensionDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,16 +55,18 @@ object ExtensionController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, pkgName ->
|
behaviorOf = { ctx, pkgName ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
Extension.installExtension(pkgName)
|
Extension.installExtension(pkgName)
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.status(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.CREATED)
|
httpCode(HttpStatus.CREATED)
|
||||||
httpCode(HttpCode.FOUND)
|
httpCode(HttpStatus.FOUND)
|
||||||
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
|
httpCode(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,18 +85,23 @@ object ExtensionController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val uploadedFile = ctx.uploadedFile("file")!!
|
val uploadedFile = ctx.uploadedFile("file")!!
|
||||||
logger.debug { "Uploaded extension file name: " + uploadedFile.filename }
|
logger.debug { "Uploaded extension file name: " + uploadedFile.filename() }
|
||||||
|
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
Extension.installExternalExtension(uploadedFile.content, uploadedFile.filename)
|
Extension.installExternalExtension(
|
||||||
},
|
uploadedFile.content(),
|
||||||
)
|
uploadedFile.filename(),
|
||||||
|
)
|
||||||
|
}.thenApply {
|
||||||
|
ctx.status(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.CREATED)
|
httpCode(HttpStatus.CREATED)
|
||||||
httpCode(HttpCode.FOUND)
|
httpCode(HttpStatus.FOUND)
|
||||||
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
|
httpCode(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -107,17 +116,19 @@ object ExtensionController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, pkgName ->
|
behaviorOf = { ctx, pkgName ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
Extension.updateExtension(pkgName)
|
Extension.updateExtension(pkgName)
|
||||||
},
|
}.thenApply {
|
||||||
)
|
ctx.status(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.CREATED)
|
httpCode(HttpStatus.CREATED)
|
||||||
httpCode(HttpCode.FOUND)
|
httpCode(HttpStatus.FOUND)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
|
httpCode(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,10 +147,10 @@ object ExtensionController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.CREATED)
|
httpCode(HttpStatus.CREATED)
|
||||||
httpCode(HttpCode.FOUND)
|
httpCode(HttpStatus.FOUND)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
httpCode(HttpCode.INTERNAL_SERVER_ERROR)
|
httpCode(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -154,19 +165,19 @@ object ExtensionController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, apkName ->
|
behaviorOf = { ctx, apkName ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { Extension.getExtensionIcon(apkName) }
|
future { Extension.getExtensionIcon(apkName) }
|
||||||
.thenApply {
|
.thenApply {
|
||||||
ctx.header("content-type", it.second)
|
ctx.header("content-type", it.second)
|
||||||
val httpCacheSeconds = 365.days.inWholeSeconds
|
val httpCacheSeconds = 365.days.inWholeSeconds
|
||||||
ctx.header("cache-control", "max-age=$httpCacheSeconds, immutable")
|
ctx.header("cache-control", "max-age=$httpCacheSeconds, immutable")
|
||||||
it.first
|
ctx.result(it.first)
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
image(HttpCode.OK)
|
image(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import suwayomi.tachidesk.manga.impl.CategoryManga
|
import suwayomi.tachidesk.manga.impl.CategoryManga
|
||||||
import suwayomi.tachidesk.manga.impl.Chapter
|
import suwayomi.tachidesk.manga.impl.Chapter
|
||||||
@@ -41,15 +41,15 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId, onlineFetch ->
|
behaviorOf = { ctx, mangaId, onlineFetch ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
Manga.getManga(mangaId, onlineFetch)
|
Manga.getManga(mangaId, onlineFetch)
|
||||||
},
|
}.thenApply { ctx.json(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<MangaDataClass>(HttpCode.OK)
|
json<MangaDataClass>(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,15 +65,15 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId, onlineFetch ->
|
behaviorOf = { ctx, mangaId, onlineFetch ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
Manga.getMangaFull(mangaId, onlineFetch)
|
Manga.getMangaFull(mangaId, onlineFetch)
|
||||||
},
|
}.thenApply { ctx.json(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<MangaDataClass>(HttpCode.OK)
|
json<MangaDataClass>(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,19 +88,19 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId ->
|
behaviorOf = { ctx, mangaId ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { Manga.getMangaThumbnail(mangaId) }
|
future { Manga.getMangaThumbnail(mangaId) }
|
||||||
.thenApply {
|
.thenApply {
|
||||||
ctx.header("content-type", it.second)
|
ctx.header("content-type", it.second)
|
||||||
val httpCacheSeconds = 1.days.inWholeSeconds
|
val httpCacheSeconds = 1.days.inWholeSeconds
|
||||||
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
||||||
it.first
|
ctx.result(it.first)
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
image(HttpCode.OK)
|
image(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -115,13 +115,14 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId ->
|
behaviorOf = { ctx, mangaId ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { Library.addMangaToLibrary(mangaId) },
|
future { Library.addMangaToLibrary(mangaId) }
|
||||||
)
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,13 +137,14 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId ->
|
behaviorOf = { ctx, mangaId ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { Library.removeMangaFromLibrary(mangaId) },
|
future { Library.removeMangaFromLibrary(mangaId) }
|
||||||
)
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,7 +162,7 @@ object MangaController {
|
|||||||
ctx.json(CategoryManga.getMangaCategories(mangaId))
|
ctx.json(CategoryManga.getMangaCategories(mangaId))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<CategoryDataClass>>(HttpCode.OK)
|
json<Array<CategoryDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -180,7 +182,7 @@ object MangaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -200,7 +202,7 @@ object MangaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -221,8 +223,8 @@ object MangaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -242,11 +244,14 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId, onlineFetch ->
|
behaviorOf = { ctx, mangaId, onlineFetch ->
|
||||||
ctx.future(future { Chapter.getChapterList(mangaId, onlineFetch) })
|
ctx.future {
|
||||||
|
future { Chapter.getChapterList(mangaId, onlineFetch) }
|
||||||
|
.thenApply { ctx.json(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<ChapterDataClass>>(HttpCode.OK)
|
json<Array<ChapterDataClass>>(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -266,7 +271,7 @@ object MangaController {
|
|||||||
Chapter.modifyChapters(input, mangaId)
|
Chapter.modifyChapters(input, mangaId)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -291,7 +296,7 @@ object MangaController {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -307,11 +312,14 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId, chapterIndex ->
|
behaviorOf = { ctx, mangaId, chapterIndex ->
|
||||||
ctx.future(future { getChapterDownloadReadyByIndex(chapterIndex, mangaId) })
|
ctx.future {
|
||||||
|
future { getChapterDownloadReadyByIndex(chapterIndex, mangaId) }
|
||||||
|
.thenApply { ctx.json(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<ChapterDataClass>(HttpCode.OK)
|
json<ChapterDataClass>(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -336,7 +344,7 @@ object MangaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -357,8 +365,8 @@ object MangaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -381,8 +389,8 @@ object MangaController {
|
|||||||
ctx.status(200)
|
ctx.status(200)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -401,19 +409,19 @@ object MangaController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId, chapterIndex, index ->
|
behaviorOf = { ctx, mangaId, chapterIndex, index ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { Page.getPageImage(mangaId, chapterIndex, index) }
|
future { Page.getPageImage(mangaId, chapterIndex, index) }
|
||||||
.thenApply {
|
.thenApply {
|
||||||
ctx.header("content-type", it.second)
|
ctx.header("content-type", it.second)
|
||||||
val httpCacheSeconds = 1.days.inWholeSeconds
|
val httpCacheSeconds = 1.days.inWholeSeconds
|
||||||
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
||||||
it.first
|
ctx.result(it.first)
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
image(HttpCode.OK)
|
image(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import suwayomi.tachidesk.manga.impl.MangaList
|
import suwayomi.tachidesk.manga.impl.MangaList
|
||||||
import suwayomi.tachidesk.manga.impl.Search
|
import suwayomi.tachidesk.manga.impl.Search
|
||||||
@@ -38,7 +38,7 @@ object SourceController {
|
|||||||
ctx.json(Source.getSourceList())
|
ctx.json(Source.getSourceList())
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<SourceDataClass>>(HttpCode.OK)
|
json<Array<SourceDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,8 +56,8 @@ object SourceController {
|
|||||||
ctx.json(Source.getSource(sourceId)!!)
|
ctx.json(Source.getSource(sourceId)!!)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<SourceDataClass>(HttpCode.OK)
|
json<SourceDataClass>(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,14 +73,14 @@ object SourceController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, sourceId, pageNum ->
|
behaviorOf = { ctx, sourceId, pageNum ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
MangaList.getMangaList(sourceId, pageNum, popular = true)
|
MangaList.getMangaList(sourceId, pageNum, popular = true)
|
||||||
},
|
}.thenApply { ctx.json(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<PagedMangaListDataClass>(HttpCode.OK)
|
json<PagedMangaListDataClass>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -96,14 +96,14 @@ object SourceController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, sourceId, pageNum ->
|
behaviorOf = { ctx, sourceId, pageNum ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
MangaList.getMangaList(sourceId, pageNum, popular = false)
|
MangaList.getMangaList(sourceId, pageNum, popular = false)
|
||||||
},
|
}.thenApply { ctx.json(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<PagedMangaListDataClass>(HttpCode.OK)
|
json<PagedMangaListDataClass>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ object SourceController {
|
|||||||
ctx.json(Source.getSourcePreferences(sourceId))
|
ctx.json(Source.getSourcePreferences(sourceId))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<Source.PreferenceObject>>(HttpCode.OK)
|
json<Array<Source.PreferenceObject>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ object SourceController {
|
|||||||
ctx.json(Source.setSourcePreference(sourceId, preferenceChange.position, preferenceChange.value))
|
ctx.json(Source.setSourcePreference(sourceId, preferenceChange.position, preferenceChange.value))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ object SourceController {
|
|||||||
ctx.json(Search.getFilterList(sourceId, reset))
|
ctx.json(Search.getFilterList(sourceId, reset))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<Search.FilterObject>>(HttpCode.OK)
|
json<Array<Search.FilterObject>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ object SourceController {
|
|||||||
ctx.json(Search.setFilter(sourceId, filterChange))
|
ctx.json(Search.setFilter(sourceId, filterChange))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -206,10 +206,13 @@ object SourceController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, sourceId, searchTerm, pageNum ->
|
behaviorOf = { ctx, sourceId, searchTerm, pageNum ->
|
||||||
ctx.future(future { Search.sourceSearch(sourceId, searchTerm, pageNum) })
|
ctx.future {
|
||||||
|
future { Search.sourceSearch(sourceId, searchTerm, pageNum) }
|
||||||
|
.thenApply { ctx.json(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<PagedMangaListDataClass>(HttpCode.OK)
|
json<PagedMangaListDataClass>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -227,10 +230,13 @@ object SourceController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx, sourceId, pageNum ->
|
behaviorOf = { ctx, sourceId, pageNum ->
|
||||||
val filter = json.decodeFromString<FilterData>(ctx.body())
|
val filter = json.decodeFromString<FilterData>(ctx.body())
|
||||||
ctx.future(future { Search.sourceFilter(sourceId, pageNum, filter) })
|
ctx.future {
|
||||||
|
future { Search.sourceFilter(sourceId, pageNum, filter) }
|
||||||
|
.thenApply { ctx.json(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<PagedMangaListDataClass>(HttpCode.OK)
|
json<PagedMangaListDataClass>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -249,7 +255,7 @@ object SourceController {
|
|||||||
ctx.json(Search.sourceGlobalSearch(searchTerm))
|
ctx.json(Search.sourceGlobalSearch(searchTerm))
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import suwayomi.tachidesk.manga.impl.track.Track
|
import suwayomi.tachidesk.manga.impl.track.Track
|
||||||
@@ -36,7 +36,7 @@ object TrackController {
|
|||||||
ctx.json(Track.getTrackerList())
|
ctx.json(Track.getTrackerList())
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<TrackerDataClass>>(HttpCode.OK)
|
json<Array<TrackerDataClass>>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,11 +52,14 @@ object TrackController {
|
|||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val input = json.decodeFromString<Track.LoginInput>(ctx.body())
|
val input = json.decodeFromString<Track.LoginInput>(ctx.body())
|
||||||
logger.debug { "tracker login $input" }
|
logger.debug { "tracker login $input" }
|
||||||
ctx.future(future { Track.login(input) })
|
ctx.future {
|
||||||
|
future { Track.login(input) }
|
||||||
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,11 +75,14 @@ object TrackController {
|
|||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val input = json.decodeFromString<Track.LogoutInput>(ctx.body())
|
val input = json.decodeFromString<Track.LogoutInput>(ctx.body())
|
||||||
logger.debug { "tracker logout $input" }
|
logger.debug { "tracker logout $input" }
|
||||||
ctx.future(future { Track.logout(input) })
|
ctx.future {
|
||||||
|
future { Track.logout(input) }
|
||||||
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -92,11 +98,14 @@ object TrackController {
|
|||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val input = json.decodeFromString<Track.SearchInput>(ctx.body())
|
val input = json.decodeFromString<Track.SearchInput>(ctx.body())
|
||||||
logger.debug { "tracker search $input" }
|
logger.debug { "tracker search $input" }
|
||||||
ctx.future(future { Track.search(input) })
|
ctx.future {
|
||||||
|
future { Track.search(input) }
|
||||||
|
.thenApply { ctx.json(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,10 +121,13 @@ object TrackController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId, trackerId, remoteId ->
|
behaviorOf = { ctx, mangaId, trackerId, remoteId ->
|
||||||
ctx.future(future { Track.bind(mangaId, trackerId, remoteId.toLong()) })
|
ctx.future {
|
||||||
|
future { Track.bind(mangaId, trackerId, remoteId.toLong()) }
|
||||||
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -131,10 +143,13 @@ object TrackController {
|
|||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val input = json.decodeFromString<Track.UpdateInput>(ctx.body())
|
val input = json.decodeFromString<Track.UpdateInput>(ctx.body())
|
||||||
logger.debug { "tracker update $input" }
|
logger.debug { "tracker update $input" }
|
||||||
ctx.future(future { Track.update(input) })
|
ctx.future {
|
||||||
|
future { Track.update(input) }
|
||||||
|
.thenApply { ctx.status(HttpStatus.OK) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -148,19 +163,19 @@ object TrackController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, trackerId ->
|
behaviorOf = { ctx, trackerId ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future { Track.getTrackerThumbnail(trackerId) }
|
future { Track.getTrackerThumbnail(trackerId) }
|
||||||
.thenApply {
|
.thenApply {
|
||||||
ctx.header("content-type", it.second)
|
ctx.header("content-type", it.second)
|
||||||
val httpCacheSeconds = 1.days.inWholeSeconds
|
val httpCacheSeconds = 1.days.inWholeSeconds
|
||||||
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
||||||
it.first
|
ctx.result(it.first)
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
image(HttpCode.OK)
|
image(HttpStatus.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpStatus.NOT_FOUND)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package suwayomi.tachidesk.manga.controller
|
package suwayomi.tachidesk.manga.controller
|
||||||
|
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import io.javalin.websocket.WsConfig
|
import io.javalin.websocket.WsConfig
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import suwayomi.tachidesk.manga.impl.Category
|
import suwayomi.tachidesk.manga.impl.Category
|
||||||
@@ -39,14 +39,14 @@ object UpdateController {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, pageNum ->
|
behaviorOf = { ctx, pageNum ->
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
Chapter.getRecentChapters(pageNum)
|
Chapter.getRecentChapters(pageNum)
|
||||||
},
|
}.thenApply { ctx.json(it) }
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<PagedMangaChapterListDataClass>(HttpCode.OK)
|
json<PagedMangaChapterListDataClass>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,13 +84,13 @@ object UpdateController {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
logger.info { "No Category found" }
|
logger.info { "No Category found" }
|
||||||
ctx.status(HttpCode.BAD_REQUEST)
|
ctx.status(HttpStatus.BAD_REQUEST)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
httpCode(HttpCode.BAD_REQUEST)
|
httpCode(HttpStatus.BAD_REQUEST)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ object UpdateController {
|
|||||||
ctx.json(updater.statusDeprecated.value)
|
ctx.json(updater.statusDeprecated.value)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<UpdateStatus>(HttpCode.OK)
|
json<UpdateStatus>(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -134,16 +134,16 @@ object UpdateController {
|
|||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
val updater = Injekt.get<IUpdater>()
|
val updater = Injekt.get<IUpdater>()
|
||||||
logger.info { "Resetting Updater" }
|
logger.info { "Resetting Updater" }
|
||||||
ctx.future(
|
ctx.future {
|
||||||
future {
|
future {
|
||||||
updater.reset()
|
updater.reset()
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
ctx.status(HttpCode.OK)
|
ctx.status(HttpStatus.OK)
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpStatus.OK)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ package suwayomi.tachidesk.manga.impl
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import com.google.common.cache.Cache
|
|
||||||
import com.google.common.cache.CacheBuilder
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterSanitizer.sanitize
|
import eu.kanade.tachiyomi.util.chapter.ChapterSanitizer.sanitize
|
||||||
|
import io.github.reactivecircus.cache4k.Cache
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@@ -47,8 +46,8 @@ import suwayomi.tachidesk.manga.model.table.toDataClass
|
|||||||
import suwayomi.tachidesk.server.serverConfig
|
import suwayomi.tachidesk.server.serverConfig
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.TreeSet
|
import java.util.TreeSet
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
private fun List<ChapterDataClass>.removeDuplicates(currentChapter: ChapterDataClass): List<ChapterDataClass> =
|
private fun List<ChapterDataClass>.removeDuplicates(currentChapter: ChapterDataClass): List<ChapterDataClass> =
|
||||||
groupBy { it.chapterNumber }
|
groupBy { it.chapterNumber }
|
||||||
@@ -124,9 +123,9 @@ object Chapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val map: Cache<Int, Mutex> =
|
val map: Cache<Int, Mutex> =
|
||||||
CacheBuilder
|
Cache
|
||||||
.newBuilder()
|
.Builder<Int, Mutex>()
|
||||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
.expireAfterAccess(10.minutes)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
suspend fun fetchChapterList(mangaId: Int): List<SChapter> {
|
suspend fun fetchChapterList(mangaId: Int): List<SChapter> {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.source.local.LocalSource
|
|||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.HttpStatus
|
||||||
import mu.KLogger
|
import mu.KLogger
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
@@ -305,9 +305,9 @@ object Manga {
|
|||||||
val tryToRefreshUrl =
|
val tryToRefreshUrl =
|
||||||
!refreshUrl &&
|
!refreshUrl &&
|
||||||
listOf(
|
listOf(
|
||||||
HttpCode.GONE.status,
|
HttpStatus.GONE.code,
|
||||||
HttpCode.MOVED_PERMANENTLY.status,
|
HttpStatus.MOVED_PERMANENTLY.code,
|
||||||
HttpCode.NOT_FOUND.status,
|
HttpStatus.NOT_FOUND.code,
|
||||||
523, // (Cloudflare) Origin Is Unreachable
|
523, // (Cloudflare) Origin Is Unreachable
|
||||||
522, // (Cloudflare) Connection timed out
|
522, // (Cloudflare) Connection timed out
|
||||||
).contains(e.code)
|
).contains(e.code)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ package suwayomi.tachidesk.manga.impl
|
|||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.json.JsonMapper
|
||||||
|
import io.javalin.json.fromJsonString
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import suwayomi.tachidesk.manga.impl.MangaList.processEntries
|
import suwayomi.tachidesk.manga.impl.MangaList.processEntries
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
|
||||||
@@ -129,7 +130,7 @@ object Search {
|
|||||||
is Filter.CheckBox -> filter.state = change.state.toBooleanStrict()
|
is Filter.CheckBox -> filter.state = change.state.toBooleanStrict()
|
||||||
is Filter.TriState -> filter.state = change.state.toInt()
|
is Filter.TriState -> filter.state = change.state.toInt()
|
||||||
is Filter.Group<*> -> {
|
is Filter.Group<*> -> {
|
||||||
val groupChange = jsonMapper.fromJsonString(change.state, FilterChange::class.java)
|
val groupChange = jsonMapper.fromJsonString<FilterChange>(change.state)
|
||||||
|
|
||||||
when (val groupFilter = filter.state[groupChange.position]) {
|
when (val groupFilter = filter.state[groupChange.position]) {
|
||||||
is Filter.CheckBox -> groupFilter.state = groupChange.state.toBooleanStrict()
|
is Filter.CheckBox -> groupFilter.state = groupChange.state.toBooleanStrict()
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.json.JsonMapper
|
||||||
|
import io.javalin.json.fromJsonString
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insert
|
||||||
@@ -131,11 +132,10 @@ object Source {
|
|||||||
value: String,
|
value: String,
|
||||||
getValue: (Preference) -> Any = { pref ->
|
getValue: (Preference) -> Any = { pref ->
|
||||||
println(jsonMapper::class.java.name)
|
println(jsonMapper::class.java.name)
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
when (pref.defaultValueType) {
|
when (pref.defaultValueType) {
|
||||||
"String" -> value
|
"String" -> value
|
||||||
"Boolean" -> value.toBoolean()
|
"Boolean" -> value.toBoolean()
|
||||||
"Set<String>" -> jsonMapper.fromJsonString(value, List::class.java as Class<List<String>>).toSet()
|
"Set<String>" -> jsonMapper.fromJsonString<List<String>>(value).toSet()
|
||||||
else -> throw RuntimeException("Unsupported type conversion")
|
else -> throw RuntimeException("Unsupported type conversion")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -104,11 +104,11 @@ object DownloadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addClient(ctx: WsContext) {
|
fun addClient(ctx: WsContext) {
|
||||||
clients[ctx.sessionId] = ctx
|
clients[ctx.sessionId()] = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeClient(ctx: WsContext) {
|
fun removeClient(ctx: WsContext) {
|
||||||
clients.remove(ctx.sessionId)
|
clients.remove(ctx.sessionId())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyClient(ctx: WsContext) {
|
fun notifyClient(ctx: WsContext) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ object UpdaterSocket : Websocket<UpdateStatus>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun addClient(ctx: WsContext) {
|
override fun addClient(ctx: WsContext) {
|
||||||
logger.info { ctx.sessionId }
|
logger.info { ctx.sessionId() }
|
||||||
super.addClient(ctx)
|
super.addClient(ctx)
|
||||||
if (job?.isActive != true) {
|
if (job?.isActive != true) {
|
||||||
job = start()
|
job = start()
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ abstract class Websocket<T> {
|
|||||||
protected val clients = ConcurrentHashMap<String, WsContext>()
|
protected val clients = ConcurrentHashMap<String, WsContext>()
|
||||||
|
|
||||||
open fun addClient(ctx: WsContext) {
|
open fun addClient(ctx: WsContext) {
|
||||||
clients[ctx.sessionId] = ctx
|
clients[ctx.sessionId()] = ctx
|
||||||
notifyClient(ctx, null)
|
notifyClient(ctx, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun removeClient(ctx: WsContext) {
|
open fun removeClient(ctx: WsContext) {
|
||||||
clients.remove(ctx.sessionId)
|
clients.remove(ctx.sessionId())
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun notifyAllClients(value: T) {
|
open fun notifyAllClients(value: T) {
|
||||||
|
|||||||
@@ -9,12 +9,8 @@ package suwayomi.tachidesk.server
|
|||||||
|
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import io.javalin.apibuilder.ApiBuilder.path
|
import io.javalin.apibuilder.ApiBuilder.path
|
||||||
import io.javalin.core.security.RouteRole
|
import io.javalin.http.UnauthorizedResponse
|
||||||
import io.javalin.http.staticfiles.Location
|
import io.javalin.http.staticfiles.Location
|
||||||
import io.javalin.plugin.openapi.OpenApiOptions
|
|
||||||
import io.javalin.plugin.openapi.OpenApiPlugin
|
|
||||||
import io.javalin.plugin.openapi.ui.SwaggerOptions
|
|
||||||
import io.swagger.v3.oas.models.info.Info
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@@ -22,7 +18,6 @@ import kotlinx.coroutines.flow.combine
|
|||||||
import kotlinx.coroutines.future.future
|
import kotlinx.coroutines.future.future
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import org.eclipse.jetty.server.Server
|
|
||||||
import org.eclipse.jetty.server.ServerConnector
|
import org.eclipse.jetty.server.ServerConnector
|
||||||
import suwayomi.tachidesk.global.GlobalAPI
|
import suwayomi.tachidesk.global.GlobalAPI
|
||||||
import suwayomi.tachidesk.graphql.GraphQL
|
import suwayomi.tachidesk.graphql.GraphQL
|
||||||
@@ -31,9 +26,7 @@ import suwayomi.tachidesk.server.util.Browser
|
|||||||
import suwayomi.tachidesk.server.util.WebInterfaceManager
|
import suwayomi.tachidesk.server.util.WebInterfaceManager
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.IllegalArgumentException
|
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import kotlin.NoSuchElementException
|
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
object JavalinSetup {
|
object JavalinSetup {
|
||||||
@@ -46,31 +39,11 @@ object JavalinSetup {
|
|||||||
fun <T> future(block: suspend CoroutineScope.() -> T): CompletableFuture<T> = scope.future(block = block)
|
fun <T> future(block: suspend CoroutineScope.() -> T): CompletableFuture<T> = scope.future(block = block)
|
||||||
|
|
||||||
fun javalinSetup() {
|
fun javalinSetup() {
|
||||||
val server = Server()
|
|
||||||
val connector =
|
|
||||||
ServerConnector(server).apply {
|
|
||||||
host = serverConfig.ip.value
|
|
||||||
port = serverConfig.port.value
|
|
||||||
}
|
|
||||||
server.addConnector(connector)
|
|
||||||
|
|
||||||
serverConfig.subscribeTo(combine(serverConfig.ip, serverConfig.port) { ip, port -> Pair(ip, port) }, { (newIp, newPort) ->
|
|
||||||
val oldIp = connector.host
|
|
||||||
val oldPort = connector.port
|
|
||||||
|
|
||||||
connector.host = newIp
|
|
||||||
connector.port = newPort
|
|
||||||
connector.stop()
|
|
||||||
connector.start()
|
|
||||||
|
|
||||||
logger.info { "Server ip and/or port changed from $oldIp:$oldPort to $newIp:$newPort " }
|
|
||||||
})
|
|
||||||
|
|
||||||
val app =
|
val app =
|
||||||
Javalin.create { config ->
|
Javalin.create { config ->
|
||||||
if (serverConfig.webUIEnabled.value) {
|
if (serverConfig.webUIEnabled.value) {
|
||||||
val serveWebUI = {
|
val serveWebUI = {
|
||||||
config.addSinglePageRoot("/", applicationDirs.webUIRoot + "/index.html", Location.EXTERNAL)
|
config.spaRoot.addFile("/root", applicationDirs.webUIRoot + "/index.html", Location.EXTERNAL)
|
||||||
}
|
}
|
||||||
WebInterfaceManager.setServeWebUI(serveWebUI)
|
WebInterfaceManager.setServeWebUI(serveWebUI)
|
||||||
|
|
||||||
@@ -79,31 +52,74 @@ object JavalinSetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info { "Serving web static files for ${serverConfig.webUIFlavor.value}" }
|
logger.info { "Serving web static files for ${serverConfig.webUIFlavor.value}" }
|
||||||
config.addStaticFiles(applicationDirs.webUIRoot, Location.EXTERNAL)
|
config.staticFiles.add(applicationDirs.webUIRoot, Location.EXTERNAL)
|
||||||
serveWebUI()
|
serveWebUI()
|
||||||
|
|
||||||
config.registerPlugin(OpenApiPlugin(getOpenApiOptions()))
|
// config.registerPlugin(OpenApiPlugin(getOpenApiOptions()))
|
||||||
}
|
}
|
||||||
|
|
||||||
config.server { server }
|
var connectorAdded = false
|
||||||
|
config.jetty.modifyServer { server ->
|
||||||
|
if (!connectorAdded) {
|
||||||
|
val connector =
|
||||||
|
ServerConnector(server).apply {
|
||||||
|
host = serverConfig.ip.value
|
||||||
|
port = serverConfig.port.value
|
||||||
|
}
|
||||||
|
server.addConnector(connector)
|
||||||
|
|
||||||
config.enableCorsForAllOrigins()
|
serverConfig.subscribeTo(
|
||||||
|
combine(
|
||||||
|
serverConfig.ip,
|
||||||
|
serverConfig.port,
|
||||||
|
) { ip, port -> Pair(ip, port) },
|
||||||
|
{ (newIp, newPort) ->
|
||||||
|
val oldIp = connector.host
|
||||||
|
val oldPort = connector.port
|
||||||
|
|
||||||
config.accessManager { handler, ctx, _ ->
|
connector.host = newIp
|
||||||
fun credentialsValid(): Boolean {
|
connector.port = newPort
|
||||||
val (username, password) = ctx.basicAuthCredentials()
|
connector.stop()
|
||||||
return username == serverConfig.basicAuthUsername.value && password == serverConfig.basicAuthPassword.value
|
connector.start()
|
||||||
|
|
||||||
|
logger.info { "Server ip and/or port changed from $oldIp:$oldPort to $newIp:$newPort " }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
connectorAdded = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (serverConfig.basicAuthEnabled.value && !(ctx.basicAuthCredentialsExist() && credentialsValid())) {
|
config.bundledPlugins.enableCors { cors ->
|
||||||
ctx.header("WWW-Authenticate", "Basic")
|
cors.addRule {
|
||||||
ctx.status(401).json("Unauthorized")
|
it.anyHost()
|
||||||
} else {
|
}
|
||||||
handler.handle(ctx)
|
}
|
||||||
|
|
||||||
|
config.router.apiBuilder {
|
||||||
|
path("api/") {
|
||||||
|
path("v1/") {
|
||||||
|
GlobalAPI.defineEndpoints()
|
||||||
|
MangaAPI.defineEndpoints()
|
||||||
|
}
|
||||||
|
GraphQL.defineEndpoints()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.beforeMatched { ctx ->
|
||||||
|
fun credentialsValid(): Boolean {
|
||||||
|
val basicAuthCredentials = ctx.basicAuthCredentials() ?: return true
|
||||||
|
val (username, password) = basicAuthCredentials
|
||||||
|
return username == serverConfig.basicAuthUsername.value &&
|
||||||
|
password == serverConfig.basicAuthPassword.value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverConfig.basicAuthEnabled.value && !credentialsValid()) {
|
||||||
|
ctx.header("WWW-Authenticate", "Basic")
|
||||||
|
throw UnauthorizedResponse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app.events { event ->
|
app.events { event ->
|
||||||
event.serverStarted {
|
event.serverStarted {
|
||||||
if (serverConfig.initialOpenInBrowserEnabled.value) {
|
if (serverConfig.initialOpenInBrowserEnabled.value) {
|
||||||
@@ -139,36 +155,22 @@ object JavalinSetup {
|
|||||||
ctx.result(e.message ?: "Bad Request")
|
ctx.result(e.message ?: "Bad Request")
|
||||||
}
|
}
|
||||||
|
|
||||||
app.routes {
|
|
||||||
path("api/") {
|
|
||||||
path("v1/") {
|
|
||||||
GlobalAPI.defineEndpoints()
|
|
||||||
MangaAPI.defineEndpoints()
|
|
||||||
}
|
|
||||||
GraphQL.defineEndpoints()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.start()
|
app.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOpenApiOptions(): OpenApiOptions {
|
// private fun getOpenApiOptions(): OpenApiOptions {
|
||||||
val applicationInfo =
|
// val applicationInfo =
|
||||||
Info().apply {
|
// Info().apply {
|
||||||
version("1.0")
|
// version("1.0")
|
||||||
description("Suwayomi-Server Api")
|
// description("Suwayomi-Server Api")
|
||||||
}
|
// }
|
||||||
return OpenApiOptions(applicationInfo).apply {
|
// return OpenApiOptions(applicationInfo).apply {
|
||||||
path("/api/openapi.json")
|
// path("/api/openapi.json")
|
||||||
swagger(
|
// swagger(
|
||||||
SwaggerOptions("/api/swagger-ui").apply {
|
// SwaggerOptions("/api/swagger-ui").apply {
|
||||||
title("Suwayomi-Server Swagger Documentation")
|
// title("Suwayomi-Server Swagger Documentation")
|
||||||
},
|
// },
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
object Auth {
|
|
||||||
enum class Role : RouteRole { ANYONE, USER_READ, USER_WRITE }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import eu.kanade.tachiyomi.App
|
|||||||
import eu.kanade.tachiyomi.createAppModule
|
import eu.kanade.tachiyomi.createAppModule
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.local.LocalSource
|
import eu.kanade.tachiyomi.source.local.LocalSource
|
||||||
import io.javalin.plugin.json.JavalinJackson
|
import io.javalin.json.JavalinJackson
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.json.JsonMapper
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
@@ -237,9 +237,7 @@ fun applicationSetup() {
|
|||||||
runMigrations(applicationDirs)
|
runMigrations(applicationDirs)
|
||||||
|
|
||||||
// Disable jetty's logging
|
// Disable jetty's logging
|
||||||
System.setProperty("org.eclipse.jetty.util.log.announce", "false")
|
setLogLevelFor("org.eclipse.jetty", Level.OFF)
|
||||||
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog")
|
|
||||||
System.setProperty("org.eclipse.jetty.LEVEL", "OFF")
|
|
||||||
|
|
||||||
// socks proxy settings
|
// socks proxy settings
|
||||||
serverConfig.subscribeTo(
|
serverConfig.subscribeTo(
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ package suwayomi.tachidesk.server.util
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.json.JsonMapper
|
||||||
|
import io.javalin.json.fromJsonString
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request.Builder
|
import okhttp3.Request.Builder
|
||||||
@@ -59,7 +60,7 @@ object AppMutex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
jsonMapper.fromJsonString(response, AboutDataClass::class.java)
|
jsonMapper.fromJsonString<AboutDataClass>(response)
|
||||||
AppMutexState.TachideskInstanceRunning
|
AppMutexState.TachideskInstanceRunning
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
AppMutexState.OtherApplicationRunning
|
AppMutexState.OtherApplicationRunning
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package suwayomi.tachidesk.server.util
|
package suwayomi.tachidesk.server.util
|
||||||
|
|
||||||
import io.javalin.http.Context
|
import io.javalin.http.Context
|
||||||
import io.javalin.http.HttpCode
|
import io.javalin.http.Handler
|
||||||
import io.javalin.plugin.openapi.dsl.DocumentedHandler
|
import io.javalin.http.HttpStatus
|
||||||
import io.javalin.plugin.openapi.dsl.OpenApiDocumentation
|
|
||||||
import io.javalin.plugin.openapi.dsl.documented
|
|
||||||
import io.swagger.v3.oas.models.Operation
|
|
||||||
|
|
||||||
fun <T> getSimpleParamItem(
|
fun <T> getSimpleParamItem(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
@@ -160,30 +157,30 @@ sealed class Param<T> {
|
|||||||
class ResultsBuilder {
|
class ResultsBuilder {
|
||||||
val results = mutableListOf<ResultType>()
|
val results = mutableListOf<ResultType>()
|
||||||
|
|
||||||
inline fun <reified T> json(code: HttpCode) {
|
inline fun <reified T> json(code: HttpStatus) {
|
||||||
results += ResultType.MimeType(code, "application/json", T::class.java)
|
results += ResultType.MimeType(code, "application/json", T::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun plainText(code: HttpCode) {
|
fun plainText(code: HttpStatus) {
|
||||||
results += ResultType.MimeType(code, "text/plain", String::class.java)
|
results += ResultType.MimeType(code, "text/plain", String::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun image(code: HttpCode) {
|
fun image(code: HttpStatus) {
|
||||||
results += ResultType.MimeType(code, "image/*", ByteArray::class.java)
|
results += ResultType.MimeType(code, "image/*", ByteArray::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stream(code: HttpCode) {
|
fun stream(code: HttpStatus) {
|
||||||
results += ResultType.MimeType(code, "application/octet-stream", ByteArray::class.java)
|
results += ResultType.MimeType(code, "application/octet-stream", ByteArray::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T> mime(
|
inline fun <reified T> mime(
|
||||||
code: HttpCode,
|
code: HttpStatus,
|
||||||
mime: String,
|
mime: String,
|
||||||
) {
|
) {
|
||||||
results += ResultType.MimeType(code, mime, T::class.java)
|
results += ResultType.MimeType(code, mime, T::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun httpCode(code: HttpCode) {
|
fun httpCode(code: HttpStatus) {
|
||||||
results += ResultType.StatusCode(code)
|
results += ResultType.StatusCode(code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,20 +189,20 @@ sealed class ResultType {
|
|||||||
abstract fun applyTo(documentation: OpenApiDocumentation)
|
abstract fun applyTo(documentation: OpenApiDocumentation)
|
||||||
|
|
||||||
data class MimeType(
|
data class MimeType(
|
||||||
val code: HttpCode,
|
val code: HttpStatus,
|
||||||
val mime: String,
|
val mime: String,
|
||||||
private val clazz: Class<*>,
|
private val clazz: Class<*>,
|
||||||
) : ResultType() {
|
) : ResultType() {
|
||||||
override fun applyTo(documentation: OpenApiDocumentation) {
|
override fun applyTo(documentation: OpenApiDocumentation) {
|
||||||
documentation.result(code.status.toString(), clazz, mime)
|
documentation.result(code.code.toString(), clazz, mime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StatusCode(
|
data class StatusCode(
|
||||||
val code: HttpCode,
|
val code: HttpStatus,
|
||||||
) : ResultType() {
|
) : ResultType() {
|
||||||
override fun applyTo(documentation: OpenApiDocumentation) {
|
override fun applyTo(documentation: OpenApiDocumentation) {
|
||||||
documentation.result<Unit>(code.status.toString())
|
documentation.result<Unit>(code.code.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -576,3 +573,67 @@ inline fun <
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
class OpenApiDocumentation {
|
||||||
|
fun operation(block: Operation.() -> Unit) {}
|
||||||
|
|
||||||
|
fun result(
|
||||||
|
toString: Any,
|
||||||
|
clazz: Class<*>,
|
||||||
|
mime: String,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> result(toString: Any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> formParam(
|
||||||
|
key: String,
|
||||||
|
defaultValue: T? = null,
|
||||||
|
isRequired: Boolean = false,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fun <T> queryParam(
|
||||||
|
key: String,
|
||||||
|
defaultValue: T? = null,
|
||||||
|
isRepeatable: Boolean = false,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fun <T> pathParam(
|
||||||
|
key: String,
|
||||||
|
defaultValue: T? = null,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fun <T> body() {}
|
||||||
|
|
||||||
|
fun uploadedFile(
|
||||||
|
name: String,
|
||||||
|
block: (DocumentationFile) -> Unit,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentationFile {
|
||||||
|
fun description(string: String) {}
|
||||||
|
|
||||||
|
fun required(boolean: Boolean) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentedHandler(
|
||||||
|
private val handler: (ctx: Context) -> Unit,
|
||||||
|
) : Handler {
|
||||||
|
override fun handle(ctx: Context) {
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun documented(
|
||||||
|
documentation: OpenApiDocumentation,
|
||||||
|
handle: (ctx: Context) -> Unit,
|
||||||
|
): DocumentedHandler = DocumentedHandler(handle)
|
||||||
|
|
||||||
|
class Operation {
|
||||||
|
fun summary(string: String) {}
|
||||||
|
|
||||||
|
fun description(string: String) {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package suwayomi.tachidesk.manga.controller
|
|||||||
import suwayomi.tachidesk.test.ApplicationTest
|
import suwayomi.tachidesk.test.ApplicationTest
|
||||||
|
|
||||||
// import io.javalin.http.Context
|
// import io.javalin.http.Context
|
||||||
// import io.javalin.http.HttpCode
|
// import io.javalin.http.HttpStatus
|
||||||
// import io.mockk.every
|
// import io.mockk.every
|
||||||
// import io.mockk.mockk
|
// import io.mockk.mockk
|
||||||
// import io.mockk.verify
|
// import io.mockk.verify
|
||||||
@@ -31,7 +31,7 @@ internal class UpdateControllerTest : ApplicationTest() {
|
|||||||
// fun `POST non existent Category Id should give error`() {
|
// fun `POST non existent Category Id should give error`() {
|
||||||
// every { ctx.formParam("category") } returns "1"
|
// every { ctx.formParam("category") } returns "1"
|
||||||
// UpdateController.categoryUpdate(ctx)
|
// UpdateController.categoryUpdate(ctx)
|
||||||
// verify { ctx.status(HttpCode.BAD_REQUEST) }
|
// verify { ctx.status(HttpStatus.BAD_REQUEST) }
|
||||||
// val updater by DI.global.instance<IUpdater>()
|
// val updater by DI.global.instance<IUpdater>()
|
||||||
// assertEquals(0, updater.status.value.numberOfJobs)
|
// assertEquals(0, updater.status.value.numberOfJobs)
|
||||||
// }
|
// }
|
||||||
@@ -43,7 +43,7 @@ internal class UpdateControllerTest : ApplicationTest() {
|
|||||||
// CategoryManga.addMangaToCategory(1, 1)
|
// CategoryManga.addMangaToCategory(1, 1)
|
||||||
// every { ctx.formParam("category") } returns "1"
|
// every { ctx.formParam("category") } returns "1"
|
||||||
// UpdateController.categoryUpdate(ctx)
|
// UpdateController.categoryUpdate(ctx)
|
||||||
// verify { ctx.status(HttpCode.OK) }
|
// verify { ctx.status(HttpStatus.OK) }
|
||||||
// val updater by DI.global.instance<IUpdater>()
|
// val updater by DI.global.instance<IUpdater>()
|
||||||
// assertEquals(1, updater.status.value.numberOfJobs)
|
// assertEquals(1, updater.status.value.numberOfJobs)
|
||||||
// }
|
// }
|
||||||
@@ -59,7 +59,7 @@ internal class UpdateControllerTest : ApplicationTest() {
|
|||||||
// createLibraryManga("mangaInDefault")
|
// createLibraryManga("mangaInDefault")
|
||||||
// every { ctx.formParam("category") } returns null
|
// every { ctx.formParam("category") } returns null
|
||||||
// UpdateController.categoryUpdate(ctx)
|
// UpdateController.categoryUpdate(ctx)
|
||||||
// verify { ctx.status(HttpCode.OK) }
|
// verify { ctx.status(HttpStatus.OK) }
|
||||||
// val updater by DI.global.instance<IUpdater>()
|
// val updater by DI.global.instance<IUpdater>()
|
||||||
// assertEquals(3, updater.status.value.numberOfJobs)
|
// assertEquals(3, updater.status.value.numberOfJobs)
|
||||||
// }
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user