Skip to content

Commit ff336b2

Browse files
committed
refactor: proxy validation and add invalid server cleanup
1 parent c937574 commit ff336b2

33 files changed

Lines changed: 740 additions & 222 deletions

app/src/main/kotlin/engine/proxy/latency/AndroidProxyLatencyTester.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ internal class AndroidProxyLatencyTester(
6969

7070
private fun realConnectionLatency(appState: AppState, server: ProxyServerState): Long {
7171
return runCatching {
72-
server.server.check()
7372
val resourceFilePaths = appContext.prepareXrayResourceFilePaths()
7473
appContext.initializeAndroidXrayCoreEnvironment(resourceFilePaths.dataDir)
7574
val configJson = XraySpeedTestConfigFactory.buildXraySpeedTestConfig(

app/src/main/kotlin/engine/xray/CustomXrayConfigRewriter.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ internal object CustomXrayConfigRewriter {
1919
request: XrayConfigRequest,
2020
server: Custom,
2121
): JsonObject {
22-
server.check()
2322
val config = parseCustomXrayConfigJsonObject(server.configJson)
2423
return if (server.overrideAsteriskInboundAndDns) {
2524
config.overwriteAsteriskInboundDns(request, server)

app/src/main/kotlin/engine/xray/XrayOutboundPlanner.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ private class XrayOutboundPlanner(
6969
}
7070

7171
private fun addCustomDnsHosts(proxyServer: Custom) {
72-
proxyServer.check()
7372
if (proxyServer.overrideAsteriskInboundAndDns) {
7473
dnsHostServers += customXrayConfigProxyServerHosts(proxyServer.configJson)
7574
}
@@ -82,7 +81,6 @@ private class XrayOutboundPlanner(
8281
allowFragment: Boolean = true,
8382
) {
8483
if (tag in addedOutboundTags) return
85-
server.server.check()
8684
proxyOutbounds += XrayProxyOutboundServer(
8785
tag = tag,
8886
server = server.server,
@@ -95,7 +93,6 @@ private class XrayOutboundPlanner(
9593
}
9694

9795
private fun addStrategyGroup(tag: String, strategyGroup: StrategyGroup) {
98-
strategyGroup.check()
9996
val members = appState.strategyGroupMembers(strategyGroup)
10097
if (members.isEmpty()) {
10198
error("Strategy group '${strategyGroup.remarks}' has no available proxy servers")
@@ -120,7 +117,6 @@ private class XrayOutboundPlanner(
120117
}
121118

122119
private fun addChainProxy(tag: String, chainProxy: ChainProxy) {
123-
chainProxy.check()
124120
val members = appState.chainProxyMembers(chainProxy)
125121
if (members.size < 2) {
126122
error("Proxy chain '${chainProxy.remarks}' requires at least two available proxy servers")
@@ -165,7 +161,6 @@ private fun AppState.strategyGroupMembers(strategyGroup: StrategyGroup): List<Pr
165161
regex?.containsMatchIn(server.server.getInfo().remarks) == true ||
166162
(regex == null && server.server.getInfo().remarks.contains(filter))
167163
}
168-
.filter { server -> runCatching { server.server.check() }.isSuccess }
169164
.toList()
170165
}
171166

app/src/main/kotlin/features/proxy/server/editor/ProxyServerPage.kt

Lines changed: 233 additions & 30 deletions
Large diffs are not rendered by default.

app/src/main/kotlin/features/proxy/server/list/ProxyServerListComponents.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ private fun proxyServerListToolMenuEntries() = listOf(
450450
ProxyServerListToolMenuEntry(stringResource(R.string.proxy_server_list_update_subscriptions), ProxyServerListToolAction.UpdateSubscriptions),
451451
ProxyServerListToolMenuEntry(stringResource(R.string.proxy_server_list_copy_all_urls), ProxyServerListToolAction.CopyAllUrls),
452452
ProxyServerListToolMenuEntry(stringResource(R.string.proxy_server_list_delete_duplicates), ProxyServerListToolAction.DeleteDuplicateServers),
453+
ProxyServerListToolMenuEntry(stringResource(R.string.proxy_server_list_delete_invalid), ProxyServerListToolAction.DeleteInvalidServers),
453454
).map { entry ->
454455
IconDropdownMenuEntry(
455456
key = entry.action,

app/src/main/kotlin/features/proxy/server/list/ProxyServerListMessages.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ internal data class ProxyServerListMessages(
2828
val importResultTemplate: String,
2929
val copied: String,
3030
val unsupported: String,
31+
val configInvalid: String,
3132
val duplicatesDeletedTemplate: String,
3233
val noDuplicates: String,
34+
val invalidServersDeletedTemplate: String,
35+
val noInvalidServers: String,
3336
)
3437

3538
@Composable
@@ -56,7 +59,10 @@ internal fun proxyServerListMessages(): ProxyServerListMessages {
5659
importResultTemplate = stringResource(R.string.proxy_server_list_import_result),
5760
copied = stringResource(R.string.common_copied),
5861
unsupported = stringResource(R.string.common_unsupported),
62+
configInvalid = stringResource(R.string.proxy_server_config_invalid),
5963
duplicatesDeletedTemplate = stringResource(R.string.proxy_server_list_duplicates_deleted),
6064
noDuplicates = stringResource(R.string.proxy_server_list_no_duplicates),
65+
invalidServersDeletedTemplate = stringResource(R.string.proxy_server_list_invalid_deleted),
66+
noInvalidServers = stringResource(R.string.proxy_server_list_no_invalid),
6167
)
6268
}

app/src/main/kotlin/features/proxy/server/list/ProxyServerListModels.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ internal enum class ProxyServerListToolAction {
2828
UpdateSubscriptions,
2929
CopyAllUrls,
3030
DeleteDuplicateServers,
31+
DeleteInvalidServers,
3132
}
3233

3334
internal data class ProxyServerListMenuEntry(

app/src/main/kotlin/features/proxy/server/list/ProxyServerListPager.kt

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ import app.collectProxyServerLatency
3030
import app.navigation.Navigator
3131
import app.navigation.Route
3232
import data.AndroidAppStateStore
33-
import features.proxy.server.usecase.proxyServerCopyTextOrNull
33+
import features.proxy.server.validation.rememberProxyServerValidationMessageResolver
34+
import features.proxy.server.usecase.ProxyServerCopyTextResult
35+
import features.proxy.server.usecase.proxyServerCopyText
3436
import kotlinx.coroutines.CoroutineScope
3537
import kotlinx.coroutines.launch
3638
import sh.calvin.reorderable.ReorderableItem
@@ -210,6 +212,7 @@ private fun ProxyServerListItem(
210212
serverId = server.id,
211213
initialLatency = server.latency,
212214
).value
215+
val validationMessageOf = rememberProxyServerValidationMessageResolver()
213216

214217
ProxyServerListItemCard(
215218
latency = latency,
@@ -235,15 +238,29 @@ private fun ProxyServerListItem(
235238
},
236239
onShare = {
237240
scope.launch {
238-
val copyText = server.proxyServerCopyTextOrNull(
239-
context = context,
240-
appState = stateStore.state.value,
241-
)
242-
if (copyText == null) {
243-
tipNotifier.show(messages.unsupported)
244-
} else {
245-
clipboard.setPlainText(copyText)
246-
tipNotifier.show(messages.copied)
241+
val basicIssues = server.server.validateBasic()
242+
if (basicIssues.isNotEmpty()) {
243+
tipNotifier.show(validationMessageOf(basicIssues.first()))
244+
return@launch
245+
}
246+
when (
247+
val result = server.proxyServerCopyText(
248+
context = context,
249+
appState = stateStore.state.value,
250+
)
251+
) {
252+
is ProxyServerCopyTextResult.Success -> {
253+
clipboard.setPlainText(result.text)
254+
tipNotifier.show(messages.copied)
255+
}
256+
257+
ProxyServerCopyTextResult.Unsupported -> {
258+
tipNotifier.show(messages.unsupported)
259+
}
260+
261+
ProxyServerCopyTextResult.InvalidConfig -> {
262+
tipNotifier.show(messages.configInvalid)
263+
}
247264
}
248265
}
249266
},

app/src/main/kotlin/features/proxy/server/list/ProxyServerListTopBar.kt

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ import features.proxy.server.usecase.ProxyServerImportFileUseCase
2424
import features.proxy.server.usecase.ProxyServerImportSource
2525
import features.proxy.server.usecase.createProxyServer
2626
import features.proxy.server.usecase.deleteDuplicateServersInGroup
27+
import features.proxy.server.usecase.deleteInvalidServersInGroup
2728
import features.proxy.server.usecase.importProxyServersFromText
2829
import features.proxy.server.usecase.sortedInGroupByLatencyResult
2930
import features.proxy.server.usecase.updatableSubscriptionGroups
31+
import features.proxy.server.usecase.withDeletedProxyServers
3032
import features.proxy.server.usecase.withImportedProxyServers
3133
import features.proxy.server.usecase.withUpdatedSubscriptionServers
3234
import features.subscription.DefaultSubscriptionGroupId
@@ -430,6 +432,20 @@ private fun handleProxyServerListToolAction(
430432
messages = messages,
431433
)
432434
}
435+
436+
ProxyServerListToolAction.DeleteInvalidServers -> {
437+
deleteInvalidServers(
438+
servers = groupState.currentGroupServers,
439+
stateStore = stateStore,
440+
updateAppState = updateAppState,
441+
proxyServiceUseCase = proxyServiceUseCase,
442+
tipNotifier = tipNotifier,
443+
scope = scope,
444+
messages = messages,
445+
serviceOperationInProgress = serviceOperationInProgress,
446+
runProxyServiceOperation = runProxyServiceOperation,
447+
)
448+
}
433449
}
434450
}
435451

@@ -540,18 +556,86 @@ private fun copyCurrentGroupUrls(
540556
messages: ProxyServerListMessages,
541557
) {
542558
scope.launch {
543-
val urls = servers
544-
.mapNotNull { server -> runCatching { server.server.getUrlOrNull() }.getOrNull() }
559+
var invalidCount = 0
560+
val urls = servers.mapNotNull { server ->
561+
if (server.server.validateBasic().isNotEmpty()) {
562+
invalidCount++
563+
return@mapNotNull null
564+
}
565+
runCatching { server.server.getUrlOrNull() }.fold(
566+
onSuccess = { url -> url },
567+
onFailure = {
568+
invalidCount++
569+
null
570+
},
571+
)
572+
}
545573
.joinToString("\n")
546574
if (urls.isBlank()) {
547-
tipNotifier.show(messages.unsupported)
575+
tipNotifier.show(if (invalidCount > 0) messages.configInvalid else messages.unsupported)
548576
} else {
549577
clipboard.setPlainText(urls)
550578
tipNotifier.show(messages.copied)
551579
}
552580
}
553581
}
554582

583+
private fun deleteInvalidServers(
584+
servers: List<ProxyServerState>,
585+
stateStore: AndroidAppStateStore,
586+
updateAppState: ((AppState) -> AppState) -> Unit,
587+
proxyServiceUseCase: ProxyServiceUseCase,
588+
tipNotifier: AndroidToastTipNotifier,
589+
scope: CoroutineScope,
590+
messages: ProxyServerListMessages,
591+
serviceOperationInProgress: Boolean,
592+
runProxyServiceOperation: (suspend () -> Unit) -> Unit,
593+
) {
594+
val currentGroupServerIds = servers.map { server -> server.id }.toSet()
595+
val stateSnapshot = stateStore.state.value
596+
val previewResult = stateSnapshot.proxyServers.deleteInvalidServersInGroup(currentGroupServerIds)
597+
if (previewResult.removedCount == 0) {
598+
scope.launch { tipNotifier.show(messages.noInvalidServers) }
599+
return
600+
}
601+
602+
fun applyDeleteAndNotify() {
603+
var removedCount = 0
604+
updateAppState { state ->
605+
val result = state.proxyServers.deleteInvalidServersInGroup(currentGroupServerIds)
606+
removedCount = result.removedCount
607+
state.withDeletedProxyServers(result.removedServerIds)
608+
}
609+
scope.launch {
610+
tipNotifier.show(
611+
if (removedCount > 0) {
612+
messages.invalidServersDeletedTemplate.formatTemplate("count" to removedCount)
613+
} else {
614+
messages.noInvalidServers
615+
},
616+
)
617+
}
618+
}
619+
620+
val selectedServerWillBeDeleted = stateSnapshot.selectedProxyServerId in previewResult.removedServerIds
621+
if (!stateSnapshot.proxyRunning || !selectedServerWillBeDeleted) {
622+
applyDeleteAndNotify()
623+
return
624+
}
625+
if (serviceOperationInProgress) return
626+
627+
runProxyServiceOperation {
628+
when (val stopResult = proxyServiceUseCase.stop(stateStore.state.value.runMode)) {
629+
is ProxyServiceResult.Success -> applyDeleteAndNotify()
630+
ProxyServiceResult.MissingServer -> applyDeleteAndNotify()
631+
is ProxyServiceResult.Failed -> {
632+
updateAppState { state -> state.copy(proxyRunning = false) }
633+
tipNotifier.showError(stopResult.error, messages.serviceStopped)
634+
}
635+
}
636+
}
637+
}
638+
555639
private fun deleteDuplicateServers(
556640
servers: List<ProxyServerState>,
557641
updateAppState: ((AppState) -> AppState) -> Unit,

app/src/main/kotlin/features/proxy/server/model/ChainProxy.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ data class ChainProxy(
2626
proxyServerIds = other.proxyServerIds
2727
}
2828

29-
override fun check() {
29+
override fun validateBasic(): List<ProxyServerValidationIssue> = validateFull()
30+
31+
override fun validateFull(): List<ProxyServerValidationIssue> = buildList {
3032
validateRemarks(remarks)
3133
if (proxyServerIds.size < 2) {
32-
proxyValidationError(ProxyServerValidationError.ChainProxyMemberCountInvalid)
34+
addIssue(ProxyServerValidationError.ChainProxyMemberCountInvalid)
3335
}
3436
}
3537
}

0 commit comments

Comments
 (0)