Clean up code

Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
2026-07-16 22:08:37 +03:00
Unverified
parent 23f668c07d
commit c53befe4b9
@@ -3,11 +3,12 @@ package ru.fromchat.api.instance
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import ru.fromchat.api.ApiClient import ru.fromchat.api.ApiClient
import ru.fromchat.api.local.WebSocketManager import ru.fromchat.api.local.WebSocketManager
import ru.fromchat.api.local.db.store.InstanceRegistryStore
import ru.fromchat.config.ServerConfigData
import ru.fromchat.api.local.cache.CacheContext import ru.fromchat.api.local.cache.CacheContext
import ru.fromchat.api.local.db.store.InstanceRegistryStore
import ru.fromchat.config.ServerConfig import ru.fromchat.config.ServerConfig
import ru.fromchat.config.ServerConfigData
import ru.fromchat.legal.DocumentRepository import ru.fromchat.legal.DocumentRepository
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.TimeSource import kotlin.time.TimeSource
sealed interface ServerProbeResult { sealed interface ServerProbeResult {
@@ -51,20 +52,26 @@ fun Throwable.isSslProtocolError(): Boolean {
return false return false
} }
suspend fun probeCallsReachable(config: ServerConfigData): Boolean { suspend fun probeCallsReachable(config: ServerConfigData) = runCatching {
val urlScheme = if (config.httpsEnabled) "https" else "http" withTimeout(CALLS_PROBE_MS.milliseconds) {
val host = config.serverIp.trim() ApiClient.probeHttpGet(
val authorityHost = host.removePrefix("[").removeSuffix("]").ifEmpty { host } buildString {
// Caddy rewrites this to LiveKit "/" (plain "OK"). GET /rtc is always 404 without a WS upgrade. append(if (config.httpsEnabled) "https" else "http")
val url = "$urlScheme://$authorityHost:${config.apiPort}/livekit-health" append("://")
return runCatching { append(
withTimeout(CALLS_PROBE_MS) { config.serverIp.trim().let {
ApiClient.probeHttpGet(url) it.removePrefix("[").removeSuffix("]").ifEmpty { it }
}
)
append(":")
append(config.apiPort)
append("/livekit-health")
}
)
} }
}.getOrDefault(false) }.getOrDefault(false)
}
suspend fun probeServer(config: ServerConfigData): ServerProbeResult = suspend fun probeServer(config: ServerConfigData) =
probeServerEndpoint( probeServerEndpoint(
host = config.serverIp, host = config.serverIp,
port = config.apiPort, port = config.apiPort,
@@ -82,44 +89,51 @@ suspend fun probeServerEndpoint(
httpsPreferred: Boolean, httpsPreferred: Boolean,
schemeExplicit: Boolean, schemeExplicit: Boolean,
): Pair<ServerConfigData, ServerProbeResult> { ): Pair<ServerConfigData, ServerProbeResult> {
val schemes = when {
schemeExplicit -> listOf(httpsPreferred)
else -> listOf(true, false)
}
var lastConfig = ServerConfigData( var lastConfig = ServerConfigData(
serverIp = host, serverIp = host,
apiPort = port, apiPort = port,
callsPort = port, callsPort = port,
httpsEnabled = httpsPreferred, httpsEnabled = httpsPreferred,
) )
var lastResult: ServerProbeResult = ServerProbeResult.Unreachable var lastResult: ServerProbeResult = ServerProbeResult.Unreachable
for ((index, https) in schemes.withIndex()) {
when {
schemeExplicit -> listOf(httpsPreferred)
else -> listOf(true, false)
}.apply {
forEachIndexed { index, https ->
val config = ServerConfigData( val config = ServerConfigData(
serverIp = host, serverIp = host,
apiPort = port, apiPort = port,
callsPort = port, callsPort = port,
httpsEnabled = https, httpsEnabled = https,
) )
lastConfig = config lastConfig = config
val (result, failure) = probeServerOnce(config) val (result, failure) = probeServerOnce(config)
lastResult = result lastResult = result
when (result) { when (result) {
is ServerProbeResult.Supported, is ServerProbeResult.Supported,
ServerProbeResult.Unsupported, ServerProbeResult.Unsupported -> return config to result
-> return config to result
ServerProbeResult.Timeout, ServerProbeResult.Timeout,
ServerProbeResult.Unreachable, ServerProbeResult.Unreachable -> {
-> { if (!(!schemeExplicit && https && index < lastIndex)) return config to result
val hasHttpFallback = !schemeExplicit && https && index < schemes.lastIndex
if (!hasHttpFallback) return config to result
// Browser-like: only fall back to HTTP after a TLS/protocol failure. // Browser-like: only fall back to HTTP after a TLS/protocol failure.
if (failure?.isSslProtocolError() == true) continue if (failure?.isSslProtocolError() == true)
if (result is ServerProbeResult.Timeout) return config to result return@forEachIndexed
if (result is ServerProbeResult.Timeout)
return config to result
// Non-SSL Unreachable (e.g. LAN cleartext / connection refused on 443): try HTTP. // Non-SSL Unreachable (e.g. LAN cleartext / connection refused on 443): try HTTP.
continue return@forEachIndexed
} }
} }
} }
}
return lastConfig to lastResult return lastConfig to lastResult
} }
@@ -127,17 +141,21 @@ private suspend fun probeServerOnce(
config: ServerConfigData, config: ServerConfigData,
): Pair<ServerProbeResult, Throwable?> { ): Pair<ServerProbeResult, Throwable?> {
val apiBase = apiBaseUrlFor(config) val apiBase = apiBaseUrlFor(config)
val mark = TimeSource.Monotonic.markNow() val time = TimeSource.Monotonic.markNow()
InstanceIdGuard.probeConfig = config InstanceIdGuard.probeConfig = config
try { try {
val pingMs = time.elapsedNow().inWholeMilliseconds.toInt().coerceAtLeast(0)
return ServerProbeResult.Supported(
when (
val resolve = resolveInstanceId( val resolve = resolveInstanceId(
config = config, config = config,
apiBaseUrl = apiBase, apiBaseUrl = apiBase,
forceNetwork = true, forceNetwork = true,
allowCachedOnFailure = false, allowCachedOnFailure = false,
) )
val pingMs = mark.elapsedNow().inWholeMilliseconds.toInt().coerceAtLeast(0) ) {
val instanceId = when (resolve) {
is InstanceIdResolveResult.Cached -> resolve.instanceId is InstanceIdResolveResult.Cached -> resolve.instanceId
is InstanceIdResolveResult.Fetched -> resolve.instanceId is InstanceIdResolveResult.Fetched -> resolve.instanceId
is InstanceIdResolveResult.InstanceIdChanged -> resolve.newId is InstanceIdResolveResult.InstanceIdChanged -> resolve.newId
@@ -151,9 +169,10 @@ private suspend fun probeServerOnce(
}.exceptionOrNull() }.exceptionOrNull()
return ServerProbeResult.Unreachable to failure return ServerProbeResult.Unreachable to failure
} }
} },
val callsOk = probeCallsReachable(config) probeCallsReachable(config),
return ServerProbeResult.Supported(instanceId, callsOk, pingMs) to null pingMs
) to null
} finally { } finally {
InstanceIdGuard.probeConfig = null InstanceIdGuard.probeConfig = null
} }
@@ -182,13 +201,15 @@ suspend fun applyServerAndNavigate(
): ApplyServerResult { ): ApplyServerResult {
val apiBase = apiBaseUrlFor(config) val apiBase = apiBaseUrlFor(config)
val token = bearer.trim() val token = bearer.trim()
if (token.isEmpty()) { if (token.isEmpty()) {
applyServerConfig(config, probe.instanceId, probe.callsOk) applyServerConfig(config, probe.instanceId, probe.callsOk)
WebSocketManager.disconnect() WebSocketManager.disconnect()
onNavigateLogin() onNavigateLogin()
return ApplyServerResult.Applied return ApplyServerResult.Applied
} }
when (val auth = ApiClient.checkAuthAt(apiBase, token)) {
when (ApiClient.checkAuthAt(apiBase, token)) {
ApiClient.CheckAuthResult.Authenticated -> { ApiClient.CheckAuthResult.Authenticated -> {
applyServerConfig(config, probe.instanceId, probe.callsOk) applyServerConfig(config, probe.instanceId, probe.callsOk)
WebSocketManager.disconnect() WebSocketManager.disconnect()
@@ -196,9 +217,11 @@ suspend fun applyServerAndNavigate(
onNavigateChat() onNavigateChat()
return ApplyServerResult.Applied return ApplyServerResult.Applied
} }
ApiClient.CheckAuthResult.Unreachable -> { ApiClient.CheckAuthResult.Unreachable -> {
return ApplyServerResult.ServerUnreachable return ApplyServerResult.ServerUnreachable
} }
ApiClient.CheckAuthResult.NotAuthenticated -> { ApiClient.CheckAuthResult.NotAuthenticated -> {
onLogoutOldHost() onLogoutOldHost()
applyServerConfig(config, probe.instanceId, probe.callsOk) applyServerConfig(config, probe.instanceId, probe.callsOk)