按审查结论重构:先补测试网,再搬代码
顺序是刻意的。前端一行测试都没有,直接挪代码挪坏了 bun test 不会红,所以先把纯逻辑 抽到能被测试的位置并补上用例,最后才动组件。测试从 37 条增加到 77 条。 抽出来的四块: - shared/outbound-chain.ts 「这个端口现在走哪条线」的链路解析,带防环,9 条测试 - web/delay-test.ts 延迟测试失败时说什么,6 个分支原本一条测试都没有 - web/runtime.ts 运行时状态的模块级单例,照 prefs.ts 已有的模式 - web/components/InboundList.vue 入站列表 出站的文本原本散成四份(读一份、显示一份、写回一份、表单又抄一份),加一种出站模式 要同时改四处,漏一个就是显示与写回不一致。合并成 OUTBOUND_PRESETS 一张表。 给认证和透传白名单补了 20 条测试。两者都是安全边界、都是纯函数、都不需要内核在线, 之前却只靠需要活内核的冒烟脚本间接覆盖。其中「形似但不是健康检查的路径」几条确认了 正则没写松。 另外:配置快照不再把整份文件原文下发(前端从不读,白白让响应体积翻倍); yaml-cosmetics 的测试改走生产路径,之前它自己拼了一遍三步流程,落盘链路漏掉其中一步 测试照样会绿。
This commit is contained in:
@@ -8,8 +8,9 @@
|
||||
*/
|
||||
import { computed } from 'vue'
|
||||
import type { Inbound, OutboundMode } from '@shared/inbounds.ts'
|
||||
import { lastSwitchableGroup, resolveChain } from '@shared/outbound-chain.ts'
|
||||
import { latestDelay } from '../api'
|
||||
import type { ProxyInfo } from '../api'
|
||||
import { kernel, providerLines, runtime } from '../runtime'
|
||||
import OutboundCard from '../components/OutboundCard.vue'
|
||||
import IconOffline from '~icons/ri/cloud-off-line'
|
||||
import IconEdit from '~icons/ri/edit-line'
|
||||
@@ -19,11 +20,8 @@ const props = defineProps<{
|
||||
inbound: Inbound
|
||||
groups: string[]
|
||||
ownProxies: string[]
|
||||
providerLines: string[]
|
||||
runtime: Record<string, ProxyInfo>
|
||||
/** 流量最终交给哪个出站。走规则的入站要查到规则表末尾的 MATCH */
|
||||
destination: string | null
|
||||
kernelOnline: boolean
|
||||
/** 正在提交的那个出站的名字。只有它显示忙碌,其余照常能点 */
|
||||
pendingOutbound: string | null
|
||||
/** 正在测延迟的线路 */
|
||||
@@ -42,39 +40,16 @@ const emit = defineEmits<{
|
||||
const locked = computed(() => props.inbound.isDefault)
|
||||
|
||||
function describe(name: string): string {
|
||||
const info = props.runtime[name]
|
||||
const info = runtime.value[name]
|
||||
if (!info) return ''
|
||||
return info.now ? `${info.type}(${info.now})` : info.type
|
||||
}
|
||||
|
||||
const delayOf = (name: string) => latestDelay(props.runtime[name])
|
||||
const delayOf = (name: string) => latestDelay(runtime.value[name])
|
||||
const isTesting = (name: string) => props.testing.includes(name)
|
||||
|
||||
/**
|
||||
* 从落脚的出站一路跟着「当前选中」往下走,直到走到一条真正的线路。
|
||||
*
|
||||
* 策略组可以选中另一个策略组,所以「这个端口现在走哪条线」往往要跳好几层。
|
||||
* 只显示第一层等于没回答问题。
|
||||
*/
|
||||
const chain = computed(() => {
|
||||
const hops: string[] = []
|
||||
let cursor = props.destination
|
||||
while (cursor && !hops.includes(cursor)) {
|
||||
hops.push(cursor)
|
||||
cursor = props.runtime[cursor]?.now ?? null
|
||||
}
|
||||
return hops
|
||||
})
|
||||
|
||||
/** 链条上最后一个还是策略组的环节——用户能在这里换选中。 */
|
||||
const switchable = computed(() => {
|
||||
for (let i = chain.value.length - 1; i >= 0; i -= 1) {
|
||||
const name = chain.value[i]!
|
||||
const info = props.runtime[name]
|
||||
if (info?.all?.length) return { group: name, members: info.all, now: info.now ?? '' }
|
||||
}
|
||||
return null
|
||||
})
|
||||
const chain = computed(() => resolveChain(runtime.value, props.destination))
|
||||
const switchable = computed(() => lastSwitchableGroup(runtime.value, chain.value))
|
||||
|
||||
function isCurrentOutbound(mode: OutboundMode): boolean {
|
||||
const now = props.inbound.outbound
|
||||
@@ -205,7 +180,7 @@ const NO_PROXY_FIELD = '该入站没有 proxy 字段'
|
||||
</template>
|
||||
|
||||
<h4 :class="locked && 'is-dim'">订阅线路</h4>
|
||||
<p v-if="!kernelOnline" class="hint">
|
||||
<p v-if="!kernel.online" class="hint">
|
||||
<IconOffline />
|
||||
内核未连接,无法获取订阅线路
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user