feat: 新增 GRE 隧道协议,管理端口调整为 18080
- backend: 新增 GRE 隧道实现(ip tunnel 点对点互联),binmgr 支持 iproute2 - backend: api/identity 调整,各协议 service(gre/ikev2/ipsec/l2tp/openvpn)统一改动 - backend: OpenVPN 认证辅助与用户口令同步逻辑完善 - frontend: 协议图标/向导/编辑器支持 GRE,VPN 用户页重构(234 行) - frontend: 新增 RouteListInput 路由列表输入组件,Dashboard/Layout 优化 - i18n: 中英文文案同步更新 - test: 新增 binmgr/common/gre 单测,openvpn 测试补充 - docs: README 补充 GRE 说明,默认监听端口 8080 → 18080 - chore: .gitignore 增加 opsx-vpn.previous(构建备份二进制)
This commit is contained in:
1 parent
c7f59f2cbf
commit
ef7eb378d2
28 files changed
+1363
-439
No files matched your search
@@ -33,6 +33,7 @@ const NAMES = computed(() => ({
|
||||
ipsec: 'IPSec',
|
||||
ikev2: 'IKEv2',
|
||||
l2tp: 'L2TP',
|
||||
gre: 'GRE',
|
||||
frp: 'FRP',
|
||||
openvpn: 'OpenVPN',
|
||||
custom: t('protocolIcon.custom'),
|
||||
@@ -62,6 +63,9 @@ const ICON_PATHS = {
|
||||
// L2TP — 手机+信号波(移动设备拨号语义)
|
||||
l2tp: 'M7 1C5.9 1 5 1.9 5 3v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2H7zm5 19c-.83 0-1.5-.67-1.5-1.5S11.17 17 12 17s1.5.67 1.5 1.5S12.83 20 12 20zm5-4H7V4h10v12z M9.5 6.5l2 2-2 2-1-1 1-1-1-1 1-1z M14.5 6.5l1 1-1 1 1 1-1 1-2-2 2-2z',
|
||||
|
||||
// GRE — two routed sites connected by a tunnel
|
||||
gre: 'M3 4h6v3H6v10h3v3H3V4zm12 0h6v16h-6v-3h3V7h-3V4zM9 10h6v2H9v-2zm0 4h6v2H9v-2z',
|
||||
|
||||
// FRP — 隧道+箭头穿透(内网穿透语义)
|
||||
frp: 'M3 8h2v8H3V8zm16 0h2v8h-2V8zM7 10h10v4H7v-4zm2 1.5l2 1.5-2 1.5v-3zm6 0v3l-2-1.5 2-1.5z M12 6l3 2H9l3-2zm0 12l-3-2h6l-3 2z',
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="route-list">
|
||||
<el-input v-model="content" type="textarea" :rows="7" resize="vertical"
|
||||
:placeholder="t('protocols.openvpn.fields.push_routes.placeholder')" @blur="validate" />
|
||||
<span v-if="error" class="route-error">{{ error }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = defineProps({ modelValue: { type: String, default: '' } })
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const { t } = useI18n()
|
||||
const content = ref(props.modelValue || '')
|
||||
const error = ref('')
|
||||
|
||||
watch(() => props.modelValue, value => {
|
||||
if (value !== content.value) content.value = value || ''
|
||||
})
|
||||
watch(content, value => emit('update:modelValue', value))
|
||||
function validRoute(value) {
|
||||
const [ip, prefix] = value.split('/')
|
||||
const parts = ip.split('.')
|
||||
if (parts.length !== 4 || parts.some(part => !/^\d+$/.test(part) || Number(part) > 255)) return false
|
||||
return prefix === undefined || (/^\d+$/.test(prefix) && Number(prefix) >= 0 && Number(prefix) <= 32)
|
||||
}
|
||||
function validate() {
|
||||
const filled = String(content.value).split('\n').flatMap(line => line.split('#', 1)[0].split(',')).map(item => item.trim()).filter(Boolean)
|
||||
const invalid = filled.find(item => !validRoute(item))
|
||||
error.value = invalid ? t('protocols.openvpn.fields.push_routes.format_error', { route: invalid }) : ''
|
||||
if (!invalid && content.value !== content.value.trim()) {
|
||||
content.value = content.value.trim()
|
||||
}
|
||||
return !invalid
|
||||
}
|
||||
defineExpose({ validate })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.route-list { display:grid; gap:8px; width:100%; }
|
||||
.route-error { color:var(--el-color-danger); font-size:12px; }
|
||||
</style>
|
||||
Reference in new issue
Block a user