修复通道提取接口权限与数据映射问题
This commit is contained in:
@@ -17,7 +17,7 @@ var Channel ChannelService = &channelBaiyinService{}
|
||||
|
||||
// 通道服务
|
||||
type ChannelService interface {
|
||||
CreateChannels(source netip.Addr, userId int32, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error)
|
||||
CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error)
|
||||
RemoveChannels(batch string, ids []int32) error
|
||||
}
|
||||
|
||||
@@ -47,12 +47,11 @@ func genPassPair() (string, string) {
|
||||
return string(username), string(password)
|
||||
}
|
||||
|
||||
func findResource(q *q.Query, resourceId int32, userId int32, count int, now time.Time) (*ResourceView, error) {
|
||||
func findResource(q *q.Query, resourceId int32, count int, now time.Time) (*ResourceView, error) {
|
||||
resource, err := q.Resource.
|
||||
Preload(field.Associations).
|
||||
Where(
|
||||
q.Resource.ID.Eq(resourceId),
|
||||
q.Resource.UserID.Eq(userId),
|
||||
q.Resource.Active.Is(true),
|
||||
).
|
||||
Take()
|
||||
@@ -64,6 +63,7 @@ func findResource(q *q.Query, resourceId int32, userId int32, count int, now tim
|
||||
Id: resource.ID,
|
||||
Active: resource.Active,
|
||||
Type: resource.Type,
|
||||
User: resource.User,
|
||||
}
|
||||
|
||||
switch resource.Type {
|
||||
@@ -114,35 +114,6 @@ func findResource(q *q.Query, resourceId int32, userId int32, count int, now tim
|
||||
info.Used = sub.Used
|
||||
}
|
||||
|
||||
// 检查套餐使用情况
|
||||
switch info.Mode {
|
||||
default:
|
||||
return nil, core.NewBizErr("不支持的套餐模式")
|
||||
|
||||
// 包时
|
||||
case m.ResourceModeTime:
|
||||
// 检查过期时间
|
||||
if info.Expire.Before(now) {
|
||||
return nil, ErrResourceExpired
|
||||
}
|
||||
// 检查每日限额
|
||||
used := 0
|
||||
if now.Format("2006-01-02") == info.DailyLast.Format("2006-01-02") {
|
||||
used = int(info.DailyUsed)
|
||||
}
|
||||
excess := used+count > int(info.DailyLimit)
|
||||
if excess {
|
||||
return nil, ErrResourceDailyLimit
|
||||
}
|
||||
|
||||
// 包量
|
||||
case m.ResourceModeQuota:
|
||||
// 检查可用配额
|
||||
if int(info.Quota)-int(info.Used) < count {
|
||||
return nil, ErrResourceExhausted
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -159,14 +130,17 @@ type ResourceView struct {
|
||||
Quota int32
|
||||
Used int32
|
||||
Expire time.Time
|
||||
User m.User
|
||||
}
|
||||
|
||||
func lockChans(batch string, count int, expire time.Time) ([]netip.AddrPort, error) {
|
||||
chans, err := g.Redis.Eval(
|
||||
context.Background(),
|
||||
RedisScriptLockChans,
|
||||
[]string{"channel"},
|
||||
batch,
|
||||
[]string{
|
||||
"channel:chans",
|
||||
"channel:lease:" + batch,
|
||||
},
|
||||
count,
|
||||
expire.Unix(),
|
||||
).StringSlice()
|
||||
@@ -187,13 +161,10 @@ func lockChans(batch string, count int, expire time.Time) ([]netip.AddrPort, err
|
||||
}
|
||||
|
||||
var RedisScriptLockChans = `
|
||||
local key = KEYS[1]
|
||||
local batch = ARGV[1]
|
||||
local count = tonumber(ARGV[2])
|
||||
local expire = tonumber(ARGV[3])
|
||||
|
||||
local chans_key = key .. ":chans"
|
||||
local lease_key = key .. ":lease:" .. batch
|
||||
local chans_key = KEYS[1]
|
||||
local lease_key = KEYS[2]
|
||||
local count = tonumber(ARGV[1])
|
||||
local expire = tonumber(ARGV[2])
|
||||
|
||||
if redis.call("SCARD", chans_key) < count then
|
||||
return nil
|
||||
@@ -210,12 +181,19 @@ return ports
|
||||
`
|
||||
|
||||
func freeChans(batch string, chans []string) error {
|
||||
values := make([]any, len(chans))
|
||||
for i, ch := range chans {
|
||||
values[i] = ch
|
||||
}
|
||||
|
||||
err := g.Redis.Eval(
|
||||
context.Background(),
|
||||
RedisScriptFreeChans,
|
||||
[]string{"channel"},
|
||||
batch,
|
||||
chans,
|
||||
[]string{
|
||||
"channel:chans",
|
||||
"channel:lease:" + batch,
|
||||
},
|
||||
values...,
|
||||
).Err()
|
||||
if err != nil {
|
||||
return core.NewBizErr("释放通道失败", err)
|
||||
@@ -225,15 +203,11 @@ func freeChans(batch string, chans []string) error {
|
||||
}
|
||||
|
||||
var RedisScriptFreeChans = `
|
||||
local key = KEYS[1]
|
||||
local batch = ARGV[1]
|
||||
local chans = ARGV[2]
|
||||
|
||||
local chans_key = key .. ":chans"
|
||||
local lease_key = key .. ":lease:" .. batch
|
||||
local chans_key = KEYS[1]
|
||||
local lease_key = KEYS[2]
|
||||
local chans = ARGV
|
||||
|
||||
redis.call("SADD", chans_key, unpack(chans))
|
||||
|
||||
redis.call("DEL", lease_key)
|
||||
|
||||
return chans
|
||||
|
||||
@@ -18,11 +18,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"gorm.io/gen/field"
|
||||
)
|
||||
|
||||
type channelBaiyinService struct{}
|
||||
|
||||
func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error) {
|
||||
func (s *channelBaiyinService) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error) {
|
||||
if count > 400 {
|
||||
return nil, core.NewBizErr("单次最多提取 400 个")
|
||||
}
|
||||
@@ -35,9 +36,21 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
now := time.Now()
|
||||
batch := ID.GenReadable("bat")
|
||||
|
||||
// 获取用户套餐
|
||||
resource, err := findResource(q.Q, resourceId, count, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查用户
|
||||
user := resource.User
|
||||
if user.IDToken == nil || *user.IDToken == "" {
|
||||
return nil, core.NewBizErr("账号未实名")
|
||||
}
|
||||
|
||||
// 获取用户白名单并检查用户 ip 地址
|
||||
whitelists, err := q.Whitelist.Where(
|
||||
q.Whitelist.UserID.Eq(userId),
|
||||
q.Whitelist.UserID.Eq(user.ID),
|
||||
).Find()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,11 +68,35 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
return nil, core.NewBizErr(fmt.Sprintf("IP 地址 %s 不在白名单内", source.String()))
|
||||
}
|
||||
|
||||
// 获取用户套餐并检查
|
||||
resource, err := findResource(q.Q, resourceId, userId, count, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// 检查套餐使用情况
|
||||
switch resource.Mode {
|
||||
default:
|
||||
return nil, core.NewBizErr("不支持的套餐模式")
|
||||
|
||||
// 包时
|
||||
case m.ResourceModeTime:
|
||||
// 检查过期时间
|
||||
if resource.Expire.Before(now) {
|
||||
return nil, ErrResourceExpired
|
||||
}
|
||||
// 检查每日限额
|
||||
used := 0
|
||||
if now.Format("2006-01-02") == resource.DailyLast.Format("2006-01-02") {
|
||||
used = int(resource.DailyUsed)
|
||||
}
|
||||
excess := used+count > int(resource.DailyLimit)
|
||||
if excess {
|
||||
return nil, ErrResourceDailyLimit
|
||||
}
|
||||
|
||||
// 包量
|
||||
case m.ResourceModeQuota:
|
||||
// 检查可用配额
|
||||
if int(resource.Quota)-int(resource.Used) < count {
|
||||
return nil, ErrResourceExhausted
|
||||
}
|
||||
}
|
||||
|
||||
expire := now.Add(resource.Live)
|
||||
|
||||
// 获取可用通道
|
||||
@@ -104,18 +141,19 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
|
||||
// 使用记录
|
||||
actions[i] = &m.LogsUserUsage{
|
||||
UserID: userId,
|
||||
UserID: user.ID,
|
||||
ResourceID: resourceId,
|
||||
Count: int32(count),
|
||||
ISP: u.P(filter.Isp.String()),
|
||||
Prov: filter.Prov,
|
||||
City: filter.City,
|
||||
IP: orm.Inet{Addr: source},
|
||||
}
|
||||
|
||||
// 通道数据
|
||||
inet := orm.Inet{Addr: ch.Addr()}
|
||||
channels[i] = &m.Channel{
|
||||
UserID: userId,
|
||||
UserID: user.ID,
|
||||
ResourceID: resourceId,
|
||||
BatchNo: batch,
|
||||
ProxyID: findProxy[inet].ID,
|
||||
@@ -124,9 +162,10 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
FilterProv: filter.Prov,
|
||||
FilterCity: filter.City,
|
||||
ExpiredAt: expire,
|
||||
Proxy: *findProxy[inet],
|
||||
}
|
||||
if authWhitelist {
|
||||
channels[i].Whitelists = &orm.Slice[string]{Arr: whitelistIPs}
|
||||
channels[i].Whitelists = u.P(strings.Join(whitelistIPs, ","))
|
||||
}
|
||||
if authPassword {
|
||||
username, password := genPassPair()
|
||||
@@ -181,7 +220,9 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
}
|
||||
|
||||
// 保存通道和分配记录
|
||||
err = q.Channel.Create(channels...)
|
||||
err = q.Channel.
|
||||
Omit(field.AssociationFields).
|
||||
Create(channels...)
|
||||
if err != nil {
|
||||
return core.NewServErr("保存通道失败", err)
|
||||
}
|
||||
@@ -226,7 +267,7 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
},
|
||||
}
|
||||
if authWhitelist {
|
||||
configs[i].Whitelist = &channel.Whitelists.Arr
|
||||
configs[i].Whitelist = &whitelistIPs
|
||||
}
|
||||
if authPassword {
|
||||
configs[i].Userpass = u.P(fmt.Sprintf("%s:%s", *channel.Username, *channel.Password))
|
||||
@@ -248,9 +289,10 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, userId int32, r
|
||||
}
|
||||
|
||||
func (s *channelBaiyinService) RemoveChannels(batch string, ids []int32) error {
|
||||
start := time.Now()
|
||||
|
||||
// 获取连接数据
|
||||
channels, err := q.Channel.Debug().
|
||||
channels, err := q.Channel.
|
||||
Preload(q.Channel.Proxy).
|
||||
Where(q.Channel.ID.In(ids...)).
|
||||
Find()
|
||||
@@ -279,7 +321,7 @@ func (s *channelBaiyinService) RemoveChannels(batch string, ids []int32) error {
|
||||
// 释放端口
|
||||
err = freeChans(batch, chans)
|
||||
if err != nil {
|
||||
return core.NewServErr("释放端口失败", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 清空配置
|
||||
@@ -304,9 +346,10 @@ func (s *channelBaiyinService) RemoveChannels(batch string, ids []int32) error {
|
||||
}
|
||||
} else {
|
||||
bytes, _ := json.Marshal(configs)
|
||||
slog.Debug("清除代理端口配置", "config", bytes)
|
||||
slog.Debug("清除代理端口配置", "config", string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
slog.Debug("清除代理端口配置", "time", time.Since(start).String())
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user