refactor(backend): specta ipc bindings (#8195)

* refactor(ipc): generate bindings with Specta

* fix(profile): project IPC inputs to consumed fields

* fix(events): remove unused refresh payloads

* fix(webdav): return only consumed backup metadata

* fix(network): trim unused interface metadata

* fix(sysinfo): return only displayed system fields

* chore(knip): check generated runtime exports

* fix(proxy): confirm runtime chain updates before UI changes

* fix(clipboard): propagate proxy environment copy failures

* refactor(dns): remove ignored runtime patch

* refactor(core): trim unused upgrade response fields

* refactor(ipc): avoid redundant response copies

* refactor(proxy): clarify automatic proxy query naming

* chore(tests): remove added IPC regression tests

* refactor(ipc): enforce typed command boundaries

* chore(contract): check generated bindings before push

* refactor(ipc): remove unused proxy liveness payloads
This commit is contained in:
Tunglies
2026-10-12 06:16:05 +00:00
committed by GitHub
parent 27efaf34f4
commit 6e08a77a84
89 changed files with 2714 additions and 1354 deletions
+28 -19
View File
@@ -445,10 +445,14 @@ export const ProxyChain = ({
if (isConnected) {
setIsConnecting(true)
try {
await mutate(() => updateProxyChainConfigInRuntime(null), {
id: 'update-proxy-chain-runtime',
errorNotice: false,
})
const result = await mutate(
() => updateProxyChainConfigInRuntime(null),
{
id: 'update-proxy-chain-runtime',
errorNotice: false,
},
)
if (!result.ok) return
const targetGroup =
mode === 'global'
@@ -461,7 +465,7 @@ export const ProxyChain = ({
id: `select-proxy-node:${targetGroup}`,
errorNotice: false,
})
recordSelection(targetGroup, 'DIRECT')
await recordSelection(targetGroup, 'DIRECT')
} catch {
if (currentProxyChain.length >= 1) {
try {
@@ -473,7 +477,7 @@ export const ProxyChain = ({
errorNotice: false,
},
)
recordSelection(targetGroup, currentProxyChain[0].name)
await recordSelection(targetGroup, currentProxyChain[0].name)
} catch {
// ignore
}
@@ -519,10 +523,14 @@ export const ProxyChain = ({
// 第一步:保存链式代理配置
const chainProxies = currentProxyChain.map((node) => node.name)
debugLog('Saving chain config:', chainProxies)
await mutate(() => updateProxyChainConfigInRuntime(chainProxies), {
id: 'update-proxy-chain-runtime',
errorNotice: false,
})
const result = await mutate(
() => updateProxyChainConfigInRuntime(chainProxies),
{
id: 'update-proxy-chain-runtime',
errorNotice: false,
},
)
if (!result.ok) return
debugLog('Chain configuration saved successfully')
// 第二步:连接到代理链的最后一个节点
@@ -545,12 +553,12 @@ export const ProxyChain = ({
)
// The chain moves the group like any other selection, so the profile has to learn about
// it: what the profile holds is what gets re-applied the next time the core starts.
recordSelection(targetGroup || 'GLOBAL', lastNode.name)
await recordSelection(targetGroup || 'GLOBAL', lastNode.name)
localStorage.setItem('proxy-chain-group', targetGroup || 'GLOBAL')
localStorage.setItem('proxy-chain-exit-node', lastNode.name)
// 刷新代理信息以更新连接状态
refreshProxy()
await refreshProxy()
debugLog('Successfully connected to proxy chain')
} catch (error) {
console.error('Failed to connect to proxy chain:', error)
@@ -619,14 +627,15 @@ export const ProxyChain = ({
<IconButton
size="small"
onClick={() => {
mutate(() => updateProxyChainConfigInRuntime(null), {
void mutate(() => updateProxyChainConfigInRuntime(null), {
id: 'update-proxy-chain-runtime',
errorNotice: false,
})
localStorage.removeItem('proxy-chain-group')
localStorage.removeItem('proxy-chain-exit-node')
localStorage.removeItem('proxy-chain-items')
onUpdateChain([])
onFulfilled: () => {
localStorage.removeItem('proxy-chain-group')
localStorage.removeItem('proxy-chain-exit-node')
localStorage.removeItem('proxy-chain-items')
onUpdateChain([])
},
}).catch(() => {})
}}
sx={{
color: theme.palette.error.main,
@@ -5,11 +5,11 @@ export type ProxyEmptyStateReason =
| 'no-proxy-info'
type ProfileSummary = {
current?: string
current?: string | null
items?: Array<{
uid?: string
type?: string
}>
uid?: string | null
type?: string | null
}> | null
}
interface ProxyListStateInput {
@@ -20,7 +20,7 @@ interface ProxyListStateInput {
isRunningModePending: boolean
}
const isSubscriptionProfile = (item: { type?: string }): boolean =>
const isSubscriptionProfile = (item: { type?: string | null }): boolean =>
item.type === 'remote' || item.type === 'local'
/** Decides only pre-render states; the built list owns whether it has renderable content. */
+14 -8
View File
@@ -400,20 +400,26 @@ export function ProxyGroupsChain(props: ProxyGroupsChainProps) {
setRuleMenuAnchor(null)
}
const handleGroupSelect = (groupName: string) => {
onGroupSelect(groupName)
handleGroupMenuClose()
const handleGroupSelect = async (groupName: string) => {
if (mode === 'rule') {
mutate(() => updateProxyChainConfigInRuntime(null), {
id: 'update-proxy-chain-runtime',
errorNotice: false,
})
try {
const result = await mutate(
() => updateProxyChainConfigInRuntime(null),
{
id: 'update-proxy-chain-runtime',
},
)
if (!result.ok) return
} catch {
return
}
localStorage.removeItem('proxy-chain-group')
localStorage.removeItem('proxy-chain-exit-node')
localStorage.removeItem('proxy-chain-items')
setProxyChain([])
}
onGroupSelect(groupName)
handleGroupMenuClose()
}
const handleCloseDuplicateWarning = useCallback(() => {
+2 -2
View File
@@ -198,7 +198,7 @@ export const ProxyRender = memo(function ProxyRender(props: RenderProps) {
{toolsOnLeft && proxyCount}
<ProxyGroupTools
side={toolsOnLeft ? 'left' : 'right'}
url={group.testUrl}
url={group.testUrl!}
groupName={group.name}
headState={headState!}
onLocation={() => onLocation(group)}
@@ -247,7 +247,7 @@ export const ProxyRender = memo(function ProxyRender(props: RenderProps) {
return (
<ProxyHead
sx={{ pl: 2, pr: 3, mt: 0.5, mb: 1 }}
url={group.testUrl}
url={group.testUrl!}
groupName={group.name}
headState={headState!}
onLocation={() => onLocation(group)}
@@ -17,7 +17,6 @@ const node = (memberIndex: number, delay: number, name = `${memberIndex}`) =>
history: [{ delay }],
extra: {
'http://cp.cloudflare.com/generate_204': {
alive: true,
history: [{ time: new Date(0).toISOString(), delay }],
},
},
+7 -7
View File
@@ -110,13 +110,13 @@ const CHAIN_DELAY_GROUP = 'chain-mode'
const virtualGroup = (members: ProxyGroupView['members']): ProxyGroupView => ({
name: CHAIN_DELAY_GROUP,
type: 'Selector',
alive: true,
udp: false,
xudp: false,
tfo: false,
mptcp: false,
smux: false,
history: [],
extra: {},
members,
})
@@ -237,7 +237,7 @@ export const useRenderList = (
selected?.name ?? CHAIN_DELAY_GROUP,
'',
0,
latencyTimeout,
latencyTimeout!,
)
if (col > 1) {
return groupOccurrences(occurrences, col).map((memberCol) => ({
@@ -289,8 +289,8 @@ export const useRenderList = (
key: group.name,
group,
headState,
icon: group.icon,
testUrl: group.testUrl,
icon: group.icon!,
testUrl: group.testUrl!,
},
]
@@ -300,7 +300,7 @@ export const useRenderList = (
group.name,
headState.filterText,
headState.sortType,
latencyTimeout,
latencyTimeout!,
{
matchCase: headState.filterMatchCase,
matchWholeWord: headState.filterMatchWholeWord,
@@ -337,11 +337,11 @@ export const useRenderList = (
}
cache.set(group.name, {
now: group.now,
now: group.now!,
members: group.members,
headState,
col,
latencyTimeout,
latencyTimeout: latencyTimeout!,
delays: groupDelays.get(group.name),
items: ret,
})