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 }
} }
}.getOrDefault(false) )
} append(":")
append(config.apiPort)
append("/livekit-health")
}
)
}
}.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()) {
val config = ServerConfigData( when {
serverIp = host, schemeExplicit -> listOf(httpsPreferred)
apiPort = port, else -> listOf(true, false)
callsPort = port, }.apply {
httpsEnabled = https, forEachIndexed { index, https ->
) val config = ServerConfigData(
lastConfig = config serverIp = host,
val (result, failure) = probeServerOnce(config) apiPort = port,
lastResult = result callsPort = port,
when (result) { httpsEnabled = https,
is ServerProbeResult.Supported, )
ServerProbeResult.Unsupported,
-> return config to result lastConfig = config
ServerProbeResult.Timeout, val (result, failure) = probeServerOnce(config)
ServerProbeResult.Unreachable, lastResult = result
-> {
val hasHttpFallback = !schemeExplicit && https && index < schemes.lastIndex when (result) {
if (!hasHttpFallback) return config to result is ServerProbeResult.Supported,
// Browser-like: only fall back to HTTP after a TLS/protocol failure. ServerProbeResult.Unsupported -> return config to result
if (failure?.isSslProtocolError() == true) continue ServerProbeResult.Timeout,
if (result is ServerProbeResult.Timeout) return config to result ServerProbeResult.Unreachable -> {
// Non-SSL Unreachable (e.g. LAN cleartext / connection refused on 443): try HTTP. if (!(!schemeExplicit && https && index < lastIndex)) return config to result
continue
// Browser-like: only fall back to HTTP after a TLS/protocol failure.
if (failure?.isSslProtocolError() == true)
return@forEachIndexed
if (result is ServerProbeResult.Timeout)
return config to result
// Non-SSL Unreachable (e.g. LAN cleartext / connection refused on 443): try HTTP.
return@forEachIndexed
}
} }
} }
} }
return lastConfig to lastResult return lastConfig to lastResult
} }
@@ -127,33 +141,38 @@ 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 resolve = resolveInstanceId( val pingMs = time.elapsedNow().inWholeMilliseconds.toInt().coerceAtLeast(0)
config = config,
apiBaseUrl = apiBase, return ServerProbeResult.Supported(
forceNetwork = true, when (
allowCachedOnFailure = false, val resolve = resolveInstanceId(
) config = config,
val pingMs = mark.elapsedNow().inWholeMilliseconds.toInt().coerceAtLeast(0) apiBaseUrl = apiBase,
val instanceId = when (resolve) { forceNetwork = true,
is InstanceIdResolveResult.Cached -> resolve.instanceId allowCachedOnFailure = false,
is InstanceIdResolveResult.Fetched -> resolve.instanceId )
is InstanceIdResolveResult.InstanceIdChanged -> resolve.newId ) {
InstanceIdResolveResult.Unsupported -> is InstanceIdResolveResult.Cached -> resolve.instanceId
return ServerProbeResult.Unsupported to null is InstanceIdResolveResult.Fetched -> resolve.instanceId
InstanceIdResolveResult.Timeout -> is InstanceIdResolveResult.InstanceIdChanged -> resolve.newId
return ServerProbeResult.Timeout to null InstanceIdResolveResult.Unsupported ->
InstanceIdResolveResult.Unreachable -> { return ServerProbeResult.Unsupported to null
val failure = runCatching { InstanceIdResolveResult.Timeout ->
ApiClient.fetchServerInstanceId(apiBase) return ServerProbeResult.Timeout to null
}.exceptionOrNull() InstanceIdResolveResult.Unreachable -> {
return ServerProbeResult.Unreachable to failure val failure = runCatching {
} ApiClient.fetchServerInstanceId(apiBase)
} }.exceptionOrNull()
val callsOk = probeCallsReachable(config) return ServerProbeResult.Unreachable to failure
return ServerProbeResult.Supported(instanceId, callsOk, pingMs) to null }
},
probeCallsReachable(config),
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)