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 f28a8068..f9c3b454 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt @@ -4,11 +4,9 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING import suwayomi.tachidesk.graphql.types.UpdateState.ERROR -import suwayomi.tachidesk.graphql.types.UpdateState.STOPPED -import suwayomi.tachidesk.graphql.types.WebUIUpdateInfo +import suwayomi.tachidesk.graphql.types.UpdateState.IDLE import suwayomi.tachidesk.graphql.types.WebUIUpdateStatus import suwayomi.tachidesk.server.JavalinSetup.future -import suwayomi.tachidesk.server.serverConfig import suwayomi.tachidesk.server.util.WebInterfaceManager import java.util.concurrent.CompletableFuture import kotlin.time.Duration.Companion.seconds @@ -37,16 +35,7 @@ class InfoMutation { return@withTimeout WebUIUpdatePayload( input.clientMutationId, - WebUIUpdateStatus( - info = - WebUIUpdateInfo( - channel = serverConfig.webUIChannel.value, - tag = version, - updateAvailable, - ), - state = if (didUpdateCheckFail) ERROR else STOPPED, - progress = 0, - ), + WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE), ) } try { @@ -62,4 +51,19 @@ class InfoMutation { } } } + + fun resetWebUIUpdateStatus(): CompletableFuture { + return future { + 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/types/WebUIUpdateType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/WebUIUpdateType.kt index 56d01820..766a3170 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/WebUIUpdateType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/WebUIUpdateType.kt @@ -14,11 +14,10 @@ data class WebUIUpdateCheck( data class WebUIUpdateInfo( val channel: String, val tag: String, - val updateAvailable: Boolean, ) enum class UpdateState { - STOPPED, + IDLE, DOWNLOADING, FINISHED, ERROR, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt index 8abd9710..c00dbe74 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt @@ -41,7 +41,7 @@ import suwayomi.tachidesk.graphql.types.UpdateState import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING import suwayomi.tachidesk.graphql.types.UpdateState.ERROR import suwayomi.tachidesk.graphql.types.UpdateState.FINISHED -import suwayomi.tachidesk.graphql.types.UpdateState.STOPPED +import suwayomi.tachidesk.graphql.types.UpdateState.IDLE import suwayomi.tachidesk.graphql.types.WebUIUpdateInfo import suwayomi.tachidesk.graphql.types.WebUIUpdateStatus import suwayomi.tachidesk.server.ApplicationDirs @@ -137,25 +137,22 @@ object WebInterfaceManager { private val notifyFlow = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST) - @OptIn(FlowPreview::class) + private val statusFlow = MutableSharedFlow() val status = - notifyFlow.sample(1.seconds) - .stateIn( - scope, - SharingStarted.Eagerly, - WebUIUpdateStatus( - info = - WebUIUpdateInfo( - channel = serverConfig.webUIChannel.value, - tag = "", - updateAvailable = false, - ), - state = STOPPED, - progress = 0, - ), - ) + statusFlow.stateIn( + scope, + SharingStarted.Eagerly, + getStatus(), + ) init { + scope.launch { + @OptIn(FlowPreview::class) + notifyFlow.sample(1.seconds).collect { + statusFlow.emit(it) + } + } + serverConfig.subscribeTo( combine(serverConfig.webUIUpdateCheckInterval, serverConfig.webUIFlavor) { interval, flavor -> Pair( @@ -168,7 +165,7 @@ object WebInterfaceManager { ) } - suspend fun getAboutInfo(): AboutWebUI { + fun getAboutInfo(): AboutWebUI { val currentVersion = getLocalVersion() val failedToGetVersion = currentVersion === "r-1" @@ -182,6 +179,26 @@ object WebInterfaceManager { ) } + fun getStatus( + version: String = "", + state: UpdateState = IDLE, + progress: Int = 0, + ): WebUIUpdateStatus { + return WebUIUpdateStatus( + info = + WebUIUpdateInfo( + channel = serverConfig.webUIChannel.value, + tag = version, + ), + state, + progress, + ) + } + + fun resetStatus() { + emitStatus("", IDLE, 0, immediate = true) + } + private var serveWebUI: () -> Unit = {} fun setServeWebUI(serveWebUI: () -> Unit) { @@ -535,20 +552,17 @@ object WebInterfaceManager { version: String, state: UpdateState, progress: Int, + immediate: Boolean = false, ) { scope.launch { - notifyFlow.emit( - WebUIUpdateStatus( - info = - WebUIUpdateInfo( - channel = serverConfig.webUIChannel.value, - tag = version, - updateAvailable = true, - ), - state, - progress, - ), - ) + val status = getStatus(version, state, progress) + + if (immediate) { + statusFlow.emit(status) + return@launch + } + + notifyFlow.emit(status) } } @@ -559,7 +573,7 @@ object WebInterfaceManager { } suspend fun downloadVersion(version: String) { - emitStatus(version, DOWNLOADING, 0) + emitStatus(version, DOWNLOADING, 0, immediate = true) try { val webUIZip = "${WebUIFlavor.WEBUI.baseFileName}-$version.zip" @@ -586,11 +600,11 @@ object WebInterfaceManager { extractDownload(webUIZipPath, applicationDirs.webUIRoot) log.info { "Extracting WebUI zip Done." } - emitStatus(version, FINISHED, 100) + emitStatus(version, FINISHED, 100, immediate = true) serveWebUI() } catch (e: Exception) { - emitStatus(version, ERROR, 0) + emitStatus(version, ERROR, 0, immediate = true) throw e } }