feat: 全面优化 — UI/UX重构 + 流量审计与会话管理 + 协议部署
UI/UX 优化: - 登录页分屏布局 + 可访问性修复 + 内联错误提示 - 侧栏菜单分组(专线运维/资源管理/系统) + 图标修正 + 子路由高亮 - Dashboard 统计卡渐变图标 + 空状态引导 + 统计卡可点击 - 创建向导: 依赖检测门控 + 高级折叠 + 必填校验 + WireGuard推荐标 - 专线列表: 空状态CTA + 操作列「更多」下拉 + 状态筛选 + SSE日志流 - 协议品牌SVG图标(ProtocolIcon组件)替换emoji - 设计令牌系统(tokens.css)统一颜色/间距/圆角/阴影 功能增强: - 在线会话管理(sessionmgr): 跨协议会话同步 + 踢下线 + 连接审计日志 - 流量配额引擎(trafficmgr): 配额检查 + 超额动作(block/warn/throttle) + 周期重置 - 异常流量告警: 突发流量检测(>90MB/15s) + 审计记录 - 节点SSH凭证管理: 密码/私钥认证 + 即时测试连接 + AES-GCM加密存储 - 协议二进制管理(binmgr): FRP/OpenVPN GitHub下载 + 系统包安装引导(apt/yum/dnf) - 右上角修改密码弹窗 + MFA独立验证页 + 密码强度提示 - 用户管理: 批量导入 + LDAP浏览导入 + 平台用户导入VPN拨号用户 - 404路由 + IPSec对接页可达性修复 - MFA备用码持久化弹窗(替代Toast) - 轮询可见性守卫 + ECharts resize修复 后端: - 3张新表: VPNSession/ConnectionLog/TrafficPolicy - monitor增强: 会话同步+配额检查+异常告警+到期审计 - changePassword统一8位+新旧不同校验 - openvpn-otp文案专业化(移除抄袭感) - SSE日志流(token降级query参数)
This commit is contained in:
1 parent
58c036c34c
commit
1e2f8ef24f
43 files changed
+6783
-460
No files matched your search
+115
-23
@@ -3,48 +3,83 @@ export const PROTOCOL_META = {
|
||||
wireguard: {
|
||||
name: 'WireGuard',
|
||||
desc: '性能最好的现代 VPN,内核级实现,漫游友好',
|
||||
color: '#88171a',
|
||||
icon: '⚡',
|
||||
color: '#c0392b',
|
||||
icon: 'wireguard',
|
||||
scenes: '内部接入 / 移动办公',
|
||||
fields: [
|
||||
{ key: 'address', label: '服务端 VPN 网段地址', placeholder: '10.8.0.1/24', required: true, tip: '服务端在 VPN 网段中的地址(CIDR)' },
|
||||
{ key: 'listen_port', label: '监听端口', type: 'number', default: 51820 },
|
||||
{ key: 'client_address', label: '客户端地址', placeholder: '10.8.0.2/32', tip: '分配给首个客户端的地址' },
|
||||
{ key: 'client_address', label: '首个客户端地址', placeholder: '10.8.0.2/32', tip: '创建后可在详情中继续添加 Peer' },
|
||||
{ key: 'public_endpoint', label: '公网接入点', placeholder: 'vpn.example.com:51820', tip: '客户端连接用,公网 IP:端口' },
|
||||
{ key: 'dns', label: 'DNS', default: '223.5.5.5' },
|
||||
{ key: 'allowed_ips', label: '客户端 AllowedIPs', default: '0.0.0.0/0, ::/0', tip: '全流量代理用 0.0.0.0/0;仅内网填内网网段' }
|
||||
{ key: 'allowed_ips', label: '客户端 AllowedIPs', default: '0.0.0.0/0, ::/0', tip: '全流量代理用 0.0.0.0/0;仅内网填内网网段' },
|
||||
{ key: 'keepalive', label: 'Keepalive(秒)', type: 'number', default: 25, tip: 'NAT 后客户端建议 15–25' },
|
||||
{ key: 'mtu', label: 'MTU', type: 'number', default: 0, tip: '0 表示默认;跨公网常见 1280–1420' },
|
||||
{ key: 'enable_nat', label: '启用 NAT 出网', type: 'switch', default: false, tip: '客户端需访问公网/其它网段时开启' },
|
||||
{ key: 'egress_interface', label: 'NAT 出口网卡', placeholder: 'eth0', tip: '留空则自动探测默认路由网卡', showIf: { enable_nat: true } }
|
||||
]
|
||||
},
|
||||
ipsec: {
|
||||
name: 'IPSec 站点互联',
|
||||
desc: '与第三方设备(Cisco/华为/飞塔等)做站点到站点对接',
|
||||
color: '#1f6fb2',
|
||||
icon: '🔗',
|
||||
icon: 'ipsec',
|
||||
scenes: '第三方对接 / 站点互联',
|
||||
fields: [
|
||||
{ key: 'vendor_template', label: '厂商模板', type: 'select', options: [
|
||||
{ value: 'generic', label: '通用标准(推荐)' },
|
||||
{ value: 'cisco', label: 'Cisco ISR/ASR' },
|
||||
{ value: 'fortigate', label: 'FortiGate' },
|
||||
{ value: 'huawei', label: '华为 USG/AR' },
|
||||
{ value: 'h3c', label: 'H3C' }
|
||||
], default: 'generic', tip: '自动预填 Phase1/Phase2 提议,仍可手动改' },
|
||||
{ key: 'public_endpoint', label: '本端公网 IP', placeholder: '1.2.3.4', required: true },
|
||||
{ key: 'remote_gateway', label: '对端网关地址', placeholder: '5.6.7.8', required: true },
|
||||
{ key: 'local_subnets', label: '本端网段', placeholder: '192.168.1.0/24', required: true, tip: '多个网段用逗号分隔' },
|
||||
{ key: 'remote_subnets', label: '对端网段', placeholder: '10.0.0.0/24', required: true },
|
||||
{ key: 'psk', label: '预共享密钥 PSK', type: 'password', required: true, secret: true },
|
||||
{ key: 'ike_version', label: 'IKE 版本', type: 'select', options: ['ikev2', 'ikev1'], default: 'ikev2' },
|
||||
{ key: 'ike_version', label: 'IKE 版本', type: 'select', options: [
|
||||
{ value: 'ikev2', label: 'IKEv2' },
|
||||
{ value: 'ikev1', label: 'IKEv1' }
|
||||
], default: 'ikev2' },
|
||||
{ key: 'ike_proposal', label: 'Phase1 提议', placeholder: 'aes256-sha256-modp2048', tip: '加密-认证-DH组,需与对端一致' },
|
||||
{ key: 'esp_proposal', label: 'Phase2 提议', placeholder: 'aes256-sha256' },
|
||||
{ key: 'pfs_group', label: 'PFS DH 组', default: 'modp2048' },
|
||||
{ key: 'ike_lifetime', label: 'IKE 生存期(秒)', type: 'number', default: 86400 },
|
||||
{ key: 'local_id', label: '本端标识 ID', placeholder: '@vpn-local' },
|
||||
{ key: 'remote_id', label: '对端标识 ID', placeholder: '@vpn-remote' }
|
||||
]
|
||||
},
|
||||
ikev2: {
|
||||
name: 'IKEv2 远程接入',
|
||||
desc: '手机/笔记本原生拨号进内网,比 L2TP 更现代',
|
||||
color: '#0d7377',
|
||||
icon: 'ikev2',
|
||||
scenes: '员工远程办公 / 原生 VPN',
|
||||
fields: [
|
||||
{ key: 'public_endpoint', label: '服务器公网地址', placeholder: 'vpn.example.com', required: true },
|
||||
{ key: 'local_id', label: '服务器 ID / 远程 ID', placeholder: '@opsx-ikev2', default: '@opsx-ikev2', tip: '客户端「远程 ID」与此一致' },
|
||||
{ key: 'psk', label: '机器认证 PSK', type: 'password', secret: true, tip: '留空自动生成;iOS/Android 共享密钥' },
|
||||
{ key: 'username', label: '默认拨号用户', default: 'vpnuser' },
|
||||
{ key: 'password', label: '默认拨号密码', type: 'password', secret: true, tip: '留空自动生成;更多账号在拨号用户页添加' },
|
||||
{ key: 'address_pool', label: '客户端地址池', default: '10.12.0.0/24' },
|
||||
{ key: 'dns', label: '下发 DNS', default: '223.5.5.5' },
|
||||
{ key: 'local_ts', label: '放行网段', default: '0.0.0.0/0', tip: '0.0.0.0/0 为全流量;可改为内网 CIDR' },
|
||||
{ key: 'ike_proposal', label: 'IKE 提议', default: 'aes256-sha256-modp2048' },
|
||||
{ key: 'esp_proposal', label: 'ESP 提议', default: 'aes256-sha256' }
|
||||
]
|
||||
},
|
||||
l2tp: {
|
||||
name: 'L2TP/IPSec',
|
||||
desc: '兼容性最好,手机/老设备/路由器原生支持',
|
||||
color: '#5a8a3c',
|
||||
icon: '📱',
|
||||
icon: 'l2tp',
|
||||
scenes: '移动设备 / 老设备兼容',
|
||||
fields: [
|
||||
{ key: 'public_endpoint', label: '服务器公网 IP', placeholder: '1.2.3.4', required: true },
|
||||
{ key: 'psk', label: 'IPSec 预共享密钥', type: 'password', required: true, secret: true },
|
||||
{ key: 'username', label: '拨号用户名', default: 'vpnuser' },
|
||||
{ key: 'password', label: '拨号密码', type: 'password', required: true, secret: true },
|
||||
{ key: 'username', label: '默认拨号用户名', default: 'vpnuser', tip: '可在「VPN 拨号用户」为本隧道添加更多账号' },
|
||||
{ key: 'password', label: '默认拨号密码', type: 'password', secret: true, tip: '若只使用拨号用户表可留空(创建时需至少有一种)' },
|
||||
{ key: 'ip_range', label: '客户端地址池', default: '10.9.0.100-10.9.0.200' },
|
||||
{ key: 'local_ip', label: '服务端隧道地址', default: '10.9.0.1' },
|
||||
{ key: 'dns', label: '下发 DNS', default: '223.5.5.5' }
|
||||
@@ -54,39 +89,87 @@ export const PROTOCOL_META = {
|
||||
name: 'FRP 内网穿透',
|
||||
desc: '国内刚需,TCP/UDP/HTTP 端口映射与反向代理',
|
||||
color: '#c0762b',
|
||||
icon: '🚇',
|
||||
icon: 'frp',
|
||||
scenes: '内网穿透 / 端口映射',
|
||||
fields: [
|
||||
{ key: 'mode', label: '运行模式', type: 'select', options: ['server', 'client'], default: 'server', tip: 'server=frps(有公网),client=frpc(内网侧)' },
|
||||
{ key: 'mode', label: '运行模式', type: 'select', options: [
|
||||
{ value: 'server', label: 'server (frps 公网侧)' },
|
||||
{ value: 'client', label: 'client (frpc 内网侧)' }
|
||||
], default: 'server', tip: 'server=frps(有公网),client=frpc(内网侧)' },
|
||||
{ key: 'bind_port', label: '绑定端口(server)', type: 'number', default: 7000, showIf: { mode: 'server' } },
|
||||
{ key: 'public_endpoint', label: '公网地址(server)', placeholder: '1.2.3.4', showIf: { mode: 'server' } },
|
||||
{ key: 'dashboard_port', label: '管理面板端口(server)', type: 'number', default: 7500, showIf: { mode: 'server' } },
|
||||
{ key: 'dashboard_user', label: '管理面板账号(server)', default: 'admin', showIf: { mode: 'server' } },
|
||||
{ key: 'dashboard_password', label: '管理面板密码(server)', type: 'password', secret: true, showIf: { mode: 'server' }, tip: '留空时自动生成强随机密码;编辑时留空保持原密码' },
|
||||
{ key: 'vhost_http_port', label: 'HTTP 虚拟主机端口', type: 'number', default: 0, showIf: { mode: 'server' }, tip: '0 表示不启用;HTTP 代理需 >0' },
|
||||
{ key: 'vhost_https_port', label: 'HTTPS 虚拟主机端口', type: 'number', default: 0, showIf: { mode: 'server' }, tip: '0 表示不启用' },
|
||||
{ key: 'allow_ports', label: '允许映射端口范围', placeholder: '6000-6100,7001', showIf: { mode: 'server' }, tip: '限制 frpc 可申请的 remotePort' },
|
||||
{ key: 'server_addr', label: 'frps 地址(client)', placeholder: '1.2.3.4', showIf: { mode: 'client' } },
|
||||
{ key: 'server_port', label: 'frps 端口(client)', type: 'number', default: 7000, showIf: { mode: 'client' } },
|
||||
{ key: 'token', label: '认证 Token', type: 'password', secret: true, tip: '留空自动生成' },
|
||||
{ key: 'proxy_name', label: '代理名称', placeholder: 'ssh' },
|
||||
{ key: 'proxy_type', label: '代理类型', type: 'select', options: ['tcp', 'udp', 'http', 'https'], default: 'tcp' },
|
||||
{ key: 'proxy_name', label: '代理名称', placeholder: 'ssh', default: 'ssh' },
|
||||
{ key: 'proxy_type', label: '代理类型', type: 'select', options: [
|
||||
{ value: 'tcp', label: 'TCP' },
|
||||
{ value: 'udp', label: 'UDP' },
|
||||
{ value: 'http', label: 'HTTP' },
|
||||
{ value: 'https', label: 'HTTPS' },
|
||||
{ value: 'stcp', label: 'STCP(安全点对点)' },
|
||||
{ value: 'xtcp', label: 'XTCP(P2P)' }
|
||||
], default: 'tcp' },
|
||||
{ key: 'local_ip', label: '内网目标 IP', default: '127.0.0.1' },
|
||||
{ key: 'local_port', label: '内网目标端口', type: 'number', default: 22 },
|
||||
{ key: 'remote_port', label: '公网映射端口', type: 'number', default: 6000, showIf: { proxy_type: ['tcp', 'udp'] } },
|
||||
{ key: 'custom_domains', label: '绑定域名', placeholder: 'app.example.com, www.example.com', showIf: { proxy_type: ['http', 'https'] }, tip: 'HTTP/HTTPS 代理必填,多个域名用逗号分隔' }
|
||||
{ key: 'custom_domains', label: '绑定域名', placeholder: 'app.example.com', showIf: { proxy_type: ['http', 'https'] }, tip: 'HTTP/HTTPS 代理必填' },
|
||||
{ key: 'secret_key', label: 'STCP/XTCP 密钥', type: 'password', secret: true, showIf: { proxy_type: ['stcp', 'xtcp'] }, tip: '访问端与提供端需一致' }
|
||||
]
|
||||
},
|
||||
openvpn: {
|
||||
name: 'OpenVPN',
|
||||
desc: '最成熟的开源 VPN,兼容各类老项目与系统',
|
||||
color: '#e07b39',
|
||||
icon: '🛡️',
|
||||
icon: 'openvpn',
|
||||
scenes: '老项目兼容 / 通用接入',
|
||||
fields: [
|
||||
{ key: 'public_endpoint', label: '公网接入地址', placeholder: 'vpn.example.com', required: true },
|
||||
{ key: 'port', label: '端口', type: 'number', default: 1194 },
|
||||
{ key: 'proto', label: '传输协议', type: 'select', options: ['udp', 'tcp'], default: 'udp' },
|
||||
{ key: 'subnet', label: 'VPN 网段', default: '10.10.0.0 255.255.255.0' },
|
||||
{ key: 'cipher', label: '加密算法', type: 'select', options: ['AES-256-GCM', 'AES-128-GCM', 'AES-256-CBC'], default: 'AES-256-GCM' },
|
||||
{ key: 'dns', label: '下发 DNS', default: '223.5.5.5' }
|
||||
{ key: 'proto', label: '传输协议', type: 'select', options: [
|
||||
{ value: 'udp', label: 'UDP(推荐,性能更好)' },
|
||||
{ value: 'tcp-server', label: 'TCP(穿透限制网络更稳)' }
|
||||
], default: 'udp', tip: '可随时在编辑中切换;切换后需重启专线,客户端需重新下载配置' },
|
||||
{ key: 'subnet', label: 'VPN 网段', default: '10.10.0.0 255.255.255.0', tip: '如 10.8.0.0 255.255.240.0 可容纳更多客户端(/20)' },
|
||||
{ key: 'cipher', label: '加密算法', type: 'select', options: [
|
||||
{ value: 'AES-256-GCM', label: 'AES-256-GCM' },
|
||||
{ value: 'AES-128-GCM', label: 'AES-128-GCM' },
|
||||
{ value: 'AES-256-CBC', label: 'AES-256-CBC' }
|
||||
], default: 'AES-256-GCM' },
|
||||
{ key: 'auth_mode', label: '认证方式', type: 'select', options: [
|
||||
{ value: 'certificate', label: '仅证书' },
|
||||
{ value: 'password', label: '仅用户名密码' },
|
||||
{ value: 'certificate+password', label: '证书 + 密码' }
|
||||
], default: 'certificate', tip: '密码模式对齐生产 verify-client-cert none + username-as-common-name' },
|
||||
{ key: 'mfa_mode', label: '拨号 MFA (2FA)', type: 'select', options: [
|
||||
{ value: 'off', label: '关闭' },
|
||||
{ value: 'optional', label: '可选(用户绑定后才校验)' },
|
||||
{ value: 'required', label: '强制(所有拨号用户必须 MFA)' }
|
||||
], default: 'off', tip: 'static-challenge + TOTP;在拨号用户页管理 OTP', showIf: { auth_mode: ['password', 'certificate+password'] } },
|
||||
{ key: 'otp_slop', label: 'OTP 时钟容差(秒)', type: 'number', default: 90, showIf: { auth_mode: ['password', 'certificate+password'] } },
|
||||
{ key: 'otp_max_failures', label: 'OTP 失败锁定次数', type: 'number', default: 5, showIf: { auth_mode: ['password', 'certificate+password'] } },
|
||||
{ key: 'otp_lockout_sec', label: 'OTP 锁定时长(秒)', type: 'number', default: 300, showIf: { auth_mode: ['password', 'certificate+password'] } },
|
||||
{ key: 'otp_disallow_reuse', label: '禁止重复使用同码', type: 'switch', default: true, showIf: { auth_mode: ['password', 'certificate+password'] } },
|
||||
{ key: 'dns', label: '下发 DNS', default: '223.5.5.5' },
|
||||
{ key: 'redirect_gateway', label: '全流量代理', type: 'switch', default: true, tip: '关闭后仅推送下方内网路由(生产常只推指定路由)' },
|
||||
{ key: 'push_routes', label: '推送内网路由', placeholder: '192.168.0.0/16,10.0.0.0/8', tip: 'CIDR 逗号分隔' },
|
||||
{ key: 'enable_nat', label: '启用 NAT 出网', type: 'switch', default: false },
|
||||
{ key: 'max_clients', label: '最大客户端数', type: 'number', default: 1000, tip: '0 不限制;生产常见 1000' },
|
||||
{ key: 'keepalive_interval', label: 'Keepalive 间隔', type: 'number', default: 5, tip: '生产常用 keepalive 5 30' },
|
||||
{ key: 'keepalive_timeout', label: 'Keepalive 超时', type: 'number', default: 30 },
|
||||
{ key: 'tun_mtu', label: 'tun-mtu', type: 'number', default: 1500, tip: '生产常用 1500' },
|
||||
{ key: 'tun_mtu_extra', label: 'tun-mtu-extra', type: 'number', default: 32 },
|
||||
{ key: 'mssfix', label: 'mssfix', type: 'number', default: 0, tip: '0=不设置;TCP 生产常用 1450,选 TCP 时默认 1450' },
|
||||
{ key: 'duplicate_cn', label: '允许 duplicate-cn', type: 'switch', default: false, tip: '同用户名/证书多终端同时在线;密码模式默认会自动开启' },
|
||||
{ key: 'reneg_sec', label: 'reneg-sec', type: 'number', default: 0, tip: '0=禁用 TLS 重协商(生产/MFA 推荐);证书-only 默认可 3600' },
|
||||
{ key: 'run_as_user', label: '降权用户', placeholder: 'openvpn', tip: '对应 user openvpn;需系统已有该用户,留空不降权' },
|
||||
{ key: 'run_as_group', label: '降权组', placeholder: 'openvpn', tip: '对应 group openvpn' }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -99,11 +182,12 @@ export const CUSTOM_FIELDS = [
|
||||
]
|
||||
|
||||
export const PROTOCOL_COLORS = {
|
||||
wireguard: '#88171a',
|
||||
wireguard: '#c0392b',
|
||||
ipsec: '#1f6fb2',
|
||||
l2tp: '#5a8a3c',
|
||||
frp: '#c0762b',
|
||||
openvpn: '#e07b39'
|
||||
ikev2: '#0d7377',
|
||||
l2tp: '#4a8a2c',
|
||||
frp: '#b8651b',
|
||||
openvpn: '#cc6529'
|
||||
}
|
||||
|
||||
export function protocolDisplay(t) {
|
||||
@@ -115,3 +199,11 @@ export function protocolColor(t) {
|
||||
if (t.startsWith('custom:')) return '#7b5ea7'
|
||||
return PROTOCOL_COLORS[t] || '#606266'
|
||||
}
|
||||
|
||||
/** 解析 select 的 options(兼容字符串与 {value,label}) */
|
||||
export function optionValue(o) {
|
||||
return typeof o === 'object' && o !== null ? o.value : o
|
||||
}
|
||||
export function optionLabel(o) {
|
||||
return typeof o === 'object' && o !== null ? (o.label || o.value) : o
|
||||
}
|
||||
Reference in new issue
Block a user