重构通道管理逻辑,支持通过任务删除不同类型通道;引入 Asynq 处理异步任务;更新数据库结构以支持通道类型区分

This commit is contained in:
2025-05-23 14:53:01 +08:00
parent 09a9cc573e
commit c83ffda611
17 changed files with 380 additions and 188 deletions

22
web/globals/asynq.go Normal file
View File

@@ -0,0 +1,22 @@
package globals
import (
"github.com/hibiken/asynq"
"log/slog"
)
var Asynq *asynq.Client
func InitAsynq() {
var client = asynq.NewClientFromRedisClient(Redis)
Asynq = client
}
func CloseAsynq() {
if Asynq != nil {
err := Asynq.Close()
if err != nil {
slog.Error("关闭 Asynq 客户端失败", "error", err)
}
}
}

View File

@@ -9,4 +9,5 @@ func Init() {
initRedis()
initOrm()
initProxy()
InitAsynq()
}

View File

@@ -8,6 +8,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
@@ -42,6 +43,7 @@ func (p *ProxyClient) Permit(host string, secret string, config []*ProxyPermitCo
return fmt.Errorf("加密请求失败: %w", err)
}
//goland:noinspection HttpUrlsUsage
resp, err := http.Post(
fmt.Sprintf("http://%s:8848%s", host, PermitEndpoint),
"application/json",
@@ -50,7 +52,9 @@ func (p *ProxyClient) Permit(host string, secret string, config []*ProxyPermitCo
if err != nil {
return err
}
defer resp.Body.Close()
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("配置端口许可失败: %s", resp.Status)

View File

@@ -154,7 +154,7 @@ func CreateChannel(c *fiber.Ctx) error {
// 创建通道
result, err := s.Channel.CreateChannel(
authContext,
authContext.Payload.Id,
req.ResourceId,
req.Protocol,
req.AuthType,
@@ -197,7 +197,7 @@ type RemoveChannelsReq struct {
func RemoveChannels(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.Protect(c, []auth.PayloadType{auth.PayloadUser, auth.PayloadInternalServer}, []string{})
authCtx, err := auth.NewProtect(c).Payload(auth.PayloadUser).Do()
if err != nil {
return err
}
@@ -209,7 +209,31 @@ func RemoveChannels(c *fiber.Ctx) error {
}
// 删除通道
err = s.Channel.RemoveChannels(c.Context(), authCtx, req.ByIds...)
err = s.Channel.RemoveChannels(req.ByIds, authCtx.Payload.Id)
if err != nil {
return err
}
return c.SendStatus(fiber.StatusOK)
}
type RemoveChannelByTaskReq []int32
func RemoveChannelByTask(c *fiber.Ctx) error {
// 检查权限
_, err := auth.NewProtect(c).Payload(auth.PayloadInternalServer).Do()
if err != nil {
return err
}
// 解析请求参数
var req RemoveChannelByTaskReq
if err := c.BodyParser(&req); err != nil {
return err
}
// 删除通道
err = s.Channel.RemoveChannels(req)
if err != nil {
return err
}

View File

@@ -31,6 +31,7 @@ type Channel struct {
EdgeHost string `gorm:"column:edge_host;type:character varying(255);comment:节点地址" json:"edge_host"` // 节点地址
EdgeID int32 `gorm:"column:edge_id;type:integer;comment:节点ID" json:"edge_id"` // 节点ID
Whitelists string `gorm:"column:whitelists;type:text;comment:IP白名单逗号分隔" json:"whitelists"` // IP白名单逗号分隔
ResourceID int32 `gorm:"column:resource_id;type:integer;comment:套餐ID" json:"resource_id"` // 套餐ID
}
// TableName Channel's table name

View File

@@ -155,11 +155,20 @@ func (b *bill) fillFieldMap() {
func (b bill) clone(db *gorm.DB) bill {
b.billDo.ReplaceConnPool(db.Statement.ConnPool)
b.Trade.db = db.Session(&gorm.Session{Initialized: true})
b.Trade.db.Statement.ConnPool = db.Statement.ConnPool
b.Refund.db = db.Session(&gorm.Session{Initialized: true})
b.Refund.db.Statement.ConnPool = db.Statement.ConnPool
b.Resource.db = db.Session(&gorm.Session{Initialized: true})
b.Resource.db.Statement.ConnPool = db.Statement.ConnPool
return b
}
func (b bill) replaceDB(db *gorm.DB) bill {
b.billDo.ReplaceDB(db)
b.Trade.db = db.Session(&gorm.Session{})
b.Refund.db = db.Session(&gorm.Session{})
b.Resource.db = db.Session(&gorm.Session{})
return b
}
@@ -196,6 +205,11 @@ func (a billBelongsToTrade) Model(m *models.Bill) *billBelongsToTradeTx {
return &billBelongsToTradeTx{a.db.Model(m).Association(a.Name())}
}
func (a billBelongsToTrade) Unscoped() *billBelongsToTrade {
a.db = a.db.Unscoped()
return &a
}
type billBelongsToTradeTx struct{ tx *gorm.Association }
func (a billBelongsToTradeTx) Find() (result *models.Trade, err error) {
@@ -234,6 +248,11 @@ func (a billBelongsToTradeTx) Count() int64 {
return a.tx.Count()
}
func (a billBelongsToTradeTx) Unscoped() *billBelongsToTradeTx {
a.tx = a.tx.Unscoped()
return &a
}
type billBelongsToRefund struct {
db *gorm.DB
@@ -267,6 +286,11 @@ func (a billBelongsToRefund) Model(m *models.Bill) *billBelongsToRefundTx {
return &billBelongsToRefundTx{a.db.Model(m).Association(a.Name())}
}
func (a billBelongsToRefund) Unscoped() *billBelongsToRefund {
a.db = a.db.Unscoped()
return &a
}
type billBelongsToRefundTx struct{ tx *gorm.Association }
func (a billBelongsToRefundTx) Find() (result *models.Refund, err error) {
@@ -305,6 +329,11 @@ func (a billBelongsToRefundTx) Count() int64 {
return a.tx.Count()
}
func (a billBelongsToRefundTx) Unscoped() *billBelongsToRefundTx {
a.tx = a.tx.Unscoped()
return &a
}
type billBelongsToResource struct {
db *gorm.DB
@@ -345,6 +374,11 @@ func (a billBelongsToResource) Model(m *models.Bill) *billBelongsToResourceTx {
return &billBelongsToResourceTx{a.db.Model(m).Association(a.Name())}
}
func (a billBelongsToResource) Unscoped() *billBelongsToResource {
a.db = a.db.Unscoped()
return &a
}
type billBelongsToResourceTx struct{ tx *gorm.Association }
func (a billBelongsToResourceTx) Find() (result *models.Resource, err error) {
@@ -383,6 +417,11 @@ func (a billBelongsToResourceTx) Count() int64 {
return a.tx.Count()
}
func (a billBelongsToResourceTx) Unscoped() *billBelongsToResourceTx {
a.tx = a.tx.Unscoped()
return &a
}
type billDo struct{ gen.DO }
func (b billDo) Debug() *billDo {

View File

@@ -44,6 +44,7 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
_channel.EdgeHost = field.NewString(tableName, "edge_host")
_channel.EdgeID = field.NewInt32(tableName, "edge_id")
_channel.Whitelists = field.NewString(tableName, "whitelists")
_channel.ResourceID = field.NewInt32(tableName, "resource_id")
_channel.fillFieldMap()
@@ -71,6 +72,7 @@ type channel struct {
EdgeHost field.String // 节点地址
EdgeID field.Int32 // 节点ID
Whitelists field.String // IP白名单逗号分隔
ResourceID field.Int32 // 套餐ID
fieldMap map[string]field.Expr
}
@@ -104,6 +106,7 @@ func (c *channel) updateTableName(table string) *channel {
c.EdgeHost = field.NewString(table, "edge_host")
c.EdgeID = field.NewInt32(table, "edge_id")
c.Whitelists = field.NewString(table, "whitelists")
c.ResourceID = field.NewInt32(table, "resource_id")
c.fillFieldMap()
@@ -120,7 +123,7 @@ func (c *channel) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (c *channel) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 17)
c.fieldMap = make(map[string]field.Expr, 18)
c.fieldMap["id"] = c.ID
c.fieldMap["user_id"] = c.UserID
c.fieldMap["proxy_id"] = c.ProxyID
@@ -138,6 +141,7 @@ func (c *channel) fillFieldMap() {
c.fieldMap["edge_host"] = c.EdgeHost
c.fieldMap["edge_id"] = c.EdgeID
c.fieldMap["whitelists"] = c.Whitelists
c.fieldMap["resource_id"] = c.ResourceID
}
func (c channel) clone(db *gorm.DB) channel {

View File

@@ -121,11 +121,14 @@ func (p *proxy) fillFieldMap() {
func (p proxy) clone(db *gorm.DB) proxy {
p.proxyDo.ReplaceConnPool(db.Statement.ConnPool)
p.Edges.db = db.Session(&gorm.Session{Initialized: true})
p.Edges.db.Statement.ConnPool = db.Statement.ConnPool
return p
}
func (p proxy) replaceDB(db *gorm.DB) proxy {
p.proxyDo.ReplaceDB(db)
p.Edges.db = db.Session(&gorm.Session{})
return p
}
@@ -162,6 +165,11 @@ func (a proxyHasManyEdges) Model(m *models.Proxy) *proxyHasManyEdgesTx {
return &proxyHasManyEdgesTx{a.db.Model(m).Association(a.Name())}
}
func (a proxyHasManyEdges) Unscoped() *proxyHasManyEdges {
a.db = a.db.Unscoped()
return &a
}
type proxyHasManyEdgesTx struct{ tx *gorm.Association }
func (a proxyHasManyEdgesTx) Find() (result []*models.Edge, err error) {
@@ -200,6 +208,11 @@ func (a proxyHasManyEdgesTx) Count() int64 {
return a.tx.Count()
}
func (a proxyHasManyEdgesTx) Unscoped() *proxyHasManyEdgesTx {
a.tx = a.tx.Unscoped()
return &a
}
type proxyDo struct{ gen.DO }
func (p proxyDo) Debug() *proxyDo {

View File

@@ -121,11 +121,17 @@ func (r *resource) fillFieldMap() {
func (r resource) clone(db *gorm.DB) resource {
r.resourceDo.ReplaceConnPool(db.Statement.ConnPool)
r.Short.db = db.Session(&gorm.Session{Initialized: true})
r.Short.db.Statement.ConnPool = db.Statement.ConnPool
r.Long.db = db.Session(&gorm.Session{Initialized: true})
r.Long.db.Statement.ConnPool = db.Statement.ConnPool
return r
}
func (r resource) replaceDB(db *gorm.DB) resource {
r.resourceDo.ReplaceDB(db)
r.Short.db = db.Session(&gorm.Session{})
r.Long.db = db.Session(&gorm.Session{})
return r
}
@@ -162,6 +168,11 @@ func (a resourceHasOneShort) Model(m *models.Resource) *resourceHasOneShortTx {
return &resourceHasOneShortTx{a.db.Model(m).Association(a.Name())}
}
func (a resourceHasOneShort) Unscoped() *resourceHasOneShort {
a.db = a.db.Unscoped()
return &a
}
type resourceHasOneShortTx struct{ tx *gorm.Association }
func (a resourceHasOneShortTx) Find() (result *models.ResourceShort, err error) {
@@ -200,6 +211,11 @@ func (a resourceHasOneShortTx) Count() int64 {
return a.tx.Count()
}
func (a resourceHasOneShortTx) Unscoped() *resourceHasOneShortTx {
a.tx = a.tx.Unscoped()
return &a
}
type resourceHasOneLong struct {
db *gorm.DB
@@ -233,6 +249,11 @@ func (a resourceHasOneLong) Model(m *models.Resource) *resourceHasOneLongTx {
return &resourceHasOneLongTx{a.db.Model(m).Association(a.Name())}
}
func (a resourceHasOneLong) Unscoped() *resourceHasOneLong {
a.db = a.db.Unscoped()
return &a
}
type resourceHasOneLongTx struct{ tx *gorm.Association }
func (a resourceHasOneLongTx) Find() (result *models.ResourceLong, err error) {
@@ -271,6 +292,11 @@ func (a resourceHasOneLongTx) Count() int64 {
return a.tx.Count()
}
func (a resourceHasOneLongTx) Unscoped() *resourceHasOneLongTx {
a.tx = a.tx.Unscoped()
return &a
}
type resourceDo struct{ gen.DO }
func (r resourceDo) Debug() *resourceDo {

View File

@@ -50,6 +50,7 @@ func ApplyRouters(app *fiber.App) {
channel.Post("/list", handlers.ListChannels)
channel.Post("/create", handlers.CreateChannel)
channel.Post("/remove", handlers.RemoveChannels)
channel.Post("/remove/by-task", handlers.RemoveChannelByTask)
// 交易
trade := api.Group("/trade")

View File

@@ -4,13 +4,13 @@ import (
"context"
"database/sql"
"fmt"
"github.com/hibiken/asynq"
"gorm.io/gen/field"
"log/slog"
"math"
"math/rand/v2"
"platform/pkg/env"
"platform/pkg/u"
"platform/web/auth"
channel2 "platform/web/domains/channel"
edge2 "platform/web/domains/edge"
proxy2 "platform/web/domains/proxy"
@@ -19,11 +19,11 @@ import (
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
"platform/web/tasks"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/redis/go-redis/v9"
)
@@ -32,47 +32,71 @@ var Channel = &channelService{}
type channelService struct {
}
// region RemoveChannel
// region 删除通道
func (s *channelService) RemoveChannels(ctx context.Context, authCtx *auth.Context, id ...int32) error {
func (s *channelService) RemoveChannels(id []int32, userId ...int32) error {
var now = time.Now()
var rid = ctx.Value(requestid.ConfigDefault.ContextKey).(string)
err := q.Q.Transaction(func(tx *q.Query) error {
// 查找通道
channels, err := tx.Channel.Where(
q.Channel.ID.In(id...),
).Find()
var do = tx.Channel.Where(q.Channel.ID.In(id...))
if len(userId) > 0 {
do.Where(q.Channel.UserID.Eq(userId[0]))
}
channels, err := tx.Channel.Where(do).Find()
if err != nil {
return err
}
// 检查权限,如果为用户操作的话,则只能删除自己的通道
proxyMap := make(map[int32]*m.Proxy)
proxyIds := make([]int32, 0)
resourceMap := make(map[int32]*m.Resource)
resourceIds := make([]int32, 0)
for _, channel := range channels {
if authCtx.Payload.Type == auth.PayloadUser && authCtx.Payload.Id != channel.UserID {
return ErrRemoveForbidden
if _, ok := proxyMap[channel.ProxyID]; !ok {
proxyIds = append(proxyIds, channel.ProxyID)
proxyMap[channel.ProxyID] = &m.Proxy{}
}
if _, ok := resourceMap[channel.ResourceID]; !ok {
resourceIds = append(resourceIds, channel.ResourceID)
resourceMap[channel.ResourceID] = &m.Resource{}
}
}
// 查找资源
resources, err := tx.Resource.Where(tx.Resource.ID.In(resourceIds...)).Find()
if err != nil {
return err
}
for _, res := range resources {
resourceMap[res.ID] = res
}
// 查找代理
proxySet := make(map[int32]struct{})
proxyIds := make([]int32, 0)
for _, channel := range channels {
if _, ok := proxySet[channel.ProxyID]; !ok {
proxyIds = append(proxyIds, channel.ProxyID)
proxySet[channel.ProxyID] = struct{}{}
}
}
proxies, err := tx.Proxy.Where(
q.Proxy.ID.In(proxyIds...),
).Find()
proxies, err := tx.Proxy.Where(q.Proxy.ID.In(proxyIds...)).Find()
if err != nil {
return err
}
for _, proxy := range proxies {
proxyMap[proxy.ID] = proxy
}
// 区分通道类型
shortToRemove := make([]*m.Channel, 0)
longToRemove := make([]*m.Channel, 0)
for _, channel := range channels {
resource := resourceMap[channel.ResourceID]
switch resource2.Type(resource.Type) {
case resource2.TypeShort:
shortToRemove = append(shortToRemove, channel)
case resource2.TypeLong:
longToRemove = append(longToRemove, channel)
}
}
// 删除指定的通道
result, err := tx.Channel.Debug().
result, err := tx.Channel.
Where(q.Channel.ID.In(id...)).
Update(q.Channel.DeletedAt, now)
if err != nil {
@@ -86,95 +110,14 @@ func (s *channelService) RemoveChannels(ctx context.Context, authCtx *auth.Conte
if env.DebugExternalChange {
var step = time.Now()
// 组织数据
var configMap = make(map[int32][]g.PortConfigsReq, len(proxies))
var proxyMap = make(map[int32]*m.Proxy, len(proxies))
for _, proxy := range proxies {
configMap[proxy.ID] = make([]g.PortConfigsReq, 0)
proxyMap[proxy.ID] = proxy
}
var portMap = make(map[uint64]struct{})
for _, channel := range channels {
var config = g.PortConfigsReq{
Port: int(channel.ProxyPort),
Edge: &[]string{},
AutoEdgeConfig: &g.AutoEdgeConfig{
Count: u.P(0),
},
Status: false,
}
configMap[channel.ProxyID] = append(configMap[channel.ProxyID], config)
key := uint64(channel.ProxyID)<<32 | uint64(channel.ProxyPort)
portMap[key] = struct{}{}
}
slog.Debug("组织数据", "rid", rid, "step", time.Since(step))
// 更新配置
for proxyId, configs := range configMap {
if len(configs) == 0 {
continue
}
proxy, ok := proxyMap[proxyId]
if !ok {
return ChannelServiceErr("代理不存在")
}
var secret = strings.Split(proxy.Secret, ":")
gateway := g.NewGateway(
proxy.Host,
secret[0],
secret[1],
)
// 查询节点配置
step = time.Now()
actives, err := gateway.GatewayPortActive()
if len(shortToRemove) > 0 {
err := removeShortChannelExternal(proxies, shortToRemove)
if err != nil {
return err
}
slog.Debug("查询节点配置", "rid", rid, "step", time.Since(step))
// 更新节点配置
step = time.Now()
err = gateway.GatewayPortConfigs(configs)
if err != nil {
return err
}
slog.Debug("更新节点配置", "rid", rid, "step", time.Since(step))
// 下线对应节点
step = time.Now()
var edges []string
for portStr, active := range actives {
port, err := strconv.Atoi(portStr)
if err != nil {
return err
}
key := uint64(proxyId)<<32 | uint64(port)
if _, ok := portMap[key]; ok {
edges = append(edges, active.Edge...)
}
}
if len(edges) > 0 {
_, err := g.Cloud.CloudDisconnect(g.CloudDisconnectReq{
Uuid: proxy.Name,
Edge: edges,
})
if err != nil {
return err
}
}
slog.Debug("下线对应节点", "rid", rid, "step", time.Since(step))
}
slog.Debug("提交删除通道配置", "step", time.Since(step))
}
return nil
@@ -186,12 +129,91 @@ func (s *channelService) RemoveChannels(ctx context.Context, authCtx *auth.Conte
return nil
}
func removeShortChannelExternal(proxies []*m.Proxy, channels []*m.Channel) error {
// 组织数据
var configMap = make(map[int32][]g.PortConfigsReq, len(proxies))
var proxyMap = make(map[int32]*m.Proxy, len(proxies))
for _, proxy := range proxies {
configMap[proxy.ID] = make([]g.PortConfigsReq, 0)
proxyMap[proxy.ID] = proxy
}
var portMap = make(map[uint64]struct{})
for _, channel := range channels {
var config = g.PortConfigsReq{
Port: int(channel.ProxyPort),
Edge: &[]string{},
AutoEdgeConfig: &g.AutoEdgeConfig{
Count: u.P(0),
},
Status: false,
}
configMap[channel.ProxyID] = append(configMap[channel.ProxyID], config)
key := uint64(channel.ProxyID)<<32 | uint64(channel.ProxyPort)
portMap[key] = struct{}{}
}
// 更新配置
for proxyId, configs := range configMap {
if len(configs) == 0 {
continue
}
proxy, ok := proxyMap[proxyId]
if !ok {
return ChannelServiceErr("代理不存在")
}
var secret = strings.Split(proxy.Secret, ":")
gateway := g.NewGateway(
proxy.Host,
secret[0],
secret[1],
)
// 查询节点配置
actives, err := gateway.GatewayPortActive()
if err != nil {
return err
}
// 更新节点配置
err = gateway.GatewayPortConfigs(configs)
if err != nil {
return err
}
// 下线对应节点
var edges []string
for portStr, active := range actives {
port, err := strconv.Atoi(portStr)
if err != nil {
return err
}
key := uint64(proxyId)<<32 | uint64(port)
if _, ok := portMap[key]; ok {
edges = append(edges, active.Edge...)
}
}
if len(edges) > 0 {
_, err := g.Cloud.CloudDisconnect(g.CloudDisconnectReq{
Uuid: proxy.Name,
Edge: edges,
})
if err != nil {
return err
}
}
}
return nil
}
// endregion
// region CreateChannel
// region 创建通道
func (s *channelService) CreateChannel(
authCtx *auth.Context,
userId int32,
resourceId int32,
protocol channel2.Protocol,
authType ChannelAuthType,
@@ -204,10 +226,11 @@ func (s *channelService) CreateChannel(
filter = edgeFilter[0]
}
var resource *ResourceInfo
err = q.Q.Transaction(func(q *q.Query) (err error) {
// 查找套餐
resource, err := findResource(q, resourceId, authCtx.Payload.Id, count, now)
resource, err = findResource(q, resourceId, userId, count, now)
if err != nil {
return err
}
@@ -215,7 +238,7 @@ func (s *channelService) CreateChannel(
// 查找白名单
var whitelist []string
if authType == ChannelAuthTypeIp {
whitelist, err = findWhitelist(q, authCtx.Payload.Id)
whitelist, err = findWhitelist(q, userId)
if err != nil {
return err
}
@@ -232,10 +255,10 @@ func (s *channelService) CreateChannel(
switch resource.Type {
case resource2.TypeShort:
config.Expiration = now.Add(time.Duration(resource.Live) * time.Second)
channels, err = assignShortChannels(q, authCtx.Payload.Id, count, config, filter, now)
channels, err = assignShortChannels(q, userId, resourceId, count, config, filter, now)
case resource2.TypeLong:
config.Expiration = now.Add(time.Duration(resource.Live) * time.Hour)
channels, err = assignLongChannels(q, authCtx.Payload.Id, count, config, filter)
channels, err = assignLongChannels(q, userId, resourceId, count, config, filter)
}
if err != nil {
return err
@@ -253,6 +276,27 @@ func (s *channelService) CreateChannel(
return nil, err
}
// 定时异步删除过期通道
var duration time.Duration
switch resource.Type {
case resource2.TypeShort:
duration = time.Duration(resource.Live) * time.Second
case resource2.TypeLong:
duration = time.Duration(resource.Live) * time.Minute
}
var ids = make([]int32, len(channels))
for i := range channels {
ids[i] = channels[i].ID
}
_, err = g.Asynq.Enqueue(
tasks.NewRemoveChannel(ids),
asynq.ProcessIn(duration),
)
if err != nil {
return nil, err
}
return channels, nil
}
@@ -287,7 +331,7 @@ func findResource(q *q.Query, resourceId int32, userId int32, count int, now tim
info.DailyLast = time.Time(sub.DailyLast)
info.Quota = sub.Quota
info.Used = sub.Used
info.Expire = time.Time(sub.DailyLast)
info.Expire = time.Time(sub.Expire)
case resource2.TypeLong:
var sub = resource.Long
info.Mode = resource2.Mode(sub.Type)
@@ -297,7 +341,7 @@ func findResource(q *q.Query, resourceId int32, userId int32, count int, now tim
info.DailyLast = time.Time(sub.DailyLast)
info.Quota = sub.Quota
info.Used = sub.Used
info.Expire = time.Time(sub.DailyLast)
info.Expire = time.Time(sub.Expire)
}
// 检查套餐状态
@@ -353,14 +397,7 @@ func findWhitelist(q *q.Query, userId int32) ([]string, error) {
return whitelist, nil
}
func assignShortChannels(
q *q.Query,
userId int32,
count int,
config ChannelCreateConfig,
filter EdgeFilterConfig,
now time.Time,
) ([]*m.Channel, error) {
func assignShortChannels(q *q.Query, userId int32, resourceId int32, count int, config ChannelCreateConfig, filter EdgeFilterConfig, now time.Time) ([]*m.Channel, error) {
// 查找网关
proxies, err := q.Proxy.
@@ -503,6 +540,7 @@ func assignShortChannels(
var newChannel = &m.Channel{
UserID: userId,
ProxyID: proxy.ID,
ResourceID: resourceId,
ProxyHost: proxy.Host,
ProxyPort: int32(port),
Protocol: int32(config.Protocol),
@@ -556,7 +594,7 @@ func assignShortChannels(
return newChannels, nil
}
func assignLongChannels(q *q.Query, userId int32, count int, config ChannelCreateConfig, filter EdgeFilterConfig) ([]*m.Channel, error) {
func assignLongChannels(q *q.Query, userId int32, resourceId int32, count int, config ChannelCreateConfig, filter EdgeFilterConfig) ([]*m.Channel, error) {
// 查询符合条件的节点,根据 channel 统计使用次数
var edges = make([]struct {
@@ -631,6 +669,7 @@ func assignLongChannels(q *q.Query, userId int32, count int, config ChannelCreat
UserID: userId,
ProxyID: edge.ProxyID,
EdgeID: edge.ID,
ResourceID: resourceId,
Protocol: int32(config.Protocol),
AuthIP: config.AuthIp,
AuthPass: config.AuthPass,
@@ -752,8 +791,6 @@ func saveAssigns(q *q.Query, resource *ResourceInfo, channels []*m.Channel, now
return nil
}
// endregion
func genPassPair() (string, string) {
//goland:noinspection SpellCheckingInspection
var alphabet = []rune("abcdefghjkmnpqrstuvwxyz")
@@ -773,6 +810,8 @@ func genPassPair() (string, string) {
return string(username), string(password)
}
// endregion
type ChannelAuthType int
const (
@@ -820,6 +859,5 @@ const (
ErrResourceExhausted = ChannelServiceErr("套餐已用完")
ErrResourceExpired = ChannelServiceErr("套餐已过期")
ErrResourceDailyLimit = ChannelServiceErr("套餐每日配额已用完")
ErrRemoveForbidden = ChannelServiceErr("删除通道失败,当前用户没有权限")
ErrEdgesNoAvailable = ChannelServiceErr("没有可用的节点")
)

18
web/tasks/channel.go Normal file
View File

@@ -0,0 +1,18 @@
package tasks
import (
"encoding/json"
"github.com/hibiken/asynq"
"log/slog"
)
const RemoveChannel = "channel:remove"
func NewRemoveChannel(ids []int32) *asynq.Task {
bytes, err := json.Marshal(ids)
if err != nil {
slog.Error("序列化删除通道任务失败", "error", err)
return nil
}
return asynq.NewTask(RemoveChannel, bytes)
}

View File

@@ -94,6 +94,8 @@ func (s *Server) Stop() {
slog.Error("Failed to close database connection", slog.Any("err", err))
}
g.CloseAsynq()
err = s.fiber.Shutdown()
if err != nil {
slog.Error("Failed to shutdown server", slog.Any("err", err))