diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 24b4b05d..210e1135 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -67,7 +67,7 @@ jobs: export LD_PRELOAD="$(pwd)/scripts/resources/catch_abort.so" JAR=$(ls ./server/build/*.jar| head -1) set +e - timeout 30s java -DcrashOnFailedMigration=true \ + timeout 30s java \ -Dsuwayomi.tachidesk.config.server.systemTrayEnabled=false \ -Dsuwayomi.tachidesk.config.server.initialOpenInBrowserEnabled=false \ -Dsuwayomi.tachidesk.config.server.databaseType=POSTGRESQL \ @@ -83,7 +83,7 @@ jobs: exit "$ecode" fi - timeout 30s java -DcrashOnFailedMigration=true \ + timeout 30s java \ -Dsuwayomi.tachidesk.config.server.systemTrayEnabled=false \ -Dsuwayomi.tachidesk.config.server.initialOpenInBrowserEnabled=false \ -jar "$JAR" diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/util/SafePath.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/util/SafePath.kt index 7d722b9e..691a4fbf 100644 --- a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/util/SafePath.kt +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/util/SafePath.kt @@ -9,6 +9,9 @@ package xyz.nulldev.androidcompat.util // adopted from: https://github.com/tachiyomiorg/tachiyomi/blob/4cefbce7c34e724b409b6ba127f3c6c5c346ad8d/app/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt object SafePath { + private const val MAX_FILENAME_CHARS = 240 + private const val MAX_FILENAME_UTF8_BYTES = 240 + /** * Mutate the given filename to make it valid for a FAT filesystem, * replacing any invalid characters with "_". This method doesn't allow hidden files (starting @@ -27,11 +30,41 @@ object SafePath { sb.append('_') } } - // Even though vfat allows 255 UCS-2 chars, we might eventually write to - // ext4 through a FUSE layer, so use that limit minus 15 reserved characters. - return sb.toString().take(240) + + return truncateFilename(sb.toString()) } + private fun truncateFilename(filename: String): String { + // Keep a safety margin under common filesystem limits and satisfy both + // character count and UTF-8 byte-length constraints. + val output = StringBuilder(minOf(filename.length, MAX_FILENAME_CHARS)) + var usedBytes = 0 + var index = 0 + + while (index < filename.length && output.length < MAX_FILENAME_CHARS) { + val codePoint = Character.codePointAt(filename, index) + val codePointBytes = utf8ByteCount(codePoint) + + if (usedBytes + codePointBytes > MAX_FILENAME_UTF8_BYTES) { + break + } + + output.appendCodePoint(codePoint) + usedBytes += codePointBytes + index += Character.charCount(codePoint) + } + + return output.toString() + } + + private fun utf8ByteCount(codePoint: Int): Int = + when { + codePoint <= 0x7f -> 1 + codePoint <= 0x7ff -> 2 + codePoint <= 0xffff -> 3 + else -> 4 + } + /** * Returns true if the given character is a valid filename character, false otherwise. */ diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt index bd3c9a76..4d0c8dcb 100644 --- a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/webkit/KcefWebViewProvider.kt @@ -55,7 +55,6 @@ import dev.datlag.kcef.KCEF import dev.datlag.kcef.KCEFBrowser import dev.datlag.kcef.KCEFClient import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import org.cef.CefSettings import org.cef.browser.CefBrowser @@ -88,7 +87,6 @@ import java.io.BufferedWriter import java.io.File import java.io.IOException import java.util.concurrent.Executor -import kotlin.collections.Map import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.full.declaredMemberFunctions diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e24186..601f1a12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - . ### Changed -- . +- (Database/H2) Use the latest H2 database engine +- (Startup) Crash on startup if an unrecoverable error happens ### Fixed +- (CloudFlareInterceptor) Don't send the `cf_clearance` cookie back to Flaresolverr - (WebUI) Handle serving non-default webui with "bundled" - (WebUI) Wait until WebUI is ready to open in browser +- (Downloads) Truncate filenames by byte length to prevent "File name too long" IO errors +- (Extension) Do not indicate an update is available when the extension is not installed ## [v2.2.2100] + [WebUI: v20260508.01] - 2026-05-08 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 13c45dbe..b38ee281 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,15 +4,15 @@ coroutines = "1.11.0" serialization = "1.11.0" jvmTarget = "21" okhttp = "5.3.2" # Major version is locked by Tachiyomi extensions -javalin = "7.2.0" +javalin = "7.2.2" jte = "3.2.4" jackson = "3.1.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency` -exposed = "0.61.0" +exposed = "1.2.0" dex2jar = "2.4.36" polyglot = "25.0.3" settings = "1.3.0" twelvemonkeys = "3.13.1" -graphqlkotlin = "8.9.0" +graphqlkotlin = "10.0.0-alpha.3" xmlserialization = "0.91.3" ktlint = "1.8.0" koin = "4.2.1" @@ -37,7 +37,7 @@ serialization-xml-core = { module = "io.github.pdvrieze.xmlutil:core", version.r serialization-xml = { module = "io.github.pdvrieze.xmlutil:serialization-jvm", version.ref = "xmlserialization" } # Logging -slf4japi = "org.slf4j:slf4j-api:2.0.17" +slf4japi = "org.slf4j:slf4j-api:2.0.18" logback = "ch.qos.logback:logback-classic:1.5.32" kotlinlogging = "io.github.oshai:kotlin-logging-jvm:8.0.02" @@ -68,12 +68,13 @@ 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" } +exposed-kotlintime = { module = "org.jetbrains.exposed:exposed-kotlin-datetime", version.ref = "exposed" } 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 +h2 = "com.h2database:h2:2.4.240" hikaricp = "com.zaxxer:HikariCP:7.0.2" # Exposed Migrations -exposed-migrations = "com.github.Suwayomi:exposed-migrations:3.8.0" +exposed-migrations = "com.github.Suwayomi:exposed-migrations:3.10.1" # Dependency Injection koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } @@ -115,7 +116,7 @@ appdirs = "ca.gosyer:kotlin-multiplatform-appdirs:2.0.0" cache4k = "io.github.reactivecircus.cache4k:cache4k:0.14.0" zip4j = "net.lingala.zip4j:zip4j:2.11.6" commonscompress = "org.apache.commons:commons-compress:1.28.0" -junrar = "com.github.junrar:junrar:7.5.10" +junrar = "com.github.junrar:junrar:7.6.0" # AES/CBC/PKCS7Padding Cypher provider bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.84" @@ -242,6 +243,7 @@ exposed = [ "exposed-dao", "exposed-jdbc", "exposed-javatime", + "exposed-kotlintime", ] systemtray = [ "systemtray-core", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b52fb7e7..df6a6ad7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ 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.5.1-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 diff --git a/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupServerSettingsGenerator.kt b/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupServerSettingsGenerator.kt index 21c432be..00cec785 100644 --- a/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupServerSettingsGenerator.kt +++ b/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupServerSettingsGenerator.kt @@ -2,7 +2,6 @@ package suwayomi.tachidesk.server.settings.generation import suwayomi.tachidesk.server.settings.SettingsRegistry import java.io.File -import kotlin.text.appendLine object SettingsBackupServerSettingsGenerator { fun generate( diff --git a/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupSettingsHandlerGenerator.kt b/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupSettingsHandlerGenerator.kt index ab942acf..e74e3aeb 100644 --- a/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupSettingsHandlerGenerator.kt +++ b/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsBackupSettingsHandlerGenerator.kt @@ -2,7 +2,6 @@ package suwayomi.tachidesk.server.settings.generation import suwayomi.tachidesk.server.settings.SettingsRegistry import java.io.File -import kotlin.text.appendLine object SettingsBackupSettingsHandlerGenerator { fun generate( diff --git a/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsGraphqlTypeGenerator.kt b/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsGraphqlTypeGenerator.kt index 56501c53..051f0c5d 100644 --- a/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsGraphqlTypeGenerator.kt +++ b/server/server-config-generate/src/main/kotlin/suwayomi/tachidesk/server/settings/generation/SettingsGraphqlTypeGenerator.kt @@ -2,7 +2,6 @@ package suwayomi.tachidesk.server.settings.generation import suwayomi.tachidesk.server.settings.SettingsRegistry import java.io.File -import kotlin.text.appendLine object SettingsGraphqlTypeGenerator { fun generate( diff --git a/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt b/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt index 6f94b4c5..3b23f0b3 100644 --- a/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt +++ b/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach -import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.v1.core.SortOrder import suwayomi.tachidesk.graphql.types.AuthMode import suwayomi.tachidesk.graphql.types.CbzMediaType import suwayomi.tachidesk.graphql.types.DatabaseType @@ -56,16 +56,14 @@ import suwayomi.tachidesk.server.settings.PathSetting import suwayomi.tachidesk.server.settings.SettingGroup import suwayomi.tachidesk.server.settings.SettingsRegistry import suwayomi.tachidesk.server.settings.StringSetting +import uy.kohesive.injekt.injectLazy import xyz.nulldev.ts.config.GlobalConfigManager import xyz.nulldev.ts.config.SystemPropertyOverridableConfigModule -import kotlin.collections.associate -import kotlin.getValue import kotlin.time.Duration import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds -import uy.kohesive.injekt.injectLazy val mutableConfigValueScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) @@ -582,7 +580,7 @@ class ServerConfig( privacySafe = true, defaultValue = SortOrder.DESC, enumClass = SortOrder::class, - typeInfo = SettingsRegistry.PartialTypeInfo(imports = listOf("org.jetbrains.exposed.sql.SortOrder")), + typeInfo = SettingsRegistry.PartialTypeInfo(imports = listOf("org.jetbrains.exposed.v1.core.SortOrder")), ) val authMode: MutableStateFlow by EnumSetting( diff --git a/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/settings/SettingsRegistry.kt b/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/settings/SettingsRegistry.kt index 8a439e61..3347a847 100644 --- a/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/settings/SettingsRegistry.kt +++ b/server/server-config/src/main/kotlin/suwayomi/tachidesk/server/settings/SettingsRegistry.kt @@ -2,7 +2,6 @@ package suwayomi.tachidesk.server.settings import com.typesafe.config.ConfigValue import com.typesafe.config.parser.ConfigDocument -import kotlin.collections.find import kotlin.reflect.KClass /** diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt index 3cf2ecad..8e6e3f70 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.network import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.okio.decodeFromBufferedSource import kotlinx.serialization.serializer diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt index cae62706..c19ee0f3 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt @@ -103,7 +103,7 @@ class CloudflareInterceptor( companion object { private val ERROR_CODES = listOf(403, 503) private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare") - private val COOKIE_NAMES = listOf("cf_clearance") + val COOKIE_NAMES = listOf("cf_clearance") private val CHROME_IMAGE_TEMPLATE_REGEX = Regex("""(.*?) \(\d+×\d+\)""") } } @@ -205,9 +205,12 @@ object CFClearance { session = serverConfig.flareSolverrSessionName.value, sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value, cookies = - network.cookieStore.get(originalRequest.url).map { - FlareSolverCookie(it.name, it.value) - }, + network.cookieStore + .get(originalRequest.url) + .filter { it.name !in CloudflareInterceptor.COOKIE_NAMES } + .map { cookie -> + FlareSolverCookie(cookie.name, cookie.value) + }, returnOnlyCookies = onlyCookies, maxTimeout = timeout.inWholeMilliseconds.toInt(), postData = diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt index a6a6cb78..41035e2c 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt @@ -36,10 +36,11 @@ import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi import nl.adaptivity.xmlutil.core.KtXmlReader import nl.adaptivity.xmlutil.serialization.XML import org.apache.commons.compress.archivers.zip.ZipFile -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.insertAndGetId -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.registerCatalogueSource import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil import suwayomi.tachidesk.manga.model.table.ExtensionTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt index 8d51d055..0c9e6040 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt @@ -1,10 +1,12 @@ package suwayomi.tachidesk.global.impl -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.global.model.table.GlobalMetaTable /* @@ -32,13 +34,14 @@ object GlobalMeta { val (existingMeta, newMeta) = meta.toList().partition { (key) -> key in dbMetaMap.keys } if (existingMeta.isNotEmpty()) { - BatchUpdateStatement(GlobalMetaTable).apply { - existingMeta.forEach { (key, value) -> - addBatch(EntityID(dbMetaMap[key]!![GlobalMetaTable.id].value, GlobalMetaTable)) - this[GlobalMetaTable.value] = value - } - execute(this@transaction) - } + BatchUpdateStatement(GlobalMetaTable) + .apply { + existingMeta.forEach { (key, value) -> + addBatch(EntityID(dbMetaMap[key]!![GlobalMetaTable.id].value, GlobalMetaTable)) + this[GlobalMetaTable.value] = value + } + }.toExecutable() + .execute(this@transaction) } if (newMeta.isNotEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt index 4872f5c1..e3f079da 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt @@ -7,12 +7,12 @@ package suwayomi.tachidesk.global.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable /** * Metadata storage for clients, server/global level. */ object GlobalMetaTable : IntIdTable() { - val key = varchar("key", 256) + val key = varchar("meta_key", 256) val value = varchar("value", 4096) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt deleted file mode 100644 index 6c3a0f74..00000000 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt +++ /dev/null @@ -1,27 +0,0 @@ -package suwayomi.tachidesk.graphql - -import com.expediagroup.graphql.server.extensions.toGraphQLError -import graphql.execution.DataFetcherResult -import io.github.oshai.kotlinlogging.KotlinLogging - -val logger = KotlinLogging.logger { } - -inline fun asDataFetcherResult(block: () -> T): DataFetcherResult { - val result = - runCatching { - block() - } - - if (result.isFailure) { - logger.error(result.exceptionOrNull()) { "asDataFetcherResult: failed due to" } - return DataFetcherResult - .newResult() - .error(result.exceptionOrNull()?.toGraphQLError()) - .build() - } - - return DataFetcherResult - .newResult() - .data(result.getOrNull()) - .build() -} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt index 742da59a..6fd089b3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt @@ -3,12 +3,8 @@ package suwayomi.tachidesk.graphql.cache import org.dataloader.CacheMap import java.util.concurrent.CompletableFuture -class CustomCacheMap : CacheMap { - private val cache: MutableMap> - - init { - cache = HashMap() - } +class CustomCacheMap : CacheMap { + private val cache: MutableMap> = HashMap() override fun containsKey(key: K): Boolean = cache.containsKey(key) @@ -18,12 +14,12 @@ class CustomCacheMap : CacheMap { override fun getAll(): Collection> = cache.values - override fun set( + override fun putIfAbsentAtomically( key: K, value: CompletableFuture, - ): CacheMap { + ): CompletableFuture { cache[key] = value - return this + return value } override fun delete(key: K): CacheMap { @@ -35,4 +31,6 @@ class CustomCacheMap : CacheMap { cache.clear() return this } + + override fun size(): Int = cache.size } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt index 12c83b62..1767706e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt @@ -11,10 +11,10 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.types.CategoryNodeList import suwayomi.tachidesk.graphql.types.CategoryNodeList.Companion.toNodeList import suwayomi.tachidesk.graphql.types.CategoryType diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt index f0fe0a89..63b6079f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt @@ -11,24 +11,28 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.count -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.count +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.greaterEq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.types.ChapterNodeList import suwayomi.tachidesk.graphql.types.ChapterNodeList.Companion.toNodeList import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.server.JavalinSetup.future -class ChapterDataLoader : KotlinDataLoader { +class ChapterDataLoader : KotlinDataLoader { override val dataLoaderName = "ChapterDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -48,7 +52,7 @@ class ChaptersForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "ChaptersForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -68,7 +72,7 @@ class DownloadedChapterCountForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "DownloadedChapterCountForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -90,7 +94,7 @@ class UnreadChapterCountForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "UnreadChapterCountForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -112,7 +116,7 @@ class BookmarkedChapterCountForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "BookmarkedChapterCountForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -157,11 +161,11 @@ class HasDuplicateChaptersForMangaDataLoader : KotlinDataLoader { } } -class LastReadChapterForMangaDataLoader : KotlinDataLoader { +class LastReadChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LastReadChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -177,11 +181,11 @@ class LastReadChapterForMangaDataLoader : KotlinDataLoader { } } -class LatestReadChapterForMangaDataLoader : KotlinDataLoader { +class LatestReadChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LatestReadChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -197,11 +201,11 @@ class LatestReadChapterForMangaDataLoader : KotlinDataLoader } } -class LatestFetchedChapterForMangaDataLoader : KotlinDataLoader { +class LatestFetchedChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LatestFetchedChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -217,11 +221,11 @@ class LatestFetchedChapterForMangaDataLoader : KotlinDataLoader { +class LatestUploadedChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LatestUploadedChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -237,11 +241,11 @@ class LatestUploadedChapterForMangaDataLoader : KotlinDataLoader { +class FirstUnreadChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "FirstUnreadChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -257,11 +261,11 @@ class FirstUnreadChapterForMangaDataLoader : KotlinDataLoader } } -class HighestNumberedChapterForMangaDataLoader : KotlinDataLoader { +class HighestNumberedChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "HighestNumberedChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt index 2c7be2fa..7701e4e5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt @@ -11,19 +11,19 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.types.ExtensionType import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.SourceTable import suwayomi.tachidesk.server.JavalinSetup.future -class ExtensionDataLoader : KotlinDataLoader { +class ExtensionDataLoader : KotlinDataLoader { override val dataLoaderName = "ExtensionDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { @@ -40,10 +40,10 @@ class ExtensionDataLoader : KotlinDataLoader { } } -class ExtensionForSourceDataLoader : KotlinDataLoader { +class ExtensionForSourceDataLoader : KotlinDataLoader { override val dataLoaderName = "ExtensionForSourceDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt index 0775d71a..08243657 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt @@ -12,11 +12,13 @@ import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory import org.dataloader.DataLoaderOptions -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.isNull +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.cache.CustomCacheMap import suwayomi.tachidesk.graphql.types.MangaNodeList import suwayomi.tachidesk.graphql.types.MangaNodeList.Companion.toNodeList @@ -25,10 +27,10 @@ import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.server.JavalinSetup.future -class MangaDataLoader : KotlinDataLoader { +class MangaDataLoader : KotlinDataLoader { override val dataLoaderName = "MangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { @@ -122,6 +124,6 @@ class MangaForIdsDataLoader : KotlinDataLoader, MangaNodeList> { } } }, - DataLoaderOptions.newOptions().setCacheMap(CustomCacheMap, MangaNodeList>()), + DataLoaderOptions.newOptions().setCacheMap(CustomCacheMap, MangaNodeList>()).build(), ) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt index a01f2392..009de4be 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt @@ -4,10 +4,10 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.graphql.types.CategoryMetaType import suwayomi.tachidesk.graphql.types.ChapterMetaType @@ -20,11 +20,11 @@ import suwayomi.tachidesk.manga.model.table.MangaMetaTable import suwayomi.tachidesk.manga.model.table.SourceMetaTable import suwayomi.tachidesk.server.JavalinSetup.future -class GlobalMetaDataLoader : KotlinDataLoader { +class GlobalMetaDataLoader : KotlinDataLoader { override val dataLoaderName = "GlobalMetaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt index be3c60e0..6b9dd05a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt @@ -11,10 +11,10 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.types.SourceNodeList import suwayomi.tachidesk.graphql.types.SourceNodeList.Companion.toNodeList import suwayomi.tachidesk.graphql.types.SourceType @@ -22,10 +22,10 @@ import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.SourceTable import suwayomi.tachidesk.server.JavalinSetup.future -class SourceDataLoader : KotlinDataLoader { +class SourceDataLoader : KotlinDataLoader { override val dataLoaderName = "SourceDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt index cfe9ebab..02f7653c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt @@ -11,10 +11,10 @@ import com.expediagroup.graphql.dataloader.KotlinDataLoader import graphql.GraphQLContext import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory -import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.addLogger -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Slf4jSqlDebugLogger +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.types.TrackRecordNodeList import suwayomi.tachidesk.graphql.types.TrackRecordNodeList.Companion.toNodeList import suwayomi.tachidesk.graphql.types.TrackRecordType diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt index 3f8b6af2..19ba10cc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt index 132a399e..18350fd8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt @@ -1,21 +1,23 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult -import org.jetbrains.exposed.sql.LikePattern -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.SqlExpressionBuilder.like -import org.jetbrains.exposed.sql.SqlExpressionBuilder.minus -import org.jetbrains.exposed.sql.SqlExpressionBuilder.plus -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insertAndGetId -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.LikePattern +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greaterEq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.lessEq +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.minus +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.core.plus +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.CategoryMetaType import suwayomi.tachidesk.graphql.types.CategoryType @@ -42,14 +44,13 @@ class CategoryMutation { ) @RequireAuth - fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, meta) = input + fun setCategoryMeta(input: SetCategoryMetaInput): SetCategoryMetaPayload? { + val (clientMutationId, meta) = input - Category.modifyMeta(meta.categoryId, meta.key, meta.value) + Category.modifyMeta(meta.categoryId, meta.key, meta.value) - SetCategoryMetaPayload(clientMutationId, meta) - } + return SetCategoryMetaPayload(clientMutationId, meta) + } data class DeleteCategoryMetaInput( val clientMutationId: String? = null, @@ -64,34 +65,33 @@ class CategoryMutation { ) @RequireAuth - fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, categoryId, key) = input + fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DeleteCategoryMetaPayload? { + val (clientMutationId, categoryId, key) = input - val (meta, category) = - transaction { - val meta = - CategoryMetaTable - .selectAll() - .where { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } - .firstOrNull() + val (meta, category) = + transaction { + val meta = + CategoryMetaTable + .selectAll() + .where { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } + .firstOrNull() - CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } + CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } - val category = - transaction { - CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq categoryId }.first()) - } + val category = + transaction { + CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq categoryId }.first()) + } - if (meta != null) { - CategoryMetaType(meta) - } else { - null - } to category - } + if (meta != null) { + CategoryMetaType(meta) + } else { + null + } to category + } - DeleteCategoryMetaPayload(clientMutationId, meta, category) - } + return DeleteCategoryMetaPayload(clientMutationId, meta, category) + } data class SetCategoryMetasItem( val categoryIds: List, @@ -110,43 +110,42 @@ class CategoryMutation { ) @RequireAuth - fun setCategoryMetas(input: SetCategoryMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun setCategoryMetas(input: SetCategoryMetasInput): SetCategoryMetasPayload? { + val (clientMutationId, items) = input - val metaByCategoryId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.categoryIds.map { categoryId -> categoryId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaByCategoryId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.categoryIds.map { categoryId -> categoryId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Category.modifyCategoriesMetas(metaByCategoryId) + Category.modifyCategoriesMetas(metaByCategoryId) - val allCategoryIds = metaByCategoryId.keys - val allMetaKeys = metaByCategoryId.values.flatMap { item -> item.keys }.distinct() + val allCategoryIds = metaByCategoryId.keys + val allMetaKeys = metaByCategoryId.values.flatMap { item -> item.keys }.distinct() - val (updatedMetas, categories) = - transaction { - val updatedMetas = - CategoryMetaTable - .selectAll() - .where { (CategoryMetaTable.ref inList allCategoryIds) and (CategoryMetaTable.key inList allMetaKeys) } - .map { CategoryMetaType(it) } + val (updatedMetas, categories) = + transaction { + val updatedMetas = + CategoryMetaTable + .selectAll() + .where { (CategoryMetaTable.ref inList allCategoryIds) and (CategoryMetaTable.key inList allMetaKeys) } + .map { CategoryMetaType(it) } - val categories = - CategoryTable - .selectAll() - .where { CategoryTable.id inList allCategoryIds } - .map { CategoryType(it) } - .distinctBy { it.id } + val categories = + CategoryTable + .selectAll() + .where { CategoryTable.id inList allCategoryIds } + .map { CategoryType(it) } + .distinctBy { it.id } - updatedMetas to categories - } + updatedMetas to categories + } - SetCategoryMetasPayload(clientMutationId, updatedMetas, categories) - } + return SetCategoryMetasPayload(clientMutationId, updatedMetas, categories) + } data class DeleteCategoryMetasItem( val categoryIds: List, @@ -166,64 +165,63 @@ class CategoryMutation { ) @RequireAuth - fun deleteCategoryMetas(input: DeleteCategoryMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun deleteCategoryMetas(input: DeleteCategoryMetasInput): DeleteCategoryMetasPayload? { + val (clientMutationId, items) = input - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allCategoryIds) = + transaction { + val deletedMetas = mutableListOf() + val categoryIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { CategoryMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (CategoryMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (CategoryMetaTable.ref inList item.categoryIds) and metaKeyCondition + + deletedMetas += + CategoryMetaTable + .selectAll() + .where { condition } + .map { CategoryMetaType(it) } + + CategoryMetaTable.deleteWhere { condition } + categoryIds += item.categoryIds } + + deletedMetas to categoryIds } - val (allDeletedMetas, allCategoryIds) = - transaction { - val deletedMetas = mutableListOf() - val categoryIds = mutableSetOf() + val categories = + transaction { + CategoryTable + .selectAll() + .where { CategoryTable.id inList allCategoryIds } + .map { CategoryType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { CategoryMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (CategoryMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (CategoryMetaTable.ref inList item.categoryIds) and metaKeyCondition - - deletedMetas += - CategoryMetaTable - .selectAll() - .where { condition } - .map { CategoryMetaType(it) } - - CategoryMetaTable.deleteWhere { condition } - categoryIds += item.categoryIds - } - - deletedMetas to categoryIds - } - - val categories = - transaction { - CategoryTable - .selectAll() - .where { CategoryTable.id inList allCategoryIds } - .map { CategoryType(it) } - .distinctBy { it.id } - } - - DeleteCategoryMetasPayload(clientMutationId, allDeletedMetas, categories) - } + return DeleteCategoryMetasPayload(clientMutationId, allDeletedMetas, categories) + } data class UpdateCategoryPatch( val name: String? = null, @@ -291,40 +289,38 @@ class CategoryMutation { } @RequireAuth - fun updateCategory(input: UpdateCategoryInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, id, patch) = input + fun updateCategory(input: UpdateCategoryInput): UpdateCategoryPayload? { + val (clientMutationId, id, patch) = input - updateCategories(listOf(id), patch) + updateCategories(listOf(id), patch) - val category = - transaction { - CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) - } + val category = + transaction { + CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) + } - UpdateCategoryPayload( - clientMutationId = clientMutationId, - category = category, - ) - } + return UpdateCategoryPayload( + clientMutationId = clientMutationId, + category = category, + ) + } @RequireAuth - fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, ids, patch) = input + fun updateCategories(input: UpdateCategoriesInput): UpdateCategoriesPayload? { + val (clientMutationId, ids, patch) = input - updateCategories(ids, patch) + updateCategories(ids, patch) - val categories = - transaction { - CategoryTable.selectAll().where { CategoryTable.id inList ids }.map { CategoryType(it) } - } + val categories = + transaction { + CategoryTable.selectAll().where { CategoryTable.id inList ids }.map { CategoryType(it) } + } - UpdateCategoriesPayload( - clientMutationId = clientMutationId, - categories = categories, - ) - } + return UpdateCategoriesPayload( + clientMutationId = clientMutationId, + categories = categories, + ) + } data class UpdateCategoryOrderPayload( val clientMutationId: String?, @@ -338,50 +334,49 @@ class CategoryMutation { ) @RequireAuth - fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, categoryId, position) = input - require(position > 0) { - "'order' must not be <= 0" - } - - transaction { - val currentOrder = - CategoryTable - .selectAll() - .where { CategoryTable.id eq categoryId } - .first()[CategoryTable.order] - - if (currentOrder != position) { - if (position < currentOrder) { - CategoryTable.update({ CategoryTable.order greaterEq position }) { - it[CategoryTable.order] = CategoryTable.order + 1 - } - } else { - CategoryTable.update({ CategoryTable.order lessEq position }) { - it[CategoryTable.order] = CategoryTable.order - 1 - } - } - - CategoryTable.update({ CategoryTable.id eq categoryId }) { - it[CategoryTable.order] = position - } - } - } - - Category.normalizeCategories() - - val categories = - transaction { - CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) } - } - - UpdateCategoryOrderPayload( - clientMutationId = clientMutationId, - categories = categories, - ) + fun updateCategoryOrder(input: UpdateCategoryOrderInput): UpdateCategoryOrderPayload? { + val (clientMutationId, categoryId, position) = input + require(position > 0) { + "'order' must not be <= 0" } + transaction { + val currentOrder = + CategoryTable + .selectAll() + .where { CategoryTable.id eq categoryId } + .first()[CategoryTable.order] + + if (currentOrder != position) { + if (position < currentOrder) { + CategoryTable.update({ CategoryTable.order greaterEq position }) { + it[CategoryTable.order] = CategoryTable.order + 1 + } + } else { + CategoryTable.update({ CategoryTable.order lessEq position }) { + it[CategoryTable.order] = CategoryTable.order - 1 + } + } + + CategoryTable.update({ CategoryTable.id eq categoryId }) { + it[CategoryTable.order] = position + } + } + } + + Category.normalizeCategories() + + val categories = + transaction { + CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) } + } + + return UpdateCategoryOrderPayload( + clientMutationId = clientMutationId, + categories = categories, + ) + } + data class CreateCategoryInput( val clientMutationId: String? = null, val name: String, @@ -397,53 +392,52 @@ class CategoryMutation { ) @RequireAuth - fun createCategory(input: CreateCategoryInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input - transaction { - require(CategoryTable.selectAll().where { CategoryTable.name eq input.name }.isEmpty()) { - "'name' must be unique" - } + fun createCategory(input: CreateCategoryInput): CreateCategoryPayload? { + val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input + transaction { + require(CategoryTable.selectAll().where { CategoryTable.name eq input.name }.isEmpty()) { + "'name' must be unique" } - require(!name.equals(Category.DEFAULT_CATEGORY_NAME, ignoreCase = true)) { - "'name' must not be ${Category.DEFAULT_CATEGORY_NAME}" - } - if (order != null) { - require(order > 0) { - "'order' must not be <= 0" - } + } + require(!name.equals(Category.DEFAULT_CATEGORY_NAME, ignoreCase = true)) { + "'name' must not be ${Category.DEFAULT_CATEGORY_NAME}" + } + if (order != null) { + require(order > 0) { + "'order' must not be <= 0" } + } - val category = - transaction { - if (order != null) { - CategoryTable.update({ CategoryTable.order greaterEq order }) { - it[CategoryTable.order] = CategoryTable.order + 1 + val category = + transaction { + if (order != null) { + CategoryTable.update({ CategoryTable.order greaterEq order }) { + it[CategoryTable.order] = CategoryTable.order + 1 + } + } + + val id = + CategoryTable.insertAndGetId { + it[CategoryTable.name] = input.name + it[CategoryTable.order] = order ?: Int.MAX_VALUE + if (default != null) { + it[CategoryTable.isDefault] = default + } + if (includeInUpdate != null) { + it[CategoryTable.includeInUpdate] = includeInUpdate.value + } + if (includeInDownload != null) { + it[CategoryTable.includeInDownload] = includeInDownload.value } } - val id = - CategoryTable.insertAndGetId { - it[CategoryTable.name] = input.name - it[CategoryTable.order] = order ?: Int.MAX_VALUE - if (default != null) { - it[CategoryTable.isDefault] = default - } - if (includeInUpdate != null) { - it[CategoryTable.includeInUpdate] = includeInUpdate.value - } - if (includeInDownload != null) { - it[CategoryTable.includeInDownload] = includeInDownload.value - } - } + Category.normalizeCategories() - Category.normalizeCategories() + CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) + } - CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) - } - - CreateCategoryPayload(clientMutationId, category) - } + return CreateCategoryPayload(clientMutationId, category) + } data class DeleteCategoryInput( val clientMutationId: String? = null, @@ -457,47 +451,45 @@ class CategoryMutation { ) @RequireAuth - fun deleteCategory(input: DeleteCategoryInput): DataFetcherResult { - return asDataFetcherResult { - val (clientMutationId, categoryId) = input - if (categoryId == 0) { // Don't delete default category - return@asDataFetcherResult DeleteCategoryPayload( - clientMutationId, - null, - emptyList(), - ) + fun deleteCategory(input: DeleteCategoryInput): DeleteCategoryPayload? { + val (clientMutationId, categoryId) = input + if (categoryId == 0) { // Don't delete default category + return DeleteCategoryPayload( + clientMutationId, + null, + emptyList(), + ) + } + + val (category, mangas) = + transaction { + val category = + CategoryTable + .selectAll() + .where { CategoryTable.id eq categoryId } + .firstOrNull() + + val mangas = + transaction { + MangaTable + .innerJoin(CategoryMangaTable) + .selectAll() + .where { CategoryMangaTable.category eq categoryId } + .map { MangaType(it) } + } + + CategoryTable.deleteWhere { CategoryTable.id eq categoryId } + + Category.normalizeCategories() + + if (category != null) { + CategoryType(category) + } else { + null + } to mangas } - val (category, mangas) = - transaction { - val category = - CategoryTable - .selectAll() - .where { CategoryTable.id eq categoryId } - .firstOrNull() - - val mangas = - transaction { - MangaTable - .innerJoin(CategoryMangaTable) - .selectAll() - .where { CategoryMangaTable.category eq categoryId } - .map { MangaType(it) } - } - - CategoryTable.deleteWhere { CategoryTable.id eq categoryId } - - Category.normalizeCategories() - - if (category != null) { - CategoryType(category) - } else { - null - } to mangas - } - - DeleteCategoryPayload(clientMutationId, category, mangas) - } + return DeleteCategoryPayload(clientMutationId, category, mangas) } data class UpdateMangaCategoriesPatch( @@ -547,38 +539,36 @@ class CategoryMutation { } @RequireAuth - fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, id, patch) = input + fun updateMangaCategories(input: UpdateMangaCategoriesInput): UpdateMangaCategoriesPayload? { + val (clientMutationId, id, patch) = input - updateMangas(listOf(id), patch) + updateMangas(listOf(id), patch) - val manga = - transaction { - MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) - } + val manga = + transaction { + MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) + } - UpdateMangaCategoriesPayload( - clientMutationId = clientMutationId, - manga = manga, - ) - } + return UpdateMangaCategoriesPayload( + clientMutationId = clientMutationId, + manga = manga, + ) + } @RequireAuth - fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, ids, patch) = input + fun updateMangasCategories(input: UpdateMangasCategoriesInput): UpdateMangasCategoriesPayload? { + val (clientMutationId, ids, patch) = input - updateMangas(ids, patch) + updateMangas(ids, patch) - val mangas = - transaction { - MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } - } + val mangas = + transaction { + MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } + } - UpdateMangasCategoriesPayload( - clientMutationId = clientMutationId, - mangas = mangas, - ) - } + return UpdateMangasCategoriesPayload( + clientMutationId = clientMutationId, + mangas = mangas, + ) + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt index 9d33fcf3..0c91186e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt @@ -1,22 +1,24 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.LikePattern -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.SqlExpressionBuilder.like -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.LikePattern +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterMetaType import suwayomi.tachidesk.graphql.types.ChapterType @@ -90,22 +92,23 @@ class ChapterMutation { if (patch.isRead != null || patch.isBookmarked != null || patch.lastPageRead != null) { val now = Instant.now().epochSecond - BatchUpdateStatement(ChapterTable).apply { - ids.forEach { chapterId -> - addBatch(EntityID(chapterId, ChapterTable)) - patch.isRead?.also { - this[ChapterTable.isRead] = it + BatchUpdateStatement(ChapterTable) + .apply { + ids.forEach { chapterId -> + addBatch(EntityID(chapterId, ChapterTable)) + patch.isRead?.also { + this[ChapterTable.isRead] = it + } + patch.isBookmarked?.also { + this[ChapterTable.isBookmarked] = it + } + patch.lastPageRead?.also { + this[ChapterTable.lastPageRead] = it.coerceAtMost(chapterIdToPageCount[chapterId] ?: 0).coerceAtLeast(0) + this[ChapterTable.lastReadAt] = now + } } - patch.isBookmarked?.also { - this[ChapterTable.isBookmarked] = it - } - patch.lastPageRead?.also { - this[ChapterTable.lastPageRead] = it.coerceAtMost(chapterIdToPageCount[chapterId] ?: 0).coerceAtLeast(0) - this[ChapterTable.lastReadAt] = now - } - } - execute(this@transaction) - } + }.toExecutable() + .execute(this@transaction) } } @@ -120,40 +123,38 @@ class ChapterMutation { } @RequireAuth - fun updateChapter(input: UpdateChapterInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, id, patch) = input + fun updateChapter(input: UpdateChapterInput): UpdateChapterPayload? { + val (clientMutationId, id, patch) = input - updateChapters(listOf(id), patch) + updateChapters(listOf(id), patch) - val chapter = - transaction { - ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq id }.first()) - } + val chapter = + transaction { + ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq id }.first()) + } - UpdateChapterPayload( - clientMutationId = clientMutationId, - chapter = chapter, - ) - } + return UpdateChapterPayload( + clientMutationId = clientMutationId, + chapter = chapter, + ) + } @RequireAuth - fun updateChapters(input: UpdateChaptersInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, ids, patch) = input + fun updateChapters(input: UpdateChaptersInput): UpdateChaptersPayload? { + val (clientMutationId, ids, patch) = input - updateChapters(ids, patch) + updateChapters(ids, patch) - val chapters = - transaction { - ChapterTable.selectAll().where { ChapterTable.id inList ids }.map { ChapterType(it) } - } + val chapters = + transaction { + ChapterTable.selectAll().where { ChapterTable.id inList ids }.map { ChapterType(it) } + } - UpdateChaptersPayload( - clientMutationId = clientMutationId, - chapters = chapters, - ) - } + return UpdateChaptersPayload( + clientMutationId = clientMutationId, + chapters = chapters, + ) + } data class FetchChaptersInput( val clientMutationId: String? = null, @@ -166,27 +167,25 @@ class ChapterMutation { ) @RequireAuth - fun fetchChapters(input: FetchChaptersInput): CompletableFuture> { + fun fetchChapters(input: FetchChaptersInput): CompletableFuture { val (clientMutationId, mangaId) = input return future { - asDataFetcherResult { - Chapter.fetchChapterList(mangaId) + Chapter.fetchChapterList(mangaId) - val chapters = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.manga eq mangaId } - .orderBy(ChapterTable.sourceOrder) - .map { ChapterType(it) } - } + val chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.manga eq mangaId } + .orderBy(ChapterTable.sourceOrder) + .map { ChapterType(it) } + } - FetchChaptersPayload( - clientMutationId = clientMutationId, - chapters = chapters, - ) - } + FetchChaptersPayload( + clientMutationId = clientMutationId, + chapters = chapters, + ) } } @@ -201,14 +200,13 @@ class ChapterMutation { ) @RequireAuth - fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, meta) = input + fun setChapterMeta(input: SetChapterMetaInput): SetChapterMetaPayload? { + val (clientMutationId, meta) = input - Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) + Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) - SetChapterMetaPayload(clientMutationId, meta) - } + return SetChapterMetaPayload(clientMutationId, meta) + } data class DeleteChapterMetaInput( val clientMutationId: String? = null, @@ -223,34 +221,33 @@ class ChapterMutation { ) @RequireAuth - fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, chapterId, key) = input + fun deleteChapterMeta(input: DeleteChapterMetaInput): DeleteChapterMetaPayload? { + val (clientMutationId, chapterId, key) = input - val (meta, chapter) = - transaction { - val meta = - ChapterMetaTable - .selectAll() - .where { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } - .firstOrNull() + val (meta, chapter) = + transaction { + val meta = + ChapterMetaTable + .selectAll() + .where { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } + .firstOrNull() - ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } + ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } - val chapter = - transaction { - ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapterId }.first()) - } + val chapter = + transaction { + ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapterId }.first()) + } - if (meta != null) { - ChapterMetaType(meta) - } else { - null - } to chapter - } + if (meta != null) { + ChapterMetaType(meta) + } else { + null + } to chapter + } - DeleteChapterMetaPayload(clientMutationId, meta, chapter) - } + return DeleteChapterMetaPayload(clientMutationId, meta, chapter) + } data class SetChapterMetasItem( val chapterIds: List, @@ -269,43 +266,42 @@ class ChapterMutation { ) @RequireAuth - fun setChapterMetas(input: SetChapterMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun setChapterMetas(input: SetChapterMetasInput): SetChapterMetasPayload? { + val (clientMutationId, items) = input - val metaByChapterId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.chapterIds.map { chapterId -> chapterId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaByChapterId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.chapterIds.map { chapterId -> chapterId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Chapter.modifyChaptersMetas(metaByChapterId) + Chapter.modifyChaptersMetas(metaByChapterId) - val allChapterIds = metaByChapterId.keys - val allMetaKeys = metaByChapterId.values.flatMap { it.keys }.distinct() + val allChapterIds = metaByChapterId.keys + val allMetaKeys = metaByChapterId.values.flatMap { it.keys }.distinct() - val (updatedMetas, chapters) = - transaction { - val updatedMetas = - ChapterMetaTable - .selectAll() - .where { (ChapterMetaTable.ref inList allChapterIds) and (ChapterMetaTable.key inList allMetaKeys) } - .map { ChapterMetaType(it) } + val (updatedMetas, chapters) = + transaction { + val updatedMetas = + ChapterMetaTable + .selectAll() + .where { (ChapterMetaTable.ref inList allChapterIds) and (ChapterMetaTable.key inList allMetaKeys) } + .map { ChapterMetaType(it) } - val chapters = - ChapterTable - .selectAll() - .where { ChapterTable.id inList allChapterIds } - .map { ChapterType(it) } - .distinctBy { it.id } + val chapters = + ChapterTable + .selectAll() + .where { ChapterTable.id inList allChapterIds } + .map { ChapterType(it) } + .distinctBy { it.id } - updatedMetas to chapters - } + updatedMetas to chapters + } - SetChapterMetasPayload(clientMutationId, updatedMetas, chapters) - } + return SetChapterMetasPayload(clientMutationId, updatedMetas, chapters) + } data class DeleteChapterMetasItem( val chapterIds: List, @@ -325,64 +321,63 @@ class ChapterMutation { ) @RequireAuth - fun deleteChapterMetas(input: DeleteChapterMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun deleteChapterMetas(input: DeleteChapterMetasInput): DeleteChapterMetasPayload? { + val (clientMutationId, items) = input - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allChapterIds) = + transaction { + val deletedMetas = mutableListOf() + val chapterIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { ChapterMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (ChapterMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (ChapterMetaTable.ref inList item.chapterIds) and metaKeyCondition + + deletedMetas += + ChapterMetaTable + .selectAll() + .where { condition } + .map { ChapterMetaType(it) } + + ChapterMetaTable.deleteWhere { condition } + chapterIds += item.chapterIds } + + deletedMetas to chapterIds } - val (allDeletedMetas, allChapterIds) = - transaction { - val deletedMetas = mutableListOf() - val chapterIds = mutableSetOf() + val chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id inList allChapterIds } + .map { ChapterType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { ChapterMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (ChapterMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (ChapterMetaTable.ref inList item.chapterIds) and metaKeyCondition - - deletedMetas += - ChapterMetaTable - .selectAll() - .where { condition } - .map { ChapterMetaType(it) } - - ChapterMetaTable.deleteWhere { condition } - chapterIds += item.chapterIds - } - - deletedMetas to chapterIds - } - - val chapters = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id inList allChapterIds } - .map { ChapterType(it) } - .distinctBy { it.id } - } - - DeleteChapterMetasPayload(clientMutationId, allDeletedMetas, chapters) - } + return DeleteChapterMetasPayload(clientMutationId, allDeletedMetas, chapters) + } data class FetchChapterPagesInput( val clientMutationId: String? = null, @@ -405,67 +400,65 @@ class ChapterMutation { ) @RequireAuth - fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture> { + fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture { val (clientMutationId, chapterId) = input val paramsMap = input.toParams() return future { - asDataFetcherResult { - var chapter = getChapterDownloadReadyById(chapterId) - val syncResult = KoreaderSyncService.checkAndPullProgress(chapter.id) - var syncConflictInfo: SyncConflictInfoType? = null + var chapter = getChapterDownloadReadyById(chapterId) + val syncResult = KoreaderSyncService.checkAndPullProgress(chapter.id) + var syncConflictInfo: SyncConflictInfoType? = null - if (syncResult != null) { - if (syncResult.isConflict) { - syncConflictInfo = - SyncConflictInfoType( - deviceName = syncResult.device, - remotePage = syncResult.pageRead, - ) - } - - if (syncResult.shouldUpdate) { - // Update DB for SILENT and RECEIVE - transaction { - ChapterTable.update({ ChapterTable.id eq chapter.id }) { - it[lastPageRead] = syncResult.pageRead - it[lastReadAt] = syncResult.timestamp - } - } - } - // For PROMPT, SILENT, and RECEIVE, return the remote progress - chapter = - chapter.copy( - lastPageRead = if (syncResult.shouldUpdate) syncResult.pageRead else chapter.lastPageRead, - lastReadAt = if (syncResult.shouldUpdate) syncResult.timestamp else chapter.lastReadAt, + if (syncResult != null) { + if (syncResult.isConflict) { + syncConflictInfo = + SyncConflictInfoType( + deviceName = syncResult.device, + remotePage = syncResult.pageRead, ) } - val params = - buildString { - if (paramsMap.isNotEmpty()) { - append("?") - paramsMap.entries.forEach { entry -> - if (length > 1) { - append("&") - } - append(entry.key) - append("=") - append(URLEncoder.encode(entry.value, Charsets.UTF_8)) - } + if (syncResult.shouldUpdate) { + // Update DB for SILENT and RECEIVE + transaction { + ChapterTable.update({ ChapterTable.id eq chapter.id }) { + it[lastPageRead] = syncResult.pageRead + it[lastReadAt] = syncResult.timestamp } } - - FetchChapterPagesPayload( - clientMutationId = clientMutationId, - pages = - List(chapter.pageCount) { index -> - "/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/${index}$params" - }, - chapter = ChapterType(chapter), - syncConflict = syncConflictInfo, - ) + } + // For PROMPT, SILENT, and RECEIVE, return the remote progress + chapter = + chapter.copy( + lastPageRead = if (syncResult.shouldUpdate) syncResult.pageRead else chapter.lastPageRead, + lastReadAt = if (syncResult.shouldUpdate) syncResult.timestamp else chapter.lastReadAt, + ) } + + val params = + buildString { + if (paramsMap.isNotEmpty()) { + append("?") + paramsMap.entries.forEach { entry -> + if (length > 1) { + append("&") + } + append(entry.key) + append("=") + append(URLEncoder.encode(entry.value, Charsets.UTF_8)) + } + } + } + + FetchChapterPagesPayload( + clientMutationId = clientMutationId, + pages = + List(chapter.pageCount) { index -> + "/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/${index}$params" + }, + chapter = ChapterType(chapter), + syncConflict = syncConflictInfo, + ) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt index 57e5dfdf..b5bd24d8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt @@ -1,11 +1,13 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.graphql.types.DownloadStatus @@ -30,23 +32,21 @@ class DownloadMutation { ) @RequireAuth - fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DataFetcherResult { + fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload? { val (clientMutationId, chapters) = input - return asDataFetcherResult { - Chapter.deleteChapters(chapters) + Chapter.deleteChapters(chapters) - DeleteDownloadedChaptersPayload( - clientMutationId = clientMutationId, - chapters = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id inList chapters } - .map { ChapterType(it) } - }, - ) - } + return DeleteDownloadedChaptersPayload( + clientMutationId = clientMutationId, + chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id inList chapters } + .map { ChapterType(it) } + }, + ) } data class DeleteDownloadedChapterInput( @@ -60,20 +60,18 @@ class DownloadMutation { ) @RequireAuth - fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DataFetcherResult { + fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload? { val (clientMutationId, chapter) = input - return asDataFetcherResult { - Chapter.deleteChapters(listOf(chapter)) + Chapter.deleteChapters(listOf(chapter)) - DeleteDownloadedChapterPayload( - clientMutationId = clientMutationId, - chapters = - transaction { - ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapter }.first()) - }, - ) - } + return DeleteDownloadedChapterPayload( + clientMutationId = clientMutationId, + chapters = + transaction { + ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapter }.first()) + }, + ) } data class EnqueueChapterDownloadsInput( @@ -87,28 +85,24 @@ class DownloadMutation { ) @RequireAuth - fun enqueueChapterDownloads( - input: EnqueueChapterDownloadsInput, - ): CompletableFuture> { + fun enqueueChapterDownloads(input: EnqueueChapterDownloadsInput): CompletableFuture { val (clientMutationId, chapters) = input return future { - asDataFetcherResult { - DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters)) + DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters)) - EnqueueChapterDownloadsPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { - DownloadManager.getStatus().queue.any { it.chapterId in chapters } - }.let { DownloadManager.getStatus() }, - ) - }, - ) - } + EnqueueChapterDownloadsPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { + DownloadManager.getStatus().queue.any { it.chapterId in chapters } + }.let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -123,25 +117,23 @@ class DownloadMutation { ) @RequireAuth - fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture> { + fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture { val (clientMutationId, chapter) = input return future { - asDataFetcherResult { - DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter))) + DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter))) - EnqueueChapterDownloadPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.updates.any { it.downloadQueueItem.chapterId == chapter } } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + EnqueueChapterDownloadPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.updates.any { it.downloadQueueItem.chapterId == chapter } } + .let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -156,30 +148,26 @@ class DownloadMutation { ) @RequireAuth - fun dequeueChapterDownloads( - input: DequeueChapterDownloadsInput, - ): CompletableFuture> { + fun dequeueChapterDownloads(input: DequeueChapterDownloadsInput): CompletableFuture { val (clientMutationId, chapters) = input return future { - asDataFetcherResult { - DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters)) + DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters)) - DequeueChapterDownloadsPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { - it.updates.any { - it.downloadQueueItem.chapterId in chapters && it.type == DEQUEUED - } - }.let { DownloadManager.getStatus() }, - ) - }, - ) - } + DequeueChapterDownloadsPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { + it.updates.any { + it.downloadQueueItem.chapterId in chapters && it.type == DEQUEUED + } + }.let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -194,28 +182,26 @@ class DownloadMutation { ) @RequireAuth - fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture> { + fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture { val (clientMutationId, chapter) = input return future { - asDataFetcherResult { - DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter))) + DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter))) - DequeueChapterDownloadPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { - it.updates.any { - it.downloadQueueItem.chapterId == chapter && it.type == DEQUEUED - } - }.let { DownloadManager.getStatus() }, - ) - }, - ) - } + DequeueChapterDownloadPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { + it.updates.any { + it.downloadQueueItem.chapterId == chapter && it.type == DEQUEUED + } + }.let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -229,23 +215,21 @@ class DownloadMutation { ) @RequireAuth - fun startDownloader(input: StartDownloaderInput): CompletableFuture> = + fun startDownloader(input: StartDownloaderInput): CompletableFuture = future { - asDataFetcherResult { - DownloadManager.start() + DownloadManager.start() - StartDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.status == Status.Started } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + StartDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.status == Status.Started } + .let { DownloadManager.getStatus() }, + ) + }, + ) } data class StopDownloaderInput( @@ -258,23 +242,21 @@ class DownloadMutation { ) @RequireAuth - fun stopDownloader(input: StopDownloaderInput): CompletableFuture> = + fun stopDownloader(input: StopDownloaderInput): CompletableFuture = future { - asDataFetcherResult { - DownloadManager.stop() + DownloadManager.stop() - StopDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.status == Status.Stopped } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + StopDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.status == Status.Stopped } + .let { DownloadManager.getStatus() }, + ) + }, + ) } data class ClearDownloaderInput( @@ -287,23 +269,21 @@ class DownloadMutation { ) @RequireAuth - fun clearDownloader(input: ClearDownloaderInput): CompletableFuture> = + fun clearDownloader(input: ClearDownloaderInput): CompletableFuture = future { - asDataFetcherResult { - DownloadManager.clear() + DownloadManager.clear() - ClearDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.status == Status.Stopped } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + ClearDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.status == Status.Stopped } + .let { DownloadManager.getStatus() }, + ) + }, + ) } data class ReorderChapterDownloadInput( @@ -318,25 +298,23 @@ class DownloadMutation { ) @RequireAuth - fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture> { + fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture { val (clientMutationId, chapter, to) = input return future { - asDataFetcherResult { - DownloadManager.reorder(chapter, to) + DownloadManager.reorder(chapter, to) - ReorderChapterDownloadPayload( - clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.updates.indexOfFirst { it.downloadQueueItem.chapterId == chapter } <= to } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + ReorderChapterDownloadPayload( + clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.updates.indexOfFirst { it.downloadQueueItem.chapterId == chapter } <= to } + .let { DownloadManager.getStatus() }, + ) + }, + ) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt index b484890c..663f32c5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt @@ -1,11 +1,14 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import eu.kanade.tachiyomi.source.local.LocalSource -import graphql.execution.DataFetcherResult import io.javalin.http.UploadedFile -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.neq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ExtensionType import suwayomi.tachidesk.manga.impl.extension.Extension @@ -75,51 +78,47 @@ class ExtensionMutation { } @RequireAuth - fun updateExtension(input: UpdateExtensionInput): CompletableFuture> { + fun updateExtension(input: UpdateExtensionInput): CompletableFuture { val (clientMutationId, id, patch) = input return future { - asDataFetcherResult { - updateExtensions(listOf(id), patch) + updateExtensions(listOf(id), patch) - val extension = - transaction { - ExtensionTable - .selectAll() - .where { ExtensionTable.pkgName eq id } - .firstOrNull() - ?.let { ExtensionType(it) } - } + val extension = + transaction { + ExtensionTable + .selectAll() + .where { ExtensionTable.pkgName eq id } + .firstOrNull() + ?.let { ExtensionType(it) } + } - UpdateExtensionPayload( - clientMutationId = clientMutationId, - extension = extension, - ) - } + UpdateExtensionPayload( + clientMutationId = clientMutationId, + extension = extension, + ) } } @RequireAuth - fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture> { + fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture { val (clientMutationId, ids, patch) = input return future { - asDataFetcherResult { - updateExtensions(ids, patch) + updateExtensions(ids, patch) - val extensions = - transaction { - ExtensionTable - .selectAll() - .where { ExtensionTable.pkgName inList ids } - .map { ExtensionType(it) } - } + val extensions = + transaction { + ExtensionTable + .selectAll() + .where { ExtensionTable.pkgName inList ids } + .map { ExtensionType(it) } + } - UpdateExtensionsPayload( - clientMutationId = clientMutationId, - extensions = extensions, - ) - } + UpdateExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions, + ) } } @@ -133,26 +132,24 @@ class ExtensionMutation { ) @RequireAuth - fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture> { + fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture { val (clientMutationId) = input return future { - asDataFetcherResult { - ExtensionsList.fetchExtensions() + ExtensionsList.fetchExtensions() - val extensions = - transaction { - ExtensionTable - .selectAll() - .where { ExtensionTable.name neq LocalSource.EXTENSION_NAME } - .map { ExtensionType(it) } - } + val extensions = + transaction { + ExtensionTable + .selectAll() + .where { ExtensionTable.name neq LocalSource.EXTENSION_NAME } + .map { ExtensionType(it) } + } - FetchExtensionsPayload( - clientMutationId = clientMutationId, - extensions = extensions, - ) - } + FetchExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions, + ) } } @@ -167,23 +164,19 @@ class ExtensionMutation { ) @RequireAuth - fun installExternalExtension( - input: InstallExternalExtensionInput, - ): CompletableFuture> { + fun installExternalExtension(input: InstallExternalExtensionInput): CompletableFuture { val (clientMutationId, extensionFile) = input return future { - asDataFetcherResult { - Extension.installExternalExtension(extensionFile.content(), extensionFile.filename()) + Extension.installExternalExtension(extensionFile.content(), extensionFile.filename()) - val dbExtension = - transaction { ExtensionTable.selectAll().where { ExtensionTable.apkName eq extensionFile.filename() }.first() } + val dbExtension = + transaction { ExtensionTable.selectAll().where { ExtensionTable.apkName eq extensionFile.filename() }.first() } - InstallExternalExtensionPayload( - clientMutationId, - extension = ExtensionType(dbExtension), - ) - } + InstallExternalExtensionPayload( + clientMutationId, + extension = ExtensionType(dbExtension), + ) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt index f899fe86..bff20e71 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import suwayomi.tachidesk.graphql.directives.RequireAuth diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt index 519f6aa7..2d2f2553 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt @@ -1,9 +1,9 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING import suwayomi.tachidesk.graphql.types.UpdateState.ERROR @@ -26,55 +26,51 @@ class InfoMutation { ) @RequireAuth - fun updateWebUI(input: WebUIUpdateInput): CompletableFuture> { + fun updateWebUI(input: WebUIUpdateInput): CompletableFuture { return future { - asDataFetcherResult { - withTimeout(30.seconds) { - if (WebInterfaceManager.status.value.state === DOWNLOADING) { - return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value) - } + withTimeout(30.seconds) { + if (WebInterfaceManager.status.value.state === DOWNLOADING) { + return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value) + } - val flavor = WebUIFlavor.current + val flavor = WebUIFlavor.current - val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor) + val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor) - if (!updateAvailable) { - val didUpdateCheckFail = version.isEmpty() + if (!updateAvailable) { + val didUpdateCheckFail = version.isEmpty() - return@withTimeout WebUIUpdatePayload( - input.clientMutationId, - WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE), - ) - } - try { - WebInterfaceManager.startDownloadInScope(flavor, version) - } catch (e: Exception) { - // ignore since we use the status anyway - } - - WebUIUpdatePayload( + return@withTimeout WebUIUpdatePayload( input.clientMutationId, - updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING }, + WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE), ) } + try { + WebInterfaceManager.startDownloadInScope(flavor, version) + } catch (e: Exception) { + // ignore since we use the status anyway + } + + WebUIUpdatePayload( + input.clientMutationId, + updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING }, + ) } } } @RequireAuth - fun resetWebUIUpdateStatus(): CompletableFuture> = + fun resetWebUIUpdateStatus(): CompletableFuture = future { - asDataFetcherResult { - withTimeout(30.seconds) { - val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING - if (!isUpdateFinished) { - throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"") - } - - WebInterfaceManager.resetStatus() - - WebInterfaceManager.status.first { it.state == IDLE } + withTimeout(30.seconds) { + val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING + if (!isUpdateFinished) { + throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"") } + + WebInterfaceManager.resetStatus() + + WebInterfaceManager.status.first { it.state == IDLE } } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt index e1ae1e4c..ff605fa1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt @@ -1,10 +1,11 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.graphql.types.KoSyncConnectPayload @@ -62,26 +63,24 @@ class KoreaderSyncMutation { ) @RequireAuth - fun pushKoSyncProgress(input: PushKoSyncProgressInput): CompletableFuture> = + fun pushKoSyncProgress(input: PushKoSyncProgressInput): CompletableFuture = future { - asDataFetcherResult { - KoreaderSyncService.pushProgress(input.chapterId) + KoreaderSyncService.pushProgress(input.chapterId) - val chapter = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id eq input.chapterId } - .firstOrNull() - ?.let { ChapterType(it) } - } + val chapter = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id eq input.chapterId } + .firstOrNull() + ?.let { ChapterType(it) } + } - PushKoSyncProgressPayload( - clientMutationId = input.clientMutationId, - success = true, - chapter = chapter, - ) - } + PushKoSyncProgressPayload( + clientMutationId = input.clientMutationId, + success = true, + chapter = chapter, + ) } data class PullKoSyncProgressInput( @@ -96,45 +95,43 @@ class KoreaderSyncMutation { ) @RequireAuth - fun pullKoSyncProgress(input: PullKoSyncProgressInput): CompletableFuture> = + fun pullKoSyncProgress(input: PullKoSyncProgressInput): CompletableFuture = future { - asDataFetcherResult { - val syncResult = KoreaderSyncService.checkAndPullProgress(input.chapterId) - var syncConflictInfo: SyncConflictInfoType? = null + val syncResult = KoreaderSyncService.checkAndPullProgress(input.chapterId) + var syncConflictInfo: SyncConflictInfoType? = null - if (syncResult != null) { - if (syncResult.isConflict) { - syncConflictInfo = - SyncConflictInfoType( - deviceName = syncResult.device, - remotePage = syncResult.pageRead, - ) - } + if (syncResult != null) { + if (syncResult.isConflict) { + syncConflictInfo = + SyncConflictInfoType( + deviceName = syncResult.device, + remotePage = syncResult.pageRead, + ) + } - if (syncResult.shouldUpdate) { - transaction { - ChapterTable.update({ ChapterTable.id eq input.chapterId }) { - it[lastPageRead] = syncResult.pageRead - it[lastReadAt] = syncResult.timestamp - } + if (syncResult.shouldUpdate) { + transaction { + ChapterTable.update({ ChapterTable.id eq input.chapterId }) { + it[lastPageRead] = syncResult.pageRead + it[lastReadAt] = syncResult.timestamp } } } - - val chapter = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id eq input.chapterId } - .firstOrNull() - ?.let { ChapterType(it) } - } - - PullKoSyncProgressPayload( - clientMutationId = input.clientMutationId, - chapter = chapter, - syncConflict = syncConflictInfo, - ) } + + val chapter = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id eq input.chapterId } + .firstOrNull() + ?.let { ChapterType(it) } + } + + PullKoSyncProgressPayload( + clientMutationId = input.clientMutationId, + chapter = chapter, + syncConflict = syncConflictInfo, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt index 175c790f..975f5567 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt @@ -1,18 +1,18 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult -import org.jetbrains.exposed.sql.LikePattern -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.SqlExpressionBuilder.like -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.LikePattern +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.MangaMetaType import suwayomi.tachidesk.graphql.types.MangaType @@ -98,44 +98,40 @@ class MangaMutation { } @RequireAuth - fun updateManga(input: UpdateMangaInput): CompletableFuture> { + fun updateManga(input: UpdateMangaInput): CompletableFuture { val (clientMutationId, id, patch) = input return future { - asDataFetcherResult { - updateMangas(listOf(id), patch) + updateMangas(listOf(id), patch) - val manga = - transaction { - MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) - } + val manga = + transaction { + MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) + } - UpdateMangaPayload( - clientMutationId = clientMutationId, - manga = manga, - ) - } + UpdateMangaPayload( + clientMutationId = clientMutationId, + manga = manga, + ) } } @RequireAuth - fun updateMangas(input: UpdateMangasInput): CompletableFuture> { + fun updateMangas(input: UpdateMangasInput): CompletableFuture { val (clientMutationId, ids, patch) = input return future { - asDataFetcherResult { - updateMangas(ids, patch) + updateMangas(ids, patch) - val mangas = - transaction { - MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } - } + val mangas = + transaction { + MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } + } - UpdateMangasPayload( - clientMutationId = clientMutationId, - mangas = mangas, - ) - } + UpdateMangasPayload( + clientMutationId = clientMutationId, + mangas = mangas, + ) } } @@ -150,22 +146,20 @@ class MangaMutation { ) @RequireAuth - fun fetchManga(input: FetchMangaInput): CompletableFuture> { + fun fetchManga(input: FetchMangaInput): CompletableFuture { val (clientMutationId, id) = input return future { - asDataFetcherResult { - Manga.fetchManga(id) + Manga.fetchManga(id) - val manga = - transaction { - MangaTable.selectAll().where { MangaTable.id eq id }.first() - } - FetchMangaPayload( - clientMutationId = clientMutationId, - manga = MangaType(manga), - ) - } + val manga = + transaction { + MangaTable.selectAll().where { MangaTable.id eq id }.first() + } + FetchMangaPayload( + clientMutationId = clientMutationId, + manga = MangaType(manga), + ) } } @@ -180,14 +174,12 @@ class MangaMutation { ) @RequireAuth - fun setMangaMeta(input: SetMangaMetaInput): DataFetcherResult { + fun setMangaMeta(input: SetMangaMetaInput): SetMangaMetaPayload? { val (clientMutationId, meta) = input - return asDataFetcherResult { - Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) + Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) - SetMangaMetaPayload(clientMutationId, meta) - } + return SetMangaMetaPayload(clientMutationId, meta) } data class DeleteMangaMetaInput( @@ -203,34 +195,32 @@ class MangaMutation { ) @RequireAuth - fun deleteMangaMeta(input: DeleteMangaMetaInput): DataFetcherResult { + fun deleteMangaMeta(input: DeleteMangaMetaInput): DeleteMangaMetaPayload? { val (clientMutationId, mangaId, key) = input - return asDataFetcherResult { - val (meta, manga) = - transaction { - val meta = - MangaMetaTable - .selectAll() - .where { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } - .firstOrNull() + val (meta, manga) = + transaction { + val meta = + MangaMetaTable + .selectAll() + .where { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } + .firstOrNull() - MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } + MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } - val manga = - transaction { - MangaType(MangaTable.selectAll().where { MangaTable.id eq mangaId }.first()) - } + val manga = + transaction { + MangaType(MangaTable.selectAll().where { MangaTable.id eq mangaId }.first()) + } - if (meta != null) { - MangaMetaType(meta) - } else { - null - } to manga - } + if (meta != null) { + MangaMetaType(meta) + } else { + null + } to manga + } - DeleteMangaMetaPayload(clientMutationId, meta, manga) - } + return DeleteMangaMetaPayload(clientMutationId, meta, manga) } data class SetMangaMetasItem( @@ -250,43 +240,41 @@ class MangaMutation { ) @RequireAuth - fun setMangaMetas(input: SetMangaMetasInput): DataFetcherResult { + fun setMangaMetas(input: SetMangaMetasInput): SetMangaMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - val metaByMangaId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.mangaIds.map { mangaId -> mangaId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaByMangaId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.mangaIds.map { mangaId -> mangaId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Manga.modifyMangasMetas(metaByMangaId) + Manga.modifyMangasMetas(metaByMangaId) - val allMangaIds = metaByMangaId.keys - val allMetaKeys = metaByMangaId.values.flatMap { it.keys }.distinct() + val allMangaIds = metaByMangaId.keys + val allMetaKeys = metaByMangaId.values.flatMap { it.keys }.distinct() - val (updatedMetas, mangas) = - transaction { - val updatedMetas = - MangaMetaTable - .selectAll() - .where { (MangaMetaTable.ref inList allMangaIds) and (MangaMetaTable.key inList allMetaKeys) } - .map { MangaMetaType(it) } + val (updatedMetas, mangas) = + transaction { + val updatedMetas = + MangaMetaTable + .selectAll() + .where { (MangaMetaTable.ref inList allMangaIds) and (MangaMetaTable.key inList allMetaKeys) } + .map { MangaMetaType(it) } - val mangas = - MangaTable - .selectAll() - .where { MangaTable.id inList allMangaIds } - .map { MangaType(it) } - .distinctBy { it.id } + val mangas = + MangaTable + .selectAll() + .where { MangaTable.id inList allMangaIds } + .map { MangaType(it) } + .distinctBy { it.id } - updatedMetas to mangas - } + updatedMetas to mangas + } - SetMangaMetasPayload(clientMutationId, updatedMetas, mangas) - } + return SetMangaMetasPayload(clientMutationId, updatedMetas, mangas) } data class DeleteMangaMetasItem( @@ -307,63 +295,61 @@ class MangaMutation { ) @RequireAuth - fun deleteMangaMetas(input: DeleteMangaMetasInput): DataFetcherResult { + fun deleteMangaMetas(input: DeleteMangaMetasInput): DeleteMangaMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allMangaIds) = + transaction { + val deletedMetas = mutableListOf() + val mangaIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { MangaMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (MangaMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (MangaMetaTable.ref inList item.mangaIds) and metaKeyCondition + + deletedMetas += + MangaMetaTable + .selectAll() + .where { condition } + .map { MangaMetaType(it) } + + MangaMetaTable.deleteWhere { condition } + mangaIds += item.mangaIds } + + deletedMetas to mangaIds } - val (allDeletedMetas, allMangaIds) = - transaction { - val deletedMetas = mutableListOf() - val mangaIds = mutableSetOf() + val mangas = + transaction { + MangaTable + .selectAll() + .where { MangaTable.id inList allMangaIds } + .map { MangaType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { MangaMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (MangaMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (MangaMetaTable.ref inList item.mangaIds) and metaKeyCondition - - deletedMetas += - MangaMetaTable - .selectAll() - .where { condition } - .map { MangaMetaType(it) } - - MangaMetaTable.deleteWhere { condition } - mangaIds += item.mangaIds - } - - deletedMetas to mangaIds - } - - val mangas = - transaction { - MangaTable - .selectAll() - .where { MangaTable.id inList allMangaIds } - .map { MangaType(it) } - .distinctBy { it.id } - } - - DeleteMangaMetasPayload(clientMutationId, allDeletedMetas, mangas) - } + return DeleteMangaMetasPayload(clientMutationId, allDeletedMetas, mangas) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt index f2926528..3d363f7a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt @@ -1,18 +1,18 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult -import org.jetbrains.exposed.sql.LikePattern -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.SqlExpressionBuilder.like -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.LikePattern +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.global.impl.GlobalMeta import suwayomi.tachidesk.global.model.table.GlobalMetaTable -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.GlobalMetaType import suwayomi.tachidesk.graphql.types.MetaInput @@ -29,14 +29,12 @@ class MetaMutation { ) @RequireAuth - fun setGlobalMeta(input: SetGlobalMetaInput): DataFetcherResult { + fun setGlobalMeta(input: SetGlobalMetaInput): SetGlobalMetaPayload? { val (clientMutationId, meta) = input - return asDataFetcherResult { - GlobalMeta.modifyMeta(meta.key, meta.value) + GlobalMeta.modifyMeta(meta.key, meta.value) - SetGlobalMetaPayload(clientMutationId, meta) - } + return SetGlobalMetaPayload(clientMutationId, meta) } data class DeleteGlobalMetaInput( @@ -50,29 +48,27 @@ class MetaMutation { ) @RequireAuth - fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DataFetcherResult { + fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DeleteGlobalMetaPayload? { val (clientMutationId, key) = input - return asDataFetcherResult { - val meta = - transaction { - val meta = - GlobalMetaTable - .selectAll() - .where { GlobalMetaTable.key eq key } - .firstOrNull() + val meta = + transaction { + val meta = + GlobalMetaTable + .selectAll() + .where { GlobalMetaTable.key eq key } + .firstOrNull() - GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key } + GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key } - if (meta != null) { - GlobalMetaType(meta) - } else { - null - } + if (meta != null) { + GlobalMetaType(meta) + } else { + null } + } - DeleteGlobalMetaPayload(clientMutationId, meta) - } + return DeleteGlobalMetaPayload(clientMutationId, meta) } data class SetGlobalMetasInput( @@ -86,23 +82,21 @@ class MetaMutation { ) @RequireAuth - fun setGlobalMetas(input: SetGlobalMetasInput): DataFetcherResult { + fun setGlobalMetas(input: SetGlobalMetasInput): SetGlobalMetasPayload? { val (clientMutationId, metas) = input - return asDataFetcherResult { - val metaMap = metas.associate { it.key to it.value } - GlobalMeta.modifyMetas(metaMap) + val metaMap = metas.associate { it.key to it.value } + GlobalMeta.modifyMetas(metaMap) - val updatedMetas = - transaction { - GlobalMetaTable - .selectAll() - .where { GlobalMetaTable.key inList metaMap.keys } - .map { GlobalMetaType(it) } - } + val updatedMetas = + transaction { + GlobalMetaTable + .selectAll() + .where { GlobalMetaTable.key inList metaMap.keys } + .map { GlobalMetaType(it) } + } - SetGlobalMetasPayload(clientMutationId, updatedMetas) - } + return SetGlobalMetasPayload(clientMutationId, updatedMetas) } data class DeleteGlobalMetasInput( @@ -117,43 +111,41 @@ class MetaMutation { ) @RequireAuth - fun deleteGlobalMetas(input: DeleteGlobalMetasInput): DataFetcherResult { + fun deleteGlobalMetas(input: DeleteGlobalMetasInput): DeleteGlobalMetasPayload? { val (clientMutationId, keys, prefixes) = input - return asDataFetcherResult { - require(!keys.isNullOrEmpty() || !prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided" + require(!keys.isNullOrEmpty() || !prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided" + } + + val metas = + transaction { + val keyCondition: Op? = keys?.takeIf { it.isNotEmpty() }?.let { GlobalMetaTable.key inList it } + + val prefixCondition: Op? = + prefixes + ?.filter { it.isNotEmpty() } + ?.map { (GlobalMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val finalCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val metas = + GlobalMetaTable + .selectAll() + .where { finalCondition } + .map { GlobalMetaType(it) } + + GlobalMetaTable.deleteWhere { finalCondition } + + metas } - val metas = - transaction { - val keyCondition: Op? = keys?.takeIf { it.isNotEmpty() }?.let { GlobalMetaTable.key inList it } - - val prefixCondition: Op? = - prefixes - ?.filter { it.isNotEmpty() } - ?.map { (GlobalMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val finalCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val metas = - GlobalMetaTable - .selectAll() - .where { finalCondition } - .map { GlobalMetaType(it) } - - GlobalMetaTable.deleteWhere { finalCondition } - - metas - } - - DeleteGlobalMetasPayload(clientMutationId, metas) - } + return DeleteGlobalMetasPayload(clientMutationId, metas) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt index 7a97f95c..42244be1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLIgnore diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt index c9b619e7..54bbfcb3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import androidx.preference.CheckBoxPreference @@ -5,18 +7,16 @@ import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.SwitchPreferenceCompat -import graphql.execution.DataFetcherResult -import org.jetbrains.exposed.sql.LikePattern -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.SqlExpressionBuilder.like -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.LikePattern +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.FilterChange import suwayomi.tachidesk.graphql.types.MangaType @@ -47,14 +47,12 @@ class SourceMutation { ) @RequireAuth - fun setSourceMeta(input: SetSourceMetaInput): DataFetcherResult { + fun setSourceMeta(input: SetSourceMetaInput): SetSourceMetaPayload? { val (clientMutationId, meta) = input - return asDataFetcherResult { - Source.modifyMeta(meta.sourceId, meta.key, meta.value) + Source.modifyMeta(meta.sourceId, meta.key, meta.value) - SetSourceMetaPayload(clientMutationId, meta) - } + return SetSourceMetaPayload(clientMutationId, meta) } data class DeleteSourceMetaInput( @@ -70,38 +68,36 @@ class SourceMutation { ) @RequireAuth - fun deleteSourceMeta(input: DeleteSourceMetaInput): DataFetcherResult { + fun deleteSourceMeta(input: DeleteSourceMetaInput): DeleteSourceMetaPayload? { val (clientMutationId, sourceId, key) = input - return asDataFetcherResult { - val (meta, source) = - transaction { - val meta = - SourceMetaTable + val (meta, source) = + transaction { + val meta = + SourceMetaTable + .selectAll() + .where { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + .firstOrNull() + + SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + + val source = + transaction { + SourceTable .selectAll() - .where { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + .where { SourceTable.id eq sourceId } .firstOrNull() + ?.let { SourceType(it) } + } - SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + if (meta != null) { + SourceMetaType(meta) + } else { + null + } to source + } - val source = - transaction { - SourceTable - .selectAll() - .where { SourceTable.id eq sourceId } - .firstOrNull() - ?.let { SourceType(it) } - } - - if (meta != null) { - SourceMetaType(meta) - } else { - null - } to source - } - - DeleteSourceMetaPayload(clientMutationId, meta, source) - } + return DeleteSourceMetaPayload(clientMutationId, meta, source) } data class SetSourceMetasItem( @@ -121,43 +117,41 @@ class SourceMutation { ) @RequireAuth - fun setSourceMetas(input: SetSourceMetasInput): DataFetcherResult { + fun setSourceMetas(input: SetSourceMetasInput): SetSourceMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - val metaBySourceId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.sourceIds.map { sourceId -> sourceId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaBySourceId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.sourceIds.map { sourceId -> sourceId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Source.modifySourceMetas(metaBySourceId) + Source.modifySourceMetas(metaBySourceId) - val allSourceIds = metaBySourceId.keys - val allMetaKeys = metaBySourceId.values.flatMap { it.keys }.distinct() + val allSourceIds = metaBySourceId.keys + val allMetaKeys = metaBySourceId.values.flatMap { it.keys }.distinct() - val (updatedMetas, sources) = - transaction { - val updatedMetas = - SourceMetaTable - .selectAll() - .where { (SourceMetaTable.ref inList allSourceIds) and (SourceMetaTable.key inList allMetaKeys) } - .map { SourceMetaType(it) } + val (updatedMetas, sources) = + transaction { + val updatedMetas = + SourceMetaTable + .selectAll() + .where { (SourceMetaTable.ref inList allSourceIds) and (SourceMetaTable.key inList allMetaKeys) } + .map { SourceMetaType(it) } - val sources = - SourceTable - .selectAll() - .where { SourceTable.id inList allSourceIds } - .mapNotNull { SourceType(it) } - .distinctBy { it.id } + val sources = + SourceTable + .selectAll() + .where { SourceTable.id inList allSourceIds } + .mapNotNull { SourceType(it) } + .distinctBy { it.id } - updatedMetas to sources - } + updatedMetas to sources + } - SetSourceMetasPayload(clientMutationId, updatedMetas, sources) - } + return SetSourceMetasPayload(clientMutationId, updatedMetas, sources) } data class DeleteSourceMetasItem( @@ -178,64 +172,62 @@ class SourceMutation { ) @RequireAuth - fun deleteSourceMetas(input: DeleteSourceMetasInput): DataFetcherResult { + fun deleteSourceMetas(input: DeleteSourceMetasInput): DeleteSourceMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allSourceIds) = + transaction { + val deletedMetas = mutableListOf() + val sourceIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { SourceMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (SourceMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (SourceMetaTable.ref inList item.sourceIds) and metaKeyCondition + + deletedMetas += + SourceMetaTable + .selectAll() + .where { condition } + .map { SourceMetaType(it) } + + SourceMetaTable.deleteWhere { condition } + sourceIds += item.sourceIds } + + deletedMetas to sourceIds } - val (allDeletedMetas, allSourceIds) = - transaction { - val deletedMetas = mutableListOf() - val sourceIds = mutableSetOf() + val sources = + transaction { + SourceTable + .selectAll() + .where { SourceTable.id inList allSourceIds } + .mapNotNull { SourceType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { SourceMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (SourceMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (SourceMetaTable.ref inList item.sourceIds) and metaKeyCondition - - deletedMetas += - SourceMetaTable - .selectAll() - .where { condition } - .map { SourceMetaType(it) } - - SourceMetaTable.deleteWhere { condition } - sourceIds += item.sourceIds - } - - deletedMetas to sourceIds - } - - val sources = - transaction { - SourceTable - .selectAll() - .where { SourceTable.id inList allSourceIds } - .mapNotNull { SourceType(it) } - .distinctBy { it.id } - } - - DeleteSourceMetasPayload(clientMutationId, allDeletedMetas, sources) - } + return DeleteSourceMetasPayload(clientMutationId, allDeletedMetas, sources) } enum class FetchSourceMangaType { @@ -260,50 +252,48 @@ class SourceMutation { ) @RequireAuth - fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture> { + fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture { val (clientMutationId, sourceId, type, page, query, filters) = input return future { - asDataFetcherResult { - val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!! - val mangasPage = - when (type) { - FetchSourceMangaType.SEARCH -> { - source.getSearchManga( - page = page, - query = query.orEmpty(), - filters = updateFilterList(source, filters), - ) - } - - FetchSourceMangaType.POPULAR -> { - source.getPopularManga(page) - } - - FetchSourceMangaType.LATEST -> { - if (!source.supportsLatest) throw Exception("Source does not support latest") - source.getLatestUpdates(page) - } + val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!! + val mangasPage = + when (type) { + FetchSourceMangaType.SEARCH -> { + source.getSearchManga( + page = page, + query = query.orEmpty(), + filters = updateFilterList(source, filters), + ) } - val mangaIds = mangasPage.insertOrUpdate(sourceId) - - val mangas = - transaction { - MangaTable - .selectAll() - .where { MangaTable.id inList mangaIds } - .map { MangaType(it) } - }.sortedBy { - mangaIds.indexOf(it.id) + FetchSourceMangaType.POPULAR -> { + source.getPopularManga(page) } - FetchSourceMangaPayload( - clientMutationId = clientMutationId, - mangas = mangas, - hasNextPage = mangasPage.hasNextPage, - ) - } + FetchSourceMangaType.LATEST -> { + if (!source.supportsLatest) throw Exception("Source does not support latest") + source.getLatestUpdates(page) + } + } + + val mangaIds = mangasPage.insertOrUpdate(sourceId) + + val mangas = + transaction { + MangaTable + .selectAll() + .where { MangaTable.id inList mangaIds } + .map { MangaType(it) } + }.sortedBy { + mangaIds.indexOf(it.id) + } + + FetchSourceMangaPayload( + clientMutationId = clientMutationId, + mangas = mangas, + hasNextPage = mangasPage.hasNextPage, + ) } } @@ -329,29 +319,27 @@ class SourceMutation { ) @RequireAuth - fun updateSourcePreference(input: UpdateSourcePreferenceInput): DataFetcherResult { + fun updateSourcePreference(input: UpdateSourcePreferenceInput): UpdateSourcePreferencePayload? { val (clientMutationId, sourceId, change) = input - return asDataFetcherResult { - Source.setSourcePreference(sourceId, change.position, "") { preference -> - when (preference) { - is SwitchPreferenceCompat -> change.switchState - is CheckBoxPreference -> change.checkBoxState - is EditTextPreference -> change.editTextState - is ListPreference -> change.listState - is MultiSelectListPreference -> change.multiSelectState?.toSet() - else -> throw RuntimeException("sealed class cannot have more subtypes!") - } ?: throw Exception("Expected change to ${preference::class.simpleName}") - } - - UpdateSourcePreferencePayload( - clientMutationId = clientMutationId, - preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }, - source = - transaction { - SourceType(SourceTable.selectAll().where { SourceTable.id eq sourceId }.first())!! - }, - ) + Source.setSourcePreference(sourceId, change.position, "") { preference -> + when (preference) { + is SwitchPreferenceCompat -> change.switchState + is CheckBoxPreference -> change.checkBoxState + is EditTextPreference -> change.editTextState + is ListPreference -> change.listState + is MultiSelectListPreference -> change.multiSelectState?.toSet() + else -> throw RuntimeException("sealed class cannot have more subtypes!") + } ?: throw Exception("Expected change to ${preference::class.simpleName}") } + + return UpdateSourcePreferencePayload( + clientMutationId = clientMutationId, + preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }, + source = + transaction { + SourceType(SourceTable.selectAll().where { SourceTable.id eq sourceId }.first())!! + }, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt index ac618133..b1bf2e7b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt @@ -1,12 +1,13 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.generator.annotations.GraphQLDescription -import graphql.execution.DataFetcherResult -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.TrackRecordType import suwayomi.tachidesk.graphql.types.TrackerType @@ -222,24 +223,22 @@ class TrackMutation { ) @RequireAuth - fun trackProgress(input: TrackProgressInput): CompletableFuture> { + fun trackProgress(input: TrackProgressInput): CompletableFuture { val (clientMutationId, mangaId) = input return future { - asDataFetcherResult { - Track.trackChapter(mangaId) - val trackRecords = - transaction { - TrackRecordTable - .selectAll() - .where { TrackRecordTable.mangaId eq mangaId } - .toList() - } - TrackProgressPayload( - clientMutationId, - trackRecords.map { TrackRecordType(it) }, - ) - } + Track.trackChapter(mangaId) + val trackRecords = + transaction { + TrackRecordTable + .selectAll() + .where { TrackRecordTable.mangaId eq mangaId } + .toList() + } + TrackProgressPayload( + clientMutationId, + trackRecords.map { TrackRecordType(it) }, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt index d8cbaa76..614f9bbb 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt @@ -1,9 +1,9 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.LibraryUpdateStatus import suwayomi.tachidesk.graphql.types.UpdateStatus @@ -28,7 +28,7 @@ class UpdateMutation { ) @RequireAuth - fun updateLibrary(input: UpdateLibraryInput): CompletableFuture> { + fun updateLibrary(input: UpdateLibraryInput): CompletableFuture { updater.addCategoriesToUpdateQueue( Category.getCategoryList().filter { input.categories?.contains(it.id) ?: true }, clear = true, @@ -36,17 +36,15 @@ class UpdateMutation { ) return future { - asDataFetcherResult { - UpdateLibraryPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - LibraryUpdateStatus( - updater.updates.first(), - ) - }, - ) - } + UpdateLibraryPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + LibraryUpdateStatus( + updater.updates.first(), + ) + }, + ) } } @@ -60,7 +58,7 @@ class UpdateMutation { ) @RequireAuth - fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture> { + fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture { updateLibrary( UpdateLibraryInput( clientMutationId = input.clientMutationId, @@ -69,15 +67,13 @@ class UpdateMutation { ) return future { - asDataFetcherResult { - UpdateLibraryMangaPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - UpdateStatus(updater.status.first()) - }, - ) - } + UpdateLibraryMangaPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + UpdateStatus(updater.status.first()) + }, + ) } } @@ -92,7 +88,7 @@ class UpdateMutation { ) @RequireAuth - fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture> { + fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture { updateLibrary( UpdateLibraryInput( clientMutationId = input.clientMutationId, @@ -101,15 +97,13 @@ class UpdateMutation { ) return future { - asDataFetcherResult { - UpdateCategoryMangaPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - UpdateStatus(updater.status.first()) - }, - ) - } + UpdateCategoryMangaPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + UpdateStatus(updater.status.first()) + }, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt index af0f86dc..46c2e1eb 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt @@ -1,8 +1,9 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.schema.DataFetchingEnvironment import suwayomi.tachidesk.global.impl.util.Jwt -import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.server.getAttribute import suwayomi.tachidesk.server.JavalinSetup.Attribute import suwayomi.tachidesk.server.serverConfig diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt index 8aae3926..d936e2d6 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt @@ -10,13 +10,13 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.Filter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt index 9965fabd..3c40dc76 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt @@ -10,14 +10,14 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.DoubleFilter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt index 50a5be2f..c2b1a8da 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt @@ -11,14 +11,14 @@ import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import eu.kanade.tachiyomi.source.local.LocalSource import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.core.neq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.Filter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt index 44b9d2e5..fdbd790b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt @@ -10,12 +10,15 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.ComparableScalarFilter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt index e853891a..95edb246 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt @@ -10,13 +10,13 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.Filter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt index 2aab3d29..216ee41b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt @@ -10,13 +10,13 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.Filter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt index 296eb72b..5eed6321 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt @@ -3,13 +3,13 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.DoubleFilter diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt index a06cf445..f1086ca4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt @@ -1,21 +1,33 @@ package suwayomi.tachidesk.graphql.queries.filter -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.ComparisonOp -import org.jetbrains.exposed.sql.Expression -import org.jetbrains.exposed.sql.ExpressionWithColumnType -import org.jetbrains.exposed.sql.LikePattern -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.QueryBuilder -import org.jetbrains.exposed.sql.SqlExpressionBuilder -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.not -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.stringParam -import org.jetbrains.exposed.sql.upperCase +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.ComparisonOp +import org.jetbrains.exposed.v1.core.Expression +import org.jetbrains.exposed.v1.core.ExpressionWithColumnType +import org.jetbrains.exposed.v1.core.LikePattern +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.QueryBuilder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.greaterEq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.isNotNull +import org.jetbrains.exposed.v1.core.isNull +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.core.lessEq +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.neq +import org.jetbrains.exposed.v1.core.not +import org.jetbrains.exposed.v1.core.notInList +import org.jetbrains.exposed.v1.core.notLike +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.core.stringParam +import org.jetbrains.exposed.v1.core.upperCase +import org.jetbrains.exposed.v1.core.wrap +import org.jetbrains.exposed.v1.jdbc.Query +import org.jetbrains.exposed.v1.jdbc.andWhere class ILikeEscapeOp( expr1: Expression<*>, @@ -88,9 +100,7 @@ class DistinctFromOp( ): DistinctFromOp = DistinctFromOp( expression, - with(SqlExpressionBuilder) { - expression.wrap(t) - }, + expression.wrap(t), false, ) @@ -100,9 +110,7 @@ class DistinctFromOp( ): DistinctFromOp = DistinctFromOp( expression, - with(SqlExpressionBuilder) { - expression.wrap(t) - }, + expression.wrap(t), true, ) @@ -112,9 +120,7 @@ class DistinctFromOp( ): DistinctFromOp = DistinctFromOp( expression, - with(SqlExpressionBuilder) { - expression.wrap(t) - }, + expression.wrap(t), false, ) @@ -124,9 +130,7 @@ class DistinctFromOp( ): DistinctFromOp = DistinctFromOp( expression, - with(SqlExpressionBuilder) { - expression.wrap(t) - }, + expression.wrap(t), true, ) } @@ -505,26 +509,26 @@ class OpAnd( ) { fun andWhere( value: T?, - andPart: SqlExpressionBuilder.(T & Any) -> Op, + andPart: (T & Any) -> Op, ) { value ?: return - val expr = Op.build { andPart(value) } + val expr = andPart(value) op = if (op == null) expr else (op!! and expr) } fun andWhere( values: List?, - andPart: SqlExpressionBuilder.(List) -> Op, + andPart: (List) -> Op, ) { @Suppress("UNCHECKED_CAST") - return andWhere(values as T?, andPart as SqlExpressionBuilder.(Any) -> Op) + return andWhere(values as T?, andPart as (Any) -> Op) } fun andWhere( valueDefault: T?, valueAll: List?, valueAny: List?, - expr: SqlExpressionBuilder.(T) -> Op, + expr: (T) -> Op, ) { andWhere(valueDefault, expr) andWhereAll(valueAll, expr) @@ -533,17 +537,17 @@ class OpAnd( fun andWhereAll( values: List?, - andPart: SqlExpressionBuilder.(T) -> Op, + andPart: (T) -> Op, ) { values?.map { andWhere(it, andPart) } } fun andWhereAny( values: List?, - andPart: SqlExpressionBuilder.(T) -> Op, + andPart: (T) -> Op, ) { values ?: return - val expr = values.map { Op.build { andPart(it) } }.reduce { acc, op -> acc or op } + val expr = values.map { andPart(it) }.reduce { acc, op -> acc or op } op = if (op == null) expr else (op!! and expr) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt index e8fefcb9..5844d6b3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt @@ -11,19 +11,20 @@ import com.expediagroup.graphql.server.execution.GraphQLRequestParser import com.expediagroup.graphql.server.types.GraphQLBatchRequest import com.expediagroup.graphql.server.types.GraphQLRequest import com.expediagroup.graphql.server.types.GraphQLServerRequest +import io.github.oshai.kotlinlogging.KotlinLogging 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 { - val jsonMapper = JavalinJackson() + private val logger = KotlinLogging.logger {} @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") override suspend fun parseRequest(context: Context): GraphQLServerRequest? { return try { + val jsonMapper = context.jsonMapper() val contentType = context.contentType() val formParam = if ( @@ -77,7 +78,8 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser { ) } } - } catch (_: IOException) { + } catch (e: IOException) { + logger.error(e) { "Error when parsing request" } null } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt index fbfd6945..162228f9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt @@ -10,13 +10,11 @@ package suwayomi.tachidesk.graphql.server import com.expediagroup.graphql.generator.execution.FlowSubscriptionExecutionStrategy import com.expediagroup.graphql.server.execution.GraphQLRequestHandler import com.expediagroup.graphql.server.execution.GraphQLServer -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import graphql.ExceptionWhileDataFetching import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy import graphql.execution.DataFetcherExceptionHandler import graphql.execution.DataFetcherExceptionHandlerResult -import graphql.schema.idl.RuntimeWiring import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.http.Context import io.javalin.websocket.WsCloseContext @@ -27,6 +25,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import suwayomi.tachidesk.graphql.server.subscriptions.ApolloSubscriptionProtocolHandler import suwayomi.tachidesk.server.JavalinSetup.future +import tools.jackson.module.kotlin.jacksonObjectMapper class TachideskGraphQLServer( requestParser: JavalinGraphQLRequestParser, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt index 413c23dd..7029663c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt @@ -58,7 +58,7 @@ private class GraphqlCursorCoercing : Coercing { ), ) } - return Cursor(input.value) + return Cursor(input.value!!) } private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt index 3469900a..633ed85b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt @@ -71,7 +71,7 @@ private class GraphqlDurationAsStringCoercing : Coercing { ) } return try { - Duration.parse(input.value) + Duration.parse(input.value!!) } catch (e: IllegalArgumentException) { throw CoercingParseLiteralException( "Invalid duration format: ${input.value}. Expected ISO-8601 duration string (e.g., 'PT30M', 'P1D')", diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt index 651c4fb8..6563c08f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt @@ -53,7 +53,7 @@ private class GraphqlLongAsStringCoercing : Coercing { ), ) } - return input.value.toLong() + return input.value!!.toLong() } private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt index d0b495c3..c8a1393e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt @@ -1,16 +1,16 @@ package suwayomi.tachidesk.graphql.server.primitives -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater -import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.or +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.jdbc.Query +import org.jetbrains.exposed.v1.jdbc.andWhere interface OrderBy { val column: Column<*> diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/QueryResults.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/QueryResults.kt index 27874b7f..9abb6b67 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/QueryResults.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/QueryResults.kt @@ -1,6 +1,6 @@ package suwayomi.tachidesk.graphql.server.primitives -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow data class QueryResults( val total: Long, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt index aea05df0..72ec0b7d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt @@ -9,9 +9,6 @@ package suwayomi.tachidesk.graphql.server.subscriptions import com.expediagroup.graphql.server.execution.GraphQLRequestHandler import com.expediagroup.graphql.server.types.GraphQLRequest -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.convertValue -import com.fasterxml.jackson.module.kotlin.readValue import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.http.Header import io.javalin.websocket.WsContext @@ -41,6 +38,9 @@ import suwayomi.tachidesk.server.JavalinSetup.Attribute import suwayomi.tachidesk.server.JavalinSetup.getAttributeOrSet import suwayomi.tachidesk.server.user.UserType import suwayomi.tachidesk.server.user.getUserFromToken +import tools.jackson.databind.ObjectMapper +import tools.jackson.module.kotlin.convertValue +import tools.jackson.module.kotlin.readValue /** * Implementation of the `graphql-transport-ws` protocol defined by Denis Badurina diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt index 21b04ea0..650e0e89 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt @@ -9,7 +9,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge import suwayomi.tachidesk.graphql.server.primitives.Node diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt index 09c30f90..7020db46 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt @@ -9,7 +9,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge import suwayomi.tachidesk.graphql.server.primitives.Node diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ExtensionType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ExtensionType.kt index 7c6a3fd7..97d6a59a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ExtensionType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ExtensionType.kt @@ -9,7 +9,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge import suwayomi.tachidesk.graphql.server.primitives.Node diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt index dc6a6424..7ac70b86 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt @@ -10,7 +10,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import eu.kanade.tachiyomi.source.model.UpdateStrategy import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.graphql.cache.CustomCacheMap import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt index 4b1aaef6..1f4d7bec 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt @@ -2,7 +2,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt index 56f85259..f6c4744a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt @@ -13,8 +13,9 @@ import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.online.HttpSource import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge import suwayomi.tachidesk.graphql.server.primitives.Node diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt index 11ae7e2d..9923df32 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt @@ -2,7 +2,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import graphql.schema.DataFetchingEnvironment -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.Edge import suwayomi.tachidesk.graphql.server.primitives.Node diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt index 4db17a45..49abb039 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt @@ -12,8 +12,9 @@ import io.javalin.http.HttpStatus import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.serialization.json.Json -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.CategoryManga import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.ChapterDownloadHelper diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt index ea1ba176..23c1ba11 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt @@ -7,25 +7,27 @@ package suwayomi.tachidesk.manga.impl * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.isNull +import org.jetbrains.exposed.v1.core.neq +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.toDataClass -import kotlin.collections.component1 -import kotlin.collections.orEmpty object Category { /** @@ -248,13 +250,14 @@ object Category { } if (existingMetaByMetaId.isNotEmpty()) { - BatchUpdateStatement(CategoryMetaTable).apply { - existingMetaByMetaId.forEach { (metaId, entry) -> - addBatch(EntityID(metaId, CategoryMetaTable)) - this[CategoryMetaTable.value] = entry.value - } - execute(this@transaction) - } + BatchUpdateStatement(CategoryMetaTable) + .apply { + existingMetaByMetaId.forEach { (metaId, entry) -> + addBatch(EntityID(metaId, CategoryMetaTable)) + this[CategoryMetaTable.value] = entry.value + } + }.toExecutable() + .execute(this@transaction) } if (newMetaByCategoryId.isNotEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt index 68852e6b..515858fb 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt @@ -7,19 +7,22 @@ package suwayomi.tachidesk.manga.impl * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.alias -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.count -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.leftJoin -import org.jetbrains.exposed.sql.max -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.wrapAsExpression +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.alias +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.count +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.isNull +import org.jetbrains.exposed.v1.core.leftJoin +import org.jetbrains.exposed.v1.core.max +import org.jetbrains.exposed.v1.core.wrapAsExpression +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.Category.DEFAULT_CATEGORY_ID import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt index b5f7d415..e0748000 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -17,17 +17,21 @@ import io.github.reactivecircus.cache4k.Cache import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.serialization.Serializable -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.less +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.download.DownloadManager import suwayomi.tachidesk.manga.impl.download.DownloadManager.EnqueueInput @@ -306,18 +310,19 @@ object Chapter { } if (chaptersToUpdate.isNotEmpty()) { - BatchUpdateStatement(ChapterTable).apply { - chaptersToUpdate.forEach { - addBatch(EntityID(it.id, ChapterTable)) - this[ChapterTable.name] = it.name - this[ChapterTable.date_upload] = it.uploadDate - this[ChapterTable.chapter_number] = it.chapterNumber - this[ChapterTable.scanlator] = it.scanlator - this[ChapterTable.sourceOrder] = it.index - this[ChapterTable.realUrl] = it.realUrl - } - execute(this@transaction) - } + BatchUpdateStatement(ChapterTable) + .apply { + chaptersToUpdate.forEach { + addBatch(EntityID(it.id, ChapterTable)) + this[ChapterTable.name] = it.name + this[ChapterTable.date_upload] = it.uploadDate + this[ChapterTable.chapter_number] = it.chapterNumber + this[ChapterTable.scanlator] = it.scanlator + this[ChapterTable.sourceOrder] = it.index + this[ChapterTable.realUrl] = it.realUrl + } + }.toExecutable() + .execute(this@transaction) } MangaTable.update({ MangaTable.id eq mangaId }) { @@ -517,11 +522,11 @@ object Chapter { // mangaId is not null, scope query under manga when { input.chapterIds != null -> { - Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.id inList input.chapterIds) } + (ChapterTable.manga eq mangaId) and (ChapterTable.id inList input.chapterIds) } input.chapterIndexes != null -> { - Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder inList input.chapterIndexes) } + (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder inList input.chapterIndexes) } else -> { @@ -534,7 +539,7 @@ object Chapter { // mangaId is null, only chapterIndexes is valid for this case when { input.chapterIds != null -> { - Op.build { (ChapterTable.id inList input.chapterIds) } + (ChapterTable.id inList input.chapterIds) } else -> { @@ -650,13 +655,14 @@ object Chapter { } if (existingMetaByMetaId.isNotEmpty()) { - BatchUpdateStatement(ChapterMetaTable).apply { - existingMetaByMetaId.forEach { (metaId, entry) -> - addBatch(EntityID(metaId, ChapterMetaTable)) - this[ChapterMetaTable.value] = entry.value - } - execute(this@transaction) - } + BatchUpdateStatement(ChapterMetaTable) + .apply { + existingMetaByMetaId.forEach { (metaId, entry) -> + addBatch(EntityID(metaId, ChapterMetaTable)) + this[ChapterMetaTable.value] = entry.value + } + }.toExecutable() + .execute(this@transaction) } if (newMetaByChapterId.isNotEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ChapterDownloadHelper.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ChapterDownloadHelper.kt index 5898534b..d4fa60ea 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ChapterDownloadHelper.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ChapterDownloadHelper.kt @@ -1,7 +1,9 @@ package suwayomi.tachidesk.manga.impl import kotlinx.coroutines.CoroutineScope -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.ArchiveProvider diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt index 9b047e72..63439e2b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt @@ -12,11 +12,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.neq +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.Manga.getManga import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt index a52c29d9..c76eee88 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt @@ -20,15 +20,18 @@ import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.http.HttpStatus import okhttp3.CacheControl import okhttp3.Response -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.impl.Source.getSource import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.MissingThumbnailException @@ -295,13 +298,14 @@ object Manga { } if (existingMetaByMetaId.isNotEmpty()) { - BatchUpdateStatement(MangaMetaTable).apply { - existingMetaByMetaId.forEach { (metaId, entry) -> - addBatch(EntityID(metaId, MangaMetaTable)) - this[MangaMetaTable.value] = entry.value - } - execute(this@transaction) - } + BatchUpdateStatement(MangaMetaTable) + .apply { + existingMetaByMetaId.forEach { (metaId, entry) -> + addBatch(EntityID(metaId, MangaMetaTable)) + this[MangaMetaTable.value] = entry.value + } + }.toExecutable() + .execute(this@transaction) } if (newMetaByMangaId.isNotEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt index bdc66ea6..9d405c50 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt @@ -9,12 +9,15 @@ package suwayomi.tachidesk.manga.impl import eu.kanade.tachiyomi.source.local.LocalSource import eu.kanade.tachiyomi.source.model.MangasPage -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass import suwayomi.tachidesk.manga.model.table.MangaTable @@ -88,27 +91,28 @@ object MangaList { } if (mangaToUpdate.isNotEmpty()) { - BatchUpdateStatement(MangaTable).apply { - mangaToUpdate.forEach { (sManga, manga) -> - addBatch(EntityID(manga[MangaTable.id].value, MangaTable)) - this[MangaTable.title] = sManga.title - this[MangaTable.artist] = sManga.artist ?: manga[MangaTable.artist] - this[MangaTable.author] = sManga.author ?: manga[MangaTable.author] - this[MangaTable.description] = sManga.description ?: manga[MangaTable.description] - this[MangaTable.genre] = sManga.genre ?: manga[MangaTable.genre] - this[MangaTable.status] = sManga.status - this[MangaTable.thumbnail_url] = sManga.thumbnail_url ?: manga[MangaTable.thumbnail_url] - this[MangaTable.updateStrategy] = sManga.update_strategy.name - if (!sManga.thumbnail_url.isNullOrEmpty() && manga[MangaTable.thumbnail_url] != sManga.thumbnail_url) { - this[MangaTable.thumbnailUrlLastFetched] = Instant.now().epochSecond - Manga.clearThumbnail(manga[MangaTable.id].value) - } else { - this[MangaTable.thumbnailUrlLastFetched] = - manga[MangaTable.thumbnailUrlLastFetched] + BatchUpdateStatement(MangaTable) + .apply { + mangaToUpdate.forEach { (sManga, manga) -> + addBatch(EntityID(manga[MangaTable.id].value, MangaTable)) + this[MangaTable.title] = sManga.title + this[MangaTable.artist] = sManga.artist ?: manga[MangaTable.artist] + this[MangaTable.author] = sManga.author ?: manga[MangaTable.author] + this[MangaTable.description] = sManga.description ?: manga[MangaTable.description] + this[MangaTable.genre] = sManga.genre ?: manga[MangaTable.genre] + this[MangaTable.status] = sManga.status + this[MangaTable.thumbnail_url] = sManga.thumbnail_url ?: manga[MangaTable.thumbnail_url] + this[MangaTable.updateStrategy] = sManga.update_strategy.name + if (!sManga.thumbnail_url.isNullOrEmpty() && manga[MangaTable.thumbnail_url] != sManga.thumbnail_url) { + this[MangaTable.thumbnailUrlLastFetched] = Instant.now().epochSecond + Manga.clearThumbnail(manga[MangaTable.id].value) + } else { + this[MangaTable.thumbnailUrlLastFetched] = + manga[MangaTable.thumbnailUrlLastFetched] + } } - } - execute(this@transaction) - } + }.toExecutable() + .execute(this@transaction) } val mangaUrlsToId = diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Page.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Page.kt index cf7915e3..7addcb92 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Page.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Page.kt @@ -13,11 +13,12 @@ import eu.kanade.tachiyomi.source.online.HttpSource import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.coroutines.flow.StateFlow import libcore.net.MimeUtils -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.graphql.types.DownloadConversion import suwayomi.tachidesk.manga.impl.util.getChapterCachePath import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt index 3fcf1b0b..fbf5e6d1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt @@ -15,12 +15,16 @@ import eu.kanade.tachiyomi.source.sourcePreferences import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.json.JsonMapper import io.javalin.json.fromJsonString -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import suwayomi.tachidesk.manga.impl.Source.preferenceScreenMap import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub @@ -210,13 +214,14 @@ object Source { } if (existingMetaByMetaId.isNotEmpty()) { - BatchUpdateStatement(SourceMetaTable).apply { - existingMetaByMetaId.forEach { (metaId, entry) -> - addBatch(EntityID(metaId, SourceMetaTable)) - this[SourceMetaTable.value] = entry.value - } - execute(this@transaction) - } + BatchUpdateStatement(SourceMetaTable) + .apply { + existingMetaByMetaId.forEach { (metaId, entry) -> + addBatch(EntityID(metaId, SourceMetaTable)) + this[SourceMetaTable.value] = entry.value + } + }.toExecutable() + .execute(this@transaction) } if (newMetaBySourceId.isNotEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt index cabf6742..bae65f7d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt @@ -19,7 +19,7 @@ import okio.Buffer import okio.Sink import okio.buffer import okio.gzip -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.backup.BackupFlags import suwayomi.tachidesk.manga.impl.backup.proto.handlers.BackupCategoryHandler import suwayomi.tachidesk.manga.impl.backup.proto.handlers.BackupGlobalMetaHandler diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupValidator.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupValidator.kt index 7bcb7dfb..9f036a51 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupValidator.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupValidator.kt @@ -11,8 +11,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore import okio.buffer import okio.gzip import okio.source -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.backup.proto.models.Backup import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager import suwayomi.tachidesk.manga.model.table.SourceTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupCategoryHandler.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupCategoryHandler.kt index 2603fe0c..60f2cc1c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupCategoryHandler.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupCategoryHandler.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.impl.backup.proto.handlers * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.jdbc.selectAll import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.impl.Category.modifyCategoriesMetas import suwayomi.tachidesk.manga.impl.backup.BackupFlags diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupMangaHandler.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupMangaHandler.kt index 4c0f2bbb..9fb0d8e2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupMangaHandler.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupMangaHandler.kt @@ -8,16 +8,18 @@ package suwayomi.tachidesk.manga.impl.backup.proto.handlers * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import eu.kanade.tachiyomi.source.model.UpdateStrategy -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.insertAndGetId -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.CategoryManga import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.Chapter.modifyChaptersMetas @@ -343,24 +345,25 @@ object BackupMangaHandler { } if (chaptersToUpdateToDbChapter.isNotEmpty()) { - BatchUpdateStatement(ChapterTable).apply { - chaptersToUpdateToDbChapter.forEach { (backupChapter, dbChapter) -> - addBatch(EntityID(dbChapter[ChapterTable.id].value, ChapterTable)) - if (flags.includeChapters) { - this[ChapterTable.isRead] = backupChapter.read || dbChapter[ChapterTable.isRead] - this[ChapterTable.lastPageRead] = - max(backupChapter.lastPageRead, dbChapter[ChapterTable.lastPageRead]).coerceAtLeast(0) - this[ChapterTable.isBookmarked] = backupChapter.bookmark || dbChapter[ChapterTable.isBookmarked] - } + BatchUpdateStatement(ChapterTable) + .apply { + chaptersToUpdateToDbChapter.forEach { (backupChapter, dbChapter) -> + addBatch(EntityID(dbChapter[ChapterTable.id].value, ChapterTable)) + if (flags.includeChapters) { + this[ChapterTable.isRead] = backupChapter.read || dbChapter[ChapterTable.isRead] + this[ChapterTable.lastPageRead] = + max(backupChapter.lastPageRead, dbChapter[ChapterTable.lastPageRead]).coerceAtLeast(0) + this[ChapterTable.isBookmarked] = backupChapter.bookmark || dbChapter[ChapterTable.isBookmarked] + } - if (flags.includeHistory) { - this[ChapterTable.lastReadAt] = - (historyByChapter[backupChapter.url]?.maxOrNull()?.milliseconds?.inWholeSeconds ?: 0) - .coerceAtLeast(dbChapter[ChapterTable.lastReadAt]) + if (flags.includeHistory) { + this[ChapterTable.lastReadAt] = + (historyByChapter[backupChapter.url]?.maxOrNull()?.milliseconds?.inWholeSeconds ?: 0) + .coerceAtLeast(dbChapter[ChapterTable.lastReadAt]) + } } - } - execute(this@dbTransaction) - } + }.toExecutable() + .execute(this@dbTransaction) } if (flags.includeClientData) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupSourceHandler.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupSourceHandler.kt index f940217a..4fe46db2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupSourceHandler.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/handlers/BackupSourceHandler.kt @@ -7,7 +7,8 @@ package suwayomi.tachidesk.manga.impl.backup.proto.handlers * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.selectAll import suwayomi.tachidesk.manga.impl.Source import suwayomi.tachidesk.manga.impl.Source.modifySourceMetas import suwayomi.tachidesk.manga.impl.backup.BackupFlags diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt index 47481668..76e9045e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt @@ -14,14 +14,14 @@ import io.github.oshai.kotlinlogging.KotlinLogging import io.github.reactivecircus.cache4k.Cache import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.ChapterDownloadHelper import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt index 7d66561f..7b3dd103 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt @@ -27,9 +27,12 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import kotlinx.serialization.Serializable -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.download.model.DownloadQueueItem import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Error @@ -343,7 +346,7 @@ object DownloadManager { transaction { ChapterTable .select(ChapterTable.id) - .where { ChapterTable.manga.eq(mangaId) and ChapterTable.sourceOrder.eq(chapterIndex) } + .where { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) } .first() } enqueue(EnqueueInput(chapterIds = listOf(chapter[ChapterTable.id].value))) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt index fb8b5337..4ed66ec6 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt @@ -17,8 +17,9 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.ChapterDownloadHelper import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReadyById import suwayomi.tachidesk.manga.impl.download.model.DownloadQueueItem diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt index 77d38f14..f66db074 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample import libcore.net.MimeUtils import org.apache.commons.compress.archivers.zip.ZipArchiveEntry -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.types.DownloadConversion +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.Page import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady import suwayomi.tachidesk.manga.impl.download.model.DownloadQueueItem @@ -26,14 +26,8 @@ import suwayomi.tachidesk.manga.impl.util.getChapterDownloadPath import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable -import suwayomi.tachidesk.server.serverConfig -import suwayomi.tachidesk.util.ConversionUtil import java.io.File import java.io.InputStream -import javax.imageio.IIOImage -import javax.imageio.ImageIO -import javax.imageio.ImageWriteParam -import javax.imageio.ImageWriter sealed class FileType { data class RegularFile( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt index 99e62654..a9f37d9f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/ArchiveProvider.kt @@ -6,8 +6,9 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream import org.apache.commons.compress.archivers.zip.ZipFile -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider import suwayomi.tachidesk.manga.impl.download.fileProvider.FileType import suwayomi.tachidesk.manga.impl.util.getChapterCachePath diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt index 9d457f01..90be1624 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/impl/FolderProvider.kt @@ -2,8 +2,9 @@ package suwayomi.tachidesk.manga.impl.download.fileProvider.impl import org.apache.commons.compress.archivers.zip.ZipArchiveEntry import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider import suwayomi.tachidesk.manga.impl.download.fileProvider.FileType.RegularFile import suwayomi.tachidesk.manga.impl.util.getChapterCachePath diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt index 83e307ea..b2a0379b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt @@ -20,12 +20,12 @@ import okhttp3.CacheControl import okio.buffer import okio.sink import okio.source -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.extensionTableAsDataClass import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.manga.impl.util.PackageTools @@ -55,7 +55,6 @@ import java.util.zip.ZipOutputStream import kotlin.io.path.Path import kotlin.io.path.absolutePathString import kotlin.io.path.outputStream -import kotlin.io.path.relativeTo object Extension { private val logger = KotlinLogging.logger {} @@ -359,6 +358,7 @@ object Extension { } else { ExtensionTable.update({ ExtensionTable.pkgName eq pkgName }) { it[isInstalled] = false + it[hasUpdate] = false } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt index 5371ba08..0398f1f5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt @@ -11,15 +11,16 @@ import eu.kanade.tachiyomi.source.local.LocalSource import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi import suwayomi.tachidesk.manga.impl.extension.github.OnlineExtension @@ -125,57 +126,59 @@ object ExtensionsList { .groupBy { it.second[ExtensionTable.isInstalled] } val installedExtensionsToUpdate = extensionsInstalled[true].orEmpty() if (installedExtensionsToUpdate.isNotEmpty()) { - BatchUpdateStatement(ExtensionTable).apply { - installedExtensionsToUpdate.forEach { (foundExtension, extensionRecord) -> - addBatch(EntityID(extensionRecord[ExtensionTable.id].value, ExtensionTable)) - // Always update icon url and repo - this[ExtensionTable.iconUrl] = foundExtension.iconUrl - this[ExtensionTable.repo] = foundExtension.repo + BatchUpdateStatement(ExtensionTable) + .apply { + installedExtensionsToUpdate.forEach { (foundExtension, extensionRecord) -> + addBatch(EntityID(extensionRecord[ExtensionTable.id].value, ExtensionTable)) + // Always update icon url and repo + this[ExtensionTable.iconUrl] = foundExtension.iconUrl + this[ExtensionTable.repo] = foundExtension.repo - // add these because batch updates need matching columns - this[ExtensionTable.hasUpdate] = extensionRecord[ExtensionTable.hasUpdate] - this[ExtensionTable.isObsolete] = extensionRecord[ExtensionTable.isObsolete] + // add these because batch updates need matching columns + this[ExtensionTable.hasUpdate] = extensionRecord[ExtensionTable.hasUpdate] + this[ExtensionTable.isObsolete] = extensionRecord[ExtensionTable.isObsolete] - // a previously removed extension is now available again - if (extensionRecord[ExtensionTable.isObsolete] && - foundExtension.versionCode >= extensionRecord[ExtensionTable.versionCode] - ) { - this[ExtensionTable.isObsolete] = false - } - - when { - foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode] -> { - // there is an update - this[ExtensionTable.hasUpdate] = true - updateMap.putIfAbsent(foundExtension.pkgName, foundExtension) + // a previously removed extension is now available again + if (extensionRecord[ExtensionTable.isObsolete] && + foundExtension.versionCode >= extensionRecord[ExtensionTable.versionCode] + ) { + this[ExtensionTable.isObsolete] = false } - foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> { - // somehow the user installed an invalid version - this[ExtensionTable.isObsolete] = true + when { + foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode] -> { + // there is an update + this[ExtensionTable.hasUpdate] = true + updateMap.putIfAbsent(foundExtension.pkgName, foundExtension) + } + + foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> { + // somehow the user installed an invalid version + this[ExtensionTable.isObsolete] = true + } } } - } - execute(this@transaction) - } + }.toExecutable() + .execute(this@transaction) } val extensionsToFullyUpdate = extensionsInstalled[false].orEmpty() if (extensionsToFullyUpdate.isNotEmpty()) { - BatchUpdateStatement(ExtensionTable).apply { - extensionsToFullyUpdate.forEach { (foundExtension, extensionRecord) -> - addBatch(EntityID(extensionRecord[ExtensionTable.id].value, ExtensionTable)) - // extension is not installed, so we can overwrite the data without a care - this[ExtensionTable.repo] = foundExtension.repo - this[ExtensionTable.name] = foundExtension.name - this[ExtensionTable.versionName] = foundExtension.versionName - this[ExtensionTable.versionCode] = foundExtension.versionCode - this[ExtensionTable.lang] = foundExtension.lang - this[ExtensionTable.isNsfw] = foundExtension.isNsfw - this[ExtensionTable.apkName] = foundExtension.apkName - this[ExtensionTable.iconUrl] = foundExtension.iconUrl - } - execute(this@transaction) - } + BatchUpdateStatement(ExtensionTable) + .apply { + extensionsToFullyUpdate.forEach { (foundExtension, extensionRecord) -> + addBatch(EntityID(extensionRecord[ExtensionTable.id].value, ExtensionTable)) + // extension is not installed, so we can overwrite the data without a care + this[ExtensionTable.repo] = foundExtension.repo + this[ExtensionTable.name] = foundExtension.name + this[ExtensionTable.versionName] = foundExtension.versionName + this[ExtensionTable.versionCode] = foundExtension.versionCode + this[ExtensionTable.lang] = foundExtension.lang + this[ExtensionTable.isNsfw] = foundExtension.isNsfw + this[ExtensionTable.apkName] = foundExtension.apkName + this[ExtensionTable.iconUrl] = foundExtension.iconUrl + } + }.toExecutable() + .execute(this@transaction) } } if (extensionsToInsert.isNotEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt index cf9a03e4..a0ca9db8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt @@ -14,8 +14,10 @@ import kotlinx.serialization.json.put import okhttp3.MediaType.Companion.toMediaType import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import org.jetbrains.exposed.v1.jdbc.update import suwayomi.tachidesk.graphql.types.KoSyncStatusPayload import suwayomi.tachidesk.graphql.types.KoreaderSyncChecksumMethod import suwayomi.tachidesk.graphql.types.KoreaderSyncConflictStrategy diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt index 094188b4..a08e8605 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt @@ -6,16 +6,17 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.serialization.Serializable -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteWhere +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import org.jsoup.Jsoup import suwayomi.tachidesk.manga.impl.track.tracker.DeletableTracker import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager @@ -427,23 +428,24 @@ object Track { fun updateTrackRecords(tracks: List) = transaction { if (tracks.isNotEmpty()) { - BatchUpdateStatement(TrackRecordTable).apply { - tracks.forEach { - addBatch(EntityID(it.id!!, TrackRecordTable)) - this[remoteId] = it.remote_id - this[libraryId] = it.library_id - this[title] = it.title - this[lastChapterRead] = it.last_chapter_read - this[totalChapters] = it.total_chapters - this[status] = it.status - this[score] = it.score - this[remoteUrl] = it.tracking_url - this[startDate] = it.started_reading_date - this[finishDate] = it.finished_reading_date - this[private] = it.private - } - execute(this@transaction) - } + BatchUpdateStatement(TrackRecordTable) + .apply { + tracks.forEach { + addBatch(EntityID(it.id!!, TrackRecordTable)) + this[remoteId] = it.remote_id + this[libraryId] = it.library_id + this[title] = it.title + this[lastChapterRead] = it.last_chapter_read + this[totalChapters] = it.total_chapters + this[status] = it.status + this[score] = it.score + this[remoteUrl] = it.tracking_url + this[startDate] = it.started_reading_date + this[finishDate] = it.finished_reading_date + this[private] = it.private + } + }.toExecutable() + .execute(this@transaction) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt index a6fb389b..7f1bd36e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt @@ -1,11 +1,9 @@ package suwayomi.tachidesk.manga.impl.track.tracker.model -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupTracking import suwayomi.tachidesk.manga.model.dataclass.TrackRecordDataClass import suwayomi.tachidesk.manga.model.table.TrackRecordTable -import suwayomi.tachidesk.manga.model.table.TrackRecordTable.lastChapterRead -import suwayomi.tachidesk.manga.model.table.TrackRecordTable.remoteUrl import suwayomi.tachidesk.manga.model.table.TrackSearchTable fun ResultRow.toTrackRecordDataClass(): TrackRecordDataClass = diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt index 276a7f75..82d14d3f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt @@ -7,10 +7,10 @@ package suwayomi.tachidesk.manga.impl.util * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/GetComicInfo.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/GetComicInfo.kt index ce29925e..89631f0c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/GetComicInfo.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/GetComicInfo.kt @@ -4,10 +4,11 @@ import eu.kanade.tachiyomi.source.local.metadata.COMIC_INFO_FILE import eu.kanade.tachiyomi.source.local.metadata.ComicInfo import eu.kanade.tachiyomi.source.local.metadata.ComicInfoPublishingStatus import nl.adaptivity.xmlutil.serialization.XML -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.ChapterTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/ExposedExtensions.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/ExposedExtensions.kt index 91c06aa0..e92e0eec 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/ExposedExtensions.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/ExposedExtensions.kt @@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.impl.util.lang * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.v1.jdbc.Query fun Query.isEmpty() = this.empty() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/source/GetCatalogueSource.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/source/GetCatalogueSource.kt index e6f88ed3..99952deb 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/source/GetCatalogueSource.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/source/GetCatalogueSource.kt @@ -12,8 +12,9 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.online.HttpSource import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.SourceTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMangaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMangaTable.kt index 5cf445ab..033e31da 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMangaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMangaTable.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable object CategoryMangaTable : IntIdTable() { val category = reference("category", CategoryTable, ReferenceOption.CASCADE) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt index ba45aaf8..d1513f35 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt @@ -7,15 +7,15 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.CategoryMetaTable.ref /** * Metadata storage for clients, about Category with id == [ref]. */ object CategoryMetaTable : IntIdTable() { - val key = varchar("key", 256) + val key = varchar("meta_key", 256) val value = varchar("value", 4096) val ref = reference("category_ref", CategoryTable, ReferenceOption.CASCADE) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt index c60facca..e234467b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.IncludeOrExclude diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt index 6584679f..24a90fc3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref /** @@ -26,7 +26,7 @@ import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref * } */ object ChapterMetaTable : IntIdTable() { - val key = varchar("key", 256) + val key = varchar("meta_key", 256) val value = varchar("value", 4096) val ref = reference("chapter_ref", ChapterTable, ReferenceOption.CASCADE) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt index 6b27729a..73517429 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt @@ -7,11 +7,12 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.Chapter.getChapterMetaMap import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass import suwayomi.tachidesk.manga.model.table.columns.truncatingVarchar diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ExtensionTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ExtensionTable.kt index 9aa02d1e..b73cfef2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ExtensionTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ExtensionTable.kt @@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable object ExtensionTable : IntIdTable() { val apkName = varchar("apk_name", 1024) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt index b4a7098a..b89b4597 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.MangaMetaTable.ref /** @@ -26,7 +26,7 @@ import suwayomi.tachidesk.manga.model.table.MangaMetaTable.ref * } */ object MangaMetaTable : IntIdTable() { - val key = varchar("key", 256) + val key = varchar("meta_key", 256) val value = varchar("value", 4096) val ref = reference("manga_ref", MangaTable, ReferenceOption.CASCADE) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt index e17b73b7..06c13931 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt @@ -9,8 +9,8 @@ package suwayomi.tachidesk.manga.model.table import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.UpdateStrategy -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/PageTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/PageTable.kt index 6135dc91..8e3bd582 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/PageTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/PageTable.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.columns.unlimitedVarchar object PageTable : IntIdTable() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceMetaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceMetaTable.kt index 3e6bc8a8..359da81e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceMetaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceMetaTable.kt @@ -7,14 +7,14 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import suwayomi.tachidesk.manga.model.table.SourceMetaTable.ref /** * Metadata storage for clients, about Source with id == [ref]. */ object SourceMetaTable : IntIdTable() { - val key = varchar("key", 256) + val key = varchar("meta_key", 256) val value = varchar("value", 4096) val ref = long("source_ref") } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt index 77a68e95..bdbaa547 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt @@ -7,7 +7,7 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IdTable +import org.jetbrains.exposed.v1.core.dao.id.IdTable object SourceTable : IdTable() { override val id = long("id").entityId() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt index 392ab5cb..3d922fd4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt @@ -7,8 +7,8 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.columns.truncatingVarchar object TrackRecordTable : IntIdTable() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt index 32572d5b..aae4ef43 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt @@ -7,14 +7,16 @@ package suwayomi.tachidesk.manga.model.table * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.statements.BatchUpdateStatement -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.statements.BatchUpdateStatement +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.track.tracker.model.TrackSearch import suwayomi.tachidesk.manga.model.table.columns.truncatingVarchar @@ -69,30 +71,31 @@ fun List.insertAll(): List { val toUpdate = grouped[true] val toInsert = grouped[false]?.map { it.second } if (!toUpdate.isNullOrEmpty()) { - BatchUpdateStatement(TrackSearchTable).apply { - toUpdate.forEach { (id, trackSearch) -> - id ?: return@forEach - addBatch(EntityID(id, TrackSearchTable)) - this[TrackSearchTable.title] = trackSearch.title - this[TrackSearchTable.totalChapters] = trackSearch.total_chapters - this[TrackSearchTable.trackingUrl] = trackSearch.tracking_url - this[TrackSearchTable.coverUrl] = trackSearch.cover_url - this[TrackSearchTable.summary] = trackSearch.summary - this[TrackSearchTable.publishingStatus] = trackSearch.publishing_status - this[TrackSearchTable.publishingType] = trackSearch.publishing_type - this[TrackSearchTable.startDate] = trackSearch.start_date - this[TrackSearchTable.libraryId] = trackSearch.library_id - this[TrackSearchTable.lastChapterRead] = trackSearch.last_chapter_read - this[TrackSearchTable.status] = trackSearch.status - this[TrackSearchTable.score] = trackSearch.score - this[TrackSearchTable.startedReadingDate] = trackSearch.started_reading_date - this[TrackSearchTable.finishedReadingDate] = trackSearch.finished_reading_date - this[TrackSearchTable.private] = trackSearch.private - this[TrackSearchTable.authors] = trackSearch.authors.ifEmpty { null }?.joinToString(",") - this[TrackSearchTable.artists] = trackSearch.artists.ifEmpty { null }?.joinToString(",") - } - execute(this@transaction) - } + BatchUpdateStatement(TrackSearchTable) + .apply { + toUpdate.forEach { (id, trackSearch) -> + id ?: return@forEach + addBatch(EntityID(id, TrackSearchTable)) + this[TrackSearchTable.title] = trackSearch.title + this[TrackSearchTable.totalChapters] = trackSearch.total_chapters + this[TrackSearchTable.trackingUrl] = trackSearch.tracking_url + this[TrackSearchTable.coverUrl] = trackSearch.cover_url + this[TrackSearchTable.summary] = trackSearch.summary + this[TrackSearchTable.publishingStatus] = trackSearch.publishing_status + this[TrackSearchTable.publishingType] = trackSearch.publishing_type + this[TrackSearchTable.startDate] = trackSearch.start_date + this[TrackSearchTable.libraryId] = trackSearch.library_id + this[TrackSearchTable.lastChapterRead] = trackSearch.last_chapter_read + this[TrackSearchTable.status] = trackSearch.status + this[TrackSearchTable.score] = trackSearch.score + this[TrackSearchTable.startedReadingDate] = trackSearch.started_reading_date + this[TrackSearchTable.finishedReadingDate] = trackSearch.finished_reading_date + this[TrackSearchTable.private] = trackSearch.private + this[TrackSearchTable.authors] = trackSearch.authors.ifEmpty { null }?.joinToString(",") + this[TrackSearchTable.artists] = trackSearch.artists.ifEmpty { null }?.joinToString(",") + } + }.toExecutable() + .execute(this@transaction) } val insertedRows = if (!toInsert.isNullOrEmpty()) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/columns/TruncatingVarCharColumn.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/columns/TruncatingVarCharColumn.kt index b5c897fa..fb6006f4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/columns/TruncatingVarCharColumn.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/columns/TruncatingVarCharColumn.kt @@ -1,9 +1,9 @@ package suwayomi.tachidesk.manga.model.table.columns import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.Table -import org.jetbrains.exposed.sql.VarCharColumnType +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.VarCharColumnType import suwayomi.tachidesk.graphql.types.DatabaseType import suwayomi.tachidesk.server.serverConfig diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt index af759c27..595ae64f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt @@ -1,7 +1,7 @@ package suwayomi.tachidesk.opds.impl import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.v1.core.SortOrder import suwayomi.tachidesk.i18n.MR import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl import suwayomi.tachidesk.manga.model.table.ChapterTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/ChapterRepository.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/ChapterRepository.kt index ed347de0..735f0778 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/ChapterRepository.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/ChapterRepository.kt @@ -1,14 +1,16 @@ package suwayomi.tachidesk.opds.repository -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.JoinType -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Column +import org.jetbrains.exposed.v1.core.JoinType +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReady import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt index d0b57c37..433aef3d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt @@ -1,23 +1,26 @@ package suwayomi.tachidesk.opds.repository import eu.kanade.tachiyomi.source.model.MangasPage -import org.jetbrains.exposed.sql.Case -import org.jetbrains.exposed.sql.JoinType -import org.jetbrains.exposed.sql.Op -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.SqlExpressionBuilder.like -import org.jetbrains.exposed.sql.alias -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.intLiteral -import org.jetbrains.exposed.sql.lowerCase -import org.jetbrains.exposed.sql.max -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.sum -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Case +import org.jetbrains.exposed.v1.core.JoinType +import org.jetbrains.exposed.v1.core.Op +import org.jetbrains.exposed.v1.core.ResultRow +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.alias +import org.jetbrains.exposed.v1.core.and +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.core.greater +import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.intLiteral +import org.jetbrains.exposed.v1.core.like +import org.jetbrains.exposed.v1.core.lowerCase +import org.jetbrains.exposed.v1.core.max +import org.jetbrains.exposed.v1.core.or +import org.jetbrains.exposed.v1.core.sum +import org.jetbrains.exposed.v1.jdbc.Query +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.manga.impl.MangaList.insertOrUpdate import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource import suwayomi.tachidesk.manga.model.dataclass.toGenreList diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/NavigationRepository.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/NavigationRepository.kt index 9b3a6daa..dca5ab4a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/NavigationRepository.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/NavigationRepository.kt @@ -1,12 +1,14 @@ package suwayomi.tachidesk.opds.repository import dev.icerock.moko.resources.StringResource -import org.jetbrains.exposed.sql.JoinType -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.alias -import org.jetbrains.exposed.sql.count -import org.jetbrains.exposed.sql.countDistinct -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.JoinType +import org.jetbrains.exposed.v1.core.SortOrder +import org.jetbrains.exposed.v1.core.alias +import org.jetbrains.exposed.v1.core.count +import org.jetbrains.exposed.v1.core.countDistinct +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.select +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.i18n.MR import suwayomi.tachidesk.manga.impl.extension.Extension import suwayomi.tachidesk.manga.model.table.CategoryMangaTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt index 3cca3236..1f2835af 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt @@ -52,7 +52,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 import kotlin.time.Duration.Companion.seconds diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/Migration.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/Migration.kt index c21e8c05..445fc42a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/Migration.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/Migration.kt @@ -4,12 +4,13 @@ import android.app.Application import android.content.Context import io.github.oshai.kotlinlogging.KotlinLogging import suwayomi.tachidesk.manga.impl.update.IUpdater +import suwayomi.tachidesk.server.database.H2Migration +import suwayomi.tachidesk.server.util.ExitCode +import suwayomi.tachidesk.server.util.shutdownApp import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.File import java.util.prefs.Preferences -import kotlin.collections.isNotEmpty -import kotlin.collections.orEmpty private fun migratePreferences( parent: String?, @@ -76,6 +77,14 @@ private fun migrateMangaDownloadDir(applicationDirs: ApplicationDirs) { } } +fun migrateH2DatabaseToV24240(applicationDirs: ApplicationDirs) { + H2Migration.migrate( + applicationDirs.dataRoot, + "1.4.200", + "2.4.240", + ) +} + private val MIGRATIONS = listOf Unit>>( "InitialMigration" to { applicationDirs -> @@ -85,35 +94,42 @@ private val MIGRATIONS = "FixGlobalUpdateScheduling" to { Injekt.get().deleteLastAutomatedUpdateTimestamp() }, + "MigrateH2DatabaseToV2.4.240" to { applicationDirs -> + migrateH2DatabaseToV24240(applicationDirs) + }, ) fun runMigrations(applicationDirs: ApplicationDirs) { val logger = KotlinLogging.logger("Migration") + try { + val migrationPreferences = + Injekt + .get() + .getSharedPreferences( + "migrations", + Context.MODE_PRIVATE, + ) + val version = migrationPreferences.getInt("version", 0) - val migrationPreferences = - Injekt - .get() - .getSharedPreferences( - "migrations", - Context.MODE_PRIVATE, - ) - val version = migrationPreferences.getInt("version", 0) + logger.info { "Running migrations, previous version $version, target version ${MIGRATIONS.size}" } - logger.info { "Running migrations, previous version $version, target version ${MIGRATIONS.size}" } + MIGRATIONS.forEachIndexed { index, (migrationName, migrationFunction) -> + val migrationVersion = index + 1 - MIGRATIONS.forEachIndexed { index, (migrationName, migrationFunction) -> - val migrationVersion = index + 1 + val isMigrationRequired = version < migrationVersion + if (!isMigrationRequired) { + logger.info { "Skipping migration version $migrationVersion: $migrationName" } + return@forEachIndexed + } - val isMigrationRequired = version < migrationVersion - if (!isMigrationRequired) { - logger.info { "Skipping migration version $migrationVersion: $migrationName" } - return@forEachIndexed + logger.info { "Running migration version $migrationVersion: $migrationName" } + + migrationFunction(applicationDirs) + + migrationPreferences.edit().putInt("version", migrationVersion).apply() } - - logger.info { "Running migration version $migrationVersion: $migrationName" } - - migrationFunction(applicationDirs) - - migrationPreferences.edit().putInt("version", migrationVersion).apply() + } catch (e: Exception) { + logger.error(e) { "Failed to run migrations" } + shutdownApp(ExitCode.MigrationsRunFailure) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt index 4d926b27..6d364eb0 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt @@ -366,6 +366,7 @@ fun applicationSetup() { } } catch (e: Exception) { logger.error(e) { "Exception while creating initial server.conf" } + shutdownApp(ExitCode.SetupConfFileFailed) } // copy local source icon @@ -378,6 +379,7 @@ fun applicationSetup() { } } catch (e: Exception) { logger.error(e) { "Exception while copying Local source's icon" } + shutdownApp(ExitCode.LocalSourceIconCopyFailure) } // fixes #119 , ref: @@ -391,9 +393,16 @@ fun applicationSetup() { "Localization service initialized. Supported languages: ${LocalizationHelper.getSupportedLocales()}" } + runMigrations(applicationDirs) + databaseUp() - LocalSource.register() + try { + LocalSource.register() + } catch (e: Exception) { + logger.error(e) { "Failed to setup LocalSource" } + shutdownApp(ExitCode.LocalSourceSetupFailure) + } serverConfig.subscribeTo( combine( @@ -440,8 +449,6 @@ fun applicationSetup() { ignoreInitialValue = false, ) - runMigrations(applicationDirs) - setLogLevelFor("org.eclipse.jetty", Level.OFF) setLogLevelFor("com.zaxxer.hikari", Level.WARN) @@ -525,7 +532,7 @@ fun applicationSetup() { } } } - download { github() } + download { github { release("jbr-release-21.0.10b1163.108") } } settings { windowlessRenderingEnabled = true cachePath = (Path(applicationDirs.dataRoot) / "cache/kcef").toString() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt index 0345c303..d83b35a2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt @@ -12,13 +12,13 @@ import com.zaxxer.hikari.HikariDataSource import de.neonew.exposed.migrations.loadMigrationsFrom import de.neonew.exposed.migrations.runMigrations import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.DatabaseConfig -import org.jetbrains.exposed.sql.ExperimentalKeywordApi -import org.jetbrains.exposed.sql.Schema -import org.jetbrains.exposed.sql.SchemaUtils -import org.jetbrains.exposed.sql.transactions.TransactionManager -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.DatabaseConfig +import org.jetbrains.exposed.v1.core.ExperimentalKeywordApi +import org.jetbrains.exposed.v1.core.Schema +import org.jetbrains.exposed.v1.jdbc.Database +import org.jetbrains.exposed.v1.jdbc.SchemaUtils +import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.types.DatabaseType import suwayomi.tachidesk.server.ApplicationDirs import suwayomi.tachidesk.server.ServerConfig @@ -27,7 +27,6 @@ import suwayomi.tachidesk.server.util.ExitCode import suwayomi.tachidesk.server.util.shutdownApp import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.sql.SQLException import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -79,7 +78,7 @@ object DBManager { fun setupDatabase(): Database { // Clean up existing connections - if (TransactionManager.isInitialized()) { + if (TransactionManager.currentOrNull() != null) { val currentDatabase = TransactionManager.currentOrNull()?.db if (currentDatabase != null) { TransactionManager.closeAndUnregister(currentDatabase) @@ -184,10 +183,8 @@ fun databaseUp() { } val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java) runMigrations(migrations) - } catch (e: SQLException) { + } catch (e: Exception) { logger.error(e) { "Error up-to-database migration" } - if (System.getProperty("crashOnFailedMigration").toBoolean()) { - shutdownApp(ExitCode.DbMigrationFailure) - } + shutdownApp(ExitCode.DbMigrationFailure) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBTransaction.util.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBTransaction.util.kt index 5c475df2..d468ad4e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBTransaction.util.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBTransaction.util.kt @@ -1,15 +1,16 @@ package suwayomi.tachidesk.server.database -import org.jetbrains.exposed.sql.Transaction -import org.jetbrains.exposed.sql.transactions.TransactionManager -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.Transaction +import org.jetbrains.exposed.v1.jdbc.JdbcTransaction +import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager +import org.jetbrains.exposed.v1.jdbc.transactions.transaction /** * Performs the given transaction block inside the parent transaction or creates a new transaction if necessary. * * Any rollback or exception in the inner transaction will be propagated to the parent transaction. */ -fun dbTransaction(block: Transaction.() -> T): T { +fun dbTransaction(block: JdbcTransaction.() -> T): T { val currentTransaction = TransactionManager.currentOrNull() return if (currentTransaction == null) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/H2Migration.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/H2Migration.kt new file mode 100644 index 00000000..9e828ecd --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/H2Migration.kt @@ -0,0 +1,233 @@ +package suwayomi.tachidesk.server.database + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.NetworkHelper +import io.github.oshai.kotlinlogging.KotlinLogging +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.net.URLClassLoader +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.bufferedReader +import kotlin.io.path.bufferedWriter +import kotlin.io.path.copyTo +import kotlin.io.path.createDirectories +import kotlin.io.path.deleteExisting +import kotlin.io.path.deleteIfExists +import kotlin.io.path.div +import kotlin.io.path.exists +import kotlin.io.path.name +import kotlin.io.path.notExists +import kotlin.io.path.outputStream + +object H2Migration { + private val logger = KotlinLogging.logger {} + + private val client by lazy { + Injekt.get().client + } + + private const val TOOL_VERSION = "1.8" + + private const val TOOL_URL = + "https://manticore-projects.com/download/H2MigrationTool-$TOOL_VERSION/H2MigrationTool-$TOOL_VERSION-all.jar" + + private const val MAVEN_BASE = + "https://repo1.maven.org/maven2/com/h2database/h2" + + fun migrate( + rootDir: String, + h2Old: String, + h2New: String, + ) { + val dbBase = "$rootDir/database" + val mvStore = Path("$dbBase.mv.db") + if (mvStore.notExists()) { + logger.info { "No H2 database found. Skipping migration." } + return + } + + val script = Path("$dbBase.${h2Old.substringAfterLast('.')}.sql") + script.deleteIfExists() + + val modifiedScript = Path("$dbBase.${h2Old.substringAfterLast('.')}.modified.sql") + modifiedScript.deleteIfExists() + + // Backup original database. + val backup = Path("$dbBase.mv.db.${h2Old.substringAfterLast('.')}.backup") + mvStore.copyTo(backup, overwrite = true) + logger.info { "Created backup: ${backup.absolutePathString()}" } + + val toolsDir = Path(rootDir) / "bin" / "h2-migration-tools" + val libsDir = toolsDir / "h2libs" + libsDir.createDirectories() + + // Download migration tool + val migrationJar = + toolsDir.resolve("H2MigrationTool-$TOOL_VERSION-all.jar") + downloadIfNeeded( + TOOL_URL, + migrationJar, + ) + downloadIfNeeded( + "$MAVEN_BASE/$h2Old/h2-$h2Old.jar", + libsDir.resolve("h2-$h2Old.bin"), + ) + downloadIfNeeded( + "$MAVEN_BASE/$h2New/h2-$h2New.jar", + libsDir.resolve("h2-$h2New.bin"), + ) + + // Delete attempted migration if failed previously + val newDatabase = Path(rootDir, "database.${h2New.substringAfterLast('.')}.mv.db") + newDatabase.deleteIfExists() + + val modifiedNewDatabase = Path(rootDir, "database.${h2Old.substringAfterLast('.')}.modified.${h2New.substringAfterLast('.')}.mv.db") + modifiedNewDatabase.deleteIfExists() + + runMigrationTool( + migrationJar = migrationJar, + libsDir = libsDir, + mvStore = mvStore, + script = script, + modifiedScript = modifiedScript, + h2Old = h2Old, + h2New = h2New, + ) + + // Move database to proper path + if (modifiedNewDatabase.exists()) { + modifiedNewDatabase.copyTo(mvStore, overwrite = true) + modifiedNewDatabase.deleteExisting() + newDatabase.deleteIfExists() + } else { + newDatabase.copyTo(mvStore, overwrite = true) + newDatabase.deleteExisting() + } + + logger.info { "H2 migration completed successfully." } + } + + private fun downloadIfNeeded( + url: String, + output: Path, + ) { + if (output.exists()) { + logger.debug { "Already downloaded: ${output.name}" } + return + } + + client + .newCall(GET(url)) + .execute() + .use { response -> + + if (!response.isSuccessful) { + throw RuntimeException( + "Failed to download $url " + + "(HTTP ${response.code})", + ) + } + + output.outputStream().use { out -> + response.body.byteStream().copyTo(out) + } + } + + logger.info { "Saved: ${output.absolutePathString()}" } + } + + private fun runMigrationTool( + migrationJar: Path, + libsDir: Path, + mvStore: Path, + script: Path, + modifiedScript: Path, + h2Old: String, + h2New: String, + ) { + URLClassLoader( + arrayOf(migrationJar.toUri().toURL()), + javaClass.classLoader, + ).use { classLoader -> + val clazz = + classLoader.loadClass("com.manticore.h2.H2MigrationTool") + + val main = + clazz.getMethod("main", Array::class.java) + + try { + main.invoke( + null, + arrayOf( + // h2 driver dir + "-l", + libsDir.absolutePathString(), + // from version + "-f", + h2Old, + // to version + "-t", + h2New, + // user + "-u", + "", + // password + "-p", + "", + // database.mv.db + "-d", + mvStore.absolutePathString(), + // database backup in SQL + "-s", + script.absolutePathString(), + ), + ) + } catch (e: Exception) { + // Modify raw .sql file as needed for compatibility + if (e.stackTraceToString().contains("Unknown data type: \"DATETIME\"; SQL statement:") && script.exists()) { + script.bufferedReader().use { reader -> + modifiedScript.bufferedWriter().use { writer -> + reader.forEachLine { line -> + writer.write( + line.replace( + " \"EXECUTED_AT\" DATETIME(9) NOT NULL", + " \"EXECUTED_AT\" TIMESTAMP(9) NOT NULL", + ), + ) + writer.newLine() + } + } + } + + main.invoke( + null, + arrayOf( + // h2 driver dir + "-l", + libsDir.absolutePathString(), + // from version + "-f", + h2Old, + // to version + "-t", + h2New, + // user + "-u", + "", + // password + "-p", + "", + // database.mv.db + "-d", + modifiedScript.absolutePathString(), + ), + ) + } else { + throw e + } + } + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt index bb18c621..294575b3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt @@ -9,9 +9,9 @@ package suwayomi.tachidesk.server.database.migration import de.neonew.exposed.migrations.helpers.AddTableMigration import eu.kanade.tachiyomi.source.model.SManga -import org.jetbrains.exposed.dao.id.IdTable -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IdTable +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable /** initial migration, create all tables */ @Suppress("ClassName", "unused") diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt index 7b3379c5..9e3121db 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt @@ -8,9 +8,9 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IdTable -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IdTable +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable @Suppress("ClassName", "unused") class M0004_AnimeTablesBatch1 : AddTableMigration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt index a1aba284..f664e38d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt @@ -10,8 +10,8 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable @Suppress("ClassName", "unused") class M0005_AnimeTablesBatch2 : AddTableMigration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt index 4ab592b1..f6db0488 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt @@ -10,8 +10,8 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable @Suppress("ClassName", "unused") class M0006_AnimeTablesBatch3 : AddTableMigration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt index 9808b8b7..014788c1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt @@ -8,9 +8,9 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt index 2bd69e96..98c75e1e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0021_GlobalAndCategoryMeta.kt @@ -8,9 +8,9 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.ChapterTable @Suppress("ClassName", "unused") diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0023_CategoryMetaRefFix.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0023_CategoryMetaRefFix.kt index 3fe87ab7..6292553f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0023_CategoryMetaRefFix.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0023_CategoryMetaRefFix.kt @@ -10,17 +10,10 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.SQLMigration -import org.jetbrains.exposed.sql.transactions.TransactionManager +import suwayomi.tachidesk.server.database.migration.helpers.toSqlName @Suppress("ClassName", "unused") class M0023_CategoryMetaRefFix : SQLMigration() { - fun String.toSqlName(): String = - TransactionManager.defaultDatabase!!.identifierManager.let { - it.quoteIfNecessary( - it.inProperCase(this), - ) - } - private val CategoryMetaTable by lazy { "CategoryMeta".toSqlName() } private val CategoryRefColumn by lazy { "category_ref".toSqlName() } private val CategoryTable by lazy { "Category".toSqlName() } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0033_TrackRecord.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0033_TrackRecord.kt index 7485fde4..b89407ea 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0033_TrackRecord.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0033_TrackRecord.kt @@ -8,9 +8,9 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ReferenceOption -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import suwayomi.tachidesk.manga.model.table.MangaTable @Suppress("ClassName", "unused") diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt index 1aed1dfc..9e364b43 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt @@ -8,8 +8,8 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable @Suppress("ClassName", "unused") class M0035_TrackSearch : AddTableMigration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0036_SourceMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0036_SourceMeta.kt index 7595d463..bc711d3e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0036_SourceMeta.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0036_SourceMeta.kt @@ -8,8 +8,8 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.AddTableMigration -import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable @Suppress("ClassName", "unused") class M0036_SourceMeta : AddTableMigration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0049_FixDuplicatedMetas.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0049_FixDuplicatedMetas.kt index a35ff941..1916ddd1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0049_FixDuplicatedMetas.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0049_FixDuplicatedMetas.kt @@ -8,6 +8,7 @@ package suwayomi.tachidesk.server.database.migration * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import de.neonew.exposed.migrations.helpers.SQLMigration +import suwayomi.tachidesk.server.database.migration.helpers.toSqlName @Suppress("ClassName", "unused") class M0049_FixDuplicatedMetas : SQLMigration() { @@ -15,7 +16,7 @@ class M0049_FixDuplicatedMetas : SQLMigration() { table: String, refColumn: String? = null, ): String { - val groupBy = listOfNotNull(refColumn, "KEY").joinToString(", ") + val groupBy = listOfNotNull(refColumn, "KEY".toSqlName()).joinToString(", ") return """ DELETE FROM $table @@ -30,10 +31,11 @@ class M0049_FixDuplicatedMetas : SQLMigration() { """.trimIndent() } - override val sql: String = + override val sql: String by lazy { createMigrationForTable("CATEGORYMETA", "CATEGORY_REF") + createMigrationForTable("CHAPTERMETA", "CHAPTER_REF") + createMigrationForTable("GLOBALMETA") + createMigrationForTable("MANGAMETA", "MANGA_REF") + createMigrationForTable("SOURCEMETA", "SOURCE_REF") + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0053_TrackersFixIds.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0053_TrackersFixIds.kt index 8ad83881..b3475461 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0053_TrackersFixIds.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0053_TrackersFixIds.kt @@ -4,8 +4,6 @@ package suwayomi.tachidesk.server.database.migration import de.neonew.exposed.migrations.helpers.SQLMigration import suwayomi.tachidesk.graphql.types.DatabaseType -import suwayomi.tachidesk.server.database.migration.helpers.MAYBE_TYPE_PREFIX -import suwayomi.tachidesk.server.database.migration.helpers.UNLIMITED_TEXT import suwayomi.tachidesk.server.database.migration.helpers.toSqlName import suwayomi.tachidesk.server.serverConfig diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0055_RenameMetaKeys.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0055_RenameMetaKeys.kt new file mode 100644 index 00000000..3e6d9f46 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0055_RenameMetaKeys.kt @@ -0,0 +1,38 @@ +package suwayomi.tachidesk.server.database.migration + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.helpers.SQLMigration +import suwayomi.tachidesk.graphql.types.DatabaseType +import suwayomi.tachidesk.server.database.migration.helpers.toSqlName +import suwayomi.tachidesk.server.serverConfig + +@Suppress("ClassName", "unused") +class M0055_RenameMetaKeys : SQLMigration() { + fun postgresRename(table: String): String = + "ALTER TABLE $table " + + "RENAME COLUMN " + "KEY".toSqlName() + " TO META_KEY;" + + fun h2Rename(table: String): String = + "ALTER TABLE $table " + + "ALTER COLUMN " + "KEY".toSqlName() + " RENAME TO META_KEY;" + + fun createRenameMigration(table: String): String = + when (serverConfig.databaseType.value) { + DatabaseType.H2 -> h2Rename(table.toSqlName()) + DatabaseType.POSTGRESQL -> postgresRename(table.toSqlName()) + } + + override val sql: String by lazy { + createRenameMigration("CATEGORYMETA") + + createRenameMigration("CHAPTERMETA") + + createRenameMigration("GLOBALMETA") + + createRenameMigration("MANGAMETA") + + createRenameMigration("SOURCEMETA") + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/RenameFieldMigration.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/RenameFieldMigration.kt index c2f5bb79..6717e69c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/RenameFieldMigration.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/RenameFieldMigration.kt @@ -1,17 +1,9 @@ package suwayomi.tachidesk.server.database.migration.helpers import de.neonew.exposed.migrations.helpers.SQLMigration -import org.jetbrains.exposed.sql.transactions.TransactionManager import suwayomi.tachidesk.graphql.types.DatabaseType import suwayomi.tachidesk.server.serverConfig -fun String.toSqlName(): String = - TransactionManager.current().db.identifierManager.let { - it.quoteIfNecessary( - it.inProperCase(this), - ) - } - abstract class RenameFieldMigration( tableName: String, originalName: String, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/ToSqlName.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/ToSqlName.kt new file mode 100644 index 00000000..4e94f438 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/ToSqlName.kt @@ -0,0 +1,10 @@ +package suwayomi.tachidesk.server.database.migration.helpers + +import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager + +fun String.toSqlName(): String = + TransactionManager.current().db.identifierManager.let { + it.quoteIfNecessary( + it.inProperCase(this), + ) + } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppExit.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppExit.kt index 283aa566..020c0b5c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppExit.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppExit.kt @@ -21,6 +21,10 @@ enum class ExitCode( WebUISetupFailure(3), ConfigMigrationMisconfiguredFailure(4), DbMigrationFailure(5), + SetupConfFileFailed(6), + LocalSourceIconCopyFailure(7), + LocalSourceSetupFailure(8), + MigrationsRunFailure(9), } fun shutdownApp(exitCode: ExitCode) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/util/ConversionUtil.kt b/server/src/main/kotlin/suwayomi/tachidesk/util/ConversionUtil.kt index 46051ab8..8d1b9e26 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/util/ConversionUtil.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/util/ConversionUtil.kt @@ -10,14 +10,12 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody import suwayomi.tachidesk.graphql.types.DownloadConversion -import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil import uy.kohesive.injekt.injectLazy import java.awt.image.BufferedImage import java.io.File import java.io.InputStream import java.nio.file.Files import javax.imageio.ImageIO -import kotlin.getValue object ConversionUtil { val logger = KotlinLogging.logger {} diff --git a/server/src/test/kotlin/suwayomi/tachidesk/SafePathTest.kt b/server/src/test/kotlin/suwayomi/tachidesk/SafePathTest.kt new file mode 100644 index 00000000..c9842d77 --- /dev/null +++ b/server/src/test/kotlin/suwayomi/tachidesk/SafePathTest.kt @@ -0,0 +1,50 @@ +package suwayomi.tachidesk + +import xyz.nulldev.androidcompat.util.SafePath +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class SafePathTest { + @Test + fun invalidCharactersAreReplacedAndEdgesAreTrimmed() { + val input = " .a:b*c?df|g\\h/i. " + + val result = SafePath.buildValidFilename(input) + + assertEquals("a_b_c_d_e_f_g_h_i", result) + } + + @Test + fun emptyAfterTrimReturnsInvalidMarker() { + assertEquals("(invalid)", SafePath.buildValidFilename(" ... . ")) + } + + @Test + fun resultIsTruncatedTo240Characters() { + val input = "a".repeat(300) + + val result = SafePath.buildValidFilename(input) + + assertEquals(240, result.length) + assertEquals("a".repeat(240), result) + } + + @Test + fun mixed256CharactersCanExceed255Utf8Bytes() { + val mixed256 = + buildString { + repeat(128) { + append('a') + append('你') + } + } + + val result = SafePath.buildValidFilename(mixed256) + + assertEquals(120, result.length) + assertEquals(mixed256.take(120), result) + assertEquals(240, result.toByteArray(Charsets.UTF_8).size) + assertTrue(result.toByteArray(Charsets.UTF_8).size == 240) + } +} diff --git a/server/src/test/kotlin/suwayomi/tachidesk/manga/controller/UpdateControllerTest.kt b/server/src/test/kotlin/suwayomi/tachidesk/manga/controller/UpdateControllerTest.kt index 919a66d2..577b13bc 100644 --- a/server/src/test/kotlin/suwayomi/tachidesk/manga/controller/UpdateControllerTest.kt +++ b/server/src/test/kotlin/suwayomi/tachidesk/manga/controller/UpdateControllerTest.kt @@ -8,8 +8,8 @@ import suwayomi.tachidesk.test.ApplicationTest // import io.mockk.mockk // import io.mockk.verify // import kotlinx.coroutines.runBlocking -// import org.jetbrains.exposed.sql.insertAndGetId -// import org.jetbrains.exposed.sql.transactions.transaction +// import org.jetbrains.exposed.v1.jdbc.insertAndGetId +// import org.jetbrains.exposed.v1.jdbc.transactions.transaction // import org.junit.jupiter.api.AfterEach // import org.junit.jupiter.api.Assertions.assertEquals // import org.junit.jupiter.api.Test diff --git a/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/update/TestUpdater.kt b/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/update/TestUpdater.kt index c600cb91..a1c2842d 100644 --- a/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/update/TestUpdater.kt +++ b/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/update/TestUpdater.kt @@ -2,7 +2,6 @@ package suwayomi.tachidesk.manga.impl.update import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass diff --git a/server/src/test/kotlin/suwayomi/tachidesk/test/ApplicationTest.kt b/server/src/test/kotlin/suwayomi/tachidesk/test/ApplicationTest.kt index d97275ef..9adab3bc 100644 --- a/server/src/test/kotlin/suwayomi/tachidesk/test/ApplicationTest.kt +++ b/server/src/test/kotlin/suwayomi/tachidesk/test/ApplicationTest.kt @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.createAppModule import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.source.local.LocalSource import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.v1.jdbc.Database import org.junit.jupiter.api.BeforeAll import org.koin.core.context.startKoin import suwayomi.tachidesk.server.ApplicationDirs diff --git a/server/src/test/kotlin/suwayomi/tachidesk/test/TestUtils.kt b/server/src/test/kotlin/suwayomi/tachidesk/test/TestUtils.kt index 64ea67ec..eb59d937 100644 --- a/server/src/test/kotlin/suwayomi/tachidesk/test/TestUtils.kt +++ b/server/src/test/kotlin/suwayomi/tachidesk/test/TestUtils.kt @@ -11,11 +11,11 @@ import ch.qos.logback.classic.Level import eu.kanade.tachiyomi.source.model.SManga import io.github.oshai.kotlinlogging.DelegatingKLogger import io.github.oshai.kotlinlogging.KotlinLogging -import org.jetbrains.exposed.dao.id.IdTable -import org.jetbrains.exposed.sql.batchInsert -import org.jetbrains.exposed.sql.deleteAll -import org.jetbrains.exposed.sql.insertAndGetId -import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.v1.core.dao.id.IdTable +import org.jetbrains.exposed.v1.jdbc.batchInsert +import org.jetbrains.exposed.v1.jdbc.deleteAll +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.jdbc.transactions.transaction import org.slf4j.Logger import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable