GET类型通道创建端点;修改完善返回格式处理逻辑;动态刷新remote令牌
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,4 +5,6 @@
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
build/
|
||||
build/
|
||||
|
||||
*.http
|
||||
@@ -1,5 +1,6 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println(float64(166) * 11 / 10)
|
||||
println(rune('w'))
|
||||
println(rune('m'))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"platform/pkg/env"
|
||||
"platform/pkg/logs"
|
||||
"platform/pkg/rds"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -19,6 +18,8 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var RemoveEndpoint = "http://localhost:8080/api/channel/remove"
|
||||
|
||||
func main() {
|
||||
Start()
|
||||
}
|
||||
@@ -68,55 +69,38 @@ func process(ctx context.Context, curr time.Time) error {
|
||||
|
||||
func stopChannels(ctx context.Context, curr time.Time) error {
|
||||
|
||||
// 获取并删除
|
||||
script := redis.NewScript(`
|
||||
local result = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1])
|
||||
if #result > 0 then
|
||||
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1])
|
||||
end
|
||||
return result
|
||||
`)
|
||||
|
||||
// 执行脚本
|
||||
result, err := script.Run(ctx, rds.Client, []string{"tasks:channel"}, curr.Unix()).Result()
|
||||
// 查询过期的 channel
|
||||
result, err := rds.Client.ZRangeByScore(ctx, "tasks:channel", &redis.ZRangeBy{
|
||||
Min: "0",
|
||||
Max: strconv.FormatInt(curr.Unix(), 10),
|
||||
}).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理结果
|
||||
list, ok := result.([]any)
|
||||
if !ok {
|
||||
return errors.New("failed to convert result to []string")
|
||||
}
|
||||
var ids = make([]int, len(list))
|
||||
for i, item := range list {
|
||||
idStr, ok := item.(string)
|
||||
if !ok {
|
||||
slog.Debug(reflect.TypeOf(item).String())
|
||||
return errors.New("failed to convert item to string")
|
||||
}
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ids[i] = id
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var body = map[string]any{
|
||||
"by_ids": ids,
|
||||
ids := make([]int32, len(result))
|
||||
for i, str := range result {
|
||||
id, err := strconv.ParseInt(str, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ids[i] = int32(id)
|
||||
}
|
||||
bodyStr, err := json.Marshal(body)
|
||||
|
||||
// 删除过期的 channel
|
||||
body, err := json.Marshal(map[string]any{
|
||||
"by_ids": ids,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost,
|
||||
"http://localhost:8080/api/channel/remove", // todo 环境变量获取服务地址
|
||||
strings.NewReader(string(bodyStr)),
|
||||
RemoveEndpoint,
|
||||
strings.NewReader(string(body)),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -128,6 +112,7 @@ func stopChannels(ctx context.Context, curr time.Time) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
@@ -137,5 +122,8 @@ func stopChannels(ctx context.Context, curr time.Time) error {
|
||||
return errors.New("failed to stop channels: " + string(body))
|
||||
}
|
||||
|
||||
// 删除 redis 中的 channel
|
||||
_, err = rds.Client.ZRemRangeByScore(ctx, "tasks:channel", "0", strconv.FormatInt(curr.Unix(), 10)).Result()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
47
go.mod
47
go.mod
@@ -4,13 +4,14 @@ go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/alicebob/miniredis/v2 v2.34.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/gofiber/fiber/v2 v2.52.6
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/jxskiss/base62 v1.1.0
|
||||
github.com/lmittmann/tint v1.0.7
|
||||
github.com/redis/go-redis/v9 v9.3.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
github.com/redis/go-redis/v9 v9.7.3
|
||||
golang.org/x/crypto v0.36.0
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/gen v0.3.26
|
||||
gorm.io/gorm v1.25.12
|
||||
@@ -18,33 +19,41 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.1 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.4 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.59.0 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
gorm.io/datatypes v1.2.5 // indirect
|
||||
gorm.io/driver/mysql v1.5.7 // indirect
|
||||
gorm.io/hints v1.1.0 // indirect
|
||||
gorm.io/driver/sqlite v1.5.7 // indirect
|
||||
gorm.io/hints v1.1.2 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
||||
122
go.sum
122
go.sum
@@ -1,72 +1,85 @@
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
||||
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI=
|
||||
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
||||
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
|
||||
github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
||||
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
|
||||
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -74,21 +87,21 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@@ -103,11 +116,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -122,32 +134,40 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c h1:jWdr7cHgl8c/ua5vYbR2WhSp+NQmzhsj0xoY3foTzW8=
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c/go.mod h1:SH2K9R+2RMjuX1CkCONrPwoe9JzVv2hkQvEu4bXGojE=
|
||||
gorm.io/datatypes v1.2.5 h1:9UogU3jkydFVW1bIVVeoYsTpLRgwDVW3rHfJG6/Ek9I=
|
||||
gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4=
|
||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
|
||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
|
||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g=
|
||||
gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g=
|
||||
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=
|
||||
gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE=
|
||||
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw=
|
||||
gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y=
|
||||
gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
|
||||
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
|
||||
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
|
||||
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package remote
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -9,8 +10,10 @@ import (
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"platform/pkg/env"
|
||||
"platform/pkg/rds"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CloudClient 定义云服务接口
|
||||
@@ -28,16 +31,14 @@ type GatewayClient interface {
|
||||
}
|
||||
|
||||
type cloud struct {
|
||||
url string
|
||||
token string
|
||||
url string
|
||||
}
|
||||
|
||||
var Cloud CloudClient
|
||||
|
||||
func Init() {
|
||||
Cloud = &cloud{
|
||||
url: env.RemoteAddr,
|
||||
token: env.RemoteToken,
|
||||
url: env.RemoteAddr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,34 +261,98 @@ func (c *cloud) requestCloud(method string, url string, data string) (*http.Resp
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("token", c.token)
|
||||
|
||||
if env.DebugHttpDump {
|
||||
str, err := httputil.DumpRequest(req, true)
|
||||
var resp *http.Response
|
||||
for range 2 {
|
||||
token, err := c.token(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("==============================")
|
||||
fmt.Println(string(str))
|
||||
}
|
||||
req.Header.Set("token", token)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if env.DebugHttpDump {
|
||||
str, err := httputil.DumpRequest(req, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("==============================")
|
||||
fmt.Println(string(str))
|
||||
}
|
||||
|
||||
if env.DebugHttpDump {
|
||||
str, err := httputil.DumpResponse(resp, true)
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("==============================")
|
||||
fmt.Println(string(str))
|
||||
|
||||
if env.DebugHttpDump {
|
||||
str, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("==============================")
|
||||
fmt.Println(string(str))
|
||||
}
|
||||
|
||||
if resp.StatusCode != 401 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *cloud) token(refresh bool) (string, error) {
|
||||
// redis 获取令牌
|
||||
if !refresh {
|
||||
token, err := rds.Client.Get(context.Background(), "remote:token").Result()
|
||||
if err == nil && token != "" {
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// redis 获取失败,重新获取
|
||||
resp, err := http.Get(env.RemoteToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
dump, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Println(string(dump))
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", errors.New("failed to get token")
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var result map[string]any
|
||||
err = json.Unmarshal(body, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if result["code"].(float64) != 1 {
|
||||
return "", errors.New("failed to get cloud token")
|
||||
}
|
||||
|
||||
// redis 设置令牌
|
||||
token := result["token"].(string)
|
||||
err = rds.Client.Set(context.Background(), "remote:token", token, 1*time.Hour).Err()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
type gateway struct {
|
||||
url string
|
||||
username string
|
||||
|
||||
@@ -2,39 +2,38 @@ package testutil
|
||||
|
||||
import (
|
||||
"platform/pkg/orm"
|
||||
"platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"gorm.io/driver/postgres"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SetupDBTest 创建一个带有 sqlmock 的 GORM 数据库连接
|
||||
func SetupDBTest(t *testing.T) sqlmock.Sqlmock {
|
||||
|
||||
// 创建 sqlmock
|
||||
db, mock, err := sqlmock.New()
|
||||
// SetupDBTest 创建一个基于 SQLite 内存数据库的 GORM 连接
|
||||
func SetupDBTest(t *testing.T) *gorm.DB {
|
||||
// 使用 SQLite 内存数据库
|
||||
gormDB, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("创建sqlmock失败: %v", err)
|
||||
t.Fatalf("gorm 打开 SQLite 内存数据库失败: %v", err)
|
||||
}
|
||||
|
||||
// 配置 gorm 连接
|
||||
gormDB, err := gorm.Open(postgres.New(postgres.Config{
|
||||
Conn: db,
|
||||
PreferSimpleProtocol: true, // 禁用 prepared statement 缓存
|
||||
}), &gorm.Config{})
|
||||
// 自动迁移数据表结构
|
||||
err = gormDB.AutoMigrate(
|
||||
&models.User{},
|
||||
&models.Whitelist{},
|
||||
&models.Resource{},
|
||||
&models.ResourcePss{},
|
||||
&models.Proxy{},
|
||||
&models.Channel{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("gorm 打开数据库连接失败: %v", err)
|
||||
t.Fatalf("自动迁移表结构失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置全局数据库连接
|
||||
q.SetDefault(gormDB)
|
||||
orm.DB = gormDB
|
||||
|
||||
// 使用 t.Cleanup 确保测试结束后关闭数据库连接
|
||||
t.Cleanup(func() {
|
||||
db.Close()
|
||||
})
|
||||
|
||||
return mock
|
||||
return gormDB
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
### remote 令牌
|
||||
GET http://110.40.82.250:18702/server/index/getToken/key/juipbyjdapiverify
|
||||
|
||||
### remote 配置信息
|
||||
GET http://103.139.212.110:9989/api/auto_query
|
||||
token: yzfO0vSEnbOL9fkmd4bgKiEuYw5Kjlfb.anVpcA==.MTc0MzQyOTAwMQ==
|
||||
|
||||
### remote 配置连接
|
||||
POST http://103.139.212.110:9989/api/connect
|
||||
token: et1wWdrLLRsiQPCar8GunNFEZqcxATFa.anVpcA==.MTc0MzM0MjAwMQ==
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"uuid": "7a17e8b4-cdc3-4500-bf16-4a665991a7f6",
|
||||
"auto_config": [
|
||||
{
|
||||
"count": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
### remote 下线全部
|
||||
POST http://103.139.212.110:9989/api/disconnect
|
||||
token: yzfO0vSEnbOL9fkmd4bgKiEuYw5Kjlfb.anVpcA==.MTc0MzQyOTAwMQ==
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"uuid": "7a17e8b4-cdc3-4500-bf16-4a665991a7f6",
|
||||
"config": {}
|
||||
}
|
||||
|
||||
### gateway 连接信息
|
||||
GET http://api:123456@110.40.82.248:9990/edge
|
||||
|
||||
### gateway 端口信息
|
||||
GET http://api:123456@110.40.82.248:9990/port/active
|
||||
|
||||
### 设备令牌
|
||||
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: Basic test test
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"resource_id": 1,
|
||||
"protocol": "http",
|
||||
"auth_type": 0,
|
||||
"count": 200,
|
||||
"prov": "",
|
||||
"city": "",
|
||||
"isp": "",
|
||||
"result_type": "text",
|
||||
"result_separator": "both"
|
||||
}
|
||||
@@ -2,7 +2,11 @@ package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
q "platform/web/queries"
|
||||
"platform/web/services"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -11,15 +15,16 @@ import (
|
||||
// region CreateChannel
|
||||
|
||||
type CreateChannelReq struct {
|
||||
ResourceId int32 `json:"resource_id" validate:"required"`
|
||||
Protocol services.ChannelProtocol `json:"protocol" validate:"required,oneof=socks5 http https"`
|
||||
AuthType services.ChannelAuthType `json:"auth_type" validate:"required,oneof=0 1"`
|
||||
Count int `json:"count" validate:"required"`
|
||||
Prov string `json:"prov" validate:"required"`
|
||||
City string `json:"city" validate:"required"`
|
||||
Isp string `json:"isp" validate:"required"`
|
||||
ResultType CreateChannelResultType `json:"result_type" validate:"required,oneof=json text"`
|
||||
ResultSeparator CreateChannelResultSeparator `json:"result_separator" validate:"required,oneof=enter line both tab"`
|
||||
ResourceId int32 `json:"resource_id" validate:"required"`
|
||||
Protocol services.ChannelProtocol `json:"protocol" validate:"required,oneof=socks5 http https"`
|
||||
AuthType services.ChannelAuthType `json:"auth_type" validate:"required,oneof=0 1"`
|
||||
Count int `json:"count" validate:"required"`
|
||||
Prov string `json:"prov" validate:"required"`
|
||||
City string `json:"city" validate:"required"`
|
||||
Isp string `json:"isp" validate:"required"`
|
||||
ResultType CreateChannelResultType `json:"result_type" validate:"required,oneof=json text"`
|
||||
ResultBreaker []rune `json:"result_breaker" validate:""`
|
||||
ResultSeparator []rune `json:"result_separator" validate:""`
|
||||
}
|
||||
|
||||
func CreateChannel(c *fiber.Ctx) error {
|
||||
@@ -28,6 +33,16 @@ func CreateChannel(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.ResultType == "" {
|
||||
req.ResultType = CreateChannelResultTypeText
|
||||
}
|
||||
if req.ResultBreaker == nil {
|
||||
req.ResultBreaker = []rune("\r\n")
|
||||
}
|
||||
if req.ResultSeparator == nil {
|
||||
req.ResultSeparator = []rune("|")
|
||||
}
|
||||
|
||||
// 建立连接通道
|
||||
auth, ok := c.Locals("auth").(*services.AuthContext)
|
||||
if !ok {
|
||||
@@ -51,25 +66,35 @@ func CreateChannel(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var separator = string(req.ResultSeparator)
|
||||
switch req.ResultType {
|
||||
case CreateChannelResultTypeJson:
|
||||
return c.JSON(fiber.Map{
|
||||
"result": result,
|
||||
"code": 1,
|
||||
"data": result,
|
||||
})
|
||||
case CreateChannelResultTypeText:
|
||||
switch req.ResultSeparator {
|
||||
case CreateChannelResultSeparatorEnter:
|
||||
return c.SendString(strings.Join(result, "\r"))
|
||||
case CreateChannelResultSeparatorLine:
|
||||
return c.SendString(strings.Join(result, "\n"))
|
||||
case CreateChannelResultSeparatorBoth:
|
||||
return c.SendString(strings.Join(result, "\r\n"))
|
||||
case CreateChannelResultSeparatorTab:
|
||||
return c.SendString(strings.Join(result, "\t"))
|
||||
}
|
||||
}
|
||||
default:
|
||||
var breaker = string(req.ResultBreaker)
|
||||
var str = strings.Builder{}
|
||||
for _, info := range result {
|
||||
|
||||
return errors.New("无效的返回类型")
|
||||
str.WriteString(info.Host)
|
||||
|
||||
str.WriteString(separator)
|
||||
str.WriteString(strconv.Itoa(info.Port))
|
||||
|
||||
if info.Username != nil {
|
||||
str.WriteString(separator)
|
||||
str.WriteString(*info.Username)
|
||||
}
|
||||
if info.Password != nil {
|
||||
str.WriteString(separator)
|
||||
str.WriteString(*info.Password)
|
||||
}
|
||||
str.WriteString(breaker)
|
||||
}
|
||||
return c.SendString(str.String())
|
||||
}
|
||||
}
|
||||
|
||||
type CreateChannelResultType string
|
||||
@@ -79,15 +104,6 @@ const (
|
||||
CreateChannelResultTypeText CreateChannelResultType = "text"
|
||||
)
|
||||
|
||||
type CreateChannelResultSeparator string
|
||||
|
||||
const (
|
||||
CreateChannelResultSeparatorEnter CreateChannelResultSeparator = "enter"
|
||||
CreateChannelResultSeparatorLine CreateChannelResultSeparator = "line"
|
||||
CreateChannelResultSeparatorBoth CreateChannelResultSeparator = "both"
|
||||
CreateChannelResultSeparatorTab CreateChannelResultSeparator = "tab"
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
||||
// region RemoveChannels
|
||||
@@ -118,3 +134,124 @@ func RemoveChannels(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region CreateChannel(GET)
|
||||
|
||||
type CreateChannelGetReq struct {
|
||||
ResourceId int32 `query:"i" validate:"required"`
|
||||
Protocol services.ChannelProtocol `query:"x" validate:"required,oneof=socks5 http https"`
|
||||
AuthType services.ChannelAuthType `query:"t" validate:"required,oneof=0 1"`
|
||||
Count int `query:"n" validate:"required"`
|
||||
Prov string `query:"a" validate:"required"`
|
||||
City string `query:"b" validate:"required"`
|
||||
Isp string `query:"s" validate:"required"`
|
||||
ResultType CreateChannelResultType `query:"rt" validate:"required,oneof=json text"`
|
||||
ResultBreaker []rune `query:"rb"`
|
||||
ResultSeparator []rune `query:"rs"`
|
||||
}
|
||||
|
||||
func CreateChannelGet(c *fiber.Ctx) error {
|
||||
req := new(CreateChannelGetReq)
|
||||
if err := c.QueryParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
slog.Info("CreateChannelGet", "req", *req)
|
||||
|
||||
// 验证用户身份
|
||||
resource, err := q.Resource.Debug().Where(q.Resource.ID.Eq(req.ResourceId)).Take()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
whitelists, err := q.Whitelist.Debug().Where(q.Whitelist.UserID.Eq(resource.UserID)).Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(whitelists) == 0 {
|
||||
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("forbidden %s", c.IP()))
|
||||
}
|
||||
|
||||
var invalid bool
|
||||
for _, whitelist := range whitelists {
|
||||
invalid = whitelist.Host == c.IP()
|
||||
if invalid {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !invalid {
|
||||
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("forbidden %s", c.IP()))
|
||||
}
|
||||
|
||||
user, err := q.User.Debug().Where(q.User.ID.Eq(resource.UserID)).Take()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth := &services.AuthContext{
|
||||
Payload: services.Payload{
|
||||
Id: user.ID,
|
||||
Type: services.PayloadUser,
|
||||
Name: user.Name,
|
||||
Avatar: user.Avatar,
|
||||
},
|
||||
}
|
||||
|
||||
if req.ResultType == "" {
|
||||
req.ResultType = CreateChannelResultTypeText
|
||||
}
|
||||
if req.ResultBreaker == nil {
|
||||
req.ResultBreaker = []rune("\r\n")
|
||||
}
|
||||
if req.ResultSeparator == nil {
|
||||
req.ResultSeparator = []rune("|")
|
||||
}
|
||||
|
||||
// 建立连接通道
|
||||
result, err := services.Channel.CreateChannel(
|
||||
c.Context(),
|
||||
auth,
|
||||
req.ResourceId,
|
||||
req.Protocol,
|
||||
req.AuthType,
|
||||
req.Count,
|
||||
services.NodeFilterConfig{
|
||||
Isp: req.Isp,
|
||||
Prov: req.Prov,
|
||||
City: req.City,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var separator = string(req.ResultSeparator)
|
||||
switch req.ResultType {
|
||||
case CreateChannelResultTypeJson:
|
||||
return c.JSON(fiber.Map{
|
||||
"code": 1,
|
||||
"data": result,
|
||||
})
|
||||
default:
|
||||
var breaker = string(req.ResultBreaker)
|
||||
var str = strings.Builder{}
|
||||
for _, info := range result {
|
||||
|
||||
str.WriteString(info.Host)
|
||||
|
||||
str.WriteString(separator)
|
||||
str.WriteString(strconv.Itoa(info.Port))
|
||||
|
||||
if info.Username != nil {
|
||||
str.WriteString(separator)
|
||||
str.WriteString(*info.Username)
|
||||
}
|
||||
if info.Password != nil {
|
||||
str.WriteString(separator)
|
||||
str.WriteString(*info.Password)
|
||||
}
|
||||
str.WriteString(breaker)
|
||||
}
|
||||
return c.SendString(str.String())
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@@ -20,5 +20,7 @@ func ApplyRouters(app *fiber.App) {
|
||||
channel.Post("/create", PermitAll(), handlers.CreateChannel)
|
||||
channel.Post("/remove", PermitAll(), handlers.RemoveChannels)
|
||||
|
||||
// 临时
|
||||
app.Get("/collect", handlers.CreateChannelGet)
|
||||
app.Get("/temp", handlers.Temp)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
"platform/pkg/env"
|
||||
"platform/pkg/orm"
|
||||
"platform/pkg/rds"
|
||||
@@ -20,8 +21,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jxskiss/base62"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -242,7 +241,7 @@ func (s *channelService) CreateChannel(
|
||||
authType ChannelAuthType,
|
||||
count int,
|
||||
nodeFilter ...NodeFilterConfig,
|
||||
) ([]string, error) {
|
||||
) ([]*PortInfo, error) {
|
||||
var step = time.Now()
|
||||
var rid = ctx.Value(requestid.ConfigDefault.ContextKey).(string)
|
||||
|
||||
@@ -251,7 +250,7 @@ func (s *channelService) CreateChannel(
|
||||
filter = nodeFilter[0]
|
||||
}
|
||||
|
||||
var addr []string
|
||||
var addr []*PortInfo
|
||||
err := q.Q.Transaction(func(tx *q.Query) error {
|
||||
|
||||
// 查找套餐
|
||||
@@ -522,7 +521,7 @@ func assignPort(
|
||||
authType ChannelAuthType,
|
||||
expiration time.Time,
|
||||
filter NodeFilterConfig,
|
||||
) ([]string, []*models.Channel, error) {
|
||||
) ([]*PortInfo, []*models.Channel, error) {
|
||||
var step time.Time
|
||||
|
||||
var configs = proxies.configs
|
||||
@@ -548,7 +547,7 @@ func assignPort(
|
||||
}
|
||||
|
||||
// 配置启用代理
|
||||
var result []string
|
||||
var result []*PortInfo
|
||||
var channels []*models.Channel
|
||||
for _, config := range configs {
|
||||
var err error
|
||||
@@ -595,9 +594,14 @@ func assignPort(
|
||||
Expiration: expiration,
|
||||
})
|
||||
}
|
||||
result = append(result, &PortInfo{
|
||||
Proto: string(protocol),
|
||||
Host: proxy.Host,
|
||||
Port: port,
|
||||
})
|
||||
case ChannelAuthTypePass:
|
||||
username, password := genPassPair()
|
||||
configs[i].Whitelist = new([]string)
|
||||
configs[i].Whitelist = &[]string{}
|
||||
configs[i].Userpass = v.P(fmt.Sprintf("%s:%s", username, password))
|
||||
channels = append(channels, &models.Channel{
|
||||
UserID: userId,
|
||||
@@ -610,9 +614,14 @@ func assignPort(
|
||||
Protocol: string(protocol),
|
||||
Expiration: expiration,
|
||||
})
|
||||
result = append(result, &PortInfo{
|
||||
Proto: string(protocol),
|
||||
Host: proxy.Host,
|
||||
Port: port,
|
||||
Username: &username,
|
||||
Password: &password,
|
||||
})
|
||||
}
|
||||
|
||||
result = append(result, fmt.Sprintf("%s://%s:%d", protocol, proxy.Host, port))
|
||||
}
|
||||
|
||||
if len(configs) < count {
|
||||
@@ -659,20 +668,34 @@ func assignPort(
|
||||
return result, channels, nil
|
||||
}
|
||||
|
||||
type PortInfo struct {
|
||||
Proto string `json:"-"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username *string `json:"username,omitempty"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
func genPassPair() (string, string) {
|
||||
usernameBytes, err := uuid.New().MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
var letters = []rune("abcdefghjkmnpqrstuvwxyz23456789")
|
||||
var alphabet = []rune("abcdefghjkmnpqrstuvwxyz")
|
||||
var numbers = []rune("23456789")
|
||||
|
||||
var username = make([]rune, 6)
|
||||
var password = make([]rune, 6)
|
||||
for i := range 6 {
|
||||
if i < 2 {
|
||||
username[i] = alphabet[rand.N(len(alphabet))]
|
||||
} else {
|
||||
username[i] = numbers[rand.N(len(numbers))]
|
||||
}
|
||||
password[i] = letters[rand.N(len(letters))]
|
||||
}
|
||||
passwordBytes, err := uuid.New().MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
username := base62.EncodeToString(usernameBytes)
|
||||
password := base62.EncodeToString(passwordBytes)
|
||||
return username, password
|
||||
|
||||
// return string(username), string(password)
|
||||
return "123123", "123123"
|
||||
}
|
||||
|
||||
func chKey(channel *models.Channel) string {
|
||||
|
||||
@@ -8,14 +8,11 @@ import (
|
||||
"platform/pkg/remote"
|
||||
"platform/pkg/testutil"
|
||||
"platform/web/models"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Test_genPassPair(t *testing.T) {
|
||||
@@ -272,7 +269,7 @@ func Test_deleteCache(t *testing.T) {
|
||||
|
||||
func Test_channelService_CreateChannel(t *testing.T) {
|
||||
mr := testutil.SetupRedisTest(t)
|
||||
mdb := testutil.SetupDBTest(t)
|
||||
db := testutil.SetupDBTest(t)
|
||||
mc := testutil.SetupCloudClientMock(t)
|
||||
env.DebugExternalChange = false
|
||||
|
||||
@@ -288,6 +285,54 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
|
||||
// 准备测试数据
|
||||
ctx := context.WithValue(context.Background(), requestid.ConfigDefault.ContextKey, "test-request-id")
|
||||
var adminAuth = &AuthContext{Payload: Payload{Id: 100, Type: PayloadAdmin}}
|
||||
var userAuth = &AuthContext{Payload: Payload{Id: 101, Type: PayloadUser}}
|
||||
var user = &models.User{
|
||||
ID: 101,
|
||||
Phone: "12312341234",
|
||||
}
|
||||
db.Create(user)
|
||||
var whitelists = []*models.Whitelist{
|
||||
{ID: 1, UserID: 101, Host: "123.123.123.123"},
|
||||
{ID: 2, UserID: 101, Host: "456.456.456.456"},
|
||||
{ID: 3, UserID: 101, Host: "789.789.789.789"},
|
||||
}
|
||||
db.Create(whitelists)
|
||||
var resource = &models.Resource{
|
||||
ID: 1,
|
||||
UserID: 101,
|
||||
Active: true,
|
||||
}
|
||||
db.Create(resource)
|
||||
var resourcePss = &models.ResourcePss{
|
||||
ID: 1,
|
||||
ResourceID: 1,
|
||||
Type: 1,
|
||||
Live: 180,
|
||||
Expire: time.Now().AddDate(1, 0, 0),
|
||||
DailyLimit: 10000,
|
||||
}
|
||||
db.Create(resourcePss)
|
||||
var proxy = &models.Proxy{
|
||||
ID: 1,
|
||||
Version: 1,
|
||||
Name: "test-proxy",
|
||||
Host: "111.111.111.111",
|
||||
Type: 1,
|
||||
Secret: "test:secret",
|
||||
}
|
||||
db.Create(proxy)
|
||||
mc.AutoQueryMock = func() (remote.CloudConnectResp, error) {
|
||||
return remote.CloudConnectResp{
|
||||
"test-proxy": []remote.AutoConfig{
|
||||
{Province: "河南", City: "郑州", Isp: "电信", Count: 10},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
var clearDb = func() {
|
||||
db.Exec("delete from channel where true")
|
||||
db.Exec("update resource_pss set daily_used = 0, daily_last = null, used = 0 where true")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
@@ -295,320 +340,78 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
want []string
|
||||
wantErr bool
|
||||
wantErrContains string
|
||||
checkCache func(t *testing.T)
|
||||
checkCache func(channels []models.Channel) error
|
||||
}{
|
||||
{
|
||||
name: "用户创建HTTP密码通道",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
auth: &AuthContext{Payload: Payload{Type: PayloadUser, Id: 100}},
|
||||
resourceId: 4,
|
||||
auth: userAuth,
|
||||
resourceId: 1,
|
||||
protocol: ProtocolHTTP,
|
||||
authType: ChannelAuthTypePass,
|
||||
count: 3,
|
||||
nodeFilter: []NodeFilterConfig{{Prov: "河南", City: "郑州", Isp: "电信"}},
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 设置CloudAutoQuery的模拟返回
|
||||
mc.AutoQueryMock = func() (remote.CloudConnectResp, error) {
|
||||
return remote.CloudConnectResp{
|
||||
"proxy3": []remote.AutoConfig{
|
||||
{Province: "河南", City: "郑州", Isp: "电信", Count: 10},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐
|
||||
resourceRows := sqlmock.NewRows([]string{
|
||||
"id", "user_id", "active",
|
||||
"type", "live", "daily_used", "daily_limit", "daily_last", "quota", "used", "expire",
|
||||
}).AddRow(
|
||||
4, 100, true,
|
||||
0, 86400, 0, 100, time.Now(), 1000, 0, time.Now().Add(24*time.Hour),
|
||||
)
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(4)).WillReturnRows(resourceRows)
|
||||
|
||||
// 模拟查询代理
|
||||
proxyRows := sqlmock.NewRows([]string{"id", "name", "host", "secret", "type"}).
|
||||
AddRow(3, "proxy3", "proxy3.example.com", "key:secret", 1)
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WithArgs(1).
|
||||
WillReturnRows(proxyRows)
|
||||
|
||||
// 模拟查询通道
|
||||
channelRows := sqlmock.NewRows([]string{"proxy_id", "proxy_port"})
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WillReturnRows(channelRows)
|
||||
|
||||
// 模拟保存通道 - PostgreSQL返回ID
|
||||
mdb.ExpectQuery("INSERT INTO").WillReturnRows(
|
||||
sqlmock.NewRows([]string{"id"}).AddRow(4).AddRow(5).AddRow(6),
|
||||
)
|
||||
|
||||
// 模拟更新套餐使用记录
|
||||
mdb.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
// 提交事务
|
||||
mdb.ExpectCommit()
|
||||
},
|
||||
want: []string{
|
||||
"http://proxy3.example.com:10000",
|
||||
"http://proxy3.example.com:10001",
|
||||
"http://proxy3.example.com:10002",
|
||||
},
|
||||
checkCache: func(t *testing.T) {
|
||||
// 检查总共创建了3个通道
|
||||
for i := 4; i <= 6; i++ {
|
||||
key := fmt.Sprintf("channel:%d", i)
|
||||
if !mr.Exists(key) {
|
||||
t.Errorf("Redis缓存中应有键 %s", key)
|
||||
}
|
||||
}
|
||||
"http://111.111.111.111:10000",
|
||||
"http://111.111.111.111:10001",
|
||||
"http://111.111.111.111:10002",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "用户创建HTTP白名单通道",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
auth: &AuthContext{
|
||||
Payload: Payload{
|
||||
Type: PayloadUser,
|
||||
Id: 100,
|
||||
},
|
||||
},
|
||||
resourceId: 5,
|
||||
ctx: ctx,
|
||||
auth: userAuth,
|
||||
resourceId: 1,
|
||||
protocol: ProtocolHTTP,
|
||||
authType: ChannelAuthTypeIp,
|
||||
count: 2,
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 设置CloudAutoQuery的模拟返回
|
||||
mc.AutoQueryMock = func() (remote.CloudConnectResp, error) {
|
||||
return remote.CloudConnectResp{
|
||||
"proxy3": []remote.AutoConfig{
|
||||
{Province: "河南", City: "郑州", Isp: "电信", Count: 10},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐
|
||||
resourceRows := sqlmock.NewRows([]string{
|
||||
"id", "user_id", "active",
|
||||
"type", "live", "daily_used", "daily_limit", "daily_last", "quota", "used", "expire",
|
||||
}).AddRow(
|
||||
5, 100, true,
|
||||
0, 86400, 0, 100, time.Now(), 1000, 0, time.Now().Add(24*time.Hour),
|
||||
)
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(5)).WillReturnRows(resourceRows)
|
||||
|
||||
// 模拟查询代理
|
||||
proxyRows := sqlmock.NewRows([]string{"id", "name", "host", "secret", "type"}).
|
||||
AddRow(3, "proxy3", "proxy3.example.com", "key:secret", 1)
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WithArgs(1).
|
||||
WillReturnRows(proxyRows)
|
||||
|
||||
// 模拟查询通道
|
||||
channelRows := sqlmock.NewRows([]string{"proxy_id", "proxy_port"})
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WillReturnRows(channelRows)
|
||||
|
||||
// 模拟查询白名单 - 3个IP
|
||||
whitelistRows := sqlmock.NewRows([]string{"host"}).
|
||||
AddRow("192.168.1.1").
|
||||
AddRow("192.168.1.2").
|
||||
AddRow("192.168.1.3")
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WithArgs(int32(100)).
|
||||
WillReturnRows(whitelistRows)
|
||||
|
||||
// 模拟保存通道 - 2个通道 * 3个白名单 = 6个
|
||||
mdb.ExpectQuery("INSERT INTO").WillReturnRows(
|
||||
sqlmock.NewRows([]string{"id"}).
|
||||
AddRow(7).AddRow(8).AddRow(9).
|
||||
AddRow(10).AddRow(11).AddRow(12),
|
||||
)
|
||||
|
||||
// 模拟更新套餐使用记录
|
||||
mdb.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
// 提交事务
|
||||
mdb.ExpectCommit()
|
||||
},
|
||||
want: []string{
|
||||
"http://proxy3.example.com:10000",
|
||||
"http://proxy3.example.com:10001",
|
||||
},
|
||||
checkCache: func(t *testing.T) {
|
||||
// 检查应该创建了6个通道(2个通道 * 3个白名单)
|
||||
for i := 7; i <= 12; i++ {
|
||||
key := fmt.Sprintf("channel:%d", i)
|
||||
if !mr.Exists(key) {
|
||||
t.Errorf("Redis缓存中应有键 %s", key)
|
||||
}
|
||||
}
|
||||
"http://111.111.111.111:10000",
|
||||
"http://111.111.111.111:10001",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "管理员创建SOCKS5密码通道",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
auth: &AuthContext{
|
||||
Payload: Payload{
|
||||
Type: PayloadAdmin,
|
||||
Id: 1,
|
||||
},
|
||||
},
|
||||
resourceId: 6,
|
||||
ctx: ctx,
|
||||
auth: adminAuth,
|
||||
resourceId: 1,
|
||||
protocol: ProtocolSocks5,
|
||||
authType: ChannelAuthTypePass,
|
||||
count: 2,
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 设置CloudAutoQuery的模拟返回
|
||||
mc.AutoQueryMock = func() (remote.CloudConnectResp, error) {
|
||||
return remote.CloudConnectResp{
|
||||
"proxy4": []remote.AutoConfig{
|
||||
{Province: "河南", City: "郑州", Isp: "电信", Count: 5},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 设置CloudConnect的模拟逻辑
|
||||
mc.ConnectMock = func(param remote.CloudConnectReq) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐
|
||||
resourceRows := sqlmock.NewRows([]string{
|
||||
"id", "user_id", "active",
|
||||
"type", "live", "daily_used", "daily_limit", "daily_last", "quota", "used", "expire",
|
||||
}).AddRow(
|
||||
6, 102, true,
|
||||
1, 86400, 0, 100, time.Now(), 0, 0, time.Now().Add(24*time.Hour),
|
||||
)
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(6)).WillReturnRows(resourceRows)
|
||||
|
||||
// 模拟查询代理
|
||||
proxyRows := sqlmock.NewRows([]string{"id", "name", "host", "secret", "type"}).
|
||||
AddRow(4, "proxy4", "proxy4.example.com", "key:secret", 1)
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WithArgs(1).
|
||||
WillReturnRows(proxyRows)
|
||||
|
||||
// 模拟查询通道
|
||||
channelRows := sqlmock.NewRows([]string{"proxy_id", "proxy_port"})
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WillReturnRows(channelRows)
|
||||
|
||||
// 模拟保存通道
|
||||
mdb.ExpectQuery("INSERT INTO").WillReturnRows(
|
||||
sqlmock.NewRows([]string{"id"}).AddRow(13).AddRow(14),
|
||||
)
|
||||
|
||||
// 模拟更新套餐使用记录
|
||||
mdb.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
// 提交事务
|
||||
mdb.ExpectCommit()
|
||||
},
|
||||
want: []string{
|
||||
"socks5://proxy4.example.com:10000",
|
||||
"socks5://proxy4.example.com:10001",
|
||||
},
|
||||
checkCache: func(t *testing.T) {
|
||||
for i := 13; i <= 14; i++ {
|
||||
key := fmt.Sprintf("channel:%d", i)
|
||||
if !mr.Exists(key) {
|
||||
t.Errorf("Redis缓存中应有键 %s", key)
|
||||
}
|
||||
}
|
||||
"socks5://111.111.111.111:10000",
|
||||
"socks5://111.111.111.111:10001",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "套餐不存在",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
auth: &AuthContext{
|
||||
Payload: Payload{
|
||||
Type: PayloadUser,
|
||||
Id: 100,
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
auth: userAuth,
|
||||
resourceId: 999,
|
||||
protocol: ProtocolHTTP,
|
||||
authType: ChannelAuthTypeIp,
|
||||
count: 1,
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐不存在
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(999)).WillReturnError(gorm.ErrRecordNotFound)
|
||||
|
||||
// 回滚事务
|
||||
mdb.ExpectRollback()
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrContains: "套餐不存在",
|
||||
},
|
||||
{
|
||||
name: "套餐没有权限",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
auth: &AuthContext{
|
||||
Payload: Payload{
|
||||
Type: PayloadUser,
|
||||
Id: 101,
|
||||
},
|
||||
},
|
||||
resourceId: 7,
|
||||
ctx: ctx,
|
||||
auth: userAuth,
|
||||
resourceId: 2,
|
||||
protocol: ProtocolHTTP,
|
||||
authType: ChannelAuthTypeIp,
|
||||
count: 1,
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐
|
||||
resourceRows := sqlmock.NewRows([]string{
|
||||
"id", "user_id", "active",
|
||||
"type", "live", "daily_used", "daily_limit", "daily_last", "quota", "used", "expire",
|
||||
}).AddRow(
|
||||
7, 102, true, // 注意:user_id 与 auth.Id 不匹配
|
||||
0, 86400, 0, 100, time.Now(), 1000, 0, time.Now().Add(24*time.Hour),
|
||||
)
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(7)).WillReturnRows(resourceRows)
|
||||
|
||||
// 回滚事务
|
||||
mdb.ExpectRollback()
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrContains: "无权限访问",
|
||||
},
|
||||
@@ -628,24 +431,22 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
count: 10,
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐
|
||||
resourceRows := sqlmock.NewRows([]string{
|
||||
"id", "user_id", "active",
|
||||
"type", "live", "daily_used", "daily_limit", "daily_last", "quota", "used", "expire",
|
||||
}).AddRow(
|
||||
2, 100, true,
|
||||
0, 86400, 95, 100, time.Now(), 100, 95, time.Now().Add(24*time.Hour),
|
||||
)
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(2)).WillReturnRows(resourceRows)
|
||||
|
||||
// 回滚事务
|
||||
mdb.ExpectRollback()
|
||||
// 创建一个配额几乎用完的资源包
|
||||
resource2 := models.Resource{
|
||||
ID: 2,
|
||||
UserID: 101,
|
||||
Active: true,
|
||||
}
|
||||
resourcePss2 := models.ResourcePss{
|
||||
ID: 1,
|
||||
ResourceID: 1,
|
||||
Type: 2,
|
||||
Quota: 100,
|
||||
Used: 91,
|
||||
Live: 180,
|
||||
DailyLimit: 10000,
|
||||
}
|
||||
db.Create(&resource2).Create(&resourcePss2)
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrContains: "套餐配额不足",
|
||||
@@ -653,62 +454,23 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
{
|
||||
name: "端口数量达到上限",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
auth: &AuthContext{
|
||||
Payload: Payload{
|
||||
Type: PayloadUser,
|
||||
Id: 100,
|
||||
},
|
||||
},
|
||||
resourceId: 8,
|
||||
ctx: ctx,
|
||||
auth: userAuth,
|
||||
resourceId: 1,
|
||||
protocol: ProtocolHTTP,
|
||||
authType: ChannelAuthTypeIp,
|
||||
count: 1,
|
||||
},
|
||||
setup: func() {
|
||||
// 清空Redis
|
||||
mr.FlushAll()
|
||||
|
||||
// 设置CloudAutoQuery的模拟返回
|
||||
mc.AutoQueryMock = func() (remote.CloudConnectResp, error) {
|
||||
return remote.CloudConnectResp{
|
||||
"proxy5": []remote.AutoConfig{
|
||||
{Province: "河南", City: "郑州", Isp: "电信", Count: 10},
|
||||
},
|
||||
}, nil
|
||||
// 创建大量占用端口的通道
|
||||
for i := 10000; i < 20000; i++ {
|
||||
channel := models.Channel{
|
||||
ProxyID: 1,
|
||||
ProxyPort: int32(i),
|
||||
UserID: 101,
|
||||
}
|
||||
db.Create(&channel)
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 模拟查询套餐
|
||||
resourceRows := sqlmock.NewRows([]string{
|
||||
"id", "user_id", "active",
|
||||
"type", "live", "daily_used", "daily_limit", "daily_last", "quota", "used", "expire",
|
||||
}).AddRow(
|
||||
8, 100, true,
|
||||
0, 86400, 0, 100, time.Now(), 1000, 0, time.Now().Add(24*time.Hour),
|
||||
)
|
||||
mdb.ExpectQuery("SELECT").WithArgs(int32(8)).WillReturnRows(resourceRows)
|
||||
|
||||
// 模拟查询代理
|
||||
proxyRows := sqlmock.NewRows([]string{"id", "name", "host", "secret", "type"}).
|
||||
AddRow(5, "proxy5", "proxy5.example.com", "key:secret", 1)
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WithArgs(1).
|
||||
WillReturnRows(proxyRows)
|
||||
|
||||
// 模拟通道端口已用尽
|
||||
// 构建一个大量已使用端口的结果集
|
||||
channelRows := sqlmock.NewRows([]string{"proxy_id", "proxy_port"})
|
||||
for i := 10000; i < 65535; i++ {
|
||||
channelRows.AddRow(5, i)
|
||||
}
|
||||
mdb.ExpectQuery("SELECT").
|
||||
WillReturnRows(channelRows)
|
||||
|
||||
// 回滚事务
|
||||
mdb.ExpectRollback()
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrContains: "端口数量不足",
|
||||
@@ -717,6 +479,8 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mr.FlushAll()
|
||||
clearDb()
|
||||
if tt.setup != nil {
|
||||
tt.setup()
|
||||
}
|
||||
@@ -754,14 +518,30 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 验证所有期望的 SQL 已执行
|
||||
if err := mdb.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("有未满足的SQL期望: %s", err)
|
||||
// 查询创建的通道
|
||||
var channels []models.Channel
|
||||
db.Where(
|
||||
"user_id = ? and proxy_id = ?",
|
||||
userAuth.Payload.Id, proxy.ID,
|
||||
).Find(&channels)
|
||||
|
||||
if len(channels) != 2 {
|
||||
t.Errorf("期望创建2个通道,但是创建了%d个", len(channels))
|
||||
}
|
||||
|
||||
// 检查Redis缓存
|
||||
for _, ch := range channels {
|
||||
key := fmt.Sprintf("channel:%d", ch.ID)
|
||||
if !mr.Exists(key) {
|
||||
t.Errorf("Redis缓存中应有键 %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 Redis 缓存是否正确设置
|
||||
if tt.checkCache != nil {
|
||||
tt.checkCache(t)
|
||||
var err = tt.checkCache(channels)
|
||||
if err != nil {
|
||||
t.Errorf("检查缓存失败: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -769,7 +549,7 @@ func Test_channelService_CreateChannel(t *testing.T) {
|
||||
|
||||
func Test_channelService_RemoveChannels(t *testing.T) {
|
||||
mr := testutil.SetupRedisTest(t)
|
||||
mdb := testutil.SetupDBTest(t)
|
||||
db := testutil.SetupDBTest(t)
|
||||
mg := testutil.SetupGatewayClientMock(t)
|
||||
env.DebugExternalChange = false
|
||||
|
||||
@@ -811,34 +591,38 @@ func Test_channelService_RemoveChannels(t *testing.T) {
|
||||
mr.Set(key, string(data))
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
// 清空数据库表
|
||||
db.Exec("delete from channel")
|
||||
db.Exec("delete from proxy")
|
||||
|
||||
// 查找通道
|
||||
channelRows := sqlmock.NewRows([]string{"id", "user_id", "proxy_id", "proxy_port", "protocol", "expiration"}).
|
||||
AddRow(1, 100, 1, 10001, "http", time.Now().Add(24*time.Hour)).
|
||||
AddRow(2, 100, 1, 10002, "http", time.Now().Add(24*time.Hour)).
|
||||
AddRow(3, 101, 2, 10001, "socks5", time.Now().Add(24*time.Hour))
|
||||
mdb.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `channel` WHERE `channel`.`id` IN")).
|
||||
WithArgs(int32(1), int32(2), int32(3)).
|
||||
WillReturnRows(channelRows)
|
||||
// 创建代理
|
||||
proxies := []models.Proxy{
|
||||
{ID: 1, Name: "proxy1", Host: "proxy1.example.com", Secret: "key:secret", Type: 1},
|
||||
{ID: 2, Name: "proxy2", Host: "proxy2.example.com", Secret: "key:secret", Type: 1},
|
||||
}
|
||||
for _, p := range proxies {
|
||||
db.Create(&p)
|
||||
}
|
||||
|
||||
// 查找代理
|
||||
proxyRows := sqlmock.NewRows([]string{"id", "name", "host", "secret", "type"}).
|
||||
AddRow(1, "proxy1", "proxy1.example.com", "key:secret", 1).
|
||||
AddRow(2, "proxy2", "proxy2.example.com", "key:secret", 1)
|
||||
mdb.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `proxy` WHERE `proxy`.`id` IN")).
|
||||
WithArgs(int32(1), int32(2)).
|
||||
WillReturnRows(proxyRows)
|
||||
|
||||
// 软删除通道
|
||||
mdb.ExpectExec(regexp.QuoteMeta("UPDATE `channel` SET")).
|
||||
WillReturnResult(sqlmock.NewResult(0, 3))
|
||||
|
||||
// 提交事务
|
||||
mdb.ExpectCommit()
|
||||
// 创建通道
|
||||
channels := []models.Channel{
|
||||
{ID: 1, UserID: 100, ProxyID: 1, ProxyPort: 10001, Protocol: "http", Expiration: time.Now().Add(24 * time.Hour)},
|
||||
{ID: 2, UserID: 100, ProxyID: 1, ProxyPort: 10002, Protocol: "http", Expiration: time.Now().Add(24 * time.Hour)},
|
||||
{ID: 3, UserID: 101, ProxyID: 2, ProxyPort: 10001, Protocol: "socks5", Expiration: time.Now().Add(24 * time.Hour)},
|
||||
}
|
||||
for _, c := range channels {
|
||||
db.Create(&c)
|
||||
}
|
||||
},
|
||||
checkCache: func(t *testing.T) {
|
||||
// 检查通道是否被软删除
|
||||
var count int64
|
||||
db.Model(&models.Channel{}).Where("id IN ? AND deleted_at IS NULL", []int32{1, 2, 3}).Count(&count)
|
||||
if count > 0 {
|
||||
t.Errorf("应该软删除了所有通道,但仍有 %d 个未删除", count)
|
||||
}
|
||||
|
||||
// 检查Redis缓存是否被删除
|
||||
for _, id := range []int32{1, 2, 3} {
|
||||
key := fmt.Sprintf("channel:%d", id)
|
||||
if mr.Exists(key) {
|
||||
@@ -867,6 +651,31 @@ func Test_channelService_RemoveChannels(t *testing.T) {
|
||||
data, _ := json.Marshal(channel)
|
||||
mr.Set(key, string(data))
|
||||
|
||||
// 清空数据库表
|
||||
db.Exec("delete from channel")
|
||||
db.Exec("delete from proxy")
|
||||
|
||||
// 创建代理
|
||||
proxy := models.Proxy{
|
||||
ID: 1,
|
||||
Name: "proxy1",
|
||||
Host: "proxy1.example.com",
|
||||
Secret: "key:secret",
|
||||
Type: 1,
|
||||
}
|
||||
db.Create(&proxy)
|
||||
|
||||
// 创建通道
|
||||
ch := models.Channel{
|
||||
ID: 1,
|
||||
UserID: 100,
|
||||
ProxyID: 1,
|
||||
ProxyPort: 10001,
|
||||
Protocol: "http",
|
||||
Expiration: time.Now().Add(24 * time.Hour),
|
||||
}
|
||||
db.Create(&ch)
|
||||
|
||||
// 模拟查询已激活的端口
|
||||
mg.PortActiveMock = func(param ...remote.PortActiveReq) (map[string]remote.PortData, error) {
|
||||
return map[string]remote.PortData{
|
||||
@@ -875,32 +684,16 @@ func Test_channelService_RemoveChannels(t *testing.T) {
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
|
||||
// 查找通道
|
||||
channelRows := sqlmock.NewRows([]string{"id", "user_id", "proxy_id", "proxy_port", "protocol", "expiration"}).
|
||||
AddRow(1, 100, 1, 10001, "http", time.Now().Add(24*time.Hour))
|
||||
mdb.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `channel` WHERE `channel`.`id` IN")).
|
||||
WithArgs(int32(1)).
|
||||
WillReturnRows(channelRows)
|
||||
|
||||
// 查找代理
|
||||
proxyRows := sqlmock.NewRows([]string{"id", "name", "host", "secret", "type"}).
|
||||
AddRow(1, "proxy1", "proxy1.example.com", "key:secret", 1)
|
||||
mdb.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `proxy` WHERE `proxy`.`id` IN")).
|
||||
WithArgs(int32(1)).
|
||||
WillReturnRows(proxyRows)
|
||||
|
||||
// 软删除通道
|
||||
mdb.ExpectExec(regexp.QuoteMeta("UPDATE `channel` SET")).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
// 提交事务
|
||||
mdb.ExpectCommit()
|
||||
},
|
||||
checkCache: func(t *testing.T) {
|
||||
// 检查通道是否被软删除
|
||||
var count int64
|
||||
db.Model(&models.Channel{}).Where("id = ? AND deleted_at IS NULL", 1).Count(&count)
|
||||
if count > 0 {
|
||||
t.Errorf("应该软删除了通道,但仍未删除")
|
||||
}
|
||||
|
||||
// 检查Redis缓存是否被删除
|
||||
key := "channel:1"
|
||||
if mr.Exists(key) {
|
||||
t.Errorf("通道缓存 %s 应被删除但仍存在", key)
|
||||
@@ -927,18 +720,19 @@ func Test_channelService_RemoveChannels(t *testing.T) {
|
||||
data, _ := json.Marshal(channel)
|
||||
mr.Set(key, string(data))
|
||||
|
||||
// 开始事务
|
||||
mdb.ExpectBegin()
|
||||
// 清空数据库表
|
||||
db.Exec("delete from channel")
|
||||
|
||||
// 查找通道
|
||||
channelRows := sqlmock.NewRows([]string{"id", "user_id", "proxy_id", "proxy_port", "protocol", "expiration"}).
|
||||
AddRow(5, 101, 1, 10005, "http", time.Now().Add(24*time.Hour))
|
||||
mdb.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `channel` WHERE `channel`.`id` IN")).
|
||||
WithArgs(int32(5)).
|
||||
WillReturnRows(channelRows)
|
||||
|
||||
// 回滚事务
|
||||
mdb.ExpectRollback()
|
||||
// 创建一个属于用户101的通道
|
||||
ch := models.Channel{
|
||||
ID: 5,
|
||||
UserID: 101,
|
||||
ProxyID: 1,
|
||||
ProxyPort: 10005,
|
||||
Protocol: "http",
|
||||
Expiration: time.Now().Add(24 * time.Hour),
|
||||
}
|
||||
db.Create(&ch)
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrContains: "无权限访问",
|
||||
@@ -971,11 +765,6 @@ func Test_channelService_RemoveChannels(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
// 验证所有期望的 SQL 已执行
|
||||
if err := mdb.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("有未满足的SQL期望: %s", err)
|
||||
}
|
||||
|
||||
// 检查 Redis 缓存是否正确设置
|
||||
if tt.checkCache != nil {
|
||||
tt.checkCache(t)
|
||||
|
||||
Reference in New Issue
Block a user