feat(api): 新增节点墙检测自动托管与显隐

新增定时墙检测命令与节点托管字段,自动为开启托管的父
节点创建检测任务,并在 blocked 时自动隐藏节点、normal
时仅恢复由墙检测自动隐藏的节点

更新自动上线服务以尊重 blocked 与自动隐藏状态,避免疑
似被墙节点被重新发布;同时补齐管理端墙检测托管开关、
刷新入口、批量设置与相关测试和知识库同步
This commit is contained in:
yinjianm
2026-04-28 00:51:49 +08:00
parent 73b1696b0a
commit ff50030364
27 changed files with 998 additions and 24 deletions
+78 -1
View File
@@ -35,6 +35,7 @@ import NodeEditorDialog from './NodeEditorDialog.vue'
import NodeSortDialog from './NodeSortDialog.vue'
import {
buildNodeTypeOptions,
countAutoGfwCheckNodes,
countAutoOnlineNodes,
countOnlineNodes,
countVisibleNodes,
@@ -77,6 +78,7 @@ const selectedNodeIds = ref<number[]>([])
const syncingSelection = ref(false)
const switchingIds = ref<number[]>([])
const autoSwitchingIds = ref<number[]>([])
const gfwSwitchingIds = ref<number[]>([])
const workingIds = ref<number[]>([])
const editorVisible = ref(false)
const editorMode = ref<NodeDialogMode>('create')
@@ -119,6 +121,7 @@ const summaryCards = computed(() => [
{ label: '在线节点', value: String(countOnlineNodes(nodes.value)) },
{ label: '显示中', value: String(countVisibleNodes(nodes.value)) },
{ label: '自动上线', value: String(countAutoOnlineNodes(nodes.value)) },
{ label: '自动墙检', value: String(countAutoGfwCheckNodes(nodes.value)) },
{ label: '已勾选', value: String(selectedNodes.value.length) },
])
@@ -166,6 +169,10 @@ function isAutoSwitching(id: number): boolean {
return autoSwitchingIds.value.includes(id)
}
function isGfwSwitching(id: number): boolean {
return gfwSwitchingIds.value.includes(id)
}
function isWorking(id: number): boolean {
return workingIds.value.includes(id)
}
@@ -293,6 +300,7 @@ async function handleBatchSubmit(payload: NodeBatchEditPayload) {
rate: payload.rate,
group_ids: payload.group_ids,
auto_online: payload.auto_online,
gfw_check_enabled: payload.gfw_check_enabled,
}
try {
@@ -453,6 +461,29 @@ async function handleToggleAutoOnline(node: AdminNodeItem, nextValue: boolean) {
}
}
async function handleToggleGfwCheck(node: AdminNodeItem, nextValue: boolean) {
const previous = node.gfw_check_enabled !== false
if (previous === nextValue) {
return
}
node.gfw_check_enabled = nextValue
markPending(gfwSwitchingIds, node.id, true)
try {
await updateNode({
id: node.id,
gfw_check_enabled: nextValue,
})
ElMessage.success(nextValue ? '已开启墙检测托管' : '已关闭墙检测托管')
} catch (error) {
node.gfw_check_enabled = previous
ElMessage.error(error instanceof Error ? error.message : '墙检测托管状态更新失败')
} finally {
markPending(gfwSwitchingIds, node.id, false)
}
}
async function handlePinTop(node: AdminNodeItem) {
const orderedNodes = sortNodesByOrder(nodes.value)
if (orderedNodes[0]?.id === node.id) {
@@ -652,6 +683,13 @@ watch(
<ElIcon><Connection /></ElIcon>
检测墙状态
</ElButton>
<ElButton
:loading="loading"
@click="loadNodeBoard"
>
<ElIcon><RefreshRight /></ElIcon>
刷新数据
</ElButton>
<ElButton
type="danger"
plain
@@ -720,13 +758,30 @@ watch(
<ElSwitch
:model-value="Boolean(row.show)"
:loading="isSwitching(row.id)"
:disabled="Boolean(row.auto_online)"
:disabled="Boolean(row.auto_online) || (Boolean(row.gfw_auto_hidden) && row.gfw_check_enabled !== false)"
@change="(value) => handleToggleShow(row, Boolean(value))"
/>
</div>
</template>
</ElTableColumn>
<ElTableColumn label="墙检测" width="118">
<template #default="{ row }">
<ElTooltip
:content="row.parent_id ? '子节点不单独检测;此开关只控制是否随父节点自动隐藏或恢复。' : '关闭后不参与自动墙检测和墙状态自动显隐。'"
placement="top"
>
<div class="switch-shell switch-shell--gfw">
<ElSwitch
:model-value="row.gfw_check_enabled !== false"
:loading="isGfwSwitching(row.id)"
@change="(value) => handleToggleGfwCheck(row, Boolean(value))"
/>
</div>
</ElTooltip>
</template>
</ElTableColumn>
<ElTableColumn label="自动上线" width="118">
<template #default="{ row }">
<div class="switch-shell switch-shell--auto">
@@ -759,6 +814,24 @@ watch(
>
自动上线
</ElTag>
<ElTag
v-if="row.gfw_check_enabled !== false"
round
effect="plain"
type="primary"
class="auto-online-tag"
>
墙检测
</ElTag>
<ElTag
v-if="row.gfw_auto_hidden"
round
effect="plain"
type="danger"
class="auto-online-tag"
>
自动隐藏
</ElTag>
<ElTooltip :content="getNodeGfwTooltip(row)" placement="top">
<ElTag
round
@@ -1048,6 +1121,10 @@ watch(
--el-switch-on-color: #0071e3;
}
.switch-shell--gfw :deep(.el-switch) {
--el-switch-on-color: #34c759;
}
.node-cell,
.stack-cell,
.online-cell {