mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 14:52:05 +01:00
Fix/remove koreader-sync credentials from server config (#1758)
* Remove koreader-sync credentials from config These are supposed to be set via the login/logout mutations and are not meant to be set manually by the user. Thus, they are not really settings and do not belong to the config * Reduce log levels of KoreaderSyncService
This commit is contained in:
@@ -35,11 +35,15 @@ dependencies {
|
||||
// GraphQL types used in ServerConfig
|
||||
implementation(libs.graphql.kotlin.scheme)
|
||||
|
||||
// Dependency Injection
|
||||
implementation(libs.injekt)
|
||||
|
||||
// AndroidCompat for SystemPropertyOverridableConfigModule
|
||||
implementation(projects.androidCompat.config)
|
||||
|
||||
// Serialization
|
||||
implementation(libs.serialization.json)
|
||||
implementation(libs.serialization.protobuf)
|
||||
implementation(project(":AndroidCompat"))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ package suwayomi.tachidesk.server
|
||||
* 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 android.app.Application
|
||||
import android.content.Context
|
||||
import com.typesafe.config.Config
|
||||
import io.github.config4k.toConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -55,11 +57,13 @@ import suwayomi.tachidesk.server.settings.StringSetting
|
||||
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)
|
||||
|
||||
@@ -67,6 +71,8 @@ const val SERVER_CONFIG_MODULE_NAME = "server"
|
||||
|
||||
val serverConfig: ServerConfig by lazy { GlobalConfigManager.module() }
|
||||
|
||||
private val application: Application by injectLazy()
|
||||
|
||||
// Settings are ordered by "protoNumber".
|
||||
class ServerConfig(
|
||||
getConfig: () -> Config,
|
||||
@@ -604,24 +610,57 @@ class ServerConfig(
|
||||
description = "KOReader Sync Server URL. Public alternative: https://kosync.ak-team.com:3042/",
|
||||
)
|
||||
|
||||
@Deprecated("Moved to preference store. User is supposed to use a login/logout mutation")
|
||||
val koreaderSyncUsername: MutableStateFlow<String> by StringSetting(
|
||||
protoNumber = 60,
|
||||
group = SettingGroup.KOREADER_SYNC,
|
||||
defaultValue = "",
|
||||
excludeFromBackup = true,
|
||||
deprecated = SettingsRegistry.SettingDeprecated(
|
||||
replaceWith = "MOVE TO PREFERENCES",
|
||||
message = "Moved to preference store. User is supposed to use a login/logout mutation",
|
||||
migrateConfig = { value, config ->
|
||||
val koreaderPreferences = application.getSharedPreferences("koreader_sync", Context.MODE_PRIVATE)
|
||||
koreaderPreferences.edit().putString("username", value.unwrapped() as? String).apply()
|
||||
|
||||
config
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
@Deprecated("Moved to preference store. User is supposed to use a login/logout mutation")
|
||||
val koreaderSyncUserkey: MutableStateFlow<String> by StringSetting(
|
||||
protoNumber = 61,
|
||||
group = SettingGroup.KOREADER_SYNC,
|
||||
defaultValue = "",
|
||||
excludeFromBackup = true,
|
||||
deprecated = SettingsRegistry.SettingDeprecated(
|
||||
replaceWith = "MOVE TO PREFERENCES",
|
||||
message = "Moved to preference store. User is supposed to use a login/logout mutation",
|
||||
migrateConfig = { value, config ->
|
||||
val koreaderPreferences = application.getSharedPreferences("koreader_sync", Context.MODE_PRIVATE)
|
||||
koreaderPreferences.edit().putString("user_key", value.unwrapped() as? String).apply()
|
||||
|
||||
config
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
@Deprecated("Moved to preference store. Is supposed to be random and gets auto generated")
|
||||
val koreaderSyncDeviceId: MutableStateFlow<String> by StringSetting(
|
||||
protoNumber = 62,
|
||||
group = SettingGroup.KOREADER_SYNC,
|
||||
defaultValue = "",
|
||||
deprecated = SettingsRegistry.SettingDeprecated(
|
||||
replaceWith = "MOVE TO PREFERENCES",
|
||||
message = "Moved to preference store. Is supposed to be random and gets auto generated",
|
||||
migrateConfig = { value, config ->
|
||||
val koreaderPreferences = application.getSharedPreferences("koreader_sync", Context.MODE_PRIVATE)
|
||||
koreaderPreferences.edit().putString("device_id", value.unwrapped() as? String).apply()
|
||||
|
||||
config
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
val koreaderSyncChecksumMethod: MutableStateFlow<KoreaderSyncChecksumMethod> by EnumSetting(
|
||||
|
||||
@@ -8,8 +8,8 @@ import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.directives.RequireAuth
|
||||
import suwayomi.tachidesk.graphql.types.ChapterType
|
||||
import suwayomi.tachidesk.graphql.types.KoSyncConnectPayload
|
||||
import suwayomi.tachidesk.graphql.types.KoSyncStatusPayload
|
||||
import suwayomi.tachidesk.graphql.types.LogoutKoSyncAccountPayload
|
||||
import suwayomi.tachidesk.graphql.types.SettingsType
|
||||
import suwayomi.tachidesk.graphql.types.SyncConflictInfoType
|
||||
import suwayomi.tachidesk.manga.impl.sync.KoreaderSyncService
|
||||
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||
@@ -26,14 +26,12 @@ class KoreaderSyncMutation {
|
||||
@RequireAuth
|
||||
fun connectKoSyncAccount(input: ConnectKoSyncAccountInput): CompletableFuture<KoSyncConnectPayload> =
|
||||
future {
|
||||
val result = KoreaderSyncService.connect(input.username, input.password)
|
||||
val (message, status) = KoreaderSyncService.connect(input.username, input.password)
|
||||
|
||||
KoSyncConnectPayload(
|
||||
clientMutationId = input.clientMutationId,
|
||||
success = result.success,
|
||||
message = result.message,
|
||||
username = result.username,
|
||||
settings = SettingsType(),
|
||||
message = message,
|
||||
status = status,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,8 +45,7 @@ class KoreaderSyncMutation {
|
||||
KoreaderSyncService.logout()
|
||||
LogoutKoSyncAccountPayload(
|
||||
clientMutationId = input.clientMutationId,
|
||||
success = true,
|
||||
settings = SettingsType(),
|
||||
status = KoSyncStatusPayload(isLoggedIn = false, username = null),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,11 @@ data class KoSyncStatusPayload(
|
||||
|
||||
data class KoSyncConnectPayload(
|
||||
val clientMutationId: String?,
|
||||
val success: Boolean,
|
||||
val status: KoSyncStatusPayload,
|
||||
val message: String?,
|
||||
val username: String?,
|
||||
val settings: SettingsType,
|
||||
)
|
||||
|
||||
data class LogoutKoSyncAccountPayload(
|
||||
val clientMutationId: String?,
|
||||
val success: Boolean,
|
||||
val settings: SettingsType,
|
||||
val status: KoSyncStatusPayload,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package suwayomi.tachidesk.manga.impl.sync
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.util.lang.Hash
|
||||
@@ -22,12 +24,20 @@ import suwayomi.tachidesk.manga.impl.util.KoreaderHelper
|
||||
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||
import suwayomi.tachidesk.server.serverConfig
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import kotlin.math.abs
|
||||
|
||||
object KoreaderSyncService {
|
||||
private val preferences = Injekt.get<Application>().getSharedPreferences("koreader_sync", Context.MODE_PRIVATE)
|
||||
|
||||
private const val USERNAME_KEY = "username"
|
||||
private const val USERKEY_KEY = "user_key"
|
||||
private const val DEVICE_ID_KEY = "client_id"
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private val network: NetworkHelper by injectLazy()
|
||||
private val json: Json by injectLazy()
|
||||
@@ -62,9 +72,8 @@ object KoreaderSyncService {
|
||||
)
|
||||
|
||||
data class ConnectResult(
|
||||
val success: Boolean,
|
||||
val message: String? = null,
|
||||
val username: String? = null,
|
||||
val status: KoSyncStatusPayload,
|
||||
)
|
||||
|
||||
private data class AuthResult(
|
||||
@@ -86,7 +95,8 @@ object KoreaderSyncService {
|
||||
.build()
|
||||
|
||||
private suspend fun getOrGenerateDeviceId(): String {
|
||||
var deviceId = serverConfig.koreaderSyncDeviceId.value
|
||||
var deviceId = preferences.getString(DEVICE_ID_KEY, "")!!
|
||||
|
||||
if (deviceId.isBlank()) {
|
||||
deviceId =
|
||||
UUID
|
||||
@@ -95,7 +105,7 @@ object KoreaderSyncService {
|
||||
.replace("-", "")
|
||||
.uppercase()
|
||||
logger.info { "[KOSYNC] Generated new KOSync Device ID: $deviceId" }
|
||||
serverConfig.koreaderSyncDeviceId.value = deviceId
|
||||
preferences.edit().putString(DEVICE_ID_KEY, deviceId).apply()
|
||||
}
|
||||
return deviceId
|
||||
}
|
||||
@@ -119,7 +129,7 @@ object KoreaderSyncService {
|
||||
val newHash =
|
||||
when (checksumMethod) {
|
||||
KoreaderSyncChecksumMethod.BINARY -> {
|
||||
logger.info { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from downloaded content." }
|
||||
logger.debug { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from downloaded content." }
|
||||
try {
|
||||
// Always create a CBZ in memory if it doesn't exist
|
||||
val (stream, _) = ChapterDownloadHelper.getArchiveStreamWithSize(mangaId, chapterId)
|
||||
@@ -141,7 +151,7 @@ object KoreaderSyncService {
|
||||
}
|
||||
}
|
||||
KoreaderSyncChecksumMethod.FILENAME -> {
|
||||
logger.info { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from filename." }
|
||||
logger.debug { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from filename." }
|
||||
(ChapterTable innerJoin MangaTable)
|
||||
.select(ChapterTable.name, MangaTable.title)
|
||||
.where { ChapterTable.id eq chapterId }
|
||||
@@ -230,6 +240,24 @@ object KoreaderSyncService {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCredentials(): Pair<String, String> {
|
||||
val username = preferences.getString(USERNAME_KEY, "")!!
|
||||
val userkey = preferences.getString(USERKEY_KEY, "")!!
|
||||
|
||||
return Pair(username, userkey)
|
||||
}
|
||||
|
||||
private fun setCredentials(
|
||||
username: String,
|
||||
userkey: String,
|
||||
) {
|
||||
preferences
|
||||
.edit()
|
||||
.putString(USERNAME_KEY, username)
|
||||
.putString(USERKEY_KEY, userkey)
|
||||
.apply()
|
||||
}
|
||||
|
||||
suspend fun connect(
|
||||
username: String,
|
||||
password: String,
|
||||
@@ -238,34 +266,30 @@ object KoreaderSyncService {
|
||||
val authResult = authorize(username, userkey)
|
||||
|
||||
if (authResult.success) {
|
||||
serverConfig.koreaderSyncUsername.value = username
|
||||
serverConfig.koreaderSyncUserkey.value = userkey
|
||||
return ConnectResult(true, "Login successful.", username)
|
||||
setCredentials(username, userkey)
|
||||
return ConnectResult("Login successful.", KoSyncStatusPayload(isLoggedIn = true, username = username))
|
||||
}
|
||||
|
||||
if (authResult.isUserNotFoundError) {
|
||||
logger.info { "[KOSYNC CONNECT] Authorization failed, attempting to register new user." }
|
||||
val registerResult = register(username, userkey)
|
||||
return if (registerResult.success) {
|
||||
serverConfig.koreaderSyncUsername.value = username
|
||||
serverConfig.koreaderSyncUserkey.value = userkey
|
||||
ConnectResult(true, "Registration successful.", username)
|
||||
setCredentials(username, userkey)
|
||||
ConnectResult("Registration successful.", KoSyncStatusPayload(isLoggedIn = true, username = username))
|
||||
} else {
|
||||
ConnectResult(false, registerResult.message ?: "Registration failed.", null)
|
||||
ConnectResult(registerResult.message ?: "Registration failed.", KoSyncStatusPayload(isLoggedIn = false, username = null))
|
||||
}
|
||||
}
|
||||
|
||||
return ConnectResult(false, authResult.message ?: "Authentication failed.", null)
|
||||
return ConnectResult(authResult.message ?: "Authentication failed.", KoSyncStatusPayload(isLoggedIn = false, username = null))
|
||||
}
|
||||
|
||||
suspend fun logout() {
|
||||
serverConfig.koreaderSyncUsername.value = ""
|
||||
serverConfig.koreaderSyncUserkey.value = ""
|
||||
setCredentials("", "")
|
||||
}
|
||||
|
||||
suspend fun getStatus(): KoSyncStatusPayload {
|
||||
val username = serverConfig.koreaderSyncUsername.value
|
||||
val userkey = serverConfig.koreaderSyncUserkey.value
|
||||
val (username, userkey) = getCredentials()
|
||||
if (username.isBlank() || userkey.isBlank()) {
|
||||
return KoSyncStatusPayload(isLoggedIn = false, username = null)
|
||||
}
|
||||
@@ -284,12 +308,9 @@ object KoreaderSyncService {
|
||||
return
|
||||
}
|
||||
|
||||
val username = serverConfig.koreaderSyncUsername.value
|
||||
val userkey = serverConfig.koreaderSyncUserkey.value
|
||||
val (username, userkey) = getCredentials()
|
||||
if (username.isBlank() || userkey.isBlank()) return
|
||||
|
||||
logger.info { "[KOSYNC PUSH] Init." }
|
||||
|
||||
val chapterHash = getOrGenerateChapterHash(chapterId)
|
||||
if (chapterHash.isNullOrBlank()) {
|
||||
logger.info { "[KOSYNC PUSH] Aborted for chapterId=$chapterId: No hash." }
|
||||
@@ -334,13 +355,11 @@ object KoreaderSyncService {
|
||||
addHeader("x-auth-key", userkey)
|
||||
}
|
||||
|
||||
logger.info { "[KOSYNC PUSH] PUT request to URL: ${request.url}" }
|
||||
logger.info { "[KOSYNC PUSH] Sending data: $requestBody" }
|
||||
logger.info { "[KOSYNC PUSH] url= ${request.url} - Sending data: $requestBody" }
|
||||
|
||||
network.client.newCall(request).await().use { response ->
|
||||
val responseBody = response.body.string()
|
||||
logger.info { "[KOSYNC PUSH] PUT response status: ${response.code}" }
|
||||
logger.info { "[KOSYNC PUSH] PUT response body: $responseBody" }
|
||||
logger.debug { "[KOSYNC PUSH] PUT response status: ${response.code}; response body: $responseBody" }
|
||||
if (!response.isSuccessful) {
|
||||
logger.warn { "[KOSYNC PUSH] Failed for chapterId=$chapterId: ${response.code}" }
|
||||
} else {
|
||||
@@ -363,13 +382,12 @@ object KoreaderSyncService {
|
||||
return null
|
||||
}
|
||||
|
||||
val username = serverConfig.koreaderSyncUsername.value
|
||||
val userkey = serverConfig.koreaderSyncUserkey.value
|
||||
val (username, userkey) = getCredentials()
|
||||
if (username.isBlank() || userkey.isBlank()) return null
|
||||
|
||||
val chapterHash = getOrGenerateChapterHash(chapterId)
|
||||
if (chapterHash.isNullOrBlank()) {
|
||||
logger.info { "[KOSYNC PULL] Aborted for chapterId=$chapterId: No hash." }
|
||||
logger.debug { "[KOSYNC PULL] Aborted for chapterId=$chapterId: No hash." }
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -380,14 +398,12 @@ object KoreaderSyncService {
|
||||
addHeader("x-auth-user", username)
|
||||
addHeader("x-auth-key", userkey)
|
||||
}
|
||||
logger.info { "[KOSYNC PULL] GET request to URL: ${request.url}" }
|
||||
|
||||
network.client.newCall(request).await().use { response ->
|
||||
logger.info { "[KOSYNC PULL] GET response status: ${response.code}" }
|
||||
logger.debug { "[KOSYNC PULL] GET response status: ${response.code}" }
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body.string()
|
||||
logger.info { "[KOSYNC PULL] GET response body: $body" }
|
||||
logger.debug { "[KOSYNC PULL] GET response body: $body" }
|
||||
if (body.isBlank() || body == "{}") return null
|
||||
|
||||
val progressResponse = json.decodeFromString(KoreaderProgressResponse.serializer(), body)
|
||||
|
||||
Reference in New Issue
Block a user