feat: audit api, sdwan persist, relay fallback updates
This commit is contained in:
@@ -33,6 +33,7 @@ type NodeInfo struct {
|
||||
ShareBandwidth int `json:"shareBandwidth"`
|
||||
RelayEnabled bool `json:"relayEnabled"`
|
||||
SuperRelay bool `json:"superRelay"`
|
||||
RelayOfficial bool `json:"relayOfficial"`
|
||||
HasIPv4 int `json:"hasIPv4"`
|
||||
IPv6 string `json:"ipv6"`
|
||||
LoginTime time.Time `json:"loginTime"`
|
||||
@@ -78,12 +79,12 @@ func New(cfg config.ServerConfig) *Server {
|
||||
if err != nil {
|
||||
log.Printf("[server] open store failed: %v", err)
|
||||
} else {
|
||||
// bootstrap default admin/admin in tenant 1
|
||||
// bootstrap default tenant if missing
|
||||
if _, gErr := st.GetTenantByID(1); gErr != nil {
|
||||
if _, _, _, cErr := st.CreateTenantWithUsers("default", "admin", "admin"); cErr != nil {
|
||||
log.Printf("[server] bootstrap default tenant failed: %v", cErr)
|
||||
} else {
|
||||
log.Printf("[server] bootstrap default tenant created (tenant=1, admin/admin)")
|
||||
log.Printf("[server] bootstrap default tenant created (tenant=1)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,7 +161,7 @@ func (s *Server) GetOnlineNodesByTenant(tenantID int64) []*NodeInfo {
|
||||
}
|
||||
|
||||
// GetRelayNodes returns nodes that can serve as relay.
|
||||
// Priority: same-user private relay → super relay
|
||||
// Priority: same-user private relay → super relay (exclude official relays)
|
||||
func (s *Server) GetRelayNodes(forUser string, excludeNodes ...string) []*NodeInfo {
|
||||
excludeSet := make(map[string]bool)
|
||||
for _, n := range excludeNodes {
|
||||
@@ -172,7 +173,7 @@ func (s *Server) GetRelayNodes(forUser string, excludeNodes ...string) []*NodeIn
|
||||
|
||||
var privateRelays, superRelays []*NodeInfo
|
||||
for _, n := range s.nodes {
|
||||
if !n.IsOnline() || excludeSet[n.Name] || !n.RelayEnabled {
|
||||
if !n.IsOnline() || excludeSet[n.Name] || !n.RelayEnabled || n.RelayOfficial {
|
||||
continue
|
||||
}
|
||||
if n.User == forUser {
|
||||
@@ -200,13 +201,33 @@ func (s *Server) GetRelayNodesByTenant(tenantID int64, excludeNodes ...string) [
|
||||
if !n.IsOnline() || excludeSet[n.Name] {
|
||||
continue
|
||||
}
|
||||
if n.TenantID == tenantID && (n.RelayEnabled || n.SuperRelay) {
|
||||
if n.TenantID == tenantID && (n.RelayEnabled || n.SuperRelay) && !n.RelayOfficial {
|
||||
relays = append(relays, n)
|
||||
}
|
||||
}
|
||||
return relays
|
||||
}
|
||||
|
||||
// GetOfficialRelays returns official relay nodes (global pool)
|
||||
func (s *Server) GetOfficialRelays(excludeNodes ...string) []*NodeInfo {
|
||||
excludeSet := make(map[string]bool)
|
||||
for _, n := range excludeNodes {
|
||||
excludeSet[n] = true
|
||||
}
|
||||
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
var relays []*NodeInfo
|
||||
for _, n := range s.nodes {
|
||||
if !n.IsOnline() || excludeSet[n.Name] || !n.RelayEnabled || !n.RelayOfficial {
|
||||
continue
|
||||
}
|
||||
relays = append(relays, n)
|
||||
}
|
||||
return relays
|
||||
}
|
||||
|
||||
// HandleWS is the WebSocket handler for client connections.
|
||||
func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
ws, err := s.upgrader.Upgrade(w, r, nil)
|
||||
@@ -287,6 +308,7 @@ func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
|
||||
ShareBandwidth: loginReq.ShareBandwidth,
|
||||
RelayEnabled: loginReq.RelayEnabled,
|
||||
SuperRelay: loginReq.SuperRelay,
|
||||
RelayOfficial: loginReq.RelayOfficial,
|
||||
PublicIP: loginReq.PublicIP,
|
||||
PublicPort: loginReq.PublicPort,
|
||||
LoginTime: time.Now(),
|
||||
@@ -464,23 +486,68 @@ func (s *Server) registerHandlers(conn *signal.Conn, node *NodeInfo) {
|
||||
|
||||
// handleRelayNodeReq finds and returns the best relay node.
|
||||
func (s *Server) handleRelayNodeReq(conn *signal.Conn, requester *NodeInfo, req protocol.RelayNodeReq) error {
|
||||
relays := s.GetRelayNodes(requester.User, requester.Name, req.PeerNode)
|
||||
|
||||
if len(relays) == 0 {
|
||||
mode := "tenant"
|
||||
if req.Mode == "official" {
|
||||
mode = "official"
|
||||
official := s.GetOfficialRelays(requester.Name, req.PeerNode)
|
||||
if len(official) == 0 {
|
||||
return conn.Write(protocol.MsgRelay, protocol.SubRelayNodeRsp, protocol.RelayNodeRsp{Error: 1})
|
||||
}
|
||||
relay := official[0]
|
||||
totp := auth.GenTOTP(relay.Token, time.Now().Unix())
|
||||
log.Printf("[server] relay selected: %s (%s) for %s → %s", relay.Name, mode, requester.Name, req.PeerNode)
|
||||
return conn.Write(protocol.MsgRelay, protocol.SubRelayNodeRsp, protocol.RelayNodeRsp{
|
||||
Error: 1,
|
||||
RelayName: relay.Name,
|
||||
RelayIP: relay.PublicIP,
|
||||
RelayPort: config.DefaultRelayPort,
|
||||
RelayToken: totp,
|
||||
Mode: mode,
|
||||
Error: 0,
|
||||
})
|
||||
}
|
||||
// prefer hub relay if sdwan mode=hub
|
||||
if requester.TenantID > 0 && s.sdwan != nil {
|
||||
cfg := s.sdwan.getTenant(requester.TenantID)
|
||||
if cfg.Mode == "hub" && cfg.HubNode != "" && cfg.HubNode != requester.Name && cfg.HubNode != req.PeerNode {
|
||||
hub := s.GetNode(cfg.HubNode)
|
||||
if hub != nil && hub.IsOnline() && hub.TenantID == requester.TenantID && hub.RelayEnabled {
|
||||
log.Printf("[server] relay selected: %s (hub) for %s → %s", hub.Name, requester.Name, req.PeerNode)
|
||||
totp := auth.GenTOTP(hub.Token, time.Now().Unix())
|
||||
return conn.Write(protocol.MsgRelay, protocol.SubRelayNodeRsp, protocol.RelayNodeRsp{
|
||||
RelayName: hub.Name,
|
||||
RelayIP: hub.PublicIP,
|
||||
RelayPort: config.DefaultRelayPort,
|
||||
RelayToken: totp,
|
||||
Mode: "private",
|
||||
Error: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// prefer same-tenant relays, exclude requester and peer
|
||||
relays := s.GetRelayNodesByTenant(requester.TenantID, requester.Name, req.PeerNode)
|
||||
if len(relays) == 0 {
|
||||
// fallback to same-user (private) then super
|
||||
relays = s.GetRelayNodes(requester.User, requester.Name, req.PeerNode)
|
||||
if len(relays) == 0 {
|
||||
// final fallback: official relays
|
||||
official := s.GetOfficialRelays(requester.Name, req.PeerNode)
|
||||
if len(official) == 0 {
|
||||
return conn.Write(protocol.MsgRelay, protocol.SubRelayNodeRsp, protocol.RelayNodeRsp{Error: 1})
|
||||
}
|
||||
relays = official
|
||||
mode = "official"
|
||||
} else if relays[0].User != requester.User {
|
||||
mode = "super"
|
||||
} else {
|
||||
mode = "private"
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the first (best) relay
|
||||
relay := relays[0]
|
||||
totp := auth.GenTOTP(relay.Token, time.Now().Unix())
|
||||
|
||||
mode := "private"
|
||||
if relay.User != requester.User {
|
||||
mode = "super"
|
||||
}
|
||||
|
||||
log.Printf("[server] relay selected: %s (%s) for %s → %s", relay.Name, mode, requester.Name, req.PeerNode)
|
||||
|
||||
return conn.Write(protocol.MsgRelay, protocol.SubRelayNodeRsp, protocol.RelayNodeRsp{
|
||||
@@ -510,6 +577,7 @@ func (s *Server) PushConnect(fromNode *NodeInfo, toNodeName string, app protocol
|
||||
FromIP: fromNode.PublicIP,
|
||||
Peer: protocol.PunchParams{
|
||||
IP: fromNode.PublicIP,
|
||||
Port: fromNode.PublicPort,
|
||||
NATType: fromNode.NATType,
|
||||
HasIPv4: fromNode.HasIPv4,
|
||||
Token: auth.GenTOTP(fromNode.Token, time.Now().Unix()),
|
||||
@@ -598,6 +666,9 @@ func (s *Server) StartCleanup() {
|
||||
cfg.Mode = "mesh"
|
||||
cfg.HubNode = ""
|
||||
_ = s.sdwan.saveTenant(tid, cfg)
|
||||
if s.store != nil {
|
||||
_ = s.store.AddAuditLog("system", "0", "sdwan_update", "tenant", fmt.Sprintf("%d", tid), "hub->mesh (hub offline)", "")
|
||||
}
|
||||
s.broadcastSDWANTenant(tid, cfg)
|
||||
log.Printf("[sdwan] hub offline, auto fallback to mesh (tenant=%d)", tid)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user