Files
platform/web/services/channel_baiyin.go

163 lines
4.5 KiB
Go

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"
)