mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 11:24: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>
88 lines
3.5 KiB
Kotlin
88 lines
3.5 KiB
Kotlin
package suwayomi.tachidesk.graphql.mutations
|
|
|
|
import graphql.execution.DataFetcherResult
|
|
import graphql.schema.DataFetchingEnvironment
|
|
import kotlinx.coroutines.flow.first
|
|
import kotlinx.coroutines.withTimeout
|
|
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
|
import suwayomi.tachidesk.graphql.server.getAttribute
|
|
import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING
|
|
import suwayomi.tachidesk.graphql.types.UpdateState.ERROR
|
|
import suwayomi.tachidesk.graphql.types.UpdateState.IDLE
|
|
import suwayomi.tachidesk.graphql.types.WebUIFlavor
|
|
import suwayomi.tachidesk.graphql.types.WebUIUpdateStatus
|
|
import suwayomi.tachidesk.server.JavalinSetup.Attribute
|
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
|
import suwayomi.tachidesk.server.JavalinSetup.getAttribute
|
|
import suwayomi.tachidesk.server.user.requireUser
|
|
import suwayomi.tachidesk.server.util.WebInterfaceManager
|
|
import java.util.concurrent.CompletableFuture
|
|
import kotlin.time.Duration.Companion.seconds
|
|
|
|
class InfoMutation {
|
|
data class WebUIUpdateInput(
|
|
val clientMutationId: String? = null,
|
|
)
|
|
|
|
data class WebUIUpdatePayload(
|
|
val clientMutationId: String?,
|
|
val updateStatus: WebUIUpdateStatus,
|
|
)
|
|
|
|
fun updateWebUI(
|
|
dataFetchingEnvironment: DataFetchingEnvironment,
|
|
input: WebUIUpdateInput,
|
|
): CompletableFuture<DataFetcherResult<WebUIUpdatePayload?>> {
|
|
return future {
|
|
asDataFetcherResult {
|
|
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
|
withTimeout(30.seconds) {
|
|
if (WebInterfaceManager.status.value.state === DOWNLOADING) {
|
|
return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value)
|
|
}
|
|
|
|
val flavor = WebUIFlavor.current
|
|
|
|
val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor)
|
|
|
|
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(
|
|
input.clientMutationId,
|
|
updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING },
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun resetWebUIUpdateStatus(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<DataFetcherResult<WebUIUpdateStatus?>> =
|
|
future {
|
|
asDataFetcherResult {
|
|
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
|
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 }
|
|
}
|
|
}
|
|
}
|
|
}
|