package services import ( "encoding/json" "fmt" "log/slog" "net/netip" "platform/pkg/env" "platform/pkg/u" "platform/web/core" e "platform/web/events" g "platform/web/globals" "platform/web/globals/orm" m "platform/web/models" q "platform/web/queries" "strings" "time" "github.com/hibiken/asynq" "gorm.io/gen/field" ) type channelBaiyinService struct{} func (s *channelBaiyinService) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error) { var filter *EdgeFilter = nil if len(edgeFilter) > 0 { filter = &edgeFilter[0] } now := time.Now() batch := ID.GenReadable("bat") // 检查并获取套餐与白名单 resource, whitelists, err := ensure(now, source, resourceId, count) if err != nil { return nil, err } user := resource.User expire := now.Add(resource.Live) // 选择代理 proxyResult := struct { m.Proxy Count int }{} err = q.Proxy. LeftJoin(q.Channel, q.Channel.ProxyID.EqCol(q.Proxy.ID), q.Channel.ExpiredAt.Gt(now)). Select(q.Proxy.ALL, field.NewUnsafeFieldRaw("10000 - count(*)").As("count")). Where( q.Proxy.Type.Eq(int(m.ProxyTypeBaiYin)), q.Proxy.Status.Eq(int(m.ProxyStatusOnline)), ). Group(q.Proxy.ID). Order(field.NewField("", "count")). Limit(1).Scan(&proxyResult) if err != nil { return nil, core.NewBizErr("获取可用代理失败", err) } if proxyResult.Count < count { return nil, core.NewBizErr("无可用主机,请稍后再试") } proxy := proxyResult.Proxy // 获取可用通道 chans, err := lockChans(proxy.ID, batch, count) if err != nil { return nil, core.NewBizErr("无可用通道,请稍后再试", err) } // 获取可用节点 edgesResp, err := g.Cloud.CloudEdges(&g.CloudEdgesReq{ Province: filter.Prov, City: filter.City, Isp: u.X(filter.Isp.String()), Limit: &count, NoRepeat: u.P(true), NoDayRepeat: u.P(true), ActiveTime: u.P(3600), IpUnchangedTime: u.P(3600), Sort: u.P("ip_unchanged_time_asc"), }) if err != nil { return nil, core.NewBizErr("获取可用节点失败", err) } if edgesResp.Total != count && len(edgesResp.Edges) != count { return nil, core.NewBizErr("无可用节点,请稍后再试") } edges := edgesResp.Edges // 准备通道数据 channels := make([]*m.Channel, count) chanConfigs := make([]*g.PortConfigsReq, count) edgeConfigs := make([]string, count) for i := range count { ch := chans[i] edge := edges[i] if err != nil { return nil, core.NewBizErr("解析通道地址失败", err) } // 通道数据 channels[i] = &m.Channel{ UserID: user.ID, ResourceID: resourceId, BatchNo: batch, ProxyID: proxy.ID, Host: u.Else(proxy.Host, proxy.IP.String()), Port: ch.Port(), EdgeRef: u.P(edge.EdgeID), FilterISP: filter.Isp, FilterProv: filter.Prov, FilterCity: filter.City, ExpiredAt: expire, } // 通道配置数据 chanConfigs[i] = &g.PortConfigsReq{ Port: int(ch.Port()), Status: true, Edge: &[]string{edge.EdgeID}, } // 白名单模式 if authWhitelist { channels[i].Whitelists = u.P(strings.Join(whitelists, ",")) chanConfigs[i].Whitelist = &whitelists } // 密码模式 if authPassword { username, password := genPassPair() channels[i].Username = &username channels[i].Password = &password chanConfigs[i].Userpass = u.P(username + ":" + password) } // 连接配置数据 edgeConfigs[i] = edge.EdgeID } // 提交异步任务关闭通道 _, err = g.Asynq.Enqueue( e.NewRemoveChannel(batch), asynq.ProcessAt(expire), ) if err != nil { return nil, core.NewServErr("提交关闭通道任务失败", err) } // 保存数据 err = q.Q.Transaction(func(q *q.Query) error { // 更新套餐用量 used := int32(count) if u.IsSameDate(now, resource.DailyLast) { used += resource.DailyUsed } switch resource.Type { case m.ResourceTypeShort: _, err = q.ResourceShort. Where( q.ResourceShort.ResourceID.Eq(resource.Id), q.ResourceShort.Used.Eq(resource.Used), q.ResourceShort.DailyUsed.Eq(resource.DailyUsed), q.ResourceShort.DailyLast.Eq(resource.DailyLast), ). UpdateSimple( q.ResourceShort.Used.Add(int32(count)), q.ResourceShort.DailyUsed.Value(used), q.ResourceShort.DailyLast.Value(now), ) case m.ResourceTypeLong: _, err = q.ResourceLong. Where( q.ResourceLong.ResourceID.Eq(resource.Id), q.ResourceLong.Used.Eq(resource.Used), q.ResourceLong.DailyUsed.Eq(resource.DailyUsed), q.ResourceLong.DailyLast.Eq(resource.DailyLast), ). UpdateSimple( q.ResourceLong.Used.Add(int32(count)), q.ResourceLong.DailyUsed.Value(used), q.ResourceLong.DailyLast.Value(now), ) } if err != nil { return core.NewServErr("更新套餐使用记录失败", err) } // 保存通道 err = q.Channel. Omit(field.AssociationFields). Create(channels...) if err != nil { return core.NewServErr("保存通道失败", err) } // 保存提取记录 err = q.LogsUserUsage.Create(&m.LogsUserUsage{ UserID: user.ID, ResourceID: resourceId, BatchNo: batch, Count: int32(count), ISP: u.P(filter.Isp.String()), Prov: filter.Prov, City: filter.City, IP: orm.Inet{Addr: source}, Time: now, }) if err != nil { return core.NewServErr("保存用户使用记录失败", err) } return nil }) if err != nil { return nil, err } // 提交配置 secret := strings.Split(u.Z(proxy.Secret), ":") gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1]) if env.DebugExternalChange { // 连接节点到网关 err = g.Cloud.CloudConnect(&g.CloudConnectReq{ Uuid: proxy.Mac, Edge: &edgeConfigs, }) if err != nil { return nil, core.NewServErr("连接云平台失败", err) } // 启用网关代理通道 err = gateway.GatewayPortConfigs(chanConfigs) if err != nil { return nil, core.NewServErr(fmt.Sprintf("配置代理 %s 端口失败", proxy.IP.String()), err) } } else { slog.Debug("提交代理端口配置", "proxy", proxy.IP.String()) for _, item := range chanConfigs { str, _ := json.Marshal(item) fmt.Println(string(str)) } } return channels, nil } func (s *channelBaiyinService) RemoveChannels(batch string) error { start := time.Now() // 获取连接数据 channels, err := q.Channel.Where(q.Channel.BatchNo.Eq(batch)).Find() if err != nil { return core.NewServErr("获取通道数据失败", err) } proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(channels[0].ProxyID)).Take() if err != nil { return core.NewServErr("获取代理数据失败", err) } // 准备配置数据 edgeConfigs := make([]string, len(channels)) configs := make([]*g.PortConfigsReq, len(channels)) for i, channel := range channels { if channel.EdgeRef != nil { edgeConfigs[i] = *channel.EdgeRef } else { slog.Warn(fmt.Sprintf("通道 %d 没有保存节点引用", channel.ID)) } configs[i] = &g.PortConfigsReq{ Status: false, Port: int(channel.Port), Edge: &[]string{}, } } // 提交配置 if env.DebugExternalChange { // 断开节点连接 g.Cloud.CloudDisconnect(&g.CloudDisconnectReq{ Uuid: proxy.Mac, Edge: &edgeConfigs, }) // 清空通道配置 secret := strings.Split(*proxy.Secret, ":") gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1]) err := gateway.GatewayPortConfigs(configs) if err != nil { return core.NewServErr(fmt.Sprintf("清空代理 %s 端口配置失败", proxy.IP.String()), err) } } else { slog.Debug("清除代理端口配置", "proxy", proxy.IP) for _, item := range configs { str, _ := json.Marshal(item) fmt.Println(string(str)) } } // 释放端口 err = freeChans(proxy.ID, batch) if err != nil { return err } slog.Debug("清除代理端口配置", "time", time.Since(start).String()) return nil }