Update javalin to v7 (major) (#1920)
* Update javalin to v7 * Update Javalin usage to v7 and Jackson 3 * Import fix --------- 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:
@@ -4,9 +4,9 @@ coroutines = "1.11.0"
|
||||
serialization = "1.11.0"
|
||||
jvmTarget = "21"
|
||||
okhttp = "5.3.2" # Major version is locked by Tachiyomi extensions
|
||||
javalin = "6.7.0"
|
||||
javalin = "7.2.0"
|
||||
jte = "3.2.4"
|
||||
jackson = "2.18.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
||||
jackson = "3.1.2" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
||||
exposed = "0.61.0"
|
||||
dex2jar = "2.4.36"
|
||||
polyglot = "25.0.3"
|
||||
@@ -51,10 +51,10 @@ okio = "com.squareup.okio:okio:3.17.0"
|
||||
# Javalin api
|
||||
javalin-core = { module = "io.javalin:javalin", version.ref = "javalin" }
|
||||
javalin-openapi = { module = "io.javalin:javalin-openapi", version.ref = "javalin" }
|
||||
javalin-rendering = { module = "io.javalin:javalin-rendering", version.ref = "javalin" }
|
||||
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
||||
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
|
||||
jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" }
|
||||
javalin-rendering = { module = "io.javalin:javalin-rendering-jte", version.ref = "javalin" }
|
||||
jackson-databind = { module = "tools.jackson.core:jackson-databind", version.ref = "jackson" }
|
||||
jackson-kotlin = { module = "tools.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
|
||||
jackson-annotations = "com.fasterxml.jackson.core:jackson-annotations:2.20"
|
||||
jte = { module = "gg.jte:jte", version.ref = "jte" }
|
||||
kte = { module = "gg.jte:jte-kotlin", version.ref = "jte" }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.javalin.websocket.WsMessageContext
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus
|
||||
import org.eclipse.jetty.websocket.core.CloseStatus
|
||||
import suwayomi.tachidesk.manga.impl.update.Websocket
|
||||
|
||||
object WebView : Websocket<String>() {
|
||||
|
||||
+7
-3
@@ -13,10 +13,14 @@ import com.expediagroup.graphql.server.types.GraphQLRequest
|
||||
import com.expediagroup.graphql.server.types.GraphQLServerRequest
|
||||
import io.javalin.http.Context
|
||||
import io.javalin.http.UploadedFile
|
||||
import io.javalin.json.JavalinJackson
|
||||
import io.javalin.json.fromJsonStream
|
||||
import io.javalin.json.fromJsonString
|
||||
import java.io.IOException
|
||||
|
||||
class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
||||
val jsonMapper = JavalinJackson()
|
||||
|
||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
override suspend fun parseRequest(context: Context): GraphQLServerRequest? {
|
||||
return try {
|
||||
@@ -29,17 +33,17 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
||||
context.formParam("operations")
|
||||
?: throw IllegalArgumentException("Cannot find 'operations' body")
|
||||
} else {
|
||||
return context.bodyAsClass(GraphQLServerRequest::class.java)
|
||||
return context.bodyInputStream().use { jsonMapper.fromJsonStream<GraphQLServerRequest>(it) }
|
||||
}
|
||||
|
||||
val request =
|
||||
context.jsonMapper().fromJsonString<GraphQLServerRequest>(formParam)
|
||||
jsonMapper.fromJsonString<GraphQLServerRequest>(formParam)
|
||||
|
||||
val map =
|
||||
context
|
||||
.formParam("map")
|
||||
?.let {
|
||||
context.jsonMapper().fromJsonString<Map<String, List<String>>>(it)
|
||||
jsonMapper.fromJsonString<Map<String, List<String>>>(it)
|
||||
}.orEmpty()
|
||||
|
||||
val mapItems =
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus
|
||||
import org.eclipse.jetty.websocket.core.CloseStatus
|
||||
import suwayomi.tachidesk.graphql.server.TachideskGraphQLContextFactory
|
||||
import suwayomi.tachidesk.graphql.server.subscriptions.SubscriptionOperationMessage.ClientMessages.GQL_CONNECTION_INIT
|
||||
import suwayomi.tachidesk.graphql.server.subscriptions.SubscriptionOperationMessage.ClientMessages.GQL_SUBSCRIBE
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus
|
||||
import org.eclipse.jetty.websocket.core.CloseStatus
|
||||
import suwayomi.tachidesk.graphql.server.toGraphQLContext
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
@@ -13,12 +13,14 @@ import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.javalin.Javalin
|
||||
import io.javalin.apibuilder.ApiBuilder.after
|
||||
import io.javalin.apibuilder.ApiBuilder.path
|
||||
import io.javalin.config.RoutesConfig
|
||||
import io.javalin.http.Context
|
||||
import io.javalin.http.HandlerType
|
||||
import io.javalin.http.HttpStatus
|
||||
import io.javalin.http.NotFoundResponse
|
||||
import io.javalin.http.RedirectResponse
|
||||
import io.javalin.http.UnauthorizedResponse
|
||||
import io.javalin.json.JavalinJackson3
|
||||
import io.javalin.rendering.template.JavalinJte
|
||||
import io.javalin.websocket.WsContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -47,6 +49,7 @@ import java.net.URLEncoder
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.text.get
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
object JavalinSetup {
|
||||
@@ -58,10 +61,12 @@ object JavalinSetup {
|
||||
|
||||
fun javalinSetup() {
|
||||
val app =
|
||||
Javalin.create { config ->
|
||||
Javalin.start { config ->
|
||||
val templateEngine = TemplateEngine.createPrecompiled(ContentType.Html)
|
||||
config.fileRenderer(JavalinJte(templateEngine))
|
||||
|
||||
config.jsonMapper(JavalinJackson3())
|
||||
|
||||
WebInterfaceManager.setup(config)
|
||||
|
||||
// config.registerPlugin(OpenApiPlugin(getOpenApiOptions()))
|
||||
@@ -104,7 +109,8 @@ object JavalinSetup {
|
||||
}
|
||||
}
|
||||
|
||||
config.router.apiBuilder {
|
||||
config.routes.defineCore()
|
||||
config.routes.apiBuilder {
|
||||
path(ServerSubpath.maybeAddAsPrefix("api/")) {
|
||||
path("v1/") {
|
||||
GlobalAPI.defineEndpoints()
|
||||
@@ -117,17 +123,32 @@ object JavalinSetup {
|
||||
after { ctx ->
|
||||
// If not matched, the request was for an invalid endpoint
|
||||
// Return a 404 instead of redirecting to the UI for usability
|
||||
if (ctx.endpointHandlerPath() == "*") {
|
||||
if (ctx.endpoints().lastHttpEndpoint()?.path == "*") {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.events.serverStarted {
|
||||
if (serverConfig.initialOpenInBrowserEnabled.value) {
|
||||
Browser.openInBrowser()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when JVM is prompted to shutdown, stop javalin gracefully
|
||||
Runtime.getRuntime().addShutdownHook(
|
||||
thread(start = false) {
|
||||
app.stop()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun RoutesConfig.defineCore() {
|
||||
val loginPath = ServerSubpath.maybeAddAsPrefix("/login.html")
|
||||
|
||||
app.get(loginPath) { ctx ->
|
||||
get(loginPath) { ctx ->
|
||||
val locale: Locale = LocalizationHelper.ctxToLocale(ctx)
|
||||
ctx.header("content-type", "text/html")
|
||||
val httpCacheSeconds = 1.days.inWholeSeconds
|
||||
@@ -141,7 +162,7 @@ object JavalinSetup {
|
||||
)
|
||||
}
|
||||
|
||||
app.post(loginPath) { ctx ->
|
||||
post(loginPath) { ctx ->
|
||||
val username = ctx.formParam("user")
|
||||
val password = ctx.formParam("pass")
|
||||
val isValid =
|
||||
@@ -174,7 +195,7 @@ object JavalinSetup {
|
||||
)
|
||||
}
|
||||
|
||||
app.beforeMatched { ctx ->
|
||||
beforeMatched { ctx ->
|
||||
val isWebManifest =
|
||||
listOf("site.webmanifest", "manifest.json", "login.html").any { ctx.path().endsWith(it) }
|
||||
val isPageIcon =
|
||||
@@ -219,60 +240,43 @@ object JavalinSetup {
|
||||
ctx.setAttribute(Attribute.TachideskBasic, credentialsValid())
|
||||
}
|
||||
|
||||
app.events { event ->
|
||||
event.serverStarted {
|
||||
if (serverConfig.initialOpenInBrowserEnabled.value) {
|
||||
Browser.openInBrowser()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.wsBefore {
|
||||
wsBefore {
|
||||
it.onConnect { ctx ->
|
||||
ctx.setAttribute(Attribute.TachideskUser, getUserFromWsContext(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
// when JVM is prompted to shutdown, stop javalin gracefully
|
||||
Runtime.getRuntime().addShutdownHook(
|
||||
thread(start = false) {
|
||||
app.stop()
|
||||
},
|
||||
)
|
||||
|
||||
app.exception(NullPointerException::class.java) { e, ctx ->
|
||||
exception(NullPointerException::class.java) { e, ctx ->
|
||||
logger.error(e) { "NullPointerException while handling the request" }
|
||||
ctx.status(404)
|
||||
}
|
||||
app.exception(NoSuchElementException::class.java) { e, ctx ->
|
||||
exception(NoSuchElementException::class.java) { e, ctx ->
|
||||
logger.error(e) { "NoSuchElementException while handling the request" }
|
||||
ctx.status(404)
|
||||
}
|
||||
app.exception(IOException::class.java) { e, ctx ->
|
||||
exception(IOException::class.java) { e, ctx ->
|
||||
logger.error(e) { "IOException while handling the request" }
|
||||
ctx.status(500)
|
||||
ctx.result(e.message ?: "Internal Server Error")
|
||||
}
|
||||
|
||||
app.exception(IllegalArgumentException::class.java) { e, ctx ->
|
||||
exception(IllegalArgumentException::class.java) { e, ctx ->
|
||||
logger.error(e) { "IllegalArgumentException while handling the request" }
|
||||
ctx.status(400)
|
||||
ctx.result(e.message ?: "Bad Request")
|
||||
}
|
||||
|
||||
app.exception(UnauthorizedException::class.java) { e, ctx ->
|
||||
exception(UnauthorizedException::class.java) { e, ctx ->
|
||||
logger.error(e) { "UnauthorizedException while handling the request" }
|
||||
ctx.status(HttpStatus.UNAUTHORIZED)
|
||||
ctx.result(e.message ?: "Unauthorized")
|
||||
}
|
||||
|
||||
app.exception(ForbiddenException::class.java) { e, ctx ->
|
||||
exception(ForbiddenException::class.java) { e, ctx ->
|
||||
logger.error(e) { "ForbiddenException while handling the request" }
|
||||
ctx.status(HttpStatus.FORBIDDEN)
|
||||
ctx.result(e.message ?: "Forbidden")
|
||||
}
|
||||
|
||||
app.start()
|
||||
}
|
||||
|
||||
// private fun getOpenApiOptions(): OpenApiOptions {
|
||||
|
||||
@@ -71,15 +71,14 @@ fun <T> getParam(
|
||||
is Param.FormParam -> ctx.formParamAsClass(param.key, clazz)
|
||||
is Param.PathParam -> ctx.pathParamAsClass(param.key, clazz)
|
||||
is Param.QueryParam -> ctx.queryParamAsClass(param.key, clazz)
|
||||
else -> throw IllegalStateException("Invalid param")
|
||||
}.let {
|
||||
if (param.nullable) {
|
||||
it.allowNullable().get() ?: param.defaultValue
|
||||
it.getOrNull() ?: param.defaultValue
|
||||
} else {
|
||||
if (param.defaultValue != null) {
|
||||
it.getOrDefault(param.defaultValue!!)
|
||||
} else {
|
||||
it.get()
|
||||
it.required().get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.github.oshai.kotlinlogging.KLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.github.reactivecircus.cache4k.Cache
|
||||
import io.javalin.config.JavalinConfig
|
||||
import io.javalin.http.staticfiles.AliasCheck
|
||||
import io.javalin.http.staticfiles.Location
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
@@ -39,7 +40,6 @@ import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import net.lingala.zip4j.ZipFile
|
||||
import org.eclipse.jetty.server.handler.ContextHandler
|
||||
import suwayomi.tachidesk.graphql.types.AboutWebUI
|
||||
import suwayomi.tachidesk.graphql.types.UpdateState
|
||||
import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING
|
||||
@@ -180,7 +180,7 @@ object WebInterfaceManager {
|
||||
// Use canonical path to avoid Jetty alias issues
|
||||
staticFiles.directory = File(applicationDirs.webUIServe).canonicalPath
|
||||
staticFiles.location = Location.EXTERNAL
|
||||
staticFiles.aliasCheck = ContextHandler.ApproveAliases()
|
||||
staticFiles.aliasCheck = AliasCheck { _, _ -> true }
|
||||
}
|
||||
|
||||
serveWebUI = {
|
||||
|
||||
Reference in New Issue
Block a user