mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 19:34:35 -05:00
* Basic JWT implementation * Move JWT to UI_LOGIN mode and bring back SIMPLE_LOGIN as before * Update server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Refresh: Update only access token Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Implement JWT Audience * Store JWT key Generates the key on startup if not set * Handle invalid Base64 * Make JWT expiry configurable * Missing value parse * Update server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Simplify Duration parsing * JWT Protect Mutations * JWT Protect Queries and Subscriptions * JWT Protect v1 WebSockets * WebSockets allow sending token via protocol header * Also respect the `suwayomi-server-token` cookie * JWT reduce default token expiry * JWT Support cookie on WebSocket as well * Lint * Authenticate graphql subscription via connection_init payload * WebView: Prefer explicit token over cookie This hack was implemented because WebView sent `"null"` if no token was supplied, just don't send a bad token, then we can do this properly * WebView: Implement basic login dialog if no token supplied --------- Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> Co-authored-by: schroda <50052685+schroda@users.noreply.github.com>
86 lines
3.9 KiB
Kotlin
86 lines
3.9 KiB
Kotlin
/*
|
|
* 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/. */
|
|
|
|
package suwayomi.tachidesk.graphql.subscriptions
|
|
|
|
import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated
|
|
import com.expediagroup.graphql.generator.annotations.GraphQLDescription
|
|
import graphql.schema.DataFetchingEnvironment
|
|
import kotlinx.coroutines.flow.Flow
|
|
import kotlinx.coroutines.flow.map
|
|
import suwayomi.tachidesk.graphql.server.getAttribute
|
|
import suwayomi.tachidesk.graphql.types.UpdateStatus
|
|
import suwayomi.tachidesk.graphql.types.UpdaterUpdates
|
|
import suwayomi.tachidesk.manga.impl.update.IUpdater
|
|
import suwayomi.tachidesk.manga.impl.update.UpdateUpdates
|
|
import suwayomi.tachidesk.server.JavalinSetup.Attribute
|
|
import suwayomi.tachidesk.server.JavalinSetup.getAttribute
|
|
import suwayomi.tachidesk.server.user.requireUser
|
|
import uy.kohesive.injekt.injectLazy
|
|
|
|
class UpdateSubscription {
|
|
private val updater: IUpdater by injectLazy()
|
|
|
|
@GraphQLDeprecated("Replaced with updates", ReplaceWith("updates(input)"))
|
|
fun updateStatusChanged(dataFetchingEnvironment: DataFetchingEnvironment): Flow<UpdateStatus> {
|
|
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
|
return updater.status.map { updateStatus ->
|
|
UpdateStatus(updateStatus)
|
|
}
|
|
}
|
|
|
|
data class LibraryUpdateStatusChangedInput(
|
|
@GraphQLDescription(
|
|
"Sets a max number of updates that can be contained in a updater update message." +
|
|
"Everything above this limit will be omitted and the \"updateStatus\" should be re-fetched via the " +
|
|
"corresponding query. Due to the graphql subscription execution strategy not supporting batching for data loaders, " +
|
|
"the data loaders run into the n+1 problem, which can cause the server to get unresponsive until the status " +
|
|
"update has been handled. This is an issue e.g. when starting an update.",
|
|
)
|
|
val maxUpdates: Int?,
|
|
)
|
|
|
|
fun libraryUpdateStatusChanged(
|
|
dataFetchingEnvironment: DataFetchingEnvironment,
|
|
input: LibraryUpdateStatusChangedInput,
|
|
): Flow<UpdaterUpdates> {
|
|
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
|
val omitUpdates = input.maxUpdates != null
|
|
val maxUpdates = input.maxUpdates ?: 50
|
|
|
|
return updater.updates.map { updates ->
|
|
val categoryUpdatesCount = updates.categoryUpdates.size
|
|
val mangaUpdatesCount = updates.mangaUpdates.size
|
|
val totalUpdatesCount = categoryUpdatesCount + mangaUpdatesCount
|
|
|
|
val needToOmitUpdates = omitUpdates && totalUpdatesCount > maxUpdates
|
|
if (!needToOmitUpdates) {
|
|
return@map UpdaterUpdates(updates, omittedUpdates = false)
|
|
}
|
|
|
|
val maxUpdatesAfterCategoryUpdates = (maxUpdates - categoryUpdatesCount).coerceAtLeast(0)
|
|
|
|
// the graphql subscription execution strategy does not support data loader batching which causes the n+1 problem,
|
|
// thus, too many updates (e.g. on mass enqueue or dequeue) causes unresponsiveness of the server until the
|
|
// update has been handled
|
|
UpdaterUpdates(
|
|
UpdateUpdates(
|
|
updates.isRunning,
|
|
updates.categoryUpdates.take(maxUpdates),
|
|
updates.mangaUpdates.take(maxUpdatesAfterCategoryUpdates),
|
|
updates.totalJobs,
|
|
updates.finishedJobs,
|
|
updates.skippedCategoriesCount,
|
|
updates.skippedMangasCount,
|
|
updates.initial,
|
|
),
|
|
omittedUpdates = true,
|
|
)
|
|
}
|
|
}
|
|
}
|