Add multi-IP binding modes and deployment guide

This commit is contained in:
2026-03-04 15:30:13 +08:00
parent 4348ee799b
commit eed1acd454
12 changed files with 509 additions and 217 deletions

View File

@@ -33,7 +33,8 @@ const route = useRoute()
const router = useRouter()
const form = reactive({
id: null,
bound_ip: '',
binding_mode: 'single',
allowed_ips_text: '',
})
const filters = reactive({
token_suffix: '',
@@ -170,16 +171,6 @@ function statusText(row) {
return isDormant(row) ? '沉寂' : '正常'
}
function ipTypeLabel(boundIp) {
if (!boundIp) {
return '未知'
}
if (!boundIp.includes('/')) {
return '单个 IP'
}
return boundIp.endsWith('/32') || boundIp.endsWith('/128') ? '单个 IP' : 'CIDR 网段'
}
function rowClassName({ row }) {
if (row.status === 2) {
return 'binding-row--banned'
@@ -238,18 +229,52 @@ async function searchBindings() {
function openEdit(row) {
form.id = row.id
form.bound_ip = row.bound_ip
form.binding_mode = row.binding_mode
form.allowed_ips_text = (row.allowed_ips || []).join('\n')
dialogVisible.value = true
}
function normalizeAllowedIpText(value) {
return value
.split(/[\n,]/)
.map((item) => item.trim())
.filter(Boolean)
}
function bindingModeLabel(mode) {
if (mode === 'all') {
return '全部放行'
}
if (mode === 'multiple') {
return '多 IP'
}
return '单地址'
}
function bindingRuleText(row) {
if (row.binding_mode === 'all') {
return '全部 IP 放行'
}
return row.bound_ip
}
async function submitEdit() {
if (!form.bound_ip) {
ElMessage.warning('请输入 CIDR 或单个 IP。')
const allowedIps = normalizeAllowedIpText(form.allowed_ips_text)
if (form.binding_mode !== 'all' && !allowedIps.length) {
ElMessage.warning('请填写至少一个 IP 或 CIDR。')
return
}
try {
await run(() => updateBindingIp({ id: form.id, bound_ip: form.bound_ip }), '更新绑定失败。')
ElMessage.success('绑定地址已更新。')
await run(
() =>
updateBindingIp({
id: form.id,
binding_mode: form.binding_mode,
allowed_ips: allowedIps,
}),
'更新绑定失败。',
)
ElMessage.success('绑定规则已更新。')
dialogVisible.value = false
await refreshBindings()
} catch {}
@@ -297,7 +322,7 @@ watch(
<PageHero
eyebrow="绑定控制"
title="围绕绑定表格完成查询、核对与处置"
description="按 Token 尾号或绑定地址快速检索,确认最近活跃时间后直接编辑 CIDR、解绑或封禁。"
description="按 Token 尾号或绑定地址快速检索,确认最近活跃时间后直接编辑规则、解绑或封禁。"
>
<template #aside>
<div class="hero-stat-pair">
@@ -321,7 +346,7 @@ watch(
<div class="binding-head-copy">
<p class="eyebrow">绑定列表</p>
<h3 class="section-title">聚焦表格本身减少干扰信息</h3>
<p class="muted">页面只保留查询状态和处置动作方便快速完成 IP 管理</p>
<p class="muted">支持单地址多个 IP 与全部放行三种规则页面只保留高频查询与处置动作</p>
</div>
<div class="binding-summary-strip" aria-label="Binding summary">
<article class="binding-summary-card">
@@ -423,7 +448,7 @@ watch(
<el-icon><SwitchButton /></el-icon>
当前匹配 {{ formatCompactNumber(total) }} 条绑定
</span>
<span class="binding-table-note">沉寂表示 {{ staleWindowDays }} 天及以上没有请求</span>
<span class="binding-table-note">沉寂表示 {{ staleWindowDays }} 天及以上没有请求规则支持单地址 IP 与全部放行</span>
</div>
</div>
@@ -445,10 +470,10 @@ watch(
<template #default="{ row }">
<div class="binding-ip-cell">
<div class="binding-ip-line">
<code>{{ row.bound_ip }}</code>
<el-button text :icon="CopyDocument" @click="copyValue(row.bound_ip, '绑定地址')">复制</el-button>
<code>{{ bindingRuleText(row) }}</code>
<el-button text :icon="CopyDocument" @click="copyValue(bindingRuleText(row), '绑定规则')">复制</el-button>
</div>
<span class="muted">{{ ipTypeLabel(row.bound_ip) }}</span>
<span class="muted">{{ bindingModeLabel(row.binding_mode) }}</span>
</div>
</template>
</el-table-column>
@@ -476,7 +501,7 @@ watch(
<el-table-column label="操作" min-width="360" fixed="right">
<template #default="{ row }">
<div class="binding-action-row">
<el-button :icon="EditPen" @click="openEdit(row)">编辑 CIDR</el-button>
<el-button :icon="EditPen" @click="openEdit(row)">编辑规则</el-button>
<el-button
:icon="row.status === 1 ? Lock : Unlock"
:type="row.status === 1 ? 'warning' : 'success'"
@@ -517,17 +542,47 @@ watch(
</div>
</section>
<el-dialog v-model="dialogVisible" title="更新绑定地址" width="420px">
<el-dialog v-model="dialogVisible" title="更新绑定规则" width="520px">
<el-form label-position="top">
<el-form-item label="CIDR 或单个 IP">
<el-form-item label="规则模式">
<el-radio-group v-model="form.binding_mode" class="binding-mode-group">
<el-radio-button value="single">单地址</el-radio-button>
<el-radio-button value="multiple">多个 IP</el-radio-button>
<el-radio-button value="all">全部放行</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.binding_mode === 'single'" label="IP 或 CIDR">
<el-input
v-model="form.bound_ip"
v-model="form.allowed_ips_text"
autocomplete="off"
name="bound_ip"
placeholder="192.168.1.0/24"
@keyup.enter="submitEdit"
/>
</el-form-item>
<el-form-item v-else-if="form.binding_mode === 'multiple'" label="多个 IP">
<el-input
v-model="form.allowed_ips_text"
type="textarea"
:rows="6"
autocomplete="off"
name="allowed_ips"
placeholder="每行一个 IP例如&#10;192.168.1.10&#10;192.168.1.11"
/>
</el-form-item>
<el-alert
v-else
type="warning"
:closable="false"
title="全部放行后,这个 Token 不再校验来源 IP。仅建议在确有必要的内部场景中使用。"
/>
<p class="muted">
单地址模式支持单个 IP 或一个 CIDR IP 模式按逐行 IP 精确放行全部放行表示跳过来源地址校验
</p>
</el-form>
<template #footer>