feat: 新增管理员踢下线 VPN 客户端功能

- vpn/service.go: ClientInfo 增加 user_id 字段,新增 KickUser 方法
  用读锁收集目标连接后逐个关闭 WebSocket,返回断开数量
- vpn/tunnel.go: info() 填充 UserID
- handler/vpn.go: 新增 KickUserClient handler,校验用户存在后调用 KickUser
- router.go: 注册 DELETE /api/admin/vpn/clients/:id
- VpnView.vue: 在线客户端表格新增操作列与踢下线按钮,handleKick 确认后调用 API 并刷新
- zh.ts/en.ts: 新增 kick/confirmKick/kickFailed 国际化 key
This commit is contained in:
2026-07-10 13:01:26 +08:00
parent 129c6c7a96
commit fea3fc62f7
7 changed files with 73 additions and 1 deletions
+16
View File
@@ -374,10 +374,26 @@ func (s *VpnService) ClientList() []ClientInfo {
}
type ClientInfo struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
IP string `json:"ip"`
IP6 string `json:"ip6,omitempty"`
ConnectedAt string `json:"connected_at"`
}
func (s *VpnService) KickUser(userID uint) int {
s.mu.RLock()
var toClose []*tunnelConn
for c := range s.clients {
if c.user.ID == userID {
toClose = append(toClose, c)
}
}
s.mu.RUnlock()
for _, c := range toClose {
c.close()
}
return len(toClose)
}
var VPN *VpnService
+1
View File
@@ -79,6 +79,7 @@ func (c *tunnelConn) close() {
func (c *tunnelConn) info() ClientInfo {
ci := ClientInfo{
UserID: c.user.ID,
Username: c.user.Username,
IP: c.assignedIP.String(),
ConnectedAt: c.connectedAt.Format("2006-01-02 15:04:05"),