添加路由支持配置用户用户组
This commit is contained in:
parent
32297ff52c
commit
888828b7a1
|
|
@ -20,6 +20,15 @@ export interface OnlineUserInfo {
|
|||
client_ip: string;
|
||||
}
|
||||
|
||||
export interface VPNRouter {
|
||||
type: number; // 4, 6, 46
|
||||
ip: string;
|
||||
prefix: number;
|
||||
metric: number;
|
||||
router_type: number; // 0-全局路由, 1-用户路由, 2-用户组路由
|
||||
target_id: number; // 目标ID: 0-全局, >0-用户ID或用户组ID
|
||||
}
|
||||
|
||||
export interface ServerConfig {
|
||||
name: string;
|
||||
server_id: string;
|
||||
|
|
@ -38,11 +47,10 @@ export interface ServerConfig {
|
|||
allow_user_id: any[];
|
||||
encryption: string;
|
||||
hash: string;
|
||||
no_policy_action:number;
|
||||
no_policy_action: number;
|
||||
user_max_device: number;
|
||||
duration_time: number;
|
||||
ipv4_router: any[];
|
||||
ipv6_router: any[];
|
||||
routers: VPNRouter[];
|
||||
vpn_status: VPNStatus;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,48 +229,68 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">IPv4路由配置</el-divider>
|
||||
<el-divider content-position="left">路由配置</el-divider>
|
||||
|
||||
<el-form-item label="IPv4路由">
|
||||
<el-form-item label="路由列表">
|
||||
<div class="router-section">
|
||||
<div
|
||||
v-for="(router, index) in selectedServer.ipv4_router"
|
||||
v-for="(router, index) in selectedServer.routers"
|
||||
:key="index"
|
||||
class="router-item"
|
||||
>
|
||||
<el-input v-model="router.ip" placeholder="IP地址 (如: 192.168.1.1)" style="flex: 1;" />
|
||||
<el-input-number v-model="router.prefix" :min="0" :max="32" placeholder="前缀长度" style="flex: 1; margin-left: 10px;" />
|
||||
<!-- metric -->
|
||||
<el-input-number v-model="router.metric" :min="0" :max="1000000" placeholder="metric" style="flex: 1; margin-left: 10px;" />
|
||||
<el-button type="danger" size="small" text @click="removeRouter('ipv4', index)" style="margin-left: 10px;">
|
||||
<el-select v-model="router.type" placeholder="IP类型" style="width: 120px;">
|
||||
<el-option label="IPv4" :value="4" />
|
||||
<el-option label="IPv6" :value="6" />
|
||||
<el-option label="IPv4/IPv6" :value="46" />
|
||||
</el-select>
|
||||
<el-input v-model="router.ip" placeholder="IP地址" style="flex: 1; margin-left: 10px;" />
|
||||
<el-input-number v-model="router.prefix" :min="0" :max="128" placeholder="前缀" style="width: 100px; margin-left: 10px;" />
|
||||
<el-input-number v-model="router.metric" :min="0" :max="1000000" placeholder="metric" style="width: 100px; margin-left: 10px;" />
|
||||
<el-select v-model="router.router_type" placeholder="路由类型" style="width: 130px; margin-left: 10px;" @change="handleRouterTypeChange(router)">
|
||||
<el-option label="全局路由" :value="0" />
|
||||
<el-option label="用户路由" :value="1" />
|
||||
<el-option label="用户组路由" :value="2" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="router.router_type === 1"
|
||||
v-model="router.target_id"
|
||||
placeholder="选择用户"
|
||||
filterable
|
||||
style="width: 150px; margin-left: 10px;"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in user_select_opts"
|
||||
:key="user.value"
|
||||
:label="user.label"
|
||||
:value="user.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="router.router_type === 2"
|
||||
v-model="router.target_id"
|
||||
placeholder="选择用户组"
|
||||
filterable
|
||||
style="width: 150px; margin-left: 10px;"
|
||||
>
|
||||
<el-option
|
||||
v-for="group in group_select_opts"
|
||||
:key="group.value"
|
||||
:label="group.label"
|
||||
:value="group.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input
|
||||
v-else
|
||||
disabled
|
||||
value="全局"
|
||||
style="width: 150px; margin-left: 10px;"
|
||||
/>
|
||||
<el-button type="danger" size="small" text @click="removeRouter(index)" style="margin-left: 10px;">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button type="primary" size="small" text @click="addRouter('ipv4')">
|
||||
添加IPv4路由
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">IPv6路由配置</el-divider>
|
||||
|
||||
<el-form-item label="IPv6路由">
|
||||
<div class="router-section">
|
||||
<div
|
||||
v-for="(router, index) in selectedServer.ipv6_router"
|
||||
:key="index"
|
||||
class="router-item"
|
||||
>
|
||||
<el-input v-model="router.ip" placeholder="IP地址 (如: 2001:db8::1)" style="flex: 1;" />
|
||||
<el-input-number v-model="router.prefix" :min="0" :max="128" placeholder="前缀长度" style="flex: 1; margin-left: 10px;" />
|
||||
<!-- metric -->
|
||||
<el-input-number v-model="router.metric" :min="0" :max="1000000" placeholder="metric" style="flex: 1; margin-left: 10px;" />
|
||||
<el-button type="danger" size="small" text @click="removeRouter('ipv6', index)" style="margin-left: 10px;">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button type="primary" size="small" text @click="addRouter('ipv6')">
|
||||
添加IPv6路由
|
||||
<el-button type="primary" size="small" text @click="addRouter">
|
||||
添加路由
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
|
@ -288,9 +308,10 @@
|
|||
<script setup lang="ts" name="vpn-server-config">
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {getAllDefaultUsers} from '@/api/user';
|
||||
import { getAllDefaultUsers } from '@/api/user';
|
||||
import { GetVPNServerConfigHandler, SetVPNServerConfigHandler, DeleteVPNServerHandler, GetVPNAddressPoolHandler, GetVPNTunnelConfigHandler, GetVPNServerOnlineListHandler } from '@/api/vpn';
|
||||
import { ServerConfig } from '@/types/vpn';
|
||||
import { ServerConfig, VPNRouter } from '@/types/vpn';
|
||||
|
||||
interface UserID {
|
||||
id: number;
|
||||
}
|
||||
|
|
@ -339,21 +360,16 @@ interface TunnelRequestAndResponse {
|
|||
config: TunnelConfig;
|
||||
}
|
||||
|
||||
interface VPNRouter {
|
||||
type: number;
|
||||
ip: string;
|
||||
prefix: number;
|
||||
metric: number;
|
||||
}
|
||||
|
||||
const serverList = ref<ServerConfig[]>([]);
|
||||
const selectedServer = ref<ServerConfig | null>(null);
|
||||
const addressPools = ref<AddressPoolRequest[]>([]);
|
||||
const tunnelConfigs = ref<TunnelRequestAndResponse[]>([]);
|
||||
const user_select_opts = ref<Array<{value: number, label: string}>>([]);
|
||||
const user_select_opts = ref<Array<{ value: number, label: string }>>([]);
|
||||
const group_select_opts = ref<Array<{ value: number, label: string }>>([]);
|
||||
const user_select_ids = ref<number[]>([]);
|
||||
const onlineServers = ref<string[]>([]);
|
||||
let timer: number | null = null;
|
||||
|
||||
// 获取服务器配置列表
|
||||
const getServerConfigs = async () => {
|
||||
try {
|
||||
|
|
@ -395,10 +411,8 @@ const getOnlineServers = async () => {
|
|||
let online_server: ServerConfig[] = [];
|
||||
online_server = response.data;
|
||||
onlineServers.value = [];
|
||||
for(
|
||||
let server of online_server
|
||||
){
|
||||
if(server.vpn_status?.status === 2){
|
||||
for (let server of online_server) {
|
||||
if (server.vpn_status?.status === 2) {
|
||||
onlineServers.value.push(server.server_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -431,21 +445,38 @@ const selectServer = (server: ServerConfig) => {
|
|||
selectedServer.value = JSON.parse(JSON.stringify(server)); // 深拷贝避免直接修改
|
||||
// 确保allow_user_id是用户ID数组
|
||||
if (server.allow_user_id && server.allow_user_id.length > 0) {
|
||||
/**
|
||||
* Extracts the id property from an item object
|
||||
* @param {Object} item - The item object containing an id property
|
||||
* @returns {*} The value of the item's id property
|
||||
*/
|
||||
user_select_ids.value = server.allow_user_id.map(item => item.id);
|
||||
user_select_ids.value = server.allow_user_id.map(item => item.id);
|
||||
} else {
|
||||
selectedServer.value.allow_user_id = [];
|
||||
}
|
||||
// 确保路由数组存在
|
||||
if (!selectedServer.value.ipv4_router) {
|
||||
selectedServer.value.ipv4_router = [];
|
||||
if (!selectedServer.value.routers) {
|
||||
selectedServer.value.routers = [];
|
||||
}
|
||||
if (!selectedServer.value.ipv6_router) {
|
||||
selectedServer.value.ipv6_router = [];
|
||||
// 兼容旧数据格式 - 将ipv4_router和ipv6_router转换为新格式
|
||||
if ((selectedServer.value as any).ipv4_router && Array.isArray((selectedServer.value as any).ipv4_router)) {
|
||||
for (const router of (selectedServer.value as any).ipv4_router) {
|
||||
selectedServer.value.routers.push({
|
||||
type: 4,
|
||||
ip: router.ip || '',
|
||||
prefix: router.prefix || 24,
|
||||
metric: router.metric || 35,
|
||||
router_type: 0,
|
||||
target_id: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
if ((selectedServer.value as any).ipv6_router && Array.isArray((selectedServer.value as any).ipv6_router)) {
|
||||
for (const router of (selectedServer.value as any).ipv6_router) {
|
||||
selectedServer.value.routers.push({
|
||||
type: 6,
|
||||
ip: router.ip || '',
|
||||
prefix: router.prefix || 64,
|
||||
metric: router.metric || 35,
|
||||
router_type: 0,
|
||||
target_id: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -480,37 +511,31 @@ const deleteServer = async (serverId: string) => {
|
|||
const saveConfig = async () => {
|
||||
if (!selectedServer.value) return;
|
||||
|
||||
// 验证IPv4路由配置
|
||||
for (let i = 0; i < selectedServer.value.ipv4_router.length; i++) {
|
||||
const router = selectedServer.value.ipv4_router[i];
|
||||
// 验证路由配置
|
||||
for (let i = 0; i < selectedServer.value.routers.length; i++) {
|
||||
const router = selectedServer.value.routers[i];
|
||||
if (!router.ip || router.ip.trim() === '') {
|
||||
ElMessage.error(`IPv4路由第${i + 1}行:IP地址不能为空`);
|
||||
ElMessage.error(`路由第${i + 1}行:IP地址不能为空`);
|
||||
return;
|
||||
}
|
||||
if (!isValidIPv4Address(router.ip)) {
|
||||
ElMessage.error(`IPv4路由第${i + 1}行:IP地址格式不正确`);
|
||||
if (router.type === 4 && !isValidIPv4Address(router.ip)) {
|
||||
ElMessage.error(`路由第${i + 1}行:IPv4地址格式不正确`);
|
||||
return;
|
||||
}
|
||||
if (!router.prefix || router.prefix < 1 || router.prefix > 32) {
|
||||
ElMessage.error(`IPv4路由第${i + 1}行:前缀长度必须在1-32之间`);
|
||||
if (router.type === 6 && !isValidIPv6Address(router.ip)) {
|
||||
ElMessage.error(`路由第${i + 1}行:IPv6地址格式不正确`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 验证IPv6路由配置
|
||||
for (let i = 0; i < selectedServer.value.ipv6_router.length; i++) {
|
||||
const router = selectedServer.value.ipv6_router[i];
|
||||
if (!router.ip || router.ip.trim() === '') {
|
||||
ElMessage.error(`IPv6路由第${i + 1}行:IP地址不能为空`);
|
||||
const maxPrefix = router.type === 4 ? 32 : 128;
|
||||
if (!router.prefix || router.prefix < 0 || router.prefix > maxPrefix) {
|
||||
ElMessage.error(`路由第${i + 1}行:前缀长度必须在0-${maxPrefix}之间`);
|
||||
return;
|
||||
}
|
||||
if (!isValidIPv6Address(router.ip)) {
|
||||
ElMessage.error(`IPv6路由第${i + 1}行:IP地址格式不正确`);
|
||||
return;
|
||||
}
|
||||
if (!router.prefix || router.prefix < 1 || router.prefix > 128) {
|
||||
ElMessage.error(`IPv6路由第${i + 1}行:前缀长度必须在1-128之间`);
|
||||
return;
|
||||
if (router.router_type === 1 || router.router_type === 2) {
|
||||
if (!router.target_id || router.target_id <= 0) {
|
||||
ElMessage.error(`路由第${i + 1}行:请选择目标用户或用户组`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -524,7 +549,7 @@ const saveConfig = async () => {
|
|||
server_ip: selectedServer.value.server_ip,
|
||||
server_info: selectedServer.value.server_info,
|
||||
config: configData
|
||||
}
|
||||
};
|
||||
try {
|
||||
let resp = await SetVPNServerConfigHandler(req);
|
||||
if (resp["code"] !== 0) {
|
||||
|
|
@ -532,7 +557,7 @@ const saveConfig = async () => {
|
|||
return;
|
||||
}
|
||||
ElMessage.success('配置保存成功');
|
||||
//获取最新数据
|
||||
// 获取最新数据
|
||||
getServerConfigs();
|
||||
} catch (error) {
|
||||
ElMessage.error('保存配置失败');
|
||||
|
|
@ -541,39 +566,35 @@ const saveConfig = async () => {
|
|||
};
|
||||
|
||||
// 添加路由
|
||||
const addRouter = (type: 'ipv4' | 'ipv6') => {
|
||||
const addRouter = () => {
|
||||
if (!selectedServer.value) return;
|
||||
|
||||
if (!selectedServer.value.routers) {
|
||||
selectedServer.value.routers = [];
|
||||
}
|
||||
|
||||
const router: VPNRouter = {
|
||||
type: type === 'ipv4' ? 4 : 6,
|
||||
type: 4,
|
||||
ip: '',
|
||||
prefix: type === 'ipv4' ? 24 : 64,
|
||||
metric: 35
|
||||
prefix: 24,
|
||||
metric: 35,
|
||||
router_type: 0,
|
||||
target_id: 0
|
||||
};
|
||||
|
||||
if (!selectedServer.value.ipv4_router) {
|
||||
selectedServer.value.ipv4_router = [];
|
||||
}
|
||||
if (!selectedServer.value.ipv6_router) {
|
||||
selectedServer.value.ipv6_router = [];
|
||||
}
|
||||
|
||||
if (type === 'ipv4') {
|
||||
selectedServer.value.ipv4_router.push(router);
|
||||
} else {
|
||||
selectedServer.value.ipv6_router.push(router);
|
||||
}
|
||||
selectedServer.value.routers.push(router);
|
||||
};
|
||||
|
||||
// 删除路由
|
||||
const removeRouter = (type: 'ipv4' | 'ipv6', index: number) => {
|
||||
const removeRouter = (index: number) => {
|
||||
if (!selectedServer.value) return;
|
||||
selectedServer.value.routers.splice(index, 1);
|
||||
};
|
||||
|
||||
if (type === 'ipv4') {
|
||||
selectedServer.value.ipv4_router.splice(index, 1);
|
||||
} else {
|
||||
selectedServer.value.ipv6_router.splice(index, 1);
|
||||
}
|
||||
// 路由类型变化处理
|
||||
const handleRouterTypeChange = (router: VPNRouter) => {
|
||||
// 重置目标ID
|
||||
router.target_id = 0;
|
||||
};
|
||||
|
||||
// 验证IPv4地址格式
|
||||
|
|
@ -599,16 +620,21 @@ const GetAllDefaultUsers = async () => {
|
|||
return;
|
||||
}
|
||||
user_select_opts.value.length = 0;
|
||||
group_select_opts.value.length = 0;
|
||||
for (let i = 0; i < response.data.length; i++) {
|
||||
let user = response.data[i];
|
||||
if (user.type === 0) {
|
||||
let item = response.data[i];
|
||||
if (item.type === 0) {
|
||||
user_select_opts.value.push({
|
||||
value: user.id,
|
||||
label: user.name
|
||||
value: item.id,
|
||||
label: item.name
|
||||
});
|
||||
} else if (item.type === 1) {
|
||||
group_select_opts.value.push({
|
||||
value: item.id,
|
||||
label: item.name
|
||||
});
|
||||
}
|
||||
}
|
||||
//console.log("user_select_opts:", user_select_opts.value);
|
||||
} catch (error) {
|
||||
console.error('获取默认用户失败:', error);
|
||||
}
|
||||
|
|
@ -717,6 +743,8 @@ onUnmounted(() => {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.router-item:last-child {
|
||||
|
|
|
|||
Loading…
Reference in New Issue