添加路由支持配置用户用户组

This commit is contained in:
lj124 2026-05-14 23:01:25 +08:00
parent 32297ff52c
commit 888828b7a1
2 changed files with 174 additions and 138 deletions

View File

@ -20,6 +20,15 @@ export interface OnlineUserInfo {
client_ip: string; 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 { export interface ServerConfig {
name: string; name: string;
server_id: string; server_id: string;
@ -38,11 +47,10 @@ export interface ServerConfig {
allow_user_id: any[]; allow_user_id: any[];
encryption: string; encryption: string;
hash: string; hash: string;
no_policy_action:number; no_policy_action: number;
user_max_device: number; user_max_device: number;
duration_time: number; duration_time: number;
ipv4_router: any[]; routers: VPNRouter[];
ipv6_router: any[];
vpn_status: VPNStatus; vpn_status: VPNStatus;
} }

View File

@ -229,48 +229,68 @@
</el-select> </el-select>
</el-form-item> </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 class="router-section">
<div <div
v-for="(router, index) in selectedServer.ipv4_router" v-for="(router, index) in selectedServer.routers"
:key="index" :key="index"
class="router-item" class="router-item"
> >
<el-input v-model="router.ip" placeholder="IP地址 (如: 192.168.1.1)" style="flex: 1;" /> <el-select v-model="router.type" placeholder="IP类型" style="width: 120px;">
<el-input-number v-model="router.prefix" :min="0" :max="32" placeholder="前缀长度" style="flex: 1; margin-left: 10px;" /> <el-option label="IPv4" :value="4" />
<!-- metric --> <el-option label="IPv6" :value="6" />
<el-input-number v-model="router.metric" :min="0" :max="1000000" placeholder="metric" style="flex: 1; margin-left: 10px;" /> <el-option label="IPv4/IPv6" :value="46" />
<el-button type="danger" size="small" text @click="removeRouter('ipv4', index)" style="margin-left: 10px;"> </el-select>
删除 <el-input v-model="router.ip" placeholder="IP地址" style="flex: 1; margin-left: 10px;" />
</el-button> <el-input-number v-model="router.prefix" :min="0" :max="128" placeholder="前缀" style="width: 100px; margin-left: 10px;" />
</div> <el-input-number v-model="router.metric" :min="0" :max="1000000" placeholder="metric" style="width: 100px; margin-left: 10px;" />
<el-button type="primary" size="small" text @click="addRouter('ipv4')"> <el-select v-model="router.router_type" placeholder="路由类型" style="width: 130px; margin-left: 10px;" @change="handleRouterTypeChange(router)">
添加IPv4路由 <el-option label="全局路由" :value="0" />
</el-button> <el-option label="用户路由" :value="1" />
</div> <el-option label="用户组路由" :value="2" />
</el-form-item> </el-select>
<el-select
<el-divider content-position="left">IPv6路由配置</el-divider> v-if="router.router_type === 1"
v-model="router.target_id"
<el-form-item label="IPv6路由"> placeholder="选择用户"
<div class="router-section"> filterable
<div style="width: 150px; margin-left: 10px;"
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-option
<el-input-number v-model="router.prefix" :min="0" :max="128" placeholder="前缀长度" style="flex: 1; margin-left: 10px;" /> v-for="user in user_select_opts"
<!-- metric --> :key="user.value"
<el-input-number v-model="router.metric" :min="0" :max="1000000" placeholder="metric" style="flex: 1; margin-left: 10px;" /> :label="user.label"
<el-button type="danger" size="small" text @click="removeRouter('ipv6', index)" style="margin-left: 10px;"> :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> </el-button>
</div> </div>
<el-button type="primary" size="small" text @click="addRouter('ipv6')"> <el-button type="primary" size="small" text @click="addRouter">
添加IPv6路由 添加路由
</el-button> </el-button>
</div> </div>
</el-form-item> </el-form-item>
@ -288,9 +308,10 @@
<script setup lang="ts" name="vpn-server-config"> <script setup lang="ts" name="vpn-server-config">
import { ref, reactive, onMounted, onUnmounted } from 'vue'; import { ref, reactive, onMounted, onUnmounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; 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 { GetVPNServerConfigHandler, SetVPNServerConfigHandler, DeleteVPNServerHandler, GetVPNAddressPoolHandler, GetVPNTunnelConfigHandler, GetVPNServerOnlineListHandler } from '@/api/vpn';
import { ServerConfig } from '@/types/vpn'; import { ServerConfig, VPNRouter } from '@/types/vpn';
interface UserID { interface UserID {
id: number; id: number;
} }
@ -339,21 +360,16 @@ interface TunnelRequestAndResponse {
config: TunnelConfig; config: TunnelConfig;
} }
interface VPNRouter {
type: number;
ip: string;
prefix: number;
metric: number;
}
const serverList = ref<ServerConfig[]>([]); const serverList = ref<ServerConfig[]>([]);
const selectedServer = ref<ServerConfig | null>(null); const selectedServer = ref<ServerConfig | null>(null);
const addressPools = ref<AddressPoolRequest[]>([]); const addressPools = ref<AddressPoolRequest[]>([]);
const tunnelConfigs = ref<TunnelRequestAndResponse[]>([]); 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 user_select_ids = ref<number[]>([]);
const onlineServers = ref<string[]>([]); const onlineServers = ref<string[]>([]);
let timer: number | null = null; let timer: number | null = null;
// //
const getServerConfigs = async () => { const getServerConfigs = async () => {
try { try {
@ -395,10 +411,8 @@ const getOnlineServers = async () => {
let online_server: ServerConfig[] = []; let online_server: ServerConfig[] = [];
online_server = response.data; online_server = response.data;
onlineServers.value = []; onlineServers.value = [];
for( for (let server of online_server) {
let server of online_server if (server.vpn_status?.status === 2) {
){
if(server.vpn_status?.status === 2){
onlineServers.value.push(server.server_id); onlineServers.value.push(server.server_id);
} }
} }
@ -431,21 +445,38 @@ const selectServer = (server: ServerConfig) => {
selectedServer.value = JSON.parse(JSON.stringify(server)); // selectedServer.value = JSON.parse(JSON.stringify(server)); //
// allow_user_idID // allow_user_idID
if (server.allow_user_id && server.allow_user_id.length > 0) { 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 { } else {
selectedServer.value.allow_user_id = []; selectedServer.value.allow_user_id = [];
} }
// //
if (!selectedServer.value.ipv4_router) { if (!selectedServer.value.routers) {
selectedServer.value.ipv4_router = []; selectedServer.value.routers = [];
}
// - ipv4_routeripv6_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
});
} }
if (!selectedServer.value.ipv6_router) {
selectedServer.value.ipv6_router = [];
} }
}; };
@ -480,37 +511,31 @@ const deleteServer = async (serverId: string) => {
const saveConfig = async () => { const saveConfig = async () => {
if (!selectedServer.value) return; if (!selectedServer.value) return;
// IPv4 //
for (let i = 0; i < selectedServer.value.ipv4_router.length; i++) { for (let i = 0; i < selectedServer.value.routers.length; i++) {
const router = selectedServer.value.ipv4_router[i]; const router = selectedServer.value.routers[i];
if (!router.ip || router.ip.trim() === '') { if (!router.ip || router.ip.trim() === '') {
ElMessage.error(`IPv4路由第${i + 1}IP地址不能为空`); ElMessage.error(`路由第${i + 1}IP地址不能为空`);
return; return;
} }
if (!isValidIPv4Address(router.ip)) { if (router.type === 4 && !isValidIPv4Address(router.ip)) {
ElMessage.error(`IPv4路由第${i + 1}IP地址格式不正确`); ElMessage.error(`路由第${i + 1}IPv4地址格式不正确`);
return; return;
} }
if (!router.prefix || router.prefix < 1 || router.prefix > 32) { if (router.type === 6 && !isValidIPv6Address(router.ip)) {
ElMessage.error(`IPv4路由第${i + 1}行:前缀长度必须在1-32之间`); ElMessage.error(`路由第${i + 1}行:IPv6地址格式不正确`);
return; return;
} }
} const maxPrefix = router.type === 4 ? 32 : 128;
if (!router.prefix || router.prefix < 0 || router.prefix > maxPrefix) {
// IPv6 ElMessage.error(`路由第${i + 1}前缀长度必须在0-${maxPrefix}之间`);
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地址不能为空`);
return; return;
} }
if (!isValidIPv6Address(router.ip)) { if (router.router_type === 1 || router.router_type === 2) {
ElMessage.error(`IPv6路由第${i + 1}IP地址格式不正确`); if (!router.target_id || router.target_id <= 0) {
ElMessage.error(`路由第${i + 1}行:请选择目标用户或用户组`);
return; return;
} }
if (!router.prefix || router.prefix < 1 || router.prefix > 128) {
ElMessage.error(`IPv6路由第${i + 1}前缀长度必须在1-128之间`);
return;
} }
} }
@ -524,7 +549,7 @@ const saveConfig = async () => {
server_ip: selectedServer.value.server_ip, server_ip: selectedServer.value.server_ip,
server_info: selectedServer.value.server_info, server_info: selectedServer.value.server_info,
config: configData config: configData
} };
try { try {
let resp = await SetVPNServerConfigHandler(req); let resp = await SetVPNServerConfigHandler(req);
if (resp["code"] !== 0) { if (resp["code"] !== 0) {
@ -532,7 +557,7 @@ const saveConfig = async () => {
return; return;
} }
ElMessage.success('配置保存成功'); ElMessage.success('配置保存成功');
// //
getServerConfigs(); getServerConfigs();
} catch (error) { } catch (error) {
ElMessage.error('保存配置失败'); ElMessage.error('保存配置失败');
@ -541,39 +566,35 @@ const saveConfig = async () => {
}; };
// //
const addRouter = (type: 'ipv4' | 'ipv6') => { const addRouter = () => {
if (!selectedServer.value) return; if (!selectedServer.value) return;
if (!selectedServer.value.routers) {
selectedServer.value.routers = [];
}
const router: VPNRouter = { const router: VPNRouter = {
type: type === 'ipv4' ? 4 : 6, type: 4,
ip: '', ip: '',
prefix: type === 'ipv4' ? 24 : 64, prefix: 24,
metric: 35 metric: 35,
router_type: 0,
target_id: 0
}; };
if (!selectedServer.value.ipv4_router) { selectedServer.value.routers.push(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);
}
}; };
// //
const removeRouter = (type: 'ipv4' | 'ipv6', index: number) => { const removeRouter = (index: number) => {
if (!selectedServer.value) return; if (!selectedServer.value) return;
selectedServer.value.routers.splice(index, 1);
};
if (type === 'ipv4') { //
selectedServer.value.ipv4_router.splice(index, 1); const handleRouterTypeChange = (router: VPNRouter) => {
} else { // ID
selectedServer.value.ipv6_router.splice(index, 1); router.target_id = 0;
}
}; };
// IPv4 // IPv4
@ -599,16 +620,21 @@ const GetAllDefaultUsers = async () => {
return; return;
} }
user_select_opts.value.length = 0; user_select_opts.value.length = 0;
group_select_opts.value.length = 0;
for (let i = 0; i < response.data.length; i++) { for (let i = 0; i < response.data.length; i++) {
let user = response.data[i]; let item = response.data[i];
if (user.type === 0) { if (item.type === 0) {
user_select_opts.value.push({ user_select_opts.value.push({
value: user.id, value: item.id,
label: user.name 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) { } catch (error) {
console.error('获取默认用户失败:', error); console.error('获取默认用户失败:', error);
} }
@ -717,6 +743,8 @@ onUnmounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 10px;
flex-wrap: wrap;
gap: 5px;
} }
.router-item:last-child { .router-item:last-child {