package handler import ( "database/sql" "encoding/json" "strconv" "strings" "time" ) const bytesPerGB int64 = 1024 * 1024 * 1024 type userTunnelPolicy struct { ID int64 UserID int64 TunnelID int64 Flow int64 InFlow int64 OutFlow int64 ExpTime int64 Status int } type gostConfigSnapshot struct { Services []namedConfigItem `json:"services"` Chains []namedConfigItem `json:"chains"` Limiters []namedConfigItem `json:"limiters"` } type namedConfigItem struct { Name string `json:"name"` } func (h *Handler) processFlowItem(item flowItem) { serviceName := strings.TrimSpace(item.N) if serviceName == "" || serviceName == "web_api" { return } forwardID, userID, userTunnelID, ok := parseFlowServiceIDs(serviceName) if ok { inFlow, outFlow := h.scaleFlowByTunnel(forwardID, item.D, item.U) _ = h.repo.AddFlow(forwardID, userID, userTunnelID, inFlow, outFlow) if userTunnelID > 0 { h.enforceFlowPolicies(userID, userTunnelID) } return } runtimeID, ok := parsePeerShareRuntimeServiceID(serviceName) if !ok { return } h.processPeerShareFlow(runtimeID, item) } func parseFlowServiceIDs(serviceName string) (int64, int64, int64, bool) { parts := strings.Split(serviceName, "_") if len(parts) < 3 { return 0, 0, 0, false } forwardID, err1 := strconv.ParseInt(parts[0], 10, 64) userID, err2 := strconv.ParseInt(parts[1], 10, 64) userTunnelID, err3 := strconv.ParseInt(parts[2], 10, 64) if err1 != nil || err2 != nil || err3 != nil || forwardID <= 0 || userID <= 0 { return 0, 0, 0, false } return forwardID, userID, userTunnelID, true } func parsePeerShareRuntimeServiceID(serviceName string) (int64, bool) { const prefix = "fed_svc_" if !strings.HasPrefix(serviceName, prefix) { return 0, false } raw := strings.TrimPrefix(serviceName, prefix) if raw == "" { return 0, false } parts := strings.SplitN(raw, "_", 2) runtimeID, err := strconv.ParseInt(parts[0], 10, 64) if err != nil || runtimeID <= 0 { return 0, false } return runtimeID, true } func (h *Handler) processPeerShareFlow(runtimeID int64, item flowItem) { if h == nil || h.repo == nil || runtimeID <= 0 { return } runtime, err := h.repo.GetPeerShareRuntimeByID(runtimeID) if err != nil || runtime == nil || runtime.ShareID <= 0 || runtime.Status != 1 { return } delta := item.D + item.U if delta <= 0 { return } _ = h.repo.AddPeerShareCurrentFlow(runtime.ShareID, delta) share, err := h.repo.GetPeerShare(runtime.ShareID) if err != nil || share == nil { return } if !isPeerShareFlowExceeded(share) { return } h.enforcePeerShareFlowLimit(share.ID) } func (h *Handler) enforcePeerShareFlowLimit(shareID int64) { if h == nil || h.repo == nil || shareID <= 0 { return } runtimes, err := h.repo.ListActivePeerShareRuntimesByShareID(shareID) if err != nil || len(runtimes) == 0 { return } now := time.Now().UnixMilli() for _, runtime := range runtimes { if h.wsServer != nil && runtime.Applied == 1 { if strings.TrimSpace(runtime.ServiceName) != "" { _, _ = h.sendNodeCommand(runtime.NodeID, "DeleteService", map[string]interface{}{"services": []string{runtime.ServiceName}}, false, true) } if strings.TrimSpace(runtime.Role) == "middle" && strings.TrimSpace(runtime.ChainName) != "" { _, _ = h.sendNodeCommand(runtime.NodeID, "DeleteChains", map[string]interface{}{"chain": runtime.ChainName}, false, true) } } _ = h.repo.MarkPeerShareRuntimeReleased(runtime.ID, now) } } func (h *Handler) scaleFlowByTunnel(forwardID int64, inFlow int64, outFlow int64) (int64, int64) { forward, err := h.getForwardRecord(forwardID) if err != nil || forward == nil { return inFlow, outFlow } tunnel, err := h.getTunnelRecord(forward.TunnelID) if err != nil || tunnel == nil { return inFlow, outFlow } scaledIn := int64(float64(inFlow)*tunnel.TrafficRatio) * tunnel.Flow scaledOut := int64(float64(outFlow)*tunnel.TrafficRatio) * tunnel.Flow return scaledIn, scaledOut } func (h *Handler) enforceFlowPolicies(userID int64, userTunnelID int64) { now := time.Now().UnixMilli() if h.shouldPauseUser(userID, now) { h.pauseUserForwards(userID, now) } policy, err := h.getUserTunnelPolicy(userTunnelID) if err != nil || policy == nil { return } if shouldPauseUserTunnel(policy, now) { h.pauseUserTunnelForwards(policy.UserID, policy.TunnelID, now) } } func (h *Handler) shouldPauseUser(userID int64, now int64) bool { user, err := h.repo.GetUserByID(userID) if err != nil || user == nil { return false } flowLimit := user.Flow * bytesPerGB current := user.InFlow + user.OutFlow if flowLimit < current { return true } if user.ExpTime > 0 && user.ExpTime <= now { return true } return user.Status != 1 } func shouldPauseUserTunnel(policy *userTunnelPolicy, now int64) bool { if policy == nil { return false } flowLimit := policy.Flow * bytesPerGB current := policy.InFlow + policy.OutFlow if current >= flowLimit { return true } if policy.ExpTime > 0 && policy.ExpTime <= now { return true } return policy.Status != 1 } func (h *Handler) getUserTunnelPolicy(userTunnelID int64) (*userTunnelPolicy, error) { if userTunnelID <= 0 { return nil, nil } row := h.repo.DB().QueryRow(` SELECT id, user_id, tunnel_id, flow, in_flow, out_flow, exp_time, status FROM user_tunnel WHERE id = ? LIMIT 1 `, userTunnelID) var policy userTunnelPolicy if err := row.Scan(&policy.ID, &policy.UserID, &policy.TunnelID, &policy.Flow, &policy.InFlow, &policy.OutFlow, &policy.ExpTime, &policy.Status); err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } return &policy, nil } func (h *Handler) pauseUserForwards(userID int64, now int64) { forwards, err := h.listActiveForwardsByUser(userID) if err != nil { return } h.pauseForwardRecords(forwards, now) } func (h *Handler) pauseUserTunnelForwards(userID int64, tunnelID int64, now int64) { forwards, err := h.listActiveForwardsByUserTunnel(userID, tunnelID) if err != nil { return } h.pauseForwardRecords(forwards, now) } func (h *Handler) pauseForwardRecords(forwards []forwardRecord, now int64) { for i := range forwards { forward := forwards[i] _ = h.controlForwardServices(&forward, "PauseService", false) _, _ = h.repo.DB().Exec(`UPDATE forward SET status = 0, updated_time = ? WHERE id = ?`, now, forward.ID) } } func (h *Handler) listActiveForwardsByUser(userID int64) ([]forwardRecord, error) { rows, err := h.repo.DB().Query(` SELECT id, user_id, user_name, name, tunnel_id, remote_addr, strategy, status FROM forward WHERE user_id = ? AND status = 1 ORDER BY id ASC `, userID) if err != nil { return nil, err } defer rows.Close() return scanForwardRecords(rows) } func (h *Handler) listActiveForwardsByUserTunnel(userID int64, tunnelID int64) ([]forwardRecord, error) { rows, err := h.repo.DB().Query(` SELECT id, user_id, user_name, name, tunnel_id, remote_addr, strategy, status FROM forward WHERE user_id = ? AND tunnel_id = ? AND status = 1 ORDER BY id ASC `, userID, tunnelID) if err != nil { return nil, err } defer rows.Close() return scanForwardRecords(rows) } func scanForwardRecords(rows *sql.Rows) ([]forwardRecord, error) { out := make([]forwardRecord, 0) for rows.Next() { var record forwardRecord if err := rows.Scan(&record.ID, &record.UserID, &record.UserName, &record.Name, &record.TunnelID, &record.RemoteAddr, &record.Strategy, &record.Status); err != nil { return nil, err } if strings.TrimSpace(record.Strategy) == "" { record.Strategy = "fifo" } out = append(out, record) } if err := rows.Err(); err != nil { return nil, err } return out, nil } func (h *Handler) cleanNodeConfigs(nodeID int64, rawConfig string) { if h == nil || h.repo == nil || h.repo.DB() == nil || nodeID <= 0 { return } if strings.TrimSpace(rawConfig) == "" { return } var snapshot gostConfigSnapshot if err := json.Unmarshal([]byte(rawConfig), &snapshot); err != nil { return } h.cleanOrphanedServices(nodeID, snapshot.Services) h.cleanOrphanedChains(nodeID, snapshot.Chains) h.cleanOrphanedLimiters(nodeID, snapshot.Limiters) } func (h *Handler) cleanOrphanedServices(nodeID int64, services []namedConfigItem) { for _, item := range services { name := strings.TrimSpace(item.Name) if name == "" || name == "web_api" { continue } parts := strings.Split(name, "_") if len(parts) >= 3 { forwardID, err := strconv.ParseInt(parts[0], 10, 64) if err == nil && forwardID > 0 && !h.forwardExists(forwardID) { _, _ = h.sendNodeCommand(nodeID, "DeleteService", map[string]interface{}{"services": []string{name, parts[0] + "_" + parts[1] + "_" + parts[2], parts[0] + "_" + parts[1] + "_" + parts[2] + "_tcp", parts[0] + "_" + parts[1] + "_" + parts[2] + "_udp"}}, false, true) continue } } suffix := parts[len(parts)-1] switch suffix { case "tls": tunnelID, err := strconv.ParseInt(parts[0], 10, 64) if err != nil || tunnelID <= 0 || h.tunnelExists(tunnelID) { continue } _, _ = h.sendNodeCommand(nodeID, "DeleteService", map[string]interface{}{"services": []string{name}}, false, true) case "tcp": if len(parts) < 4 { continue } forwardID, err := strconv.ParseInt(parts[0], 10, 64) if err != nil || forwardID <= 0 || h.forwardExists(forwardID) { continue } base := strings.TrimSuffix(name, "_tcp") _, _ = h.sendNodeCommand(nodeID, "DeleteService", map[string]interface{}{"services": []string{base + "_tcp", base + "_udp"}}, false, true) } } } func (h *Handler) cleanOrphanedChains(nodeID int64, chains []namedConfigItem) { for _, item := range chains { name := strings.TrimSpace(item.Name) if name == "" { continue } idx := strings.LastIndex(name, "_") if idx <= 0 || idx >= len(name)-1 { continue } tunnelID, err := strconv.ParseInt(name[idx+1:], 10, 64) if err != nil || tunnelID <= 0 || h.tunnelExists(tunnelID) { continue } _, _ = h.sendNodeCommand(nodeID, "DeleteChains", map[string]interface{}{"chain": name}, false, true) } } func (h *Handler) cleanOrphanedLimiters(nodeID int64, limiters []namedConfigItem) { for _, item := range limiters { name := strings.TrimSpace(item.Name) if name == "" || h.speedLimiterExists(name) { continue } _, _ = h.sendNodeCommand(nodeID, "DeleteLimiters", map[string]interface{}{"limiter": name}, false, true) } } func (h *Handler) tunnelExists(tunnelID int64) bool { var count int err := h.repo.DB().QueryRow(`SELECT COUNT(1) FROM tunnel WHERE id = ?`, tunnelID).Scan(&count) return err == nil && count > 0 } func (h *Handler) forwardExists(forwardID int64) bool { var count int err := h.repo.DB().QueryRow(`SELECT COUNT(1) FROM forward WHERE id = ?`, forwardID).Scan(&count) return err == nil && count > 0 } func (h *Handler) speedLimiterExists(name string) bool { if name == "" { return false } id, err := strconv.ParseInt(name, 10, 64) if err != nil || id <= 0 { return false } var count int err = h.repo.DB().QueryRow(`SELECT COUNT(1) FROM speed_limit WHERE id = ?`, id).Scan(&count) return err == nil && count > 0 }