317 lines
7.9 KiB
Go
317 lines
7.9 KiB
Go
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
|
|
}
|