Fix connection logs: real-time status.log parsing
Root cause: listConnLogs only read from db.json which was never
populated (background sync silently failed due to Status check and
broken regex).
Fix approach: read status.log directly in API handlers — no DB
dependency, works immediately when OpenVPN is running.
Changes:
api/router.go:
- listConnLogs: parse status.log from all instances in real-time,
merge with historical DB entries for disconnected sessions
- dashboard: same real-time approach for recent_conn_logs
pkg/openvpn/manager.go:
- Fix regex for status-version 3 format (11 fields):
CLIENT_LIST,CN,RealAddr,VPNAddr,IPv6,BytesRecv,BytesSent,ConnectedSince,...
Old regex assumed wrong field order (CN,ConnectedSince,RealAddr)
- Add parseConnTime() helper supporting multiple time formats
internal/service/service.go:
- Remove Status=='running' check (was blocking sync for all instances)
- Add log.Printf debug output for sync operations
- Add 'log' import
internal/store/store.go:
- Add ListActiveConns(instanceID) for background sync
- Add UpdateActiveConnStats() for traffic updates without flush
This commit is contained in:
@@ -217,6 +217,33 @@ func (s *Server) dashboard(c *gin.Context) {
|
||||
"allow_networks": in.AllowNetworks,
|
||||
})
|
||||
}
|
||||
// 实时连接日志:从 status.log 读取在线用户
|
||||
var recentConnLogs []model.ConnectionLog
|
||||
for _, in := range instances {
|
||||
statusPath := filepath.Join(s.Svc.Cfg.InstanceDir(in.Name), "status.log")
|
||||
entries, _ := s.Svc.Ovm.ParseStatus(statusPath)
|
||||
for _, e := range entries {
|
||||
recentConnLogs = append(recentConnLogs, model.ConnectionLog{
|
||||
InstanceID: in.ID, CommonName: e.CommonName,
|
||||
RealIP: e.RealAddress, VPNIP: e.VPNAddress,
|
||||
BytesIn: e.BytesRecv, BytesOut: e.BytesSent,
|
||||
ConnectedAt: e.ConnectedAt,
|
||||
})
|
||||
}
|
||||
}
|
||||
// 补充历史已断开的
|
||||
history := s.Svc.Store.ListConnLogs("", 20)
|
||||
liveSet := map[string]bool{}
|
||||
for _, l := range recentConnLogs {
|
||||
liveSet[l.CommonName] = true
|
||||
}
|
||||
for _, h := range history {
|
||||
if h.DisconnectedAt == nil && liveSet[h.CommonName] {
|
||||
continue
|
||||
}
|
||||
recentConnLogs = append(recentConnLogs, h)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"instances": len(instances),
|
||||
"running": countByStatus(instances, "running"),
|
||||
@@ -226,7 +253,7 @@ func (s *Server) dashboard(c *gin.Context) {
|
||||
"online": online,
|
||||
"expiring_certs": expiring,
|
||||
"recent_audits": s.Svc.Store.ListAudits(20),
|
||||
"recent_conn_logs": s.Svc.Store.ListConnLogs("", 20),
|
||||
"recent_conn_logs": recentConnLogs,
|
||||
"access_overview": overview,
|
||||
})
|
||||
}
|
||||
@@ -467,7 +494,48 @@ func (s *Server) listCerts(c *gin.Context) {
|
||||
// ---- conn logs ----
|
||||
|
||||
func (s *Server) listConnLogs(c *gin.Context) {
|
||||
c.JSON(200, s.Svc.Store.ListConnLogs(c.Query("instance"), 200))
|
||||
// 1) 数据库里的历史日志(有断开时间的老连接)
|
||||
history := s.Svc.Store.ListConnLogs(c.Query("instance"), 200)
|
||||
|
||||
// 2) 实时解析所有实例的 status.log,生成当前在线用户
|
||||
var live []model.ConnectionLog
|
||||
instances := s.Svc.Store.ListInstances()
|
||||
for _, in := range instances {
|
||||
if c.Query("instance") != "" && c.Query("instance") != in.ID {
|
||||
continue
|
||||
}
|
||||
statusPath := filepath.Join(s.Svc.Cfg.InstanceDir(in.Name), "status.log")
|
||||
entries, err := s.Svc.Ovm.ParseStatus(statusPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, e := range entries {
|
||||
live = append(live, model.ConnectionLog{
|
||||
InstanceID: in.ID,
|
||||
CommonName: e.CommonName,
|
||||
RealIP: e.RealAddress,
|
||||
VPNIP: e.VPNAddress,
|
||||
BytesIn: e.BytesRecv,
|
||||
BytesOut: e.BytesSent,
|
||||
ConnectedAt: e.ConnectedAt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 合并:先输出在线的,再输出历史的(去重:跳过 history 中与 live CN 重复的未断开记录)
|
||||
liveSet := map[string]bool{}
|
||||
for _, l := range live {
|
||||
liveSet[l.CommonName] = true
|
||||
}
|
||||
var merged []model.ConnectionLog
|
||||
merged = append(merged, live...)
|
||||
for _, h := range history {
|
||||
if h.DisconnectedAt == nil && liveSet[h.CommonName] {
|
||||
continue // 已用实时数据替代
|
||||
}
|
||||
merged = append(merged, h)
|
||||
}
|
||||
c.JSON(200, merged)
|
||||
}
|
||||
|
||||
// ---- backups ----
|
||||
|
||||
Reference in New Issue
Block a user