完善通道创建与同步流程
This commit is contained in:
@@ -1,85 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"platform/pkg/env"
|
||||
"platform/pkg/logs"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// 初始化环境
|
||||
env.Init()
|
||||
logs.Init()
|
||||
|
||||
// 上下文
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// 监听退出
|
||||
exit := make(chan os.Signal, 1)
|
||||
defer close(exit)
|
||||
signal.Notify(exit, os.Interrupt, os.Kill)
|
||||
defer signal.Stop(exit)
|
||||
|
||||
// 启动管理子线程
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
err := start(ctx)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-exit:
|
||||
slog.Debug("exit by signal")
|
||||
cancel()
|
||||
case err := <-errCh:
|
||||
slog.Error("exit by error", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 连接池,硬编码提供 10000 的容量
|
||||
var idle = 100
|
||||
var maximum = 10000
|
||||
|
||||
var pool = make(map[string]*Node, 10000)
|
||||
|
||||
var tick = 1 * time.Minute
|
||||
|
||||
type Node struct {
|
||||
Ip string
|
||||
}
|
||||
|
||||
var last time.Time
|
||||
|
||||
func start(ctx context.Context) error {
|
||||
ticker := time.NewTicker(tick)
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
ticker.Stop()
|
||||
}()
|
||||
|
||||
for curr := range ticker.C {
|
||||
last = curr
|
||||
go func() {
|
||||
process(ctx, curr)
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func process(ctx context.Context, curr time.Time) {
|
||||
|
||||
// 查询节点状态
|
||||
|
||||
// 筛选在线节点添加到节点池
|
||||
|
||||
//
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func Init() {
|
||||
// todo 从环境变量中获取参数
|
||||
Client = client{
|
||||
url: "http://103.139.212.110:9989",
|
||||
token: "tHDarLc1ct6M9NMAxeO98lN2YsEadYSx.anVpcA==.MTc0MzA4MTAwMQ==",
|
||||
token: "PhdnRF3z6VF2sPgygTSl1Xx6QJN0yFIK.anVpcA==.MTc0MzE2ODAwMQ==",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,11 +279,11 @@ type PortConfigsReq struct {
|
||||
}
|
||||
|
||||
type AutoEdgeConfig struct {
|
||||
Province string `json:"province"`
|
||||
City string `json:"city"`
|
||||
Isp string `json:"isp"`
|
||||
Count int `json:"count"`
|
||||
PacketLoss int `json:"packet_loss"`
|
||||
Province string `json:"province,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
Isp string `json:"isp,omitempty"`
|
||||
Count int `json:"count,omitempty"`
|
||||
PacketLoss int `json:"packet_loss,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Gateway) GatewayPortConfigs(params []PortConfigsReq) error {
|
||||
@@ -319,7 +319,7 @@ func (c *Gateway) GatewayPortConfigs(params []PortConfigsReq) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if result["code"] != 0 {
|
||||
if result["code"].(float64) != 0 {
|
||||
return errors.New("failed to configure port")
|
||||
}
|
||||
|
||||
@@ -395,14 +395,9 @@ func (c *Gateway) GatewayPortActive(param PortActiveReq) (*PortActiveResp, error
|
||||
|
||||
// endregion
|
||||
|
||||
func (c *Gateway) requestGateway(method string, url string, data any) (*http.Response, error) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (c *Gateway) requestGateway(method string, url string, data string) (*http.Response, error) {
|
||||
url = fmt.Sprintf("http://%s:%s@%s:9990%s", c.username, c.password, c.url, url)
|
||||
req, err := http.NewRequest(method, url, strings.NewReader(string(jsonData)))
|
||||
req, err := http.NewRequest(method, url, strings.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,28 +3,65 @@ GET http://110.40.82.250:18702/server/index/getToken/key/juipbyjdapiverify
|
||||
|
||||
### remote 配置信息
|
||||
GET http://103.139.212.110:9989/api/auto_query
|
||||
token: tHDarLc1ct6M9NMAxeO98lN2YsEadYSx.anVpcA==.MTc0MzA4MTAwMQ==
|
||||
|
||||
### gateway 端口信息
|
||||
GET http://api:123456@110.40.82.248:9990/port/active/
|
||||
token: PhdnRF3z6VF2sPgygTSl1Xx6QJN0yFIK.anVpcA==.MTc0MzE2ODAwMQ==
|
||||
|
||||
### remote 配置重置
|
||||
POST http://103.139.212.110:9989/api/connect
|
||||
token: tHDarLc1ct6M9NMAxeO98lN2YsEadYSx.anVpcA==.MTc0MzA4MTAwMQ==
|
||||
token: PhdnRF3z6VF2sPgygTSl1Xx6QJN0yFIK.anVpcA==.MTc0MzE2ODAwMQ==
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"uuid": "7a17e8b4-cdc3-4500-bf16-4a665991a7f6",
|
||||
"auto_config": [
|
||||
{
|
||||
"count": 200
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
### gateway 端口信息
|
||||
GET http://api:123456@110.40.82.248:9990/port/active/
|
||||
|
||||
### gateway 配置端口代理
|
||||
POST http://api:123456@110.40.82.248:9990/port/configs
|
||||
Content-Type: application/json
|
||||
|
||||
//[
|
||||
// {
|
||||
// "port": 10000,
|
||||
// "status": true,
|
||||
// "userpass": "mIfSlXIBwVUrKqObNdTzvB:cyDaGtfeBlhRfojTYJP2tR",
|
||||
// "auto_edge_config": {
|
||||
// "count": 1
|
||||
// }
|
||||
// }
|
||||
//]
|
||||
[
|
||||
{
|
||||
"port": 10000,
|
||||
"status": false,
|
||||
"edge": []
|
||||
}
|
||||
]
|
||||
|
||||
### 设备令牌
|
||||
POST http://localhost:8080/api/auth/token
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"client_id": "test",
|
||||
"client_secret": "test",
|
||||
"grant_type": "client_credentials"
|
||||
}
|
||||
|
||||
> {%
|
||||
client.global.set("access_token", response.body.access_token);
|
||||
client.global.set("refresh_token", response.body.refresh_token);
|
||||
%}
|
||||
|
||||
### 密码模式代理
|
||||
POST http://localhost:8080/api/channel/create
|
||||
Authorization: Bearer b21568ed-09a9-4f1c-add6-d6b24bde7473
|
||||
Authorization: Bearer {{access_token}}
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
@@ -32,7 +69,7 @@ Accept: application/json
|
||||
"resource_id": 1,
|
||||
"protocol": "http",
|
||||
"auth_type": 1,
|
||||
"count": 200,
|
||||
"count": 1,
|
||||
"prov": "",
|
||||
"city": "",
|
||||
"isp": "",
|
||||
|
||||
61
web/auth.go
61
web/auth.go
@@ -2,6 +2,7 @@ package web
|
||||
|
||||
import (
|
||||
"platform/web/common"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"platform/web/services"
|
||||
@@ -9,6 +10,66 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func Permit(types []services.PayloadType, permissions ...string) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
// 获取令牌
|
||||
var header = c.Get("Authorization")
|
||||
var token = strings.TrimPrefix(header, "Bearer ")
|
||||
if token == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "没有权限",
|
||||
})
|
||||
}
|
||||
|
||||
// 验证令牌
|
||||
auth, err := services.Session.Find(c.Context(), token)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "没有权限",
|
||||
})
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
// switch auth.Payload.Type {
|
||||
// case services.PayloadAdmin:
|
||||
// // 管理员不需要权限检查
|
||||
// case services.PayloadUser:
|
||||
// if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
|
||||
// return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||
// Error: true,
|
||||
// Message: "拒绝访问",
|
||||
// })
|
||||
// }
|
||||
// default:
|
||||
// return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||
// Error: true,
|
||||
// Message: "拒绝访问",
|
||||
// })
|
||||
// }
|
||||
if !slices.Contains(types, auth.Payload.Type) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "拒绝访问",
|
||||
})
|
||||
}
|
||||
if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "拒绝访问",
|
||||
})
|
||||
}
|
||||
|
||||
// 将认证信息存储在上下文中
|
||||
c.Locals("auth", auth)
|
||||
c.Locals("access_token", token) // 存储原始令牌,便于后续操作
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PermitUser 创建针对单个路由的鉴权中间件
|
||||
func PermitUser(permissions ...string) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
|
||||
@@ -2,6 +2,7 @@ package web
|
||||
|
||||
import (
|
||||
"platform/web/handlers"
|
||||
"platform/web/services"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
@@ -17,5 +18,10 @@ func ApplyRouters(app *fiber.App) {
|
||||
|
||||
// 通道
|
||||
channel := api.Group("/channel")
|
||||
channel.Post("/create", PermitUser(), handlers.CreateChannel)
|
||||
channel.Post("/create", Permit([]services.PayloadType{
|
||||
services.PayloadClientConfidential,
|
||||
services.PayloadClientPublic,
|
||||
services.PayloadUser,
|
||||
services.PayloadAdmin,
|
||||
}), handlers.CreateChannel)
|
||||
}
|
||||
|
||||
@@ -286,30 +286,38 @@ func (s *channelService) RemoteCreateChannel(
|
||||
}
|
||||
slog.Debug("检查用户权限完成")
|
||||
|
||||
var postAssigns []AssignPortResult
|
||||
err = q.Q.Transaction(func(tx *q.Query) error {
|
||||
// 申请节点
|
||||
edgeAssigns, err := assignEdge(count, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
debugAssigned := fmt.Sprintf("%+v", edgeAssigns)
|
||||
slog.Debug("申请节点完成", "edgeAssigns", debugAssigned)
|
||||
|
||||
// 分配端口
|
||||
expiration := time.Now().Add(time.Duration(resource.Live) * time.Second)
|
||||
portAssigns, err := assignPort(edgeAssigns, auth.Payload.Id, protocol, authType, expiration, filter)
|
||||
postAssigns, err = assignPort(edgeAssigns, auth.Payload.Id, protocol, authType, expiration, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
debugChannels := fmt.Sprintf("%+v", portAssigns)
|
||||
debugChannels := fmt.Sprintf("%+v", postAssigns)
|
||||
slog.Debug("分配端口完成", "portAssigns", debugChannels)
|
||||
|
||||
// 缓存并关闭代理
|
||||
err = cache(ctx, portAssigns)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return portAssigns, nil
|
||||
// 缓存并关闭代理
|
||||
err = cache(ctx, postAssigns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return postAssigns, nil
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -349,13 +357,9 @@ func checkUser(auth *AuthContext, resource *ResourceInfo, count int) error {
|
||||
}
|
||||
|
||||
// assignEdge 分配边缘节点数量
|
||||
func assignEdge(count int, filter NodeFilterConfig) ([]*AssignEdgeResult, error) {
|
||||
// 查询现有节点连接情况
|
||||
edgeConfigs, err := remote.Client.CloudAutoQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func assignEdge(count int, filter NodeFilterConfig) (*AssignEdgeResult, error) {
|
||||
|
||||
// 查询可以使用的网关
|
||||
proxies, err := q.Proxy.
|
||||
Where(q.Proxy.Type.Eq(1)).
|
||||
Find()
|
||||
@@ -363,81 +367,18 @@ func assignEdge(count int, filter NodeFilterConfig) ([]*AssignEdgeResult, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 过滤需要变动的连接配置
|
||||
type ConfigInfo struct {
|
||||
proxy *models.Proxy
|
||||
config *remote.AutoConfig
|
||||
// 查询已配置的节点
|
||||
allConfigs, err := remote.Client.CloudAutoQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var total = count
|
||||
var assigns = make([]*AssignEdgeResult, len(proxies), len(proxies))
|
||||
|
||||
// 查询已分配的节点
|
||||
var proxyIds = make([]int32, len(proxies))
|
||||
for i, proxy := range proxies {
|
||||
remoteConfigs := edgeConfigs[proxy.Name]
|
||||
for _, config := range remoteConfigs {
|
||||
if config.Isp == filter.Isp && config.City == filter.City && config.Province == filter.Prov {
|
||||
total += config.Count
|
||||
assigns[i] = &AssignEdgeResult{
|
||||
proxy: proxy,
|
||||
count: config.Count,
|
||||
proxyIds[i] = proxy.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
if assigns[i] == nil {
|
||||
assigns[i] = &AssignEdgeResult{
|
||||
proxy: proxy,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
avg := int(math.Ceil(float64(total) / float64(len(proxies))))
|
||||
|
||||
for i, assign := range assigns {
|
||||
var prev = assign.count
|
||||
var next = assign.count
|
||||
if prev < avg && prev < total {
|
||||
next = int(math.Min(float64(avg), float64(total)))
|
||||
assigns[i].count = next - prev
|
||||
total -= next
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
// err := remote.Client.CloudConnect(remote.CloudConnectReq{
|
||||
// Uuid: assign.Proxy.Name,
|
||||
// Edge: nil,
|
||||
// AutoConfig: []remote.AutoConfig{{
|
||||
// Province: filter.Prov,
|
||||
// City: filter.City,
|
||||
// Isp: filter.Isp,
|
||||
// Count: next,
|
||||
// }},
|
||||
// })
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
}
|
||||
|
||||
return assigns, nil
|
||||
}
|
||||
|
||||
type AssignEdgeResult struct {
|
||||
proxy *models.Proxy
|
||||
count int
|
||||
}
|
||||
|
||||
// assignPort 分配指定数量的端口
|
||||
func assignPort(
|
||||
assigns []*AssignEdgeResult,
|
||||
userId int32,
|
||||
protocol ChannelProtocol,
|
||||
authType ChannelAuthType,
|
||||
expiration time.Time,
|
||||
filter NodeFilterConfig,
|
||||
) ([]AssignPortResult, error) {
|
||||
// 查询代理已配置端口
|
||||
var proxyIds = make([]int32, 0, len(assigns))
|
||||
for _, assigned := range assigns {
|
||||
proxyIds = append(proxyIds, assigned.proxy.ID)
|
||||
}
|
||||
channels, err := q.Channel.
|
||||
assigns, err := q.Channel.
|
||||
Select(
|
||||
q.Channel.ProxyID,
|
||||
q.Channel.ProxyPort).
|
||||
@@ -452,6 +393,99 @@ func assignPort(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 过滤需要变动的连接配置
|
||||
var current = 0
|
||||
var result = make([]ProxyConfig, len(proxies))
|
||||
for i, proxy := range proxies {
|
||||
remoteConfigs, ok := allConfigs[proxy.Name]
|
||||
if !ok {
|
||||
result[i] = ProxyConfig{
|
||||
proxy: proxy,
|
||||
config: &remote.AutoConfig{
|
||||
Province: filter.Prov,
|
||||
City: filter.City,
|
||||
Isp: filter.Isp,
|
||||
Count: 0,
|
||||
},
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, config := range remoteConfigs {
|
||||
if config.Isp == filter.Isp && config.City == filter.City && config.Province == filter.Prov {
|
||||
current += config.Count
|
||||
result[i] = ProxyConfig{
|
||||
proxy: proxy,
|
||||
config: &config,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果需要新增节点
|
||||
var needed = len(assigns) + count
|
||||
if needed-current > 0 {
|
||||
slog.Debug("新增新节点", "needed", needed, "current", current)
|
||||
avg := int(math.Ceil(float64(needed) / float64(len(proxies))))
|
||||
for i, assign := range result {
|
||||
var prev = assign.config.Count
|
||||
var next = assign.config.Count
|
||||
if prev >= avg || prev >= needed {
|
||||
continue
|
||||
}
|
||||
|
||||
next = int(math.Min(float64(avg), float64(needed)))
|
||||
result[i].config.Count = next - prev
|
||||
needed -= next
|
||||
err := remote.Client.CloudConnect(remote.CloudConnectReq{
|
||||
Uuid: assign.proxy.Name,
|
||||
Edge: nil,
|
||||
AutoConfig: []remote.AutoConfig{{
|
||||
Province: filter.Prov,
|
||||
City: filter.City,
|
||||
Isp: filter.Isp,
|
||||
Count: next,
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &AssignEdgeResult{
|
||||
configs: result,
|
||||
channels: assigns,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type AssignEdgeResult struct {
|
||||
configs []ProxyConfig
|
||||
channels []*models.Channel
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
proxy *models.Proxy
|
||||
config *remote.AutoConfig
|
||||
}
|
||||
|
||||
// assignPort 分配指定数量的端口
|
||||
func assignPort(
|
||||
proxies *AssignEdgeResult,
|
||||
userId int32,
|
||||
protocol ChannelProtocol,
|
||||
authType ChannelAuthType,
|
||||
expiration time.Time,
|
||||
filter NodeFilterConfig,
|
||||
) ([]AssignPortResult, error) {
|
||||
var assigns = proxies.configs
|
||||
var channels = proxies.channels
|
||||
|
||||
// 查询代理已配置端口
|
||||
var proxyIds = make([]int32, 0, len(assigns))
|
||||
for _, assigned := range assigns {
|
||||
proxyIds = append(proxyIds, assigned.proxy.ID)
|
||||
}
|
||||
|
||||
// 端口查找表
|
||||
var proxyPorts = make(map[uint64]struct{})
|
||||
for _, channel := range channels {
|
||||
@@ -462,8 +496,9 @@ func assignPort(
|
||||
// 配置启用代理
|
||||
var result = make([]AssignPortResult, len(assigns))
|
||||
for i, assign := range assigns {
|
||||
var err error
|
||||
var proxy = assign.proxy
|
||||
var count = assign.count
|
||||
var count = assign.config.Count
|
||||
|
||||
result[i] = AssignPortResult{
|
||||
Proxy: proxy,
|
||||
@@ -540,15 +575,15 @@ func assignPort(
|
||||
}
|
||||
|
||||
// 提交端口配置
|
||||
// gateway := remote.InitGateway(
|
||||
// proxy.Host,
|
||||
// "api",
|
||||
// "123456",
|
||||
// )
|
||||
// err = gateway.GatewayPortConfigs(configs)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
gateway := remote.InitGateway(
|
||||
proxy.Host,
|
||||
"api",
|
||||
"123456",
|
||||
)
|
||||
err = gateway.GatewayPortConfigs(configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
err = q.Channel.
|
||||
|
||||
Reference in New Issue
Block a user