添加在线用户查看及踢出用户
This commit is contained in:
parent
fcb81756e2
commit
97534d399a
|
|
@ -86,6 +86,13 @@ export const GetClientDownloadURLHandler = () => {
|
|||
return request.get("/vpn/clients_url")
|
||||
}
|
||||
|
||||
export const GetServerOnlineUsers = (serverID:string) => {
|
||||
return request.get("/vpn/get_client_online_users?server_id=" + serverID)
|
||||
}
|
||||
export const KickOutOnlineUser = (Data) => {
|
||||
return request.post("/vpn/kick_out_user", Data)
|
||||
}
|
||||
|
||||
export const LocalClientConnectHandler = (Data) => {
|
||||
return local_request.post('/vpn/connect', Data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,12 @@ export const menuData: Menus[] = [
|
|||
index: '/vpn-tunnel',
|
||||
title: '隧道配置',
|
||||
},
|
||||
{
|
||||
id: '755',
|
||||
pid: '75',
|
||||
index: '/vpn-online-user',
|
||||
title: '在线用户',
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -164,6 +164,16 @@ const routes: RouteRecordRaw[] = [
|
|||
},
|
||||
component: () => import(/* webpackChunkName: "system-user" */ '../views/system/vpn-online-connect.vue'),
|
||||
},
|
||||
{
|
||||
path: '/vpn-online-user',
|
||||
name: 'vpn-online-user',
|
||||
meta: {
|
||||
title: 'VPN在线用户连接',
|
||||
permiss: '755',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "system-user" */ '../views/system/vpn-server-online-user.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
path: '/callback',
|
||||
name: 'callback',
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export const usePermissStore = defineStore("permiss", {
|
|||
"752", //VPN地址池管理
|
||||
"753", //VPN隧道管理
|
||||
"754", //VPN客户端UI
|
||||
"755", //VPN在线用户连接
|
||||
],
|
||||
user: ["0", "8", "7", "9", "51" ,"53","55" ,"56", "57", "58", "59", "61", "71", "754"],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
<template>
|
||||
<div class="vpn-server-online-user">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧服务器列表 -->
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>服务器列表</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="server-list">
|
||||
<div
|
||||
v-for="server in serverList"
|
||||
:key="server.server_id"
|
||||
class="server-item"
|
||||
:class="{ active: selectedServer?.server_id === server.server_id }"
|
||||
@click="selectServer(server)"
|
||||
>
|
||||
<div class="server-info">
|
||||
<div class="server-name">
|
||||
<span class="status-indicator" :class="{ 'online': onlineServers.includes(server.server_id) }"></span>
|
||||
{{ server.name }}
|
||||
</div>
|
||||
<div class="server-ip">{{ server.server_ip }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="serverList.length === 0" description="暂无服务器配置" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧在线用户列表 -->
|
||||
<el-col :span="16">
|
||||
<el-card shadow="hover" v-if="selectedServer">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ selectedServer.name }} - 在线用户 ({{ onlineUsers.length }})</span>
|
||||
<el-button type="primary" @click="KickOutAllOnlineUser" :loading="loading">
|
||||
踢出所有用户
|
||||
</el-button>
|
||||
<el-button type="primary" @click="refreshOnlineUsers" :loading="loading">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
:data="onlineUsers"
|
||||
style="width: 100%"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
border
|
||||
>
|
||||
<el-table-column prop="id" label="密钥ID" width="120" />
|
||||
<el-table-column prop="user_name" label="用户名" width="120" />
|
||||
<el-table-column prop="private_ipv4" label="内网IPv4" width="140" />
|
||||
<el-table-column prop="private_ipv6" label="内网IPv6" width="180">
|
||||
<template #default="scope">
|
||||
{{ scope.row.private_ipv6 || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="uuid" label="会话ID" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="last_update_time" label="最后更新时间" width="180">
|
||||
<template #default="scope">
|
||||
{{ formatTime(scope.row.last_update_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="last_update_time" label="操作" width="180" #default="scope">
|
||||
<el-button type="primary" @click="KickOutSomeUser(scope.row)">踢出</el-button>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-empty v-if="!loading && onlineUsers.length === 0" description="该服务器暂无在线用户" />
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="hover" v-else>
|
||||
<el-empty description="请选择要查看的服务器" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { GetVPNServerConfigHandler, GetVPNServerOnlineListHandler,GetServerOnlineUsers,KickOutOnlineUser } from '@/api/vpn';
|
||||
import { on } from 'events';
|
||||
|
||||
interface ServerConfig {
|
||||
name: string;
|
||||
server_id: string;
|
||||
server_ip: string;
|
||||
server_ipv6: string;
|
||||
server_ip_type: number;
|
||||
server_info: string;
|
||||
udp_port: number;
|
||||
tcp_port: number;
|
||||
protocol: number;
|
||||
ip_type: number;
|
||||
ipv4_address_pool: string;
|
||||
ipv6_address_pool: string;
|
||||
dns_server: string;
|
||||
tunnel: string;
|
||||
allow_user_id: any[];
|
||||
encryption: string;
|
||||
hash: string;
|
||||
user_max_device: number;
|
||||
duration_time: number;
|
||||
ipv4_router: any[];
|
||||
ipv6_router: any[];
|
||||
}
|
||||
|
||||
interface OnlineUserInfo {
|
||||
id: number;
|
||||
user_id: number;
|
||||
user_name: string;
|
||||
private_ipv4: string;
|
||||
private_ipv6: string;
|
||||
vpn_dp_secret: string;
|
||||
uuid: string;
|
||||
last_update_time: number;
|
||||
}
|
||||
|
||||
const serverList = ref<ServerConfig[]>([]);
|
||||
const selectedServer = ref<ServerConfig | null>(null);
|
||||
const onlineUsers = ref<OnlineUserInfo[]>([]);
|
||||
const onlineServers = ref<string[]>([]);
|
||||
const loading = ref(false);
|
||||
let timer: number | null = null;
|
||||
let timer2: number | null = null;
|
||||
|
||||
// 获取服务器配置列表
|
||||
const getServerConfigs = async () => {
|
||||
try {
|
||||
const response = await GetVPNServerConfigHandler();
|
||||
serverList.value = response.data;
|
||||
} catch (error) {
|
||||
ElMessage.error('获取服务器配置失败');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const KickOutAllOnlineUser = async() =>{
|
||||
let req = {
|
||||
server_id: selectedServer.value.server_id,
|
||||
type: 1, //all
|
||||
session: [{
|
||||
"user_id": 1,
|
||||
"session": "kickout all"
|
||||
}]
|
||||
}
|
||||
try{
|
||||
let resp = await KickOutOnlineUser(req);
|
||||
if (resp &&resp["code"] == 0){
|
||||
ElMessage.success("踢出成功");
|
||||
}else{
|
||||
ElMessage.error(resp["message"] || "踢出失败");
|
||||
}
|
||||
}catch(error){
|
||||
ElMessage.error("踢出失败");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
const KickOutSomeUser = async(user:OnlineUserInfo) =>{
|
||||
let req = {
|
||||
server_id: selectedServer.value.server_id,
|
||||
sessions: [{
|
||||
"user_id": user.user_id,
|
||||
"session": user.uuid
|
||||
}]
|
||||
|
||||
}
|
||||
try{
|
||||
let resp = await KickOutOnlineUser(req);
|
||||
if (resp &&resp["code"] == 0){
|
||||
ElMessage.success("踢出成功");
|
||||
}else{
|
||||
ElMessage.error(resp["message"] || "踢出失败");
|
||||
}
|
||||
}catch(error){
|
||||
ElMessage.error("踢出失败");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取在线服务器列表
|
||||
const getOnlineServers = async () => {
|
||||
try {
|
||||
const response = await GetVPNServerOnlineListHandler();
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
onlineServers.value = response.data.map((server: any) => server.server_id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取在线服务器状态失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取指定服务器的在线用户
|
||||
const getOnlineUsers = async (serverId: string) => {
|
||||
if (!serverId) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await GetServerOnlineUsers(serverId);
|
||||
if (response["code"] === 0) {
|
||||
if(response.data){
|
||||
onlineUsers.value = response.data;
|
||||
}else{
|
||||
onlineUsers.value = [];
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(response["message"]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取在线用户失败:', error);
|
||||
ElMessage.error('获取在线用户失败');
|
||||
onlineUsers.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新在线用户
|
||||
const refreshOnlineUsers = () => {
|
||||
if (selectedServer.value) {
|
||||
getOnlineUsers(selectedServer.value.server_id);
|
||||
}
|
||||
};
|
||||
|
||||
// 选择服务器
|
||||
const selectServer = (server: ServerConfig) => {
|
||||
selectedServer.value = server;
|
||||
getOnlineUsers(server.server_id);
|
||||
};
|
||||
|
||||
// 格式化时间戳
|
||||
const formatTime = (timestamp: number) => {
|
||||
if (!timestamp) return '-';
|
||||
const date = new Date(timestamp * 1000);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
// 启动定时查询
|
||||
const startOnlineStatusTimer = () => {
|
||||
// 立即查询一次
|
||||
getOnlineServers();
|
||||
// 设置定时器,每2秒查询一次
|
||||
timer = window.setInterval(() => {
|
||||
getOnlineServers();
|
||||
}, 10000);
|
||||
timer2 = window.setInterval(() => {
|
||||
if (selectedServer.value){
|
||||
GetServerOnlineUsers(selectedServer.value.server_id);
|
||||
}
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
// 停止定时查询
|
||||
const stopOnlineStatusTimer = () => {
|
||||
if (timer !== null) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
getServerConfigs();
|
||||
startOnlineStatusTimer();
|
||||
});
|
||||
|
||||
// 组件卸载时清理定时器
|
||||
onUnmounted(() => {
|
||||
stopOnlineStatusTimer();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vpn-server-online-user {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.server-list {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.server-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.server-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.server-item.active {
|
||||
background-color: #ecf5ff;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.server-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.server-name {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #909399;
|
||||
margin-right: 8px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.status-indicator.online {
|
||||
background-color: #67c23a;
|
||||
box-shadow: 0 0 4px rgba(103, 194, 58, 0.5);
|
||||
}
|
||||
|
||||
.server-ip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue