Bind Feishu identity to OpenVPN sessions and refine management UI
This commit is contained in:
1 parent
347799cb1e
commit
8fd8c0cd73
27 files changed
+1556
-1011
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"vpn-platform/backend/internal/core"
|
||||
"vpn-platform/backend/internal/identityprovider"
|
||||
"vpn-platform/backend/internal/model"
|
||||
"vpn-platform/backend/internal/store"
|
||||
)
|
||||
@@ -28,6 +29,13 @@ func SyncSessions(tunnelID uint, protocol string, clients []ClientInfo) {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
subjects := make([]string, 0, len(clients))
|
||||
for _, client := range clients {
|
||||
subjects = append(subjects, client.Username)
|
||||
}
|
||||
var tunnel model.Tunnel
|
||||
store.DB.First(&tunnel, tunnelID)
|
||||
names := identityprovider.ProfileNames(&tunnel, subjects)
|
||||
|
||||
// 1. 收集当前在线的标识 key(username|realAddress)
|
||||
currentKeys := map[string]bool{}
|
||||
@@ -44,10 +52,12 @@ func SyncSessions(tunnelID uint, protocol string, clients []ClientInfo) {
|
||||
// 已有会话 → 更新流量
|
||||
store.DB.Model(&existing).Updates(map[string]interface{}{
|
||||
"rx_bytes": c.RxBytes, "tx_bytes": c.TxBytes,
|
||||
"display_name": names[c.Username],
|
||||
})
|
||||
} else {
|
||||
// 新连接 → 创建会话 + 审计日志
|
||||
session := model.VPNSession{
|
||||
DisplayName: names[c.Username],
|
||||
TunnelID: tunnelID,
|
||||
Username: c.Username,
|
||||
Protocol: protocol,
|
||||
@@ -75,7 +85,7 @@ func SyncSessions(tunnelID uint, protocol string, clients []ClientInfo) {
|
||||
key := s.Username + "|" + s.RealAddress
|
||||
if !currentKeys[key] {
|
||||
store.DB.Model(&s).Updates(map[string]interface{}{
|
||||
"status": "disconnected",
|
||||
"status": "disconnected",
|
||||
"disconnected_at": now,
|
||||
})
|
||||
duration := now.Sub(s.ConnectedAt).Round(time.Second)
|
||||
@@ -84,16 +94,6 @@ func SyncSessions(tunnelID uint, protocol string, clients []ClientInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 如果 clients 为空(隧道无在线用户),标记该隧道所有 active 会话为 disconnected
|
||||
if len(clients) == 0 {
|
||||
for _, s := range activeSessions {
|
||||
store.DB.Model(&s).Updates(map[string]interface{}{
|
||||
"status": "disconnected",
|
||||
"disconnected_at": now,
|
||||
})
|
||||
LogConnection(tunnelID, s.Username, "disconnect", s.RealAddress, "隧道无在线用户")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SyncFromPeers 从 core.Status.Peers 同步(WireGuard 用)
|
||||
@@ -132,12 +132,16 @@ func LogConnection(tunnelID uint, username, event, ip, detail string) {
|
||||
if store.DB == nil {
|
||||
return
|
||||
}
|
||||
var tunnel model.Tunnel
|
||||
store.DB.First(&tunnel, tunnelID)
|
||||
names := identityprovider.ProfileNames(&tunnel, []string{username})
|
||||
store.DB.Create(&model.ConnectionLog{
|
||||
TunnelID: tunnelID,
|
||||
Username: username,
|
||||
Event: event,
|
||||
IPAddress: ip,
|
||||
Detail: detail,
|
||||
DisplayName: names[username],
|
||||
TunnelID: tunnelID,
|
||||
Username: username,
|
||||
Event: event,
|
||||
IPAddress: ip,
|
||||
Detail: detail,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package sessionmgr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"vpn-platform/backend/internal/model"
|
||||
"vpn-platform/backend/internal/store"
|
||||
)
|
||||
|
||||
func TestDisconnectLoggedOnceWhenTunnelHasNoClients(t *testing.T) {
|
||||
previous := store.DB
|
||||
t.Cleanup(func() {
|
||||
if store.DB != nil {
|
||||
if db, err := store.DB.DB(); err == nil {
|
||||
_ = db.Close()
|
||||
}
|
||||
}
|
||||
store.DB = previous
|
||||
})
|
||||
if err := store.Init(t.TempDir()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tunnel := model.Tunnel{Name: "audit-test", Protocol: "openvpn"}
|
||||
if err := store.DB.Create(&tunnel).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := ClientInfo{Username: "tenant:ou_123", RealAddress: "203.0.113.1:1234", RxBytes: 42, TxBytes: 84}
|
||||
SyncSessions(tunnel.ID, "openvpn", []ClientInfo{client})
|
||||
SyncSessions(tunnel.ID, "openvpn", nil)
|
||||
SyncSessions(tunnel.ID, "openvpn", nil)
|
||||
var logs []model.ConnectionLog
|
||||
if err := store.DB.Where("tunnel_id = ?", tunnel.ID).Order("id").Find(&logs).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(logs) != 2 || logs[0].Event != "connect" || logs[1].Event != "disconnect" || logs[1].Username != client.Username {
|
||||
t.Fatalf("expected one connect and one disconnect with stable subject, got %+v", logs)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user