优化白名单与通道提取功能

This commit is contained in:
2026-05-07 12:43:15 +08:00
parent a4d9c28702
commit 8fc1d30578
3 changed files with 112 additions and 38 deletions

View File

@@ -1,6 +1,8 @@
## TODO ## TODO
异常展示全部信息,只返回 bizerr 的内容,控制台打印全部 err 内容
错误提示增强,展示整链路信息
ip 提取频率限制,在 ensure 函数加逻辑,通过 redis 或者 pg 计算分钟内提取次数,只允许每分钟提取 30 次 ip 提取频率限制,在 ensure 函数加逻辑,通过 redis 或者 pg 计算分钟内提取次数,只允许每分钟提取 30 次

View File

@@ -2,6 +2,7 @@ package handlers
import ( import (
"errors" "errors"
"fmt"
"platform/pkg/env" "platform/pkg/env"
"platform/pkg/u" "platform/pkg/u"
"platform/web/auth" "platform/web/auth"
@@ -97,13 +98,31 @@ func CreateWhitelist(c *fiber.Ctx) error {
} }
// 创建白名单 // 创建白名单
err = q.Whitelist.Create(&m.Whitelist{ uid := authCtx.User.ID
UserID: authCtx.User.ID, err = g.Redsync.WithLock(whitelistKey(uid), func() error {
IP: u.Z(ip), count, err := q.Whitelist.Where(
Remark: &req.Remark, q.Whitelist.UserID.Eq(uid),
).Count()
if err != nil {
return core.NewServErr("获取白名单数量失败", err)
}
if count >= 5 {
return core.NewBizErr("白名单数量已达上限")
}
err = q.Whitelist.Create(&m.Whitelist{
UserID: authCtx.User.ID,
IP: u.Z(ip),
Remark: &req.Remark,
})
if err != nil {
return core.NewServErr("添加白名单失败", err)
}
return nil
}) })
if err != nil { if err != nil {
return core.NewServErr("添加白名单失败", err) return err
} }
return nil return nil
@@ -206,3 +225,7 @@ func secureAddr(str string) (*orm.Inet, error) {
} }
return ip, nil return ip, nil
} }
func whitelistKey(userID int32) string {
return fmt.Sprintf("platform:whitelist:add:%d", userID)
}

View File

@@ -17,7 +17,6 @@ import (
"time" "time"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"gorm.io/gen"
"gorm.io/gen/field" "gorm.io/gen/field"
) )
@@ -87,30 +86,20 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
} }
// 获取可用节点 // 获取可用节点
edgesResp, err := g.Cloud.CloudEdges(&g.CloudEdgesReq{ secret := strings.Split(u.Z(proxy.Secret), ":")
Province: filter.Prov, if len(secret) != 2 {
City: filter.City, return nil, core.NewServErr(fmt.Sprintf("代理 %s 密钥格式错误", proxy.IP.String()), nil)
Isp: u.X(filter.Isp.String()), }
Limit: &count, gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
NoRepeat: u.P(true), edges, err := getAvailableEdges(gateway, filter, count)
NoDayRepeat: u.P(true),
ActiveTime: u.P(3600),
IpUnchangedTime: u.P(3600),
Sort: u.P("ip_unchanged_time_asc"),
})
if err != nil { if err != nil {
fmt.Printf("获取可用节点失败: %v\n", err) return nil, err
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) channels := make([]*m.Channel, count)
chanConfigs := make([]*g.PortConfigsReq, count) chanConfigs := make([]*g.PortConfigsReq, count)
edgeConfigs := make([]string, count) edgeConfigs := make([]string, 0, count)
for i := range count { for i := range count {
ch := chans[i] ch := chans[i]
edge := edges[i] edge := edges[i]
@@ -152,7 +141,9 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
} }
// 连接配置数据 // 连接配置数据
edgeConfigs[i] = edge.EdgeID if edge.Type == EdgeInfoCloud {
edgeConfigs = append(edgeConfigs, edge.EdgeID)
}
} }
// 提交异步任务关闭通道 // 提交异步任务关闭通道
@@ -166,15 +157,13 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
// 保存数据 // 保存数据
err = q.Q.Transaction(func(q *q.Query) error { err = q.Q.Transaction(func(q *q.Query) error {
var rs gen.ResultInfo
// 根据套餐类型和模式更新使用记录 // 根据套餐类型和模式更新使用记录
isShortType := resource.Type == m.ResourceTypeShort isShortType := resource.Type == m.ResourceTypeShort
isLongType := resource.Type == m.ResourceTypeLong isLongType := resource.Type == m.ResourceTypeLong
switch { switch {
case isShortType: case isShortType:
rs, err = q.ResourceShort. _, err = q.ResourceShort.
Where( Where(
q.ResourceShort.ID.Eq(*resource.ShortId), q.ResourceShort.ID.Eq(*resource.ShortId),
q.ResourceShort.Used.Eq(resource.Used), q.ResourceShort.Used.Eq(resource.Used),
@@ -187,7 +176,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
) )
case isLongType: case isLongType:
rs, err = q.ResourceLong. _, err = q.ResourceLong.
Where( Where(
q.ResourceLong.ID.Eq(*resource.LongId), q.ResourceLong.ID.Eq(*resource.LongId),
q.ResourceLong.Used.Eq(resource.Used), q.ResourceLong.Used.Eq(resource.Used),
@@ -205,9 +194,6 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
if err != nil { if err != nil {
return core.NewServErr("更新套餐使用记录失败", err) return core.NewServErr("更新套餐使用记录失败", err)
} }
if rs.RowsAffected == 0 {
return core.NewServErr("套餐使用记录不存在")
}
// 保存通道 // 保存通道
err = q.Channel. err = q.Channel.
@@ -240,11 +226,6 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
} }
// 提交配置 // 提交配置
secret := strings.Split(u.Z(proxy.Secret), ":")
if len(secret) != 2 {
return nil, core.NewServErr(fmt.Sprintf("代理 %s 密钥格式错误", proxy.IP.String()), nil)
}
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
if env.RunMode == env.RunModeProd { if env.RunMode == env.RunModeProd {
// 连接节点到网关 // 连接节点到网关
@@ -259,6 +240,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
// 启用网关代理通道 // 启用网关代理通道
err = gateway.GatewayPortConfigs(chanConfigs) err = gateway.GatewayPortConfigs(chanConfigs)
if err != nil { if err != nil {
slog.Warn("提交代理端口配置失败", "error", err.Error())
return nil, core.NewServErr(fmt.Sprintf("配置代理 %s 端口失败", proxy.IP.String()), err) return nil, core.NewServErr(fmt.Sprintf("配置代理 %s 端口失败", proxy.IP.String()), err)
} }
} else { } else {
@@ -340,3 +322,70 @@ func (s *channelBaiyinProvider) RemoveChannels(batch string) error {
slog.Debug("清除代理端口配置", "duration", time.Since(start).String()) slog.Debug("清除代理端口配置", "duration", time.Since(start).String())
return nil return nil
} }
func getAvailableEdges(gateway g.GatewayClient, filter *EdgeFilter, count int) ([]EdgeInfo, error) {
edges := make([]EdgeInfo, 0, count)
// 先查本地
localEdgesResp, err := gateway.GatewayEdge(&g.GatewayEdgeReq{
Province: filter.Prov,
City: filter.City,
Isp: u.X(filter.Isp.String()),
Limit: &count,
Assigned: u.P(false),
})
if err != nil {
return nil, core.NewBizErr("获取可用节点失败", err)
}
for id, _ := range localEdgesResp {
edges = append(edges, EdgeInfo{
Type: EdgeInfoLocal,
EdgeID: id,
})
}
if len(edges) >= count {
return edges, nil
}
// 再查云端无重复
remaining := count - len(edges)
cloudEdgesResp, err := g.Cloud.CloudEdges(&g.CloudEdgesReq{
Province: filter.Prov,
City: filter.City,
Isp: u.X(filter.Isp.String()),
Limit: &remaining,
NoRepeat: u.P(true),
ActiveTime: u.P(3600),
IpUnchangedTime: u.P(3600),
})
if err != nil {
return nil, core.NewBizErr("获取可用节点失败", err)
}
for _, edge := range cloudEdgesResp.Edges {
edges = append(edges, EdgeInfo{
Type: EdgeInfoCloud,
EdgeID: edge.EdgeID,
})
}
if len(edges) >= count {
return edges, nil
}
// 不能和已有的重复,如果有重复则再次查询云端补足,二次提取还有重复则放弃
return nil, core.NewBizErr("地区可用节点数量不足")
}
type EdgeInfo struct {
Type EdgeInfoType
EdgeID string
}
type EdgeInfoType string
const (
EdgeInfoLocal EdgeInfoType = "local"
EdgeInfoCloud EdgeInfoType = "cloud"
)