add some stuff to backend base

This commit is contained in:
2026-09-13 16:32:32 +03:00
parent 688d687d4d
commit 1c573c4366
27 changed files with 440 additions and 313 deletions
@@ -0,0 +1,85 @@
package ru.shadowsparky.http.data
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.retryWhen
import kotlinx.serialization.json.Json
import org.koin.core.annotation.Single
import ru.shadowsparky.domain.Log
import ru.shadowsparky.http.domain.AuthRequest
import ru.shadowsparky.http.domain.AuthResponse
import kotlin.math.pow
import kotlin.time.Duration.Companion.milliseconds
@Single
class RemoveEventProcessor(
private val json: Json,
private val log: Log
) {
fun process(
token: String,
session: DefaultClientWebSocketSession,
onTokenInvalid: suspend () -> Unit
): Flow<String> {
val incoming = session.incoming
val outgoing = session.outgoing
return flow {
authV2(token, incoming, outgoing, onTokenInvalid)
for (frame in incoming) {
if (frame is Frame.Text) {
val text = frame.readText()
try {
log.d(TAG, "receive $text")
emit(text)
} catch (e: Exception) {
log.e(TAG, e, "failed to decode message: $text")
}
}
}
}
}
private suspend fun authV2(
token: String,
input: ReceiveChannel<Frame>,
output: SendChannel<Frame>,
onTokenInvalid: suspend () -> Unit
) {
output.send(Frame.Text(json.encodeToString(AuthRequest(token))))
val rawFrame = (input.receive() as Frame.Text).readText()
val response = json.decodeFromString<AuthResponse>(rawFrame)
if (!response.ok) {
onTokenInvalid()
error("Authentication failed")
}
}
private companion object {
const val TAG = "RemoveEventProcessor"
}
}
fun <T> Flow<T>.retryExponential(
maxRetries: Int = Int.MAX_VALUE,
initialDelay: Long = 5000L,
maxDelay: Long = 300_000L,
factor: Double = 2.0,
shouldRetry: (Throwable) -> Boolean = { true }
): Flow<T> = retryWhen { cause, attempt ->
if (!shouldRetry(cause) || attempt >= maxRetries || cause is CancellationException) {
false
} else {
val delayTime = (initialDelay * factor.pow(attempt.toDouble()))
.toLong()
.coerceAtMost(maxDelay)
delay(delayTime.milliseconds)
true
}
}
@@ -0,0 +1,12 @@
package ru.shadowsparky.http.domain
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("AuthRequest")
data class AuthRequest(val token: String)
@Serializable
@SerialName("AuthResponse")
data class AuthResponse(val ok: Boolean, val error: String? = null)