优化表结构,重构模型,重新实现基于白银网关的提取节点流程

This commit is contained in:
2025-11-24 18:44:06 +08:00
parent 9a574f55cb
commit cb2a963a37
142 changed files with 6528 additions and 5808 deletions

View File

@@ -3,7 +3,6 @@ package handlers
import (
"platform/web/auth"
"platform/web/core"
"platform/web/globals/orm"
q "platform/web/queries"
"time"
@@ -39,13 +38,13 @@ func ListBill(c *fiber.Ctx) error {
Where(q.Bill.UserID.Eq(authCtx.User.ID))
if req.Type != nil {
do.Where(q.Bill.Type.Eq(int32(*req.Type)))
do.Where(q.Bill.Type.Eq(int(*req.Type)))
}
if req.CreateAfter != nil {
do.Where(q.Bill.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter)))
do.Where(q.Bill.CreatedAt.Gte(*req.CreateAfter))
}
if req.CreateBefore != nil {
do.Where(q.Bill.CreatedAt.Lte(orm.LocalDateTime(*req.CreateBefore)))
do.Where(q.Bill.CreatedAt.Lte(*req.CreateBefore))
}
if req.BillNo != nil && *req.BillNo != "" {
do.Where(q.Bill.BillNo.Eq(*req.BillNo))

View File

@@ -1,11 +1,11 @@
package handlers
import (
"fmt"
"net/netip"
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
channel2 "platform/web/domains/channel"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"time"
@@ -40,18 +40,18 @@ func ListChannels(c *fiber.Ctx) error {
Where(q.Channel.UserID.Eq(authContext.User.ID))
switch req.AuthType {
case s.ChannelAuthTypeIp:
cond.Where(q.Channel.AuthIP.Is(true))
cond.Where(q.Channel.Whitelists.IsNotNull())
case s.ChannelAuthTypePass:
cond.Where(q.Channel.AuthPass.Is(true))
cond.Where(q.Channel.Username.IsNotNull(), q.Channel.Password.IsNotNull())
default:
break
}
if req.ExpireAfter != nil {
cond.Where(q.Channel.Expiration.Gte(orm.LocalDateTime(*req.ExpireAfter)))
cond.Where(q.Channel.ExpiredAt.Gte(*req.ExpireAfter))
}
if req.ExpireBefore != nil {
cond.Where(q.Channel.Expiration.Lte(orm.LocalDateTime(*req.ExpireBefore)))
cond.Where(q.Channel.ExpiredAt.Lte(*req.ExpireBefore))
}
// 查询数据
@@ -92,19 +92,19 @@ func ListChannels(c *fiber.Ctx) error {
type CreateChannelReq struct {
ResourceId int32 `json:"resource_id" validate:"required"`
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
Protocol channel2.Protocol `json:"protocol" validate:"required"`
Protocol int `json:"protocol" validate:"required"`
Count int `json:"count" validate:"required"`
Prov string `json:"prov"`
City string `json:"city"`
Isp string `json:"isp"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *int `json:"isp"`
}
type CreateChannelRespItem struct {
Proto channel2.Protocol `json:"-"`
Host string `json:"host"`
Port int32 `json:"port"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
Proto int `json:"-"`
Host string `json:"host"`
Port uint16 `json:"port"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
}
func CreateChannel(c *fiber.Ctx) error {
@@ -115,48 +115,32 @@ func CreateChannel(c *fiber.Ctx) error {
return err
}
// 检查用户其他权限
user := authCtx.User
if user.IDToken == nil || *user.IDToken == "" {
return fiber.NewError(fiber.StatusForbidden, "账号未实名")
}
count, err := q.Whitelist.Where(
q.Whitelist.UserID.Eq(user.ID),
q.Whitelist.Host.Eq(c.IP()),
).Count()
if err != nil {
return err
}
if count == 0 {
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("非白名单IP %s", c.IP()))
}
// 解析参数
req := new(CreateChannelReq)
if err := c.BodyParser(req); err != nil {
return err
}
var isp string
switch req.Isp {
case "1":
isp = "电信"
case "2":
isp = "联通"
case "3":
isp = "移动"
ip, err := netip.ParseAddr(c.Get(core.HeaderUserIP))
if err != nil {
return core.NewBizErr("解析请求头客户端 IP 地址失败", err)
}
// 创建通道
result, err := s.Channel.CreateChannel(
c,
result, err := s.Channel.CreateChannels(
ip,
user.ID,
req.ResourceId,
req.Protocol,
req.AuthType,
req.AuthType == s.ChannelAuthTypeIp,
req.AuthType == s.ChannelAuthTypePass,
req.Count,
s.EdgeFilter{
Isp: isp,
Isp: u.ElseTo(req.Isp, m.ToEdgeISP),
Prov: req.Prov,
City: req.City,
},
@@ -170,8 +154,8 @@ func CreateChannel(c *fiber.Ctx) error {
for i, channel := range result {
resp[i] = &CreateChannelRespItem{
Proto: req.Protocol,
Host: channel.ProxyHost,
Port: channel.ProxyPort,
Host: channel.Proxy.IP.String(),
Port: channel.Port,
}
if req.AuthType == s.ChannelAuthTypePass {
resp[i].Username = channel.Username
@@ -188,12 +172,13 @@ type CreateChannelResultType string
// region RemoveChannels
type RemoveChannelsReq struct {
ByIds []int32 `json:"by_ids" validate:"required"`
Batch string `json:"batch" validate:"required"`
Ids []int32 `json:"ids" validate:"required"`
}
func RemoveChannels(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
return err
}
@@ -205,7 +190,7 @@ func RemoveChannels(c *fiber.Ctx) error {
}
// 删除通道
err = s.Channel.RemoveChannels(req.ByIds, authCtx.User.ID)
err = s.Channel.RemoveChannels(req.Batch, req.Ids)
if err != nil {
return err
}

View File

@@ -1,20 +1,8 @@
package handlers
import (
"errors"
"log/slog"
"platform/pkg/u"
"platform/web/auth"
edge2 "platform/web/domains/edge"
proxy2 "platform/web/domains/proxy"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"gorm.io/gen/field"
"gorm.io/gorm"
"github.com/gofiber/fiber/v2"
)
@@ -29,80 +17,82 @@ type RegisterEdgeResp struct {
}
func AssignEdge(c *fiber.Ctx) (err error) {
// 验证请求参数
var req = new(RegisterEdgeReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 全局锁,防止并发注册
var mutex = g.Redsync.NewMutex("edge:discovery")
if err := mutex.Lock(); err != nil {
return errors.New("服务繁忙,请稍后重试")
}
defer func() {
if ok, err := mutex.Unlock(); err != nil {
slog.Error("解锁失败", slog.Bool("ok", ok), slog.Any("err", err))
}
}()
// 检查节点
var fwd *m.Proxy
var edge *m.Edge
edge, err = q.Edge.
Where(q.Edge.Name.Eq(req.Name)).
Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
// 挑选合适的转发服务
fwd, err = q.Proxy.
LeftJoin(q.Edge, q.Edge.ProxyID.EqCol(q.Proxy.ID), q.Edge.Status.Eq(1)).
Select(q.Proxy.ALL, q.Edge.ALL.Count().As("count")).
Where(q.Proxy.Type.Eq(int32(proxy2.TypeSelfHosted))).
Group(q.Proxy.ID).
Order(field.NewField("", "count").Desc()).
First()
if err != nil {
return err
}
// 保存节点信息
edge = &m.Edge{
Name: req.Name,
Version: int32(req.Version),
Type: int32(edge2.TypeSelfHosted),
ProxyID: &fwd.ID,
}
err = q.Edge.Create(edge)
if err != nil {
return err
}
} else if err == nil {
// 获取已配置的转发服务
fwd, err = q.Proxy.
Where(q.Proxy.ID.Eq(*edge.ProxyID)).
Take()
if err != nil {
return err
}
// 节点已存在,更新版本号
if edge.Version < int32(req.Version) {
_, err = q.Edge.
Where(q.Edge.ID.Eq(edge.ID)).
UpdateSimple(q.Edge.Version.Value(int32(req.Version)))
if err != nil {
return err
}
}
} else {
return err
}
// 返回服务地址
return c.JSON(RegisterEdgeResp{
Id: edge.ID,
Host: fwd.Host,
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
// // 验证请求参数
// var req = new(RegisterEdgeReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// // 全局锁,防止并发注册
// var mutex = g.Redsync.NewMutex("edge:discovery")
// if err := mutex.Lock(); err != nil {
// return errors.New("服务繁忙,请稍后重试")
// }
// defer func() {
// if ok, err := mutex.Unlock(); err != nil {
// slog.Error("解锁失败", slog.Bool("ok", ok), slog.Any("err", err))
// }
// }()
// // 检查节点
// var fwd *m.Proxy
// var edge *m.Edge
// edge, err = q.Edge.
// Where(q.Edge.Mac.Eq(req.Name)).
// Take()
// if errors.Is(err, gorm.ErrRecordNotFound) {
// // 挑选合适的转发服务
// fwd, err = q.Proxy.
// LeftJoin(q.Edge, q.Edge.ProxyID.EqCol(q.Proxy.ID), q.Edge.Status.Eq(1)).
// Select(q.Proxy.ALL, q.Edge.ALL.Count().As("count")).
// Where(q.Proxy.Type.Eq(int32(proxy2.TypeSelfHosted))).
// Group(q.Proxy.ID).
// Order(field.NewField("", "count").Desc()).
// First()
// if err != nil {
// return err
// }
// // 保存节点信息
// edge = &m.Edge{
// Name: req.Name,
// Version: int32(req.Version),
// Type: int32(edge2.TypeSelfHosted),
// ProxyID: &fwd.ID,
// }
// err = q.Edge.Create(edge)
// if err != nil {
// return err
// }
// } else if err == nil {
// // 获取已配置的转发服务
// fwd, err = q.Proxy.
// Where(q.Proxy.ID.Eq(*edge.ProxyID)).
// Take()
// if err != nil {
// return err
// }
// // 节点已存在,更新版本号
// if edge.Version < int32(req.Version) {
// _, err = q.Edge.
// Where(q.Edge.ID.Eq(edge.ID)).
// UpdateSimple(q.Edge.Version.Value(int32(req.Version)))
// if err != nil {
// return err
// }
// }
// } else {
// return err
// }
// // 返回服务地址
// return c.JSON(RegisterEdgeResp{
// Id: edge.ID,
// Host: fwd.Host,
// })
}
type AllEdgesAvailableReq struct {
@@ -120,36 +110,40 @@ type AllEdgesAvailableRespItem struct {
}
func AllEdgesAvailable(c *fiber.Ctx) (err error) {
// 检查权限
_, err = auth.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
// 验证请求参数
var req = new(AllEdgesAvailableReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// // 检查权限
// _, err = auth.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// 获取可用的转发服务
infos, err := s.Edge.AllEdges(req.Count, req.EdgeFilter)
if err != nil {
return err
}
// // 验证请求参数
// var req = new(AllEdgesAvailableReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// 返回结果
var edges = make([]AllEdgesAvailableRespItem, len(infos))
for i, info := range infos {
edges[i] = AllEdgesAvailableRespItem{
Ip: info.Host,
Port: u.Z(info.ProxyPort),
Isp: edge2.ISP(info.Isp).String(),
Prov: info.Prov,
City: info.City,
Status: info.Status,
}
}
return c.JSON(edges)
// // 获取可用的转发服务
// infos, err := s.Edge.AllEdges(req.Count, req.EdgeFilter)
// if err != nil {
// return err
// }
// // 返回结果
// var edges = make([]AllEdgesAvailableRespItem, len(infos))
// for i, info := range infos {
// edges[i] = AllEdgesAvailableRespItem{
// Ip: info.Host,
// Port: u.Z(info.ProxyPort),
// Isp: edge2.ISP(info.Isp).String(),
// Prov: info.Prov,
// City: info.City,
// Status: info.Status,
// }
// }
// return c.JSON(edges)
}

View File

@@ -24,7 +24,7 @@ import (
// region Identify
type IdentifyReq struct {
Type int32 `json:"type" validate:"required,oneof=1 2"`
Type int `json:"type" validate:"required,oneof=1 2"`
Name string `json:"name" validate:"required"`
IdenNo string `json:"iden_no" validate:"required"`
}
@@ -173,7 +173,7 @@ func IdentifyCallback(c *fiber.Ctx) error {
q.User.Name,
).
Updates(m.User{
IDType: info.Type,
IDType: m.UserIDType(info.Type),
IDNo: &info.IdNo,
IDToken: &info.Token,
Name: &info.Name,
@@ -202,7 +202,7 @@ func idenKey(id string) string {
type idenInfo struct {
Uid int32 `json:"uid"`
Type int32 `json:"type"`
Type int `json:"type"`
Name string `json:"name"`
IdNo string `json:"id_no"`
Token string `json:"token"`

View File

@@ -1,26 +1,9 @@
package handlers
import (
"crypto/rand"
"encoding/base32"
"fmt"
"log/slog"
"platform/pkg/u"
auth2 "platform/web/auth"
"platform/web/core"
edge2 "platform/web/domains/edge"
proxy2 "platform/web/domains/proxy"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"gorm.io/gen/field"
"gorm.io/gorm/clause"
)
// region 报告上线
@@ -38,103 +21,106 @@ type ProxyReportOnlineResp struct {
}
func ProxyReportOnline(c *fiber.Ctx) (err error) {
// 检查接口权限
_, err = auth2.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
// 验证请求参数
var req = new(ProxyReportOnlineReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 创建代理
var ip = c.Context().RemoteIP()
var secretBytes = make([]byte, 16)
if _, err := rand.Read(secretBytes); err != nil {
return err
}
var secret = base32.StdEncoding.
WithPadding(base32.NoPadding).
EncodeToString(secretBytes)
slog.Debug("生成随机密钥", "ip", ip, "secret", secret)
var proxy = &m.Proxy{
Name: req.Name,
Version: int32(req.Version),
Type: int32(proxy2.TypeSelfHosted),
Host: ip.String(),
Secret: &secret,
Status: 1,
}
err = q.Proxy.
Clauses(clause.OnConflict{
UpdateAll: true,
Columns: []clause.Column{
{Name: q.Proxy.Name.ColumnName().String()},
},
}).
Create(proxy)
if err != nil {
return err
}
// 获取边缘节点信息
data, err := q.Edge.Where(
q.Edge.ProxyID.Eq(proxy.ID),
).Find()
if err != nil {
return err
}
edges := make([]*ProxyEdge, len(data))
for i, edge := range data {
edges[i] = &ProxyEdge{
Id: edge.ID,
Port: edge.ProxyPort,
Prov: &edge.Prov,
City: &edge.City,
Isp: u.P(edge2.ISP(edge.Isp).String()),
Status: &edge.Status,
Loss: edge.Loss,
Rtt: edge.Rtt,
}
}
// 获取许可配置
channels, err := q.Channel.Where(
q.Channel.ProxyID.Eq(proxy.ID),
q.Channel.Expiration.Gt(orm.LocalDateTime(time.Now())),
).Find()
if err != nil {
return err
}
var permits = make([]*ProxyPermit, len(channels))
for i, channel := range channels {
if channel.EdgeID == nil {
return core.NewBizErr(fmt.Sprintf("权限解析异常通道缺少边缘节点ID %d", channel.ID))
}
permits[i] = &ProxyPermit{
Id: *channel.EdgeID,
Expire: time.Time(channel.Expiration),
Whitelists: u.P(strings.Split(u.Z(channel.Whitelists), ",")),
Username: channel.Username,
Password: channel.Password,
}
}
slog.Debug("注册转发服务", "ip", ip, "id", proxy.ID)
return c.JSON(&ProxyReportOnlineResp{
Id: proxy.ID,
Secret: secret,
Edges: edges,
Permits: permits,
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
// // 检查接口权限
// _, err = auth2.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// // 验证请求参数
// var req = new(ProxyReportOnlineReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// // 创建代理
// var ip = c.Context().RemoteIP()
// var secretBytes = make([]byte, 16)
// if _, err := rand.Read(secretBytes); err != nil {
// return err
// }
// var secret = base32.StdEncoding.
// WithPadding(base32.NoPadding).
// EncodeToString(secretBytes)
// slog.Debug("生成随机密钥", "ip", ip, "secret", secret)
// var proxy = &m.Proxy{
// Mac: req.Name,
// Version: int32(req.Version),
// Type: m.ProxyTypeSelfHosted,
// IP: ip,
// Secret: &secret,
// Status: 1,
// }
// err = q.Proxy.
// Clauses(clause.OnConflict{
// UpdateAll: true,
// Columns: []clause.Column{
// {Name: q.Proxy.Mac.ColumnName().String()},
// },
// }).
// Create(proxy)
// if err != nil {
// return err
// }
// // 获取边缘节点信息
// data, err := q.Edge.Where(
// q.Edge.ProxyID.Eq(proxy.ID),
// ).Find()
// if err != nil {
// return err
// }
// edges := make([]*ProxyEdge, len(data))
// for i, edge := range data {
// edges[i] = &ProxyEdge{
// Id: edge.ID,
// Port: edge.ProxyPort,
// Prov: &edge.Prov,
// City: &edge.City,
// Isp: u.P(edge2.ISP(edge.Isp).String()),
// Status: &edge.Status,
// Loss: edge.Loss,
// Rtt: edge.Rtt,
// }
// }
// // 获取许可配置
// channels, err := q.Channel.Where(
// q.Channel.ProxyID.Eq(proxy.ID),
// q.Channel.Expiration.Gt(orm.LocalDateTime(time.Now())),
// ).Find()
// if err != nil {
// return err
// }
// var permits = make([]*ProxyPermit, len(channels))
// for i, channel := range channels {
// if channel.EdgeID == nil {
// return core.NewBizErr(fmt.Sprintf("权限解析异常通道缺少边缘节点ID %d", channel.ID))
// }
// permits[i] = &ProxyPermit{
// Id: *channel.EdgeID,
// Expire: time.Time(channel.Expiration),
// Whitelists: u.P(strings.Split(u.Z(channel.Whitelists), ",")),
// Username: channel.Username,
// Password: channel.Password,
// }
// }
// slog.Debug("注册转发服务", "ip", ip, "id", proxy.ID)
// return c.JSON(&ProxyReportOnlineResp{
// Id: proxy.ID,
// Secret: secret,
// Edges: edges,
// Permits: permits,
// })
}
// endregion
@@ -146,36 +132,40 @@ type ProxyReportOfflineReq struct {
}
func ProxyReportOffline(c *fiber.Ctx) (err error) {
// 检查接口权限
_, err = auth2.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
// 验证请求参数
var req = new(ProxyReportOfflineReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// // 检查接口权限
// _, err = auth2.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// 下线转发服务
_, err = q.Proxy.
Where(q.Proxy.ID.Eq(req.Id)).
UpdateSimple(q.Proxy.Status.Value(0))
if err != nil {
return err
}
// // 验证请求参数
// var req = new(ProxyReportOfflineReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// 下线所有相关的边缘节点
_, err = q.Edge.
Where(q.Edge.ProxyID.Eq(req.Id)).
UpdateSimple(q.Edge.Status.Value(0))
if err != nil {
return err
}
// // 下线转发服务
// _, err = q.Proxy.
// Where(q.Proxy.ID.Eq(req.Id)).
// UpdateSimple(q.Proxy.Status.Value(0))
// if err != nil {
// return err
// }
return nil
// // 下线所有相关的边缘节点
// _, err = q.Edge.
// Where(q.Edge.ProxyID.Eq(req.Id)).
// UpdateSimple(q.Edge.Status.Value(0))
// if err != nil {
// return err
// }
// return nil
}
// endregion
@@ -188,157 +178,161 @@ type ProxyReportUpdateReq struct {
}
func ProxyReportUpdate(c *fiber.Ctx) (err error) {
// 检查接口权限
_, err = auth2.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
// 验证请求参数
var req = new(ProxyReportUpdateReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 更新节点信息
var idsActive = make([]int32, 0, len(req.Edges))
var idsInactive = make([]int32, 0, len(req.Edges))
var idsIspUnknown = make([]int32, 0, len(req.Edges))
var idsIspTelecom = make([]int32, 0, len(req.Edges))
var idsIspUnicom = make([]int32, 0, len(req.Edges))
var idsIspMobile = make([]int32, 0, len(req.Edges))
var otherEdges = make([]*ProxyEdge, 0, len(req.Edges))
for _, edge := range req.Edges {
// 检查更新ISP
if edge.Isp != nil {
switch edge2.ISPFromStr(*edge.Isp) {
case edge2.IspUnknown:
idsIspUnknown = append(idsIspUnknown, edge.Id)
case edge2.IspChinaTelecom:
idsIspTelecom = append(idsIspTelecom, edge.Id)
case edge2.IspChinaUnicom:
idsIspUnicom = append(idsIspUnicom, edge.Id)
case edge2.IspChinaMobile:
idsIspMobile = append(idsIspMobile, edge.Id)
}
}
// 检查更新状态
if edge.Status != nil {
if *edge.Status == 1 {
idsActive = append(idsActive, edge.Id)
} else {
idsInactive = append(idsInactive, edge.Id)
}
}
// 无法分类更新
if edge.Host != nil || edge.Port != nil || edge.Prov != nil || edge.City != nil {
otherEdges = append(otherEdges, edge)
continue
}
}
slog.Debug("更新边缘节点信息",
"active", len(idsActive),
"inactive", len(idsInactive),
"isp_unknown", len(idsIspUnknown),
"isp_telecom", len(idsIspTelecom),
"isp_unicom", len(idsIspUnicom),
"isp_mobile", len(idsIspMobile),
"other_edges", len(otherEdges),
)
err = q.Q.Transaction(func(q *q.Query) error {
// 更新边缘节点状态
if len(idsActive) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsActive...)).
UpdateSimple(q.Edge.Status.Value(1))
if err != nil {
return err
}
}
if len(idsInactive) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsInactive...)).
UpdateSimple(q.Edge.Status.Value(0))
if err != nil {
return err
}
}
// 更新边缘节点ISP
if len(idsIspUnknown) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspUnknown...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspUnknown)))
if err != nil {
return err
}
}
if len(idsIspTelecom) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspTelecom...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaTelecom)))
if err != nil {
return err
}
}
if len(idsIspUnicom) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspUnicom...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaUnicom)))
if err != nil {
return err
}
}
if len(idsIspMobile) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspMobile...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaMobile)))
if err != nil {
return err
}
}
// 更新其他边缘节点信息
for _, edge := range otherEdges {
do := q.Edge.Debug().Where(q.Edge.ID.Eq(edge.Id))
var assigns = make([]field.AssignExpr, 0, 5)
if edge.Host != nil {
assigns = append(assigns, q.Edge.Host.Value(*edge.Host))
}
if edge.Port != nil {
assigns = append(assigns, q.Edge.ProxyPort.Value(*edge.Port))
}
if edge.Prov != nil {
assigns = append(assigns, q.Edge.Prov.Value(*edge.Prov))
}
if edge.City != nil {
assigns = append(assigns, q.Edge.City.Value(*edge.City))
}
// 更新边缘节点
_, err := do.UpdateSimple(assigns...)
if err != nil {
return fmt.Errorf("更新边缘节点 %d 失败: %w", edge.Id, err)
}
}
return nil
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
if err != nil {
return err
}
return nil
// // 检查接口权限
// _, err = auth2.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// // 验证请求参数
// var req = new(ProxyReportUpdateReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// // 更新节点信息
// var idsActive = make([]int32, 0, len(req.Edges))
// var idsInactive = make([]int32, 0, len(req.Edges))
// var idsIspUnknown = make([]int32, 0, len(req.Edges))
// var idsIspTelecom = make([]int32, 0, len(req.Edges))
// var idsIspUnicom = make([]int32, 0, len(req.Edges))
// var idsIspMobile = make([]int32, 0, len(req.Edges))
// var otherEdges = make([]*ProxyEdge, 0, len(req.Edges))
// for _, edge := range req.Edges {
// // 检查更新ISP
// if edge.Isp != nil {
// switch edge2.ISPFromStr(*edge.Isp) {
// case edge2.IspUnknown:
// idsIspUnknown = append(idsIspUnknown, edge.Id)
// case edge2.IspChinaTelecom:
// idsIspTelecom = append(idsIspTelecom, edge.Id)
// case edge2.IspChinaUnicom:
// idsIspUnicom = append(idsIspUnicom, edge.Id)
// case edge2.IspChinaMobile:
// idsIspMobile = append(idsIspMobile, edge.Id)
// }
// }
// // 检查更新状态
// if edge.Status != nil {
// if *edge.Status == 1 {
// idsActive = append(idsActive, edge.Id)
// } else {
// idsInactive = append(idsInactive, edge.Id)
// }
// }
// // 无法分类更新
// if edge.Host != nil || edge.Port != nil || edge.Prov != nil || edge.City != nil {
// otherEdges = append(otherEdges, edge)
// continue
// }
// }
// slog.Debug("更新边缘节点信息",
// "active", len(idsActive),
// "inactive", len(idsInactive),
// "isp_unknown", len(idsIspUnknown),
// "isp_telecom", len(idsIspTelecom),
// "isp_unicom", len(idsIspUnicom),
// "isp_mobile", len(idsIspMobile),
// "other_edges", len(otherEdges),
// )
// err = q.Q.Transaction(func(q *q.Query) error {
// // 更新边缘节点状态
// if len(idsActive) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsActive...)).
// UpdateSimple(q.Edge.Status.Value(1))
// if err != nil {
// return err
// }
// }
// if len(idsInactive) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsInactive...)).
// UpdateSimple(q.Edge.Status.Value(0))
// if err != nil {
// return err
// }
// }
// // 更新边缘节点ISP
// if len(idsIspUnknown) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspUnknown...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspUnknown)))
// if err != nil {
// return err
// }
// }
// if len(idsIspTelecom) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspTelecom...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaTelecom)))
// if err != nil {
// return err
// }
// }
// if len(idsIspUnicom) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspUnicom...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaUnicom)))
// if err != nil {
// return err
// }
// }
// if len(idsIspMobile) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspMobile...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaMobile)))
// if err != nil {
// return err
// }
// }
// // 更新其他边缘节点信息
// for _, edge := range otherEdges {
// do := q.Edge.Debug().Where(q.Edge.ID.Eq(edge.Id))
// var assigns = make([]field.AssignExpr, 0, 5)
// if edge.Host != nil {
// assigns = append(assigns, q.Edge.Host.Value(*edge.Host))
// }
// if edge.Port != nil {
// assigns = append(assigns, q.Edge.ProxyPort.Value(*edge.Port))
// }
// if edge.Prov != nil {
// assigns = append(assigns, q.Edge.Prov.Value(*edge.Prov))
// }
// if edge.City != nil {
// assigns = append(assigns, q.Edge.City.Value(*edge.City))
// }
// // 更新边缘节点
// _, err := do.UpdateSimple(assigns...)
// if err != nil {
// return fmt.Errorf("更新边缘节点 %d 失败: %w", edge.Id, err)
// }
// }
// return nil
// })
// if err != nil {
// return err
// }
// return nil
}
// endregion

View File

@@ -4,9 +4,8 @@ import (
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
resource2 "platform/web/domains/resource"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"time"
@@ -43,7 +42,7 @@ func ListResourceShort(c *fiber.Ctx) error {
// 查询套餐列表
do := q.Resource.Where(
q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Type.Eq(int32(resource2.TypeShort)),
q.Resource.Type.Eq(int(m.ResourceTypeShort)),
)
if req.ResourceNo != nil && *req.ResourceNo != "" {
do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
@@ -52,19 +51,19 @@ func ListResourceShort(c *fiber.Ctx) error {
do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Type != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Type.Eq(int32(*req.Type)))
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Type.Eq(*req.Type))
}
if req.CreateAfter != nil {
do.Where(q.Resource.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter)))
do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
}
if req.CreateBefore != nil {
do.Where(q.Resource.CreatedAt.Lte(orm.LocalDateTime(*req.CreateBefore)))
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
}
if req.ExpireAfter != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Gte(orm.LocalDateTime(*req.ExpireAfter)))
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Gte(*req.ExpireAfter))
}
if req.ExpireBefore != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Lte(orm.LocalDateTime(*req.ExpireBefore)))
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Lte(*req.ExpireBefore))
}
resource, err := q.Resource.Debug().Where(do).
@@ -124,7 +123,7 @@ func ListResourceLong(c *fiber.Ctx) error {
// 查询套餐列表
do := q.Resource.Where(
q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Type.Eq(int32(resource2.TypeLong)),
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
)
if req.ResourceNo != nil && *req.ResourceNo != "" {
do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
@@ -133,19 +132,19 @@ func ListResourceLong(c *fiber.Ctx) error {
do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Type != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Type.Eq(int32(*req.Type)))
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Type.Eq(int(*req.Type)))
}
if req.CreateAfter != nil {
do.Where(q.Resource.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter)))
do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
}
if req.CreateBefore != nil {
do.Where(q.Resource.CreatedAt.Lte(orm.LocalDateTime(*req.CreateBefore)))
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
}
if req.ExpireAfter != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Gte(orm.LocalDateTime(*req.ExpireAfter)))
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Gte(*req.ExpireAfter))
}
if req.ExpireBefore != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Lte(orm.LocalDateTime(*req.ExpireBefore)))
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Lte(*req.ExpireBefore))
}
resource, err := q.Resource.Debug().Where(do).
@@ -202,27 +201,27 @@ func AllActiveResource(c *fiber.Ctx) error {
q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Active.Is(true),
q.Resource.Where(
q.Resource.Type.Eq(int32(resource2.TypeShort)),
q.Resource.Type.Eq(int(m.ResourceTypeShort)),
q.ResourceShort.As(q.Resource.Short.Name()).Where(
short.Type.Eq(int32(resource2.ModeTime)),
short.Expire.Gte(orm.LocalDateTime(now)),
short.Type.Eq(int(m.ResourceModeTime)),
short.Expire.Gte(now),
q.ResourceShort.As(q.Resource.Short.Name()).
Where(short.DailyLast.Lt(orm.LocalDateTime(u.Today()))).
Where(short.DailyLast.Lt(u.Today())).
Or(short.DailyLimit.GtCol(short.DailyUsed)),
).Or(
short.Type.Eq(int32(resource2.ModeCount)),
short.Type.Eq(int(m.ResourceModeQuota)),
short.Quota.GtCol(short.Used),
),
).Or(
q.Resource.Type.Eq(int32(resource2.TypeLong)),
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
q.ResourceLong.As(q.Resource.Long.Name()).Where(
long.Type.Eq(int32(resource2.ModeTime)),
long.Expire.Gte(orm.LocalDateTime(now)),
long.Type.Eq(int(m.ResourceModeTime)),
long.Expire.Gte(now),
q.ResourceLong.As(q.Resource.Long.Name()).
Where(long.DailyLast.Lt(orm.LocalDateTime(u.Today()))).
Where(long.DailyLast.Lt(u.Today())).
Or(long.DailyLimit.GtCol(long.DailyUsed)),
).Or(
long.Type.Eq(int32(resource2.ModeCount)),
long.Type.Eq(int(m.ResourceModeQuota)),
long.Quota.GtCol(long.Used),
),
),
@@ -282,23 +281,23 @@ func StatisticResourceFree(c *fiber.Ctx) error {
switch {
// 短效包量
case resource2.Type(resource.Type) == resource2.TypeShort && resource2.Mode(resource.Short.Type) == resource2.ModeCount:
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeQuota:
if u.Z(resource.Short.Quota) > resource.Short.Used {
shortCount++
shortQuotaSum += int(u.Z(resource.Short.Quota) - resource.Short.Used)
}
// 长效包量
case resource2.Type(resource.Type) == resource2.TypeLong && resource2.Mode(resource.Long.Type) == resource2.ModeCount:
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeQuota:
if u.Z(resource.Long.Quota) > resource.Long.Used {
longCount++
longQuotaSum += int(u.Z(resource.Long.Quota) - resource.Long.Used)
}
// 短效包时
case resource2.Type(resource.Type) == resource2.TypeShort && resource2.Mode(resource.Short.Type) == resource2.ModeTime:
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeTime:
if time.Time(*resource.Short.Expire).After(time.Now()) {
if resource.Short.DailyLast == nil || u.SameDate(time.Time(*resource.Short.DailyLast)) == false {
if resource.Short.DailyLast == nil || u.IsToday(time.Time(*resource.Short.DailyLast)) == false {
shortCount++
shortDailyFreeSum += int(resource.Short.DailyLimit)
} else if resource.Short.DailyLimit > resource.Short.DailyUsed {
@@ -308,9 +307,9 @@ func StatisticResourceFree(c *fiber.Ctx) error {
}
// 长效包时
case resource2.Type(resource.Type) == resource2.TypeLong && resource2.Mode(resource.Long.Type) == resource2.ModeTime:
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeTime:
if time.Time(*resource.Long.Expire).After(time.Now()) {
if resource.Long.DailyLast == nil || u.SameDate(time.Time(*resource.Long.DailyLast)) == false {
if resource.Long.DailyLast == nil || u.IsToday(time.Time(*resource.Long.DailyLast)) == false {
longCount++
longDailyFreeSum += int(resource.Long.DailyLimit)
} else if resource.Long.DailyLimit > resource.Long.DailyUsed {
@@ -376,10 +375,10 @@ func StatisticResourceUsage(c *fiber.Ctx) error {
do.Where(q.LogsUserUsage.ResourceID.Eq(resourceID))
}
if req.TimeAfter != nil {
do.Where(q.LogsUserUsage.Time.Gte(orm.LocalDateTime(*req.TimeAfter)))
do.Where(q.LogsUserUsage.Time.Gte(*req.TimeAfter))
}
if req.TimeBefore != nil {
do.Where(q.LogsUserUsage.Time.Lte(orm.LocalDateTime(*req.TimeBefore)))
do.Where(q.LogsUserUsage.Time.Lte(*req.TimeBefore))
}
var data = new(StatisticResourceUsageResp)

View File

@@ -4,8 +4,8 @@ import (
"log/slog"
"platform/web/auth"
"platform/web/core"
trade2 "platform/web/domains/trade"
g "platform/web/globals"
m "platform/web/models"
s "platform/web/services"
"time"
@@ -14,7 +14,7 @@ import (
type TradeCreateReq struct {
s.CreateTradeData
Type trade2.Type `json:"type" validate:"required"`
Type m.TradeType `json:"type" validate:"required"`
Resource *s.CreateResourceData `json:"resource,omitempty"`
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"`
}
@@ -38,12 +38,12 @@ func TradeCreate(c *fiber.Ctx) error {
}
switch req.Type {
case trade2.TypePurchase:
case m.TradeTypePurchase:
if req.Resource == nil {
return core.NewBizErr("购买信息不能为空")
}
req.Product = req.Resource
case trade2.TypeRecharge:
case m.TradeTypeRecharge:
if req.Recharge == nil {
return core.NewBizErr("充值信息不能为空")
}

View File

@@ -19,7 +19,7 @@ type VerifierReq struct {
func SmsCode(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitInternalClient()
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
return err
}

View File

@@ -1,11 +1,12 @@
package handlers
import (
"net"
"platform/pkg/env"
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
"time"
@@ -89,7 +90,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
return err
}
err = secureAddr(req.Host)
ip, err := secureAddr(req.Host)
if err != nil {
return err
}
@@ -97,7 +98,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
// 创建白名单
err = q.Whitelist.Create(&m.Whitelist{
UserID: authCtx.User.ID,
Host: req.Host,
IP: u.Z(ip),
Remark: &req.Remark,
})
return nil
@@ -125,17 +126,21 @@ func UpdateWhitelist(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "id is required")
}
ip, err := secureAddr(req.Host)
if err != nil {
return err
}
// 更新白名单
_, err = q.Whitelist.
Where(
q.Whitelist.ID.Eq(req.ID),
q.Whitelist.UserID.Eq(authCtx.User.ID),
).
Updates(&m.Whitelist{
ID: req.ID,
Host: req.Host,
Remark: &req.Remark,
})
UpdateSimple(
q.Whitelist.IP.Value(ip),
q.Whitelist.Remark.Value(req.Remark),
)
if err != nil {
return err
}
@@ -186,13 +191,13 @@ func RemoveWhitelist(c *fiber.Ctx) error {
return nil
}
func secureAddr(str string) error {
var addr = net.ParseIP(str)
if addr == nil {
return fiber.NewError(fiber.StatusBadRequest, "IP 解析失败")
func secureAddr(str string) (*orm.Inet, error) {
ip, err := orm.ParseInet(str)
if err != nil {
return nil, err
}
if env.RunMode == env.RunModeDev || addr.IsGlobalUnicast() {
return nil
if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
return nil, fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用")
}
return fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用")
return ip, nil
}