新增提取函数,实现通过套餐编号提取
This commit is contained in:
@@ -102,13 +102,17 @@ func CreateChannel(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建通道
|
// 创建通道
|
||||||
|
no, err := s.FindResourceNoById(req.ResourceId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var isp *m.EdgeISP
|
var isp *m.EdgeISP
|
||||||
if req.Isp != nil {
|
if req.Isp != nil {
|
||||||
isp = u.X(m.ToEdgeISP(*req.Isp))
|
isp = u.X(m.ToEdgeISP(*req.Isp))
|
||||||
}
|
}
|
||||||
result, err := s.Channel.CreateChannels(
|
result, err := s.Channel.CreateChannels(
|
||||||
ip,
|
ip, no,
|
||||||
req.ResourceId,
|
|
||||||
req.AuthType == s.ChannelAuthTypeIp,
|
req.AuthType == s.ChannelAuthTypeIp,
|
||||||
req.AuthType == s.ChannelAuthTypePass,
|
req.AuthType == s.ChannelAuthTypePass,
|
||||||
req.Count,
|
req.Count,
|
||||||
@@ -149,6 +153,69 @@ type CreateChannelReq struct {
|
|||||||
Isp *int `json:"isp"`
|
Isp *int `json:"isp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChannelV2 创建新通道 v2,使用 resource_no 替代 resource_id
|
||||||
|
func CreateChannelV2(c *fiber.Ctx) error {
|
||||||
|
// 不检查权限,允许 api 调用
|
||||||
|
|
||||||
|
// 解析参数
|
||||||
|
req := new(CreateChannelReqV2)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return core.NewBizErr("解析参数失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := netip.ParseAddr(c.IP())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取客户端地址失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建通道
|
||||||
|
var isp *m.EdgeISP
|
||||||
|
if req.Isp != nil {
|
||||||
|
isp = u.X(m.ToEdgeISP(*req.Isp))
|
||||||
|
}
|
||||||
|
result, err := s.Channel.CreateChannels(
|
||||||
|
ip,
|
||||||
|
req.ResourceNo,
|
||||||
|
req.AuthType == s.ChannelAuthTypeIp,
|
||||||
|
req.AuthType == s.ChannelAuthTypePass,
|
||||||
|
req.Count,
|
||||||
|
&s.EdgeFilter{
|
||||||
|
Isp: isp,
|
||||||
|
Prov: req.Prov,
|
||||||
|
City: req.City,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
var resp = make([]*CreateChannelRespItem, len(result))
|
||||||
|
for i, channel := range result {
|
||||||
|
resp[i] = &CreateChannelRespItem{
|
||||||
|
Proto: req.Protocol,
|
||||||
|
Host: channel.Host,
|
||||||
|
IP: channel.Proxy.IP.String(),
|
||||||
|
Port: channel.Port,
|
||||||
|
}
|
||||||
|
if req.AuthType == s.ChannelAuthTypePass {
|
||||||
|
resp[i].Username = channel.Username
|
||||||
|
resp[i].Password = channel.Password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.JSON(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateChannelReqV2 struct {
|
||||||
|
ResourceNo string `json:"resource_no" validate:"required"`
|
||||||
|
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
|
||||||
|
Protocol int `json:"protocol" validate:"required"`
|
||||||
|
Count int `json:"count" validate:"required"`
|
||||||
|
Prov *string `json:"prov"`
|
||||||
|
City *string `json:"city"`
|
||||||
|
Isp *int `json:"isp"`
|
||||||
|
}
|
||||||
|
|
||||||
type CreateChannelRespItem struct {
|
type CreateChannelRespItem struct {
|
||||||
Proto int `json:"-"`
|
Proto int `json:"-"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func userRouter(api fiber.Router) {
|
|||||||
channel := api.Group("/channel")
|
channel := api.Group("/channel")
|
||||||
channel.Post("/list", handlers.ListChannel)
|
channel.Post("/list", handlers.ListChannel)
|
||||||
channel.Post("/create", handlers.CreateChannel)
|
channel.Post("/create", handlers.CreateChannel)
|
||||||
|
channel.Post("/create/v2", handlers.CreateChannelV2)
|
||||||
|
|
||||||
// 交易
|
// 交易
|
||||||
trade := api.Group("/trade")
|
trade := api.Group("/trade")
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ var Channel = &channelServer{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChannelServiceProvider interface {
|
type ChannelServiceProvider interface {
|
||||||
CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error)
|
CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error)
|
||||||
RemoveChannels(batch string) error
|
RemoveChannels(batch string) error
|
||||||
ClearExpiredChannels(proxyId int32) (int, error)
|
ClearExpiredChannels(proxyId int32) (int, error)
|
||||||
}
|
}
|
||||||
@@ -34,8 +34,8 @@ type channelServer struct {
|
|||||||
provider ChannelServiceProvider
|
provider ChannelServiceProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *channelServer) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) {
|
func (s *channelServer) CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) {
|
||||||
return s.provider.CreateChannels(source, resourceId, authWhitelist, authPassword, count, edgeFilter)
|
return s.provider.CreateChannels(source, resourceNo, authWhitelist, authPassword, count, edgeFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *channelServer) RemoveChannels(batch string) error {
|
func (s *channelServer) RemoveChannels(batch string) error {
|
||||||
@@ -115,12 +115,23 @@ func genPassPair() (string, string) {
|
|||||||
return string(username), string(password)
|
return string(username), string(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindResourceNoById(resourceId int32) (string, error) {
|
||||||
|
resource, err := q.Resource.
|
||||||
|
Select(q.Resource.ResourceNo).
|
||||||
|
Where(q.Resource.ID.Eq(resourceId)).
|
||||||
|
Take()
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrResourceNotExist
|
||||||
|
}
|
||||||
|
return u.Z(resource.ResourceNo), nil
|
||||||
|
}
|
||||||
|
|
||||||
// 查找资源
|
// 查找资源
|
||||||
func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
func findResourceViewByNo(resourceNo string, now time.Time) (*ResourceView, error) {
|
||||||
resource, err := q.Resource.
|
resource, err := q.Resource.
|
||||||
Preload(field.Associations).
|
Preload(field.Associations).
|
||||||
Where(
|
Where(
|
||||||
q.Resource.ID.Eq(resourceId),
|
q.Resource.ResourceNo.Eq(resourceNo),
|
||||||
q.Resource.Active.Is(true),
|
q.Resource.Active.Is(true),
|
||||||
).
|
).
|
||||||
Take()
|
Take()
|
||||||
@@ -131,7 +142,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
|||||||
return nil, ErrResourceNotExist
|
return nil, ErrResourceNotExist
|
||||||
}
|
}
|
||||||
var info = &ResourceView{
|
var info = &ResourceView{
|
||||||
Id: resource.ID,
|
ID: resource.ID,
|
||||||
User: *resource.User,
|
User: *resource.User,
|
||||||
Active: resource.Active,
|
Active: resource.Active,
|
||||||
Type: resource.Type,
|
Type: resource.Type,
|
||||||
@@ -177,7 +188,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
|||||||
|
|
||||||
// ResourceView 套餐数据的简化视图,便于直接获取主要数据
|
// ResourceView 套餐数据的简化视图,便于直接获取主要数据
|
||||||
type ResourceView struct {
|
type ResourceView struct {
|
||||||
Id int32
|
ID int32
|
||||||
User m.User
|
User m.User
|
||||||
Active bool
|
Active bool
|
||||||
Type m.ResourceType
|
Type m.ResourceType
|
||||||
@@ -195,13 +206,13 @@ type ResourceView struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户是否可提取
|
// 检查用户是否可提取
|
||||||
func ensure(now time.Time, source netip.Addr, resourceId int32, authWhitelist bool, count int) (*ResourceView, []string, error) {
|
func ensure(now time.Time, source netip.Addr, resourceNo string, authWhitelist bool, count int) (*ResourceView, []string, error) {
|
||||||
if count > 400 {
|
if count > 400 {
|
||||||
return nil, nil, core.NewBizErr("单次最多提取 400 个")
|
return nil, nil, core.NewBizErr("单次最多提取 400 个")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户套餐
|
// 获取用户套餐
|
||||||
resource, err := findResource(resourceId, now)
|
resource, err := findResourceViewByNo(resourceNo, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
type channelBaiyinProvider struct{}
|
type channelBaiyinProvider struct{}
|
||||||
|
|
||||||
func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, filter *EdgeFilter) ([]*m.Channel, error) {
|
func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, filter *EdgeFilter) ([]*m.Channel, error) {
|
||||||
if filter == nil {
|
if filter == nil {
|
||||||
return nil, core.NewBizErr("缺少节点过滤条件")
|
return nil, core.NewBizErr("缺少节点过滤条件")
|
||||||
}
|
}
|
||||||
@@ -34,9 +34,9 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
channels := make([]*m.Channel, count)
|
channels := make([]*m.Channel, count)
|
||||||
|
|
||||||
// 资源锁,防止并发扣减失败导致的端口悬空问题
|
// 资源锁,防止并发扣减失败导致的端口悬空问题
|
||||||
err := g.Redsync.WithLock(lockChannelCreateKey(resourceId), func() error {
|
err := g.Redsync.WithLock(lockChannelCreateKey(resourceNo), func() error {
|
||||||
// 检查并获取套餐与白名单
|
// 检查并获取套餐与白名单
|
||||||
resource, whitelists, err := ensure(now, source, resourceId, authWhitelist, count)
|
resource, whitelists, err := ensure(now, source, resourceNo, authWhitelist, count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
// 通道数据
|
// 通道数据
|
||||||
channels[i] = &m.Channel{
|
channels[i] = &m.Channel{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
ResourceID: resourceId,
|
ResourceID: resource.ID,
|
||||||
BatchNo: batchNo,
|
BatchNo: batchNo,
|
||||||
ProxyID: proxy.ID,
|
ProxyID: proxy.ID,
|
||||||
Host: u.Else(proxy.Host, proxy.IP.String()),
|
Host: u.Else(proxy.Host, proxy.IP.String()),
|
||||||
@@ -181,7 +181,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
// 保存提取记录
|
// 保存提取记录
|
||||||
err = q.LogsUserUsage.Create(&m.LogsUserUsage{
|
err = q.LogsUserUsage.Create(&m.LogsUserUsage{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
ResourceID: resourceId,
|
ResourceID: resource.ID,
|
||||||
BatchNo: batchNo,
|
BatchNo: batchNo,
|
||||||
Count: int32(count),
|
Count: int32(count),
|
||||||
ISP: u.X(filter.Isp.String()),
|
ISP: u.X(filter.Isp.String()),
|
||||||
@@ -335,8 +335,8 @@ func (s *channelBaiyinProvider) ClearExpiredChannels(proxyId int32) (int, error)
|
|||||||
return len(batchSet), nil
|
return len(batchSet), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lockChannelCreateKey(resourceId int32) string {
|
func lockChannelCreateKey(resourceNo string) string {
|
||||||
return fmt.Sprintf("platform:channel:create:%d", resourceId)
|
return fmt.Sprintf("platform:channel:create:%s", resourceNo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lockChannelRemoveKey(bid string) string {
|
func lockChannelRemoveKey(bid string) string {
|
||||||
|
|||||||
Reference in New Issue
Block a user