package services import ( "fmt" "log/slog" "platform/pkg/u" "platform/web/core" g "platform/web/globals" m "platform/web/models" q "platform/web/queries" ) type channelBaiyinProvider struct{} func (s *channelBaiyinProvider) selectProxy(count int) (*m.Proxy, error) { return selectProxyByType(m.ProxyTypeBaiYin, count) } func (s *channelBaiyinProvider) prepareCreate(ctx *channelCreateContext) (*channelCreateResult, error) { gateway, err := proxyGateway(ctx.Proxy) if err != nil { return nil, core.NewServErr("创建代理网关失败", err) } prov, city := areaProvinceCity(ctx.Area) channels := make([]*m.Channel, len(ctx.Ports)) chanConfigs := make([]*g.PortConfigsReq, len(ctx.Ports)) for i, portRef := range ctx.Ports { channel := newBaseChannel(ctx, portRef.Port()) cfg := &g.PortConfigsReq{ Port: int(portRef.Port()), Status: true, AutoEdgeConfig: &g.AutoEdgeConfig{ Province: u.Z(prov), City: u.Z(city), Isp: ctx.Filter.Isp.String(), Count: u.P(1), }, } if ctx.AuthWhitelist { cfg.Whitelist = &ctx.Whitelists } if username, password, ok := applyChannelAuth(ctx, channel); ok { cfg.Userpass = u.P(username + ":" + password) } channels[i] = channel chanConfigs[i] = cfg } return &channelCreateResult{ Channels: channels, applyRemote: func() error { slog.Debug("提交代理端口配置", "proxy", ctx.Proxy.IP.String(), "total_count", len(chanConfigs)) if err := ensureEdges(ctx.Proxy, gateway, ctx.Area, ctx.Filter.Isp, ctx.Count); err != nil { slog.Warn("ensureEdges 失败", "err", err) } if len(chanConfigs) > 0 { if err := gateway.GatewayPortConfigs(chanConfigs); err != nil { slog.Warn("提交代理端口配置失败", "error", err.Error()) return core.NewServErr(fmt.Sprintf("配置代理 %s 端口失败", ctx.Proxy.IP.String()), err) } } return nil }, }, nil } func (s *channelBaiyinProvider) removeRemote(_ string, batch *usedChanBatch) error { configs := make([]*g.PortConfigsReq, len(batch.Chans)) for i, ch := range batch.Chans { configs[i] = &g.PortConfigsReq{ Port: int(ch.Port()), Edge: &[]string{}, AutoEdgeConfig: &g.AutoEdgeConfig{Count: u.P(0)}, Status: false, } } proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(batch.ProxyID)).Take() if err != nil { return core.NewServErr("获取代理数据失败", err) } gateway, err := proxyGateway(proxy) if err != nil { return core.NewServErr("创建代理网关失败", err) } if err = gateway.GatewayPortConfigs(configs); err != nil { return core.NewServErr(fmt.Sprintf("清空代理 %s 端口配置失败", proxy.IP.String()), err) } return nil } // ensureEdges 检查本地节点是否足够,如果不足从云端连入 // 本地节点通过 Assigned = false 排除已分配节点 // 云端节点通过 NoRepeat = true 排除已分配节点 func ensureEdges(proxy *m.Proxy, gateway g.GatewayClient, area *m.Area, isp *m.EdgeISP, count int) error { prov, city := areaProvinceCity(area) if prov == nil && city == nil && u.X(isp.String()) == nil { return nil // 没有过滤条件,直接返回空,避免无意义的查询 } // 先查本地 localEdges, err := gateway.GatewayEdge(&g.GatewayEdgeReq{ Province: prov, City: city, Isp: u.X(isp.String()), Limit: &count, Assigned: u.P(false), }) if err != nil { return core.NewBizErr("检查可用节点失败[1]", err) } if len(localEdges) >= count { return nil // 本地节点足够,直接返回空,后续逻辑会优先使用本地节点 } // 再查云端 remaining := count - len(localEdges) cloudEdges, err := g.Cloud.CloudEdges(&g.CloudEdgesReq{ Province: prov, City: city, Isp: u.X(isp.String()), Limit: &remaining, NoRepeat: u.P(true), ActiveTime: u.P(3600), IpUnchangedTime: u.P(3600), }) if err != nil { return core.NewBizErr("检查可用节点失败[2]", err) } if len(cloudEdges.Edges) < remaining { return core.NewBizErr("地区可用节点数量不足") } // 连入云端节点 edges := make([]string, remaining) for i, edge := range cloudEdges.Edges { edges[i] = edge.EdgeID } if err := g.Cloud.CloudConnect(&g.CloudConnectReq{Uuid: proxy.Mac, Edge: &edges}); err != nil { return core.NewServErr("连接云平台失败", err) } return nil } type EdgeInfo struct { Type EdgeInfoType EdgeID string } type EdgeInfoType string const ( EdgeInfoLocal EdgeInfoType = "local" EdgeInfoCloud EdgeInfoType = "cloud" )