Compare commits

..

4 Commits

Author SHA1 Message Date
Syer10
4a3096e0a0 Lint 2026-05-09 11:36:34 -04:00
Syer10
64d7939b74 Use new format 2026-05-09 11:33:06 -04:00
Syer10
3f610da0c5 Context Parameters 2026-05-09 11:19:28 -04:00
renovate[bot]
47e0b835d5 Update kotlin to v2.3.21 2026-05-09 00:09:22 +00:00
15 changed files with 129 additions and 127 deletions

View File

@@ -54,14 +54,14 @@ jobs:
run: ./gradlew :server:shadowJar --stacktrace
- name: Upload Jar
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: jar
path: master/server/build/*.jar
if-no-files-found: error
- name: Upload icons
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: icon
path: master/server/src/main/resources/icon
@@ -71,7 +71,7 @@ jobs:
run: tar -cvzf scripts.tar.gz -C master/ scripts/
- name: Upload scripts.tar.gz
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: scripts
path: scripts.tar.gz
@@ -103,7 +103,7 @@ jobs:
run: jlink --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.naming,java.prefs,java.scripting,java.se,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.attach,jdk.crypto.ec,jdk.jdi,jdk.management,jdk.net,jdk.unsupported,jdk.unsupported.desktop,jdk.zipfs,jdk.accessibility --output suwa --strip-debug --no-man-pages --no-header-files --compress=2
- name: Upload JRE package
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.name }}-jre
path: suwa
@@ -134,26 +134,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download Jar
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: jar
path: server/build
- name: Download JRE
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
if: matrix.name != 'linux-assets' && matrix.name != 'debian-all'
with:
name: ${{ matrix.jre }}-jre
path: jre
- name: Download icons
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: icon
path: server/src/main/resources/icon
- name: Download scripts.tar.gz
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: scripts
@@ -164,7 +164,7 @@ jobs:
scripts/bundler.sh -o upload/ ${{ matrix.name }}
- name: Upload ${{ matrix.name }} release
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.name }}
path: upload/*
@@ -174,35 +174,35 @@ jobs:
needs: bundle
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: jar
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: debian-all
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: appimage
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: linux-assets
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: linux-x64
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: macOS-x64
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: macOS-arm64
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: windows-x64
path: release
@@ -240,7 +240,7 @@ jobs:
git push origin $TAG
- name: Upload Preview Release
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.DEPLOY_PREVIEW_TOKEN }}
repository: "Suwayomi/Suwayomi-Server-preview"

View File

@@ -56,14 +56,14 @@ jobs:
run: ./gradlew :server:downloadWebUI :server:shadowJar --stacktrace
- name: Upload Jar
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: jar
path: master/server/build/*.jar
if-no-files-found: error
- name: Upload icons
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: icon
path: master/server/src/main/resources/icon
@@ -73,7 +73,7 @@ jobs:
run: tar -cvzf scripts.tar.gz -C master/ scripts/
- name: Upload scripts.tar.gz
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: scripts
path: scripts.tar.gz
@@ -105,7 +105,7 @@ jobs:
run: jlink --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.naming,java.prefs,java.scripting,java.se,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.attach,jdk.crypto.ec,jdk.jdi,jdk.management,jdk.net,jdk.unsupported,jdk.unsupported.desktop,jdk.zipfs,jdk.accessibility --output suwa --strip-debug --no-man-pages --no-header-files --compress=2
- name: Upload JDK package
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.name }}-jre
path: suwa
@@ -136,26 +136,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download Jar
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: jar
path: server/build
- name: Download JRE
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
if: matrix.name != 'linux-assets' && matrix.name != 'debian-all'
with:
name: ${{ matrix.jre }}-jre
path: jre
- name: Download icons
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: icon
path: server/src/main/resources/icon
- name: Download scripts.tar.gz
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
with:
name: scripts
@@ -166,7 +166,7 @@ jobs:
scripts/bundler.sh -o upload/ ${{ matrix.name }}
- name: Upload ${{ matrix.name }} files
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.name }}
path: upload/*
@@ -177,35 +177,35 @@ jobs:
needs: bundle
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: jar
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: debian-all
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: appimage
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: linux-assets
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: linux-x64
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: macOS-x64
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: macOS-arm64
path: release
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v7
with:
name: windows-x64
path: release
@@ -214,7 +214,7 @@ jobs:
run: cd release && sha256sum * > Checksums.sha256
- name: Release
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.DEPLOY_RELEASE_TOKEN }}
draft: true

View File

@@ -76,9 +76,9 @@ These clients are built-in options, and the server can keep them automatically u
- [Suwayomi-WebUI](https://github.com/Suwayomi/Suwayomi-WebUI): Web app, PWA
- [Suwayomi-VUI](https://github.com/Suwayomi/Suwayomi-VUI): Web app, PWA
##### Other clients (potentially inactive or abandoned)
##### Other clients (potentially inactive or abondend)
- [Tachidesk-VaadinUI](https://github.com/Suwayomi/Tachidesk-VaadinUI): Desktop app (windows, linux, mac); UI in the browser, manages its own suwayomi server instance
- [Moku](https://github.com/Youwes09/Moku): Desktop app (windows, linux, mac), can manage its own suwayomi server instance
- [Moku](https://github.com/Youwes09/Moku): Desktop app (windows, linux, mac), requires access to a running server
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): Desktop app (windows, linux, mac); can manage its own suwayomi server instance
- [Tachidesk-Sorayomi](https://github.com/Suwayomi/Tachidesk-Sorayomi): Web app; Desktop app (windows, linux, mac); Android app; requires access to a running server
- [Tachidesk-qtui](https://github.com/Suwayomi/Tachidesk-qtui): Android app; iOS app Desktop app (linux); requires access to a running server

View File

@@ -1,22 +1,22 @@
[versions]
kotlin = "2.3.21"
coroutines = "1.11.0"
serialization = "1.11.0"
coroutines = "1.10.2"
serialization = "1.10.0"
jvmTarget = "21"
okhttp = "5.3.2" # Major version is locked by Tachiyomi extensions
javalin = "7.2.0"
jte = "3.2.4"
jackson = "3.1.2" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
javalin = "6.7.0"
jte = "3.2.3"
jackson = "2.18.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
exposed = "0.61.0"
dex2jar = "2.4.36"
polyglot = "25.0.3"
dex2jar = "2.4.34"
polyglot = "24.2.2"
settings = "1.3.0"
twelvemonkeys = "3.13.1"
graphqlkotlin = "8.9.0"
graphqlkotlin = "8.8.1"
xmlserialization = "0.91.3"
ktlint = "1.8.0"
koin = "4.2.1"
moko = "0.26.4"
koin = "4.1.1"
moko = "0.26.0"
[libraries]
# Kotlin
@@ -39,22 +39,22 @@ serialization-xml = { module = "io.github.pdvrieze.xmlutil:serialization-jvm", v
# Logging
slf4japi = "org.slf4j:slf4j-api:2.0.17"
logback = "ch.qos.logback:logback-classic:1.5.32"
kotlinlogging = "io.github.oshai:kotlin-logging-jvm:8.0.02"
kotlinlogging = "io.github.oshai:kotlin-logging-jvm:8.0.01"
# OkHttp
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp" }
okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp" }
okio = "com.squareup.okio:okio:3.17.0"
okio = "com.squareup.okio:okio:3.16.4"
# 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-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"
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" }
jte = { module = "gg.jte:jte", version.ref = "jte" }
kte = { module = "gg.jte:jte-kotlin", version.ref = "jte" }
@@ -68,7 +68,7 @@ exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "e
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
exposed-javatime = { module = "org.jetbrains.exposed:exposed-java-time", version.ref = "exposed" }
postgres = "org.postgresql:postgresql:42.7.11"
postgres = "org.postgresql:postgresql:42.7.10"
h2 = "com.h2database:h2:1.4.200" # current database driver, can't update to h2 v2 without sql migration
hikaricp = "com.zaxxer:HikariCP:7.0.2"
@@ -86,10 +86,10 @@ systemtray-desktop = "com.dorkbox:Desktop:1.1" # version locked by SystemTray
# dependencies of Tachiyomi extensions
injekt = "com.github.null2264:injekt-koin:ee267b2e27"
rxjava = "io.reactivex:rxjava:1.3.8"
jsoup = "org.jsoup:jsoup:1.22.2"
jsoup = "org.jsoup:jsoup:1.22.1"
# Config
config = "com.typesafe:config:1.4.8"
config = "com.typesafe:config:1.4.5"
config4k = "io.github.config4k:config4k:0.7.0"
# Sort
@@ -105,7 +105,7 @@ dex2jar-tools = { module = "de.femtopedia.dex2jar:dex-tools", version.ref = "dex
# APK
apk-parser = "net.dongliu:apk-parser:2.6.10"
apksig = "com.android.tools.build:apksig:9.2.1"
apksig = "com.android.tools.build:apksig:9.0.1"
# Xml
xmlpull = "xmlpull:xmlpull:1.1.3.4a"
@@ -118,10 +118,10 @@ commonscompress = "org.apache.commons:commons-compress:1.28.0"
junrar = "com.github.junrar:junrar:7.5.10"
# AES/CBC/PKCS7Padding Cypher provider
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.84"
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.83"
# AndroidX annotations
android-annotations = "androidx.annotation:annotation:1.10.0"
android-annotations = "androidx.annotation:annotation:1.9.1"
# Substitute for duktape-android
polyglot-core = { module = "org.graalvm.polyglot:polyglot", version.ref = "polyglot" }
@@ -132,7 +132,7 @@ settings-core = { module = "com.russhwolf:multiplatform-settings-jvm", version.r
settings-serialization = { module = "com.russhwolf:multiplatform-settings-serialization-jvm", version.ref = "settings" }
# ICU4J
icu4j = "com.ibm.icu:icu4j:78.3"
icu4j = "com.ibm.icu:icu4j:78.2"
# Image Decoding implementation provider
twelvemonkeys-common-lang = { module = "com.twelvemonkeys.common:common-lang", version.ref = "twelvemonkeys" }
@@ -158,7 +158,7 @@ cronUtils = "com.cronutils:cron-utils:9.2.1"
kcef = "dev.datlag:kcef:2024.04.20.4"
# User
jwt = "com.auth0:java-jwt:4.5.2"
jwt = "com.auth0:java-jwt:4.5.1"
# lint - used for renovate to update ktlint version
ktlint = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint" }
@@ -173,16 +173,16 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin"}
# Linter
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "14.2.0"}
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "14.0.1"}
# Build config
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "6.0.9"}
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "6.0.7"}
# Download
download = { id = "de.undercouch.download", version = "5.7.0"}
# ShadowJar
shadowjar = { id = "com.gradleup.shadow", version = "8.3.10"}
shadowjar = { id = "com.gradleup.shadow", version = "8.3.9"}
# Moko
moko = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" }

Binary file not shown.

View File

@@ -1,9 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

2
gradlew vendored
View File

@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.

31
gradlew.bat vendored
View File

@@ -23,8 +23,8 @@
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,7 +65,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto fail
:execute
@rem Setup the command line
@@ -73,10 +73,21 @@ echo location of your Java installation. 1>&2
@rem Execute Gradle
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -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.core.CloseStatus
import org.eclipse.jetty.websocket.api.CloseStatus
import suwayomi.tachidesk.manga.impl.update.Websocket
object WebView : Websocket<String>() {

View File

@@ -13,14 +13,10 @@ 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 {
@@ -33,17 +29,17 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
context.formParam("operations")
?: throw IllegalArgumentException("Cannot find 'operations' body")
} else {
return context.bodyInputStream().use { jsonMapper.fromJsonStream<GraphQLServerRequest>(it) }
return context.bodyAsClass(GraphQLServerRequest::class.java)
}
val request =
jsonMapper.fromJsonString<GraphQLServerRequest>(formParam)
context.jsonMapper().fromJsonString<GraphQLServerRequest>(formParam)
val map =
context
.formParam("map")
?.let {
jsonMapper.fromJsonString<Map<String, List<String>>>(it)
context.jsonMapper().fromJsonString<Map<String, List<String>>>(it)
}.orEmpty()
val mapItems =

View File

@@ -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.core.CloseStatus
import org.eclipse.jetty.websocket.api.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

View File

@@ -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.core.CloseStatus
import org.eclipse.jetty.websocket.api.CloseStatus
import suwayomi.tachidesk.graphql.server.toGraphQLContext
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList

View File

@@ -13,14 +13,12 @@ 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
@@ -49,7 +47,6 @@ 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 {
@@ -61,12 +58,10 @@ object JavalinSetup {
fun javalinSetup() {
val app =
Javalin.start { config ->
Javalin.create { config ->
val templateEngine = TemplateEngine.createPrecompiled(ContentType.Html)
config.fileRenderer(JavalinJte(templateEngine))
config.jsonMapper(JavalinJackson3())
WebInterfaceManager.setup(config)
// config.registerPlugin(OpenApiPlugin(getOpenApiOptions()))
@@ -109,8 +104,7 @@ object JavalinSetup {
}
}
config.routes.defineCore()
config.routes.apiBuilder {
config.router.apiBuilder {
path(ServerSubpath.maybeAddAsPrefix("api/")) {
path("v1/") {
GlobalAPI.defineEndpoints()
@@ -123,32 +117,17 @@ 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.endpoints().lastHttpEndpoint()?.path == "*") {
if (ctx.endpointHandlerPath() == "*") {
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")
get(loginPath) { ctx ->
app.get(loginPath) { ctx ->
val locale: Locale = LocalizationHelper.ctxToLocale(ctx)
ctx.header("content-type", "text/html")
val httpCacheSeconds = 1.days.inWholeSeconds
@@ -162,7 +141,7 @@ object JavalinSetup {
)
}
post(loginPath) { ctx ->
app.post(loginPath) { ctx ->
val username = ctx.formParam("user")
val password = ctx.formParam("pass")
val isValid =
@@ -195,7 +174,7 @@ object JavalinSetup {
)
}
beforeMatched { ctx ->
app.beforeMatched { ctx ->
val isWebManifest =
listOf("site.webmanifest", "manifest.json", "login.html").any { ctx.path().endsWith(it) }
val isPageIcon =
@@ -240,43 +219,60 @@ object JavalinSetup {
ctx.setAttribute(Attribute.TachideskBasic, credentialsValid())
}
wsBefore {
app.events { event ->
event.serverStarted {
if (serverConfig.initialOpenInBrowserEnabled.value) {
Browser.openInBrowser()
}
}
}
app.wsBefore {
it.onConnect { ctx ->
ctx.setAttribute(Attribute.TachideskUser, getUserFromWsContext(ctx))
}
}
exception(NullPointerException::class.java) { e, 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 ->
logger.error(e) { "NullPointerException while handling the request" }
ctx.status(404)
}
exception(NoSuchElementException::class.java) { e, ctx ->
app.exception(NoSuchElementException::class.java) { e, ctx ->
logger.error(e) { "NoSuchElementException while handling the request" }
ctx.status(404)
}
exception(IOException::class.java) { e, ctx ->
app.exception(IOException::class.java) { e, ctx ->
logger.error(e) { "IOException while handling the request" }
ctx.status(500)
ctx.result(e.message ?: "Internal Server Error")
}
exception(IllegalArgumentException::class.java) { e, ctx ->
app.exception(IllegalArgumentException::class.java) { e, ctx ->
logger.error(e) { "IllegalArgumentException while handling the request" }
ctx.status(400)
ctx.result(e.message ?: "Bad Request")
}
exception(UnauthorizedException::class.java) { e, ctx ->
app.exception(UnauthorizedException::class.java) { e, ctx ->
logger.error(e) { "UnauthorizedException while handling the request" }
ctx.status(HttpStatus.UNAUTHORIZED)
ctx.result(e.message ?: "Unauthorized")
}
exception(ForbiddenException::class.java) { e, ctx ->
app.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 {

View File

@@ -71,14 +71,15 @@ 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.getOrNull() ?: param.defaultValue
it.allowNullable().get() ?: param.defaultValue
} else {
if (param.defaultValue != null) {
it.getOrDefault(param.defaultValue!!)
} else {
it.required().get()
it.get()
}
}
}

View File

@@ -17,7 +17,6 @@ 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
@@ -40,6 +39,7 @@ 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 = AliasCheck { _, _ -> true }
staticFiles.aliasCheck = ContextHandler.ApproveAliases()
}
serveWebUI = {