Compare commits

...

31 Commits

Author SHA1 Message Date
renovate[bot]
dff66547b4 Update jackson monorepo (#1906)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 18:16:05 -04:00
Mitchell Syer
6fef27bb56 Wait until WebUI is ready to open in browser (#2010)
* Wait until WebUI is ready

* Changelog

* Move openInBrowser out of timeout
2026-05-09 18:15:43 -04:00
Mitchell Syer
505e966653 Fix Polyglot (#2011) 2026-05-09 18:15:33 -04:00
renovate[bot]
6ee3348f50 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>
2026-05-09 14:52:00 -04:00
renovate[bot]
c98899d501 Update plugin shadowjar to v8.3.10 (#2007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 12:01:08 -04:00
renovate[bot]
7654653a25 Update plugin buildconfig to v6.0.9 (#2006)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 12:00:44 -04:00
renovate[bot]
0c1a0ef408 Update Gradle to v9.5.0 (#2004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 12:00:30 -04:00
renovate[bot]
e7f2192579 Update plugin ktlint to v14.2.0 (#2005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:59:40 -04:00
renovate[bot]
5c3b1e0b07 Update moko to v0.26.4 (#2003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:58:02 -04:00
renovate[bot]
6ad59f2e2b Update dex2jar to v2.4.36 (#2001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:51:32 -04:00
renovate[bot]
03dd778fac Update jte to v3.2.4 (#2002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:50:46 -04:00
renovate[bot]
6b833a38d1 Update kotlinx-coroutines monorepo to v1.11.0 (#1995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:47:26 -04:00
renovate[bot]
a83885353c Update dependency com.typesafe:config to v1.4.8 (#1922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:46:20 -04:00
renovate[bot]
10d7c7c06d Update polyglot to v25 (#1651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:45:53 -04:00
Shozikan
3b575271cb Chore: Updated Moku's Description & Quick Spelling Fix (#1999)
Corrected spelling of 'abandoned' and clarified Moku's server management capabilities.
2026-05-09 11:44:29 -04:00
renovate[bot]
5ad92413f3 Update GitHub Artifact Actions (#1986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:44:11 -04:00
renovate[bot]
7fbdb39319 Update dependency io.github.oshai:kotlin-logging-jvm to v8.0.02 (#1983)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:44:02 -04:00
renovate[bot]
92abdf7fb7 Update dependency com.auth0:java-jwt to v4.5.2 (#1980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:43:47 -04:00
renovate[bot]
ea0c666cfe Update dependency com.android.tools.build:apksig to v9.2.1 (#1984)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:43:36 -04:00
renovate[bot]
b5e395a039 Update dependency com.ibm.icu:icu4j to v78.3 (#1985)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:43:25 -04:00
renovate[bot]
e530072a07 Update softprops/action-gh-release action to v3 (#1987)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:43:14 -04:00
renovate[bot]
f85cbe1ca5 Update dependency org.jsoup:jsoup to v1.22.2 (#1988)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:43:04 -04:00
renovate[bot]
5f9126eb2f Update dependency org.postgresql:postgresql to v42.7.11 (#1989)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:42:53 -04:00
renovate[bot]
d77c57ede0 Update dependency androidx.annotation:annotation to v1.10.0 (#1990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:42:43 -04:00
renovate[bot]
02f9a0d1d7 Update dependency androidx.annotation:annotation to v1.10.0 (#1990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:42:36 -04:00
renovate[bot]
53192f56ca Update serialization to v1.11.0 (#1996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:42:23 -04:00
renovate[bot]
01d89cbb48 Update dependency org.bouncycastle:bcprov-jdk18on to v1.84 (#1994)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:42:04 -04:00
renovate[bot]
be55cb974b Update dependency io.insert-koin:koin-core to v4.2.1 (#1993)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:41:49 -04:00
renovate[bot]
34c394ed19 Update dependency com.squareup.okio:okio to v3.17.0 (#1992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:41:39 -04:00
renovate[bot]
1433a21abd Update graphqlkotlin to v8.9.0 (#1981)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 11:41:33 -04:00
renovate[bot]
ec28794655 Update kotlin to v2.3.21 (#1991)
* Update kotlin to v2.3.21

* Context Parameters

* Use new format

* Lint

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2026-05-09 11:41:27 -04:00
19 changed files with 147 additions and 138 deletions

View File

@@ -54,14 +54,14 @@ jobs:
run: ./gradlew :server:shadowJar --stacktrace
- name: Upload Jar
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: jar
path: master/server/build/*.jar
if-no-files-found: error
- name: Upload icons
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
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@v6
uses: actions/upload-artifact@v7
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@v6
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.name }}-jre
path: suwa
@@ -134,26 +134,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download Jar
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: jar
path: server/build
- name: Download JRE
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
if: matrix.name != 'linux-assets' && matrix.name != 'debian-all'
with:
name: ${{ matrix.jre }}-jre
path: jre
- name: Download icons
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: icon
path: server/src/main/resources/icon
- name: Download scripts.tar.gz
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: scripts
@@ -164,7 +164,7 @@ jobs:
scripts/bundler.sh -o upload/ ${{ matrix.name }}
- name: Upload ${{ matrix.name }} release
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.name }}
path: upload/*
@@ -174,35 +174,35 @@ jobs:
needs: bundle
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: jar
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: debian-all
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: appimage
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: linux-assets
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: linux-x64
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: macOS-x64
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: macOS-arm64
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: windows-x64
path: release
@@ -240,7 +240,7 @@ jobs:
git push origin $TAG
- name: Upload Preview Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
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@v6
uses: actions/upload-artifact@v7
with:
name: jar
path: master/server/build/*.jar
if-no-files-found: error
- name: Upload icons
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
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@v6
uses: actions/upload-artifact@v7
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@v6
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.name }}-jre
path: suwa
@@ -136,26 +136,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download Jar
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: jar
path: server/build
- name: Download JRE
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
if: matrix.name != 'linux-assets' && matrix.name != 'debian-all'
with:
name: ${{ matrix.jre }}-jre
path: jre
- name: Download icons
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: icon
path: server/src/main/resources/icon
- name: Download scripts.tar.gz
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: scripts
@@ -166,7 +166,7 @@ jobs:
scripts/bundler.sh -o upload/ ${{ matrix.name }}
- name: Upload ${{ matrix.name }} files
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.name }}
path: upload/*
@@ -177,35 +177,35 @@ jobs:
needs: bundle
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: jar
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: debian-all
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: appimage
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: linux-assets
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: linux-x64
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: macOS-x64
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: macOS-arm64
path: release
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
name: windows-x64
path: release
@@ -214,7 +214,7 @@ jobs:
run: cd release && sha256sum * > Checksums.sha256
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
token: ${{ secrets.DEPLOY_RELEASE_TOKEN }}
draft: true

View File

@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Fixed
- (WebUI) Handle serving non-default webui with "bundled"
- (WebUI) Wait until WebUI is ready to open in browser
## [v2.2.2100] + [WebUI: v20260508.01] - 2026-05-08

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 abondend)
##### Other clients (potentially inactive or abandoned)
- [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), requires access to a running server
- [Moku](https://github.com/Youwes09/Moku): Desktop app (windows, linux, mac), can manage its own suwayomi server instance
- [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

@@ -53,7 +53,7 @@ subprojects {
}
compilerOptions {
jvmTarget = JvmTarget.fromTarget(libs.versions.jvmTarget.get())
freeCompilerArgs.add("-Xcontext-receivers")
freeCompilerArgs.add("-Xcontext-parameters")
}
}
}

View File

@@ -1,22 +1,22 @@
[versions]
kotlin = "2.3.10"
coroutines = "1.10.2"
serialization = "1.10.0"
kotlin = "2.3.21"
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"
jte = "3.2.3"
jackson = "2.18.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
javalin = "7.2.0"
jte = "3.2.4"
jackson = "3.1.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
exposed = "0.61.0"
dex2jar = "2.4.34"
polyglot = "24.2.2"
dex2jar = "2.4.36"
polyglot = "25.0.3"
settings = "1.3.0"
twelvemonkeys = "3.13.1"
graphqlkotlin = "8.8.1"
graphqlkotlin = "8.9.0"
xmlserialization = "0.91.3"
ktlint = "1.8.0"
koin = "4.1.1"
moko = "0.26.0"
koin = "4.2.1"
moko = "0.26.4"
[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.01"
kotlinlogging = "io.github.oshai:kotlin-logging-jvm:8.0.02"
# 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.16.4"
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.21"
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.10"
postgres = "org.postgresql:postgresql:42.7.11"
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.1"
jsoup = "org.jsoup:jsoup:1.22.2"
# Config
config = "com.typesafe:config:1.4.5"
config = "com.typesafe:config:1.4.8"
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.0.1"
apksig = "com.android.tools.build:apksig:9.2.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.83"
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.84"
# AndroidX annotations
android-annotations = "androidx.annotation:annotation:1.9.1"
android-annotations = "androidx.annotation:annotation:1.10.0"
# 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.2"
icu4j = "com.ibm.icu:icu4j:78.3"
# 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.1"
jwt = "com.auth0:java-jwt:4.5.2"
# 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.0.1"}
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "14.2.0"}
# Build config
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "6.0.7"}
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "6.0.9"}
# Download
download = { id = "de.undercouch.download", version = "5.7.0"}
# ShadowJar
shadowjar = { id = "com.gradleup.shadow", version = "8.3.9"}
shadowjar = { id = "com.gradleup.shadow", version = "8.3.10"}
# Moko
moko = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" }

Binary file not shown.

View File

@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-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/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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 with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
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
goto fail
"%COMSPEC%" /c exit 1
: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
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@@ -73,21 +73,10 @@ goto fail
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
@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
: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
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%

View File

@@ -171,6 +171,7 @@ tasks {
"Implementation-Vendor" to "The Suwayomi Project",
"Specification-Version" to getTachideskVersion(),
"Implementation-Version" to getTachideskRevision(),
"Multi-Release" to true, // needed for polyglot
)
}
archiveBaseName.set(rootProject.name)

View File

@@ -140,17 +140,16 @@ fun OkHttpClient.newCachelessCallWithProgress(
return progressClient.newCall(request)
}
context(Json)
context(_: Json)
inline fun <reified T> Response.parseAs(): T = decodeFromJsonResponse(serializer(), this)
@OptIn(ExperimentalSerializationApi::class)
context(Json)
context(json: Json)
fun <T> decodeFromJsonResponse(
deserializer: DeserializationStrategy<T>,
response: Response,
): T =
response.body.source().use {
decodeFromBufferedSource(deserializer, it)
json.decodeFromBufferedSource(deserializer, it)
}
class HttpException(

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

View File

@@ -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 =

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.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

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.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

View File

@@ -13,19 +13,24 @@ 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
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.future.future
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import org.eclipse.jetty.server.ServerConnector
import suwayomi.tachidesk.global.GlobalAPI
import suwayomi.tachidesk.graphql.GraphQL
@@ -47,7 +52,9 @@ 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
import kotlin.time.Duration.Companion.seconds
object JavalinSetup {
private val logger = KotlinLogging.logger {}
@@ -58,10 +65,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 +113,8 @@ object JavalinSetup {
}
}
config.router.apiBuilder {
config.routes.defineCore()
config.routes.apiBuilder {
path(ServerSubpath.maybeAddAsPrefix("api/")) {
path("v1/") {
GlobalAPI.defineEndpoints()
@@ -117,17 +127,37 @@ 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) {
scope.launch {
withTimeoutOrNull(10.seconds) {
WebInterfaceManager.isSetupComplete.first { it }
}
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 +171,7 @@ object JavalinSetup {
)
}
app.post(loginPath) { ctx ->
post(loginPath) { ctx ->
val username = ctx.formParam("user")
val password = ctx.formParam("pass")
val isValid =
@@ -174,7 +204,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 +249,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 {

View File

@@ -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()
}
}
}

View File

@@ -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
@@ -26,6 +27,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.sample
@@ -39,7 +41,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
@@ -90,7 +91,7 @@ object WebInterfaceManager {
private val preferences = Injekt.get<Application>().getSharedPreferences("server_util", Context.MODE_PRIVATE)
private var currentUpdateTaskId: String = ""
private var isSetupComplete = false
val isSetupComplete = MutableStateFlow(false)
private val json: Json by injectLazy()
private val network: NetworkHelper by injectLazy()
@@ -180,7 +181,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 = {
@@ -196,7 +197,7 @@ object WebInterfaceManager {
@OptIn(DelicateCoroutinesApi::class)
GlobalScope.launchIO {
setupWebUI()
isSetupComplete = true
isSetupComplete.value = true
}
}
@@ -260,7 +261,7 @@ object WebInterfaceManager {
val lastAutomatedUpdate = preferences.getLong(LAST_WEBUI_UPDATE_CHECK_KEY, System.currentTimeMillis())
val task = {
if (isSetupComplete) {
if (isSetupComplete.value) {
val log =
KotlinLogging.logger(
"${logger.name}::scheduleWebUIUpdateCheck(" +