通道的增删接口实现,数据表和目录结构调整
This commit is contained in:
11
README.md
11
README.md
@@ -19,6 +19,12 @@
|
|||||||
- [ ] Limiter
|
- [ ] Limiter
|
||||||
- [ ] Compress
|
- [ ] Compress
|
||||||
|
|
||||||
|
有些地方在用手动事务,有时间改成自动事务
|
||||||
|
|
||||||
|
remote 用环境变量保存账号密码!
|
||||||
|
|
||||||
|
重新手动实现 model 层
|
||||||
|
|
||||||
环境变量配置默认会话配置
|
环境变量配置默认会话配置
|
||||||
|
|
||||||
oauth token 验证授权范围
|
oauth token 验证授权范围
|
||||||
@@ -50,3 +56,8 @@ captcha_id 关联用户本机信息,实现验证码设备绑定(或者其他
|
|||||||
| proxy/shared-static | pss | 动态代理 |
|
| proxy/shared-static | pss | 动态代理 |
|
||||||
| proxy/shared-rotate | psr | 隧道代理 |
|
| proxy/shared-rotate | psr | 隧道代理 |
|
||||||
| proxy/private-static | pps | 独享代理 |
|
| proxy/private-static | pps | 独享代理 |
|
||||||
|
|
||||||
|
### 外部服务
|
||||||
|
|
||||||
|
服务器ip 110.40.82.248
|
||||||
|
api账密:api:123456
|
||||||
@@ -4,10 +4,10 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"platform/init/env"
|
"platform/pkg/env"
|
||||||
"platform/init/logs"
|
"platform/pkg/logs"
|
||||||
"platform/init/orm"
|
"platform/pkg/orm"
|
||||||
"platform/init/rds"
|
"platform/pkg/rds"
|
||||||
"platform/web"
|
"platform/web"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|||||||
7
cmd/playground/main.go
Normal file
7
cmd/playground/main.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "encoding/base64"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println(base64.URLEncoding.EncodeToString([]byte("app:123456")))
|
||||||
|
}
|
||||||
68
cmd/tasks/main.go
Normal file
68
cmd/tasks/main.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
"platform/pkg/env"
|
||||||
|
"platform/pkg/logs"
|
||||||
|
"platform/pkg/orm"
|
||||||
|
"platform/pkg/rds"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start() {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
env.Init()
|
||||||
|
logs.Init()
|
||||||
|
rds.Init()
|
||||||
|
orm.Init()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for curr := range ticker.C {
|
||||||
|
err := process(ctx, curr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func process(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:session"}, curr.Unix()).Result()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
list, ok := result.([]string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("failed to convert result to []string")
|
||||||
|
}
|
||||||
|
for _, item := range list {
|
||||||
|
// 从数据库删除授权信息
|
||||||
|
slog.Debug(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
85
cmd/wrapper/main.go
Normal file
85
cmd/wrapper/main.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
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) {
|
||||||
|
|
||||||
|
// 查询节点状态
|
||||||
|
|
||||||
|
// 筛选在线节点添加到节点池
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
2246
docs/proxy 并发协程数分析.excalidraw
Normal file
2246
docs/proxy 并发协程数分析.excalidraw
Normal file
File diff suppressed because it is too large
Load Diff
2472
docs/数据表结构.excalidraw
Normal file
2472
docs/数据表结构.excalidraw
Normal file
File diff suppressed because it is too large
Load Diff
1407
docs/系统结构.excalidraw
Normal file
1407
docs/系统结构.excalidraw
Normal file
File diff suppressed because it is too large
Load Diff
7
go.mod
7
go.mod
@@ -7,9 +7,9 @@ require (
|
|||||||
github.com/gofiber/fiber/v2 v2.52.6
|
github.com/gofiber/fiber/v2 v2.52.6
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/jxskiss/base62 v1.1.0
|
||||||
github.com/lmittmann/tint v1.0.7
|
github.com/lmittmann/tint v1.0.7
|
||||||
github.com/redis/go-redis/v9 v9.3.0
|
github.com/redis/go-redis/v9 v9.3.0
|
||||||
github.com/stretchr/testify v1.8.1
|
|
||||||
golang.org/x/crypto v0.17.0
|
golang.org/x/crypto v0.17.0
|
||||||
gorm.io/driver/postgres v1.5.11
|
gorm.io/driver/postgres v1.5.11
|
||||||
gorm.io/gen v0.3.26
|
gorm.io/gen v0.3.26
|
||||||
@@ -21,7 +21,6 @@ require (
|
|||||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
@@ -31,13 +30,10 @@ require (
|
|||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
@@ -47,7 +43,6 @@ require (
|
|||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
golang.org/x/tools v0.26.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect
|
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect
|
||||||
gorm.io/driver/mysql v1.5.7 // indirect
|
gorm.io/driver/mysql v1.5.7 // indirect
|
||||||
gorm.io/hints v1.1.0 // indirect
|
gorm.io/hints v1.1.0 // indirect
|
||||||
|
|||||||
15
go.sum
15
go.sum
@@ -10,7 +10,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
|||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
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 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -41,12 +40,10 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
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 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
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/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
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.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
|
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/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 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@@ -67,15 +64,9 @@ github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0
|
|||||||
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
@@ -132,8 +123,6 @@ 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.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
0
init/env/env.go → pkg/env/env.go
vendored
0
init/env/env.go → pkg/env/env.go
vendored
@@ -3,7 +3,7 @@ package logs
|
|||||||
import (
|
import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"platform/init/env"
|
"platform/pkg/env"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lmittmann/tint"
|
"github.com/lmittmann/tint"
|
||||||
@@ -3,7 +3,7 @@ package orm
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"platform/init/env"
|
"platform/pkg/env"
|
||||||
"platform/web/queries"
|
"platform/web/queries"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -2,7 +2,7 @@ package rds
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"platform/init/env"
|
"platform/pkg/env"
|
||||||
|
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
||||||
126
pkg/remote/remote.go
Normal file
126
pkg/remote/remote.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
gatewayUrl string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
cloudUrl string
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Client client
|
||||||
|
|
||||||
|
func Init() error {
|
||||||
|
// todo 从环境变量中获取参数
|
||||||
|
Client = client{
|
||||||
|
gatewayUrl: "http://110.40.82.248:9990",
|
||||||
|
username: "api",
|
||||||
|
password: "123456",
|
||||||
|
cloudUrl: "http://103.139.212.110",
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortConfig struct {
|
||||||
|
Port string `json:"port"`
|
||||||
|
Edge []string `json:"edge"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Time int `json:"time"`
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Rate int `json:"rate"`
|
||||||
|
Whitelist []string `json:"whitelist"`
|
||||||
|
Userpass string `json:"userpass"`
|
||||||
|
AutoEdgeConfig AutoEdgeConfig `json:"auto_edge_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AutoEdgeConfig struct {
|
||||||
|
Province string `json:"province"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Isp string `json:"isp"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
PacketLoss int `json:"packet_loss"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) PortConfigs(params ...PortConfig) error {
|
||||||
|
resp, err := c.requestCloud("/port/configs", params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
_ = Body.Close()
|
||||||
|
}(resp.Body)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return errors.New("failed to configure port")
|
||||||
|
}
|
||||||
|
|
||||||
|
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"] != 0 {
|
||||||
|
return errors.New("failed to configure port")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) requestGateway(url string, data any) (*http.Response, error) {
|
||||||
|
jsonData, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", c.gatewayUrl+url, strings.NewReader(string(jsonData)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.SetBasicAuth(c.username, c.password)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) requestCloud(url string, data any) (*http.Response, error) {
|
||||||
|
jsonData, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", c.cloudUrl+url, strings.NewReader(string(jsonData)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("token", c.token)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
@@ -516,16 +516,19 @@ create table resource (
|
|||||||
user_id int not null references "user" (id)
|
user_id int not null references "user" (id)
|
||||||
on update cascade
|
on update cascade
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
|
active bool default true,
|
||||||
created_at timestamp default current_timestamp,
|
created_at timestamp default current_timestamp,
|
||||||
updated_at timestamp default current_timestamp,
|
updated_at timestamp default current_timestamp,
|
||||||
deleted_at timestamp
|
deleted_at timestamp
|
||||||
);
|
);
|
||||||
create index resource_user_id_index on resource (user_id);
|
create index resource_user_id_index on resource (user_id);
|
||||||
|
create index resource_active_index on resource (active);
|
||||||
|
|
||||||
-- resource表字段注释
|
-- resource表字段注释
|
||||||
comment on table resource is '套餐表';
|
comment on table resource is '套餐表';
|
||||||
comment on column resource.id is '套餐ID';
|
comment on column resource.id is '套餐ID';
|
||||||
comment on column resource.user_id is '用户ID';
|
comment on column resource.user_id is '用户ID';
|
||||||
|
comment on column resource.active is '套餐状态';
|
||||||
comment on column resource.created_at is '创建时间';
|
comment on column resource.created_at is '创建时间';
|
||||||
comment on column resource.updated_at is '更新时间';
|
comment on column resource.updated_at is '更新时间';
|
||||||
comment on column resource.deleted_at is '删除时间';
|
comment on column resource.deleted_at is '删除时间';
|
||||||
@@ -537,14 +540,14 @@ create table resource_pss (
|
|||||||
resource_id int not null references resource (id)
|
resource_id int not null references resource (id)
|
||||||
on update cascade
|
on update cascade
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
active bool not null default false,
|
|
||||||
type int,
|
type int,
|
||||||
live int,
|
live int,
|
||||||
quota int,
|
quota int,
|
||||||
used int,
|
used int,
|
||||||
expire timestamp,
|
expire timestamp,
|
||||||
limit_day int,
|
daily_limit int,
|
||||||
last_used timestamp,
|
daily_used int,
|
||||||
|
daily_last timestamp,
|
||||||
created_at timestamp default current_timestamp,
|
created_at timestamp default current_timestamp,
|
||||||
updated_at timestamp default current_timestamp,
|
updated_at timestamp default current_timestamp,
|
||||||
deleted_at timestamp
|
deleted_at timestamp
|
||||||
@@ -555,14 +558,14 @@ create index resource_pss_resource_id_index on resource_pss (resource_id);
|
|||||||
comment on table resource_pss is '动态代理套餐表';
|
comment on table resource_pss is '动态代理套餐表';
|
||||||
comment on column resource_pss.id is 'ID';
|
comment on column resource_pss.id is 'ID';
|
||||||
comment on column resource_pss.resource_id is '套餐ID';
|
comment on column resource_pss.resource_id is '套餐ID';
|
||||||
comment on column resource_pss.active is '是否启用';
|
|
||||||
comment on column resource_pss.type is '套餐类型:1-包时,2-包量';
|
comment on column resource_pss.type is '套餐类型:1-包时,2-包量';
|
||||||
comment on column resource_pss.live is '可用时长(秒)';
|
comment on column resource_pss.live is '可用时长(秒)';
|
||||||
comment on column resource_pss.quota is '配额数量';
|
comment on column resource_pss.quota is '配额数量';
|
||||||
comment on column resource_pss.used is '已用数量';
|
comment on column resource_pss.used is '已用数量';
|
||||||
comment on column resource_pss.expire is '过期时间';
|
comment on column resource_pss.expire is '过期时间';
|
||||||
comment on column resource_pss.limit_day is '每日限额';
|
comment on column resource_pss.daily_limit is '每日限制';
|
||||||
comment on column resource_pss.last_used is '最后提取时间';
|
comment on column resource_pss.daily_used is '今日已用数量';
|
||||||
|
comment on column resource_pss.daily_last is '今日最后使用时间';
|
||||||
comment on column resource_pss.created_at is '创建时间';
|
comment on column resource_pss.created_at is '创建时间';
|
||||||
comment on column resource_pss.updated_at is '更新时间';
|
comment on column resource_pss.updated_at is '更新时间';
|
||||||
comment on column resource_pss.deleted_at is '删除时间';
|
comment on column resource_pss.deleted_at is '删除时间';
|
||||||
@@ -574,7 +577,6 @@ create table resource_psr (
|
|||||||
resource_id int not null references resource (id)
|
resource_id int not null references resource (id)
|
||||||
on update cascade
|
on update cascade
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
active bool not null default false,
|
|
||||||
live int,
|
live int,
|
||||||
conn int,
|
conn int,
|
||||||
expire timestamp,
|
expire timestamp,
|
||||||
@@ -589,7 +591,6 @@ create index resource_psr_resource_id_index on resource_psr (resource_id);
|
|||||||
comment on table resource_psr is '隧道代理套餐表';
|
comment on table resource_psr is '隧道代理套餐表';
|
||||||
comment on column resource_psr.id is 'ID';
|
comment on column resource_psr.id is 'ID';
|
||||||
comment on column resource_psr.resource_id is '套餐ID';
|
comment on column resource_psr.resource_id is '套餐ID';
|
||||||
comment on column resource_psr.active is '是否启用';
|
|
||||||
comment on column resource_psr.live is '轮换周期(秒)';
|
comment on column resource_psr.live is '轮换周期(秒)';
|
||||||
comment on column resource_psr.conn is '最大连接数';
|
comment on column resource_psr.conn is '最大连接数';
|
||||||
comment on column resource_psr.expire is '过期时间';
|
comment on column resource_psr.expire is '过期时间';
|
||||||
@@ -605,7 +606,6 @@ create table resource_pps (
|
|||||||
resource_id int not null references resource (id)
|
resource_id int not null references resource (id)
|
||||||
on update cascade
|
on update cascade
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
active bool not null default false,
|
|
||||||
created_at timestamp default current_timestamp,
|
created_at timestamp default current_timestamp,
|
||||||
updated_at timestamp default current_timestamp,
|
updated_at timestamp default current_timestamp,
|
||||||
deleted_at timestamp
|
deleted_at timestamp
|
||||||
@@ -616,7 +616,6 @@ create index resource_pps_resource_id_index on resource_pps (resource_id);
|
|||||||
comment on table resource_pps is '独享代理套餐表';
|
comment on table resource_pps is '独享代理套餐表';
|
||||||
comment on column resource_pps.id is 'ID';
|
comment on column resource_pps.id is 'ID';
|
||||||
comment on column resource_pps.resource_id is '套餐ID';
|
comment on column resource_pps.resource_id is '套餐ID';
|
||||||
comment on column resource_pps.active is '是否启用';
|
|
||||||
comment on column resource_pps.created_at is '创建时间';
|
comment on column resource_pps.created_at is '创建时间';
|
||||||
comment on column resource_pps.updated_at is '更新时间';
|
comment on column resource_pps.updated_at is '更新时间';
|
||||||
comment on column resource_pps.deleted_at is '删除时间';
|
comment on column resource_pps.deleted_at is '删除时间';
|
||||||
|
|||||||
14
web/auth.go
14
web/auth.go
@@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Protect 创建针对单个路由的鉴权中间件
|
// PermitUser 创建针对单个路由的鉴权中间件
|
||||||
func Protect(permissions ...string) fiber.Handler {
|
func PermitUser(permissions ...string) fiber.Handler {
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
// 获取令牌
|
// 获取令牌
|
||||||
var header = c.Get("Authorization")
|
var header = c.Get("Authorization")
|
||||||
@@ -32,12 +32,22 @@ func Protect(permissions ...string) fiber.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查权限
|
// 检查权限
|
||||||
|
switch auth.Payload.Type {
|
||||||
|
case services.PayloadAdmin:
|
||||||
|
// 管理员不需要权限检查
|
||||||
|
case services.PayloadUser:
|
||||||
if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
|
if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
|
||||||
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||||
Error: true,
|
Error: true,
|
||||||
Message: "拒绝访问",
|
Message: "拒绝访问",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||||
|
Error: true,
|
||||||
|
Message: "拒绝访问",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 将认证信息存储在上下文中
|
// 将认证信息存储在上下文中
|
||||||
c.Locals("auth", auth)
|
c.Locals("auth", auth)
|
||||||
|
|||||||
13
web/common/errors.go
Normal file
13
web/common/errors.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
type AuthUnAuthorizedErr string
|
||||||
|
|
||||||
|
func (e AuthUnAuthorizedErr) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthForbiddenErr string
|
||||||
|
|
||||||
|
func (e AuthForbiddenErr) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
type CreateChannelReq struct {
|
type CreateChannelReq struct {
|
||||||
Region string `json:"region" validate:"required"`
|
Region string `json:"region" validate:"required"`
|
||||||
Provider string `json:"provider" validate:"required"`
|
Provider string `json:"provider" validate:"required"`
|
||||||
Protocol services.Protocol `json:"protocol" validate:"required,oneof=socks5 http https"`
|
Protocol services.ChannelProtocol `json:"protocol" validate:"required,oneof=socks5 http https"`
|
||||||
ResourceId int `json:"resource_id" validate:"required"`
|
ResourceId int `json:"resource_id" validate:"required"`
|
||||||
Count int `json:"count" validate:"required"`
|
Count int `json:"count" validate:"required"`
|
||||||
ResultType CreateChannelResultType `json:"return_type" validate:"required,oneof=json text"`
|
ResultType CreateChannelResultType `json:"return_type" validate:"required,oneof=json text"`
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const TableNameResource = "resource"
|
|||||||
type Resource struct {
|
type Resource struct {
|
||||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:套餐ID" json:"id"` // 套餐ID
|
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:套餐ID" json:"id"` // 套餐ID
|
||||||
UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
|
UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
|
||||||
|
Active bool `gorm:"column:active;default:true;comment:套餐状态" json:"active"` // 套餐状态
|
||||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const TableNameResourcePps = "resource_pps"
|
|||||||
type ResourcePps struct {
|
type ResourcePps struct {
|
||||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
|
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
|
||||||
ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
|
ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
|
||||||
Active bool `gorm:"column:active;not null;comment:是否启用" json:"active"` // 是否启用
|
|
||||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const TableNameResourcePsr = "resource_psr"
|
|||||||
type ResourcePsr struct {
|
type ResourcePsr struct {
|
||||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
|
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
|
||||||
ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
|
ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
|
||||||
Active bool `gorm:"column:active;not null;comment:是否启用" json:"active"` // 是否启用
|
|
||||||
Live int32 `gorm:"column:live;comment:轮换周期(秒)" json:"live"` // 轮换周期(秒)
|
Live int32 `gorm:"column:live;comment:轮换周期(秒)" json:"live"` // 轮换周期(秒)
|
||||||
Conn int32 `gorm:"column:conn;comment:最大连接数" json:"conn"` // 最大连接数
|
Conn int32 `gorm:"column:conn;comment:最大连接数" json:"conn"` // 最大连接数
|
||||||
Expire time.Time `gorm:"column:expire;comment:过期时间" json:"expire"` // 过期时间
|
Expire time.Time `gorm:"column:expire;comment:过期时间" json:"expire"` // 过期时间
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ const TableNameResourcePss = "resource_pss"
|
|||||||
type ResourcePss struct {
|
type ResourcePss struct {
|
||||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
|
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
|
||||||
ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
|
ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
|
||||||
Active bool `gorm:"column:active;not null;comment:是否启用" json:"active"` // 是否启用
|
|
||||||
Type int32 `gorm:"column:type;comment:套餐类型:1-包时,2-包量" json:"type"` // 套餐类型:1-包时,2-包量
|
Type int32 `gorm:"column:type;comment:套餐类型:1-包时,2-包量" json:"type"` // 套餐类型:1-包时,2-包量
|
||||||
Live int32 `gorm:"column:live;comment:可用时长(秒)" json:"live"` // 可用时长(秒)
|
Live int32 `gorm:"column:live;comment:可用时长(秒)" json:"live"` // 可用时长(秒)
|
||||||
Quota int32 `gorm:"column:quota;comment:配额数量" json:"quota"` // 配额数量
|
Quota int32 `gorm:"column:quota;comment:配额数量" json:"quota"` // 配额数量
|
||||||
Used int32 `gorm:"column:used;comment:已用数量" json:"used"` // 已用数量
|
Used int32 `gorm:"column:used;comment:已用数量" json:"used"` // 已用数量
|
||||||
Expire time.Time `gorm:"column:expire;comment:过期时间" json:"expire"` // 过期时间
|
Expire time.Time `gorm:"column:expire;comment:过期时间" json:"expire"` // 过期时间
|
||||||
LimitDay int32 `gorm:"column:limit_day;comment:每日限额" json:"limit_day"` // 每日限额
|
DailyLimit int32 `gorm:"column:daily_limit;comment:每日限制" json:"daily_limit"` // 每日限制
|
||||||
LastUsed time.Time `gorm:"column:last_used;comment:最后提取时间" json:"last_used"` // 最后提取时间
|
DailyUsed int32 `gorm:"column:daily_used;comment:今日已用数量" json:"daily_used"` // 今日已用数量
|
||||||
|
DailyLast time.Time `gorm:"column:daily_last;comment:今日最后使用时间" json:"daily_last"` // 今日最后使用时间
|
||||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
|
|||||||
_resource.ALL = field.NewAsterisk(tableName)
|
_resource.ALL = field.NewAsterisk(tableName)
|
||||||
_resource.ID = field.NewInt32(tableName, "id")
|
_resource.ID = field.NewInt32(tableName, "id")
|
||||||
_resource.UserID = field.NewInt32(tableName, "user_id")
|
_resource.UserID = field.NewInt32(tableName, "user_id")
|
||||||
|
_resource.Active = field.NewBool(tableName, "active")
|
||||||
_resource.CreatedAt = field.NewTime(tableName, "created_at")
|
_resource.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_resource.UpdatedAt = field.NewTime(tableName, "updated_at")
|
_resource.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
_resource.DeletedAt = field.NewField(tableName, "deleted_at")
|
_resource.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
@@ -44,6 +45,7 @@ type resource struct {
|
|||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32 // 套餐ID
|
ID field.Int32 // 套餐ID
|
||||||
UserID field.Int32 // 用户ID
|
UserID field.Int32 // 用户ID
|
||||||
|
Active field.Bool // 套餐状态
|
||||||
CreatedAt field.Time // 创建时间
|
CreatedAt field.Time // 创建时间
|
||||||
UpdatedAt field.Time // 更新时间
|
UpdatedAt field.Time // 更新时间
|
||||||
DeletedAt field.Field // 删除时间
|
DeletedAt field.Field // 删除时间
|
||||||
@@ -65,6 +67,7 @@ func (r *resource) updateTableName(table string) *resource {
|
|||||||
r.ALL = field.NewAsterisk(table)
|
r.ALL = field.NewAsterisk(table)
|
||||||
r.ID = field.NewInt32(table, "id")
|
r.ID = field.NewInt32(table, "id")
|
||||||
r.UserID = field.NewInt32(table, "user_id")
|
r.UserID = field.NewInt32(table, "user_id")
|
||||||
|
r.Active = field.NewBool(table, "active")
|
||||||
r.CreatedAt = field.NewTime(table, "created_at")
|
r.CreatedAt = field.NewTime(table, "created_at")
|
||||||
r.UpdatedAt = field.NewTime(table, "updated_at")
|
r.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
r.DeletedAt = field.NewField(table, "deleted_at")
|
r.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
@@ -84,9 +87,10 @@ func (r *resource) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resource) fillFieldMap() {
|
func (r *resource) fillFieldMap() {
|
||||||
r.fieldMap = make(map[string]field.Expr, 5)
|
r.fieldMap = make(map[string]field.Expr, 6)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["user_id"] = r.UserID
|
r.fieldMap["user_id"] = r.UserID
|
||||||
|
r.fieldMap["active"] = r.Active
|
||||||
r.fieldMap["created_at"] = r.CreatedAt
|
r.fieldMap["created_at"] = r.CreatedAt
|
||||||
r.fieldMap["updated_at"] = r.UpdatedAt
|
r.fieldMap["updated_at"] = r.UpdatedAt
|
||||||
r.fieldMap["deleted_at"] = r.DeletedAt
|
r.fieldMap["deleted_at"] = r.DeletedAt
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func newResourcePps(db *gorm.DB, opts ...gen.DOOption) resourcePps {
|
|||||||
_resourcePps.ALL = field.NewAsterisk(tableName)
|
_resourcePps.ALL = field.NewAsterisk(tableName)
|
||||||
_resourcePps.ID = field.NewInt32(tableName, "id")
|
_resourcePps.ID = field.NewInt32(tableName, "id")
|
||||||
_resourcePps.ResourceID = field.NewInt32(tableName, "resource_id")
|
_resourcePps.ResourceID = field.NewInt32(tableName, "resource_id")
|
||||||
_resourcePps.Active = field.NewBool(tableName, "active")
|
|
||||||
_resourcePps.CreatedAt = field.NewTime(tableName, "created_at")
|
_resourcePps.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_resourcePps.UpdatedAt = field.NewTime(tableName, "updated_at")
|
_resourcePps.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
_resourcePps.DeletedAt = field.NewField(tableName, "deleted_at")
|
_resourcePps.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
@@ -45,7 +44,6 @@ type resourcePps struct {
|
|||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32 // ID
|
ID field.Int32 // ID
|
||||||
ResourceID field.Int32 // 套餐ID
|
ResourceID field.Int32 // 套餐ID
|
||||||
Active field.Bool // 是否启用
|
|
||||||
CreatedAt field.Time // 创建时间
|
CreatedAt field.Time // 创建时间
|
||||||
UpdatedAt field.Time // 更新时间
|
UpdatedAt field.Time // 更新时间
|
||||||
DeletedAt field.Field // 删除时间
|
DeletedAt field.Field // 删除时间
|
||||||
@@ -67,7 +65,6 @@ func (r *resourcePps) updateTableName(table string) *resourcePps {
|
|||||||
r.ALL = field.NewAsterisk(table)
|
r.ALL = field.NewAsterisk(table)
|
||||||
r.ID = field.NewInt32(table, "id")
|
r.ID = field.NewInt32(table, "id")
|
||||||
r.ResourceID = field.NewInt32(table, "resource_id")
|
r.ResourceID = field.NewInt32(table, "resource_id")
|
||||||
r.Active = field.NewBool(table, "active")
|
|
||||||
r.CreatedAt = field.NewTime(table, "created_at")
|
r.CreatedAt = field.NewTime(table, "created_at")
|
||||||
r.UpdatedAt = field.NewTime(table, "updated_at")
|
r.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
r.DeletedAt = field.NewField(table, "deleted_at")
|
r.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
@@ -87,10 +84,9 @@ func (r *resourcePps) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourcePps) fillFieldMap() {
|
func (r *resourcePps) fillFieldMap() {
|
||||||
r.fieldMap = make(map[string]field.Expr, 6)
|
r.fieldMap = make(map[string]field.Expr, 5)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["resource_id"] = r.ResourceID
|
r.fieldMap["resource_id"] = r.ResourceID
|
||||||
r.fieldMap["active"] = r.Active
|
|
||||||
r.fieldMap["created_at"] = r.CreatedAt
|
r.fieldMap["created_at"] = r.CreatedAt
|
||||||
r.fieldMap["updated_at"] = r.UpdatedAt
|
r.fieldMap["updated_at"] = r.UpdatedAt
|
||||||
r.fieldMap["deleted_at"] = r.DeletedAt
|
r.fieldMap["deleted_at"] = r.DeletedAt
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func newResourcePsr(db *gorm.DB, opts ...gen.DOOption) resourcePsr {
|
|||||||
_resourcePsr.ALL = field.NewAsterisk(tableName)
|
_resourcePsr.ALL = field.NewAsterisk(tableName)
|
||||||
_resourcePsr.ID = field.NewInt32(tableName, "id")
|
_resourcePsr.ID = field.NewInt32(tableName, "id")
|
||||||
_resourcePsr.ResourceID = field.NewInt32(tableName, "resource_id")
|
_resourcePsr.ResourceID = field.NewInt32(tableName, "resource_id")
|
||||||
_resourcePsr.Active = field.NewBool(tableName, "active")
|
|
||||||
_resourcePsr.Live = field.NewInt32(tableName, "live")
|
_resourcePsr.Live = field.NewInt32(tableName, "live")
|
||||||
_resourcePsr.Conn = field.NewInt32(tableName, "conn")
|
_resourcePsr.Conn = field.NewInt32(tableName, "conn")
|
||||||
_resourcePsr.Expire = field.NewTime(tableName, "expire")
|
_resourcePsr.Expire = field.NewTime(tableName, "expire")
|
||||||
@@ -49,7 +48,6 @@ type resourcePsr struct {
|
|||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32 // ID
|
ID field.Int32 // ID
|
||||||
ResourceID field.Int32 // 套餐ID
|
ResourceID field.Int32 // 套餐ID
|
||||||
Active field.Bool // 是否启用
|
|
||||||
Live field.Int32 // 轮换周期(秒)
|
Live field.Int32 // 轮换周期(秒)
|
||||||
Conn field.Int32 // 最大连接数
|
Conn field.Int32 // 最大连接数
|
||||||
Expire field.Time // 过期时间
|
Expire field.Time // 过期时间
|
||||||
@@ -75,7 +73,6 @@ func (r *resourcePsr) updateTableName(table string) *resourcePsr {
|
|||||||
r.ALL = field.NewAsterisk(table)
|
r.ALL = field.NewAsterisk(table)
|
||||||
r.ID = field.NewInt32(table, "id")
|
r.ID = field.NewInt32(table, "id")
|
||||||
r.ResourceID = field.NewInt32(table, "resource_id")
|
r.ResourceID = field.NewInt32(table, "resource_id")
|
||||||
r.Active = field.NewBool(table, "active")
|
|
||||||
r.Live = field.NewInt32(table, "live")
|
r.Live = field.NewInt32(table, "live")
|
||||||
r.Conn = field.NewInt32(table, "conn")
|
r.Conn = field.NewInt32(table, "conn")
|
||||||
r.Expire = field.NewTime(table, "expire")
|
r.Expire = field.NewTime(table, "expire")
|
||||||
@@ -99,10 +96,9 @@ func (r *resourcePsr) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourcePsr) fillFieldMap() {
|
func (r *resourcePsr) fillFieldMap() {
|
||||||
r.fieldMap = make(map[string]field.Expr, 10)
|
r.fieldMap = make(map[string]field.Expr, 9)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["resource_id"] = r.ResourceID
|
r.fieldMap["resource_id"] = r.ResourceID
|
||||||
r.fieldMap["active"] = r.Active
|
|
||||||
r.fieldMap["live"] = r.Live
|
r.fieldMap["live"] = r.Live
|
||||||
r.fieldMap["conn"] = r.Conn
|
r.fieldMap["conn"] = r.Conn
|
||||||
r.fieldMap["expire"] = r.Expire
|
r.fieldMap["expire"] = r.Expire
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ func newResourcePss(db *gorm.DB, opts ...gen.DOOption) resourcePss {
|
|||||||
_resourcePss.ALL = field.NewAsterisk(tableName)
|
_resourcePss.ALL = field.NewAsterisk(tableName)
|
||||||
_resourcePss.ID = field.NewInt32(tableName, "id")
|
_resourcePss.ID = field.NewInt32(tableName, "id")
|
||||||
_resourcePss.ResourceID = field.NewInt32(tableName, "resource_id")
|
_resourcePss.ResourceID = field.NewInt32(tableName, "resource_id")
|
||||||
_resourcePss.Active = field.NewBool(tableName, "active")
|
|
||||||
_resourcePss.Type = field.NewInt32(tableName, "type")
|
_resourcePss.Type = field.NewInt32(tableName, "type")
|
||||||
_resourcePss.Live = field.NewInt32(tableName, "live")
|
_resourcePss.Live = field.NewInt32(tableName, "live")
|
||||||
_resourcePss.Quota = field.NewInt32(tableName, "quota")
|
_resourcePss.Quota = field.NewInt32(tableName, "quota")
|
||||||
_resourcePss.Used = field.NewInt32(tableName, "used")
|
_resourcePss.Used = field.NewInt32(tableName, "used")
|
||||||
_resourcePss.Expire = field.NewTime(tableName, "expire")
|
_resourcePss.Expire = field.NewTime(tableName, "expire")
|
||||||
_resourcePss.LimitDay = field.NewInt32(tableName, "limit_day")
|
_resourcePss.DailyLimit = field.NewInt32(tableName, "daily_limit")
|
||||||
_resourcePss.LastUsed = field.NewTime(tableName, "last_used")
|
_resourcePss.DailyUsed = field.NewInt32(tableName, "daily_used")
|
||||||
|
_resourcePss.DailyLast = field.NewTime(tableName, "daily_last")
|
||||||
_resourcePss.CreatedAt = field.NewTime(tableName, "created_at")
|
_resourcePss.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_resourcePss.UpdatedAt = field.NewTime(tableName, "updated_at")
|
_resourcePss.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
_resourcePss.DeletedAt = field.NewField(tableName, "deleted_at")
|
_resourcePss.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
@@ -52,14 +52,14 @@ type resourcePss struct {
|
|||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32 // ID
|
ID field.Int32 // ID
|
||||||
ResourceID field.Int32 // 套餐ID
|
ResourceID field.Int32 // 套餐ID
|
||||||
Active field.Bool // 是否启用
|
|
||||||
Type field.Int32 // 套餐类型:1-包时,2-包量
|
Type field.Int32 // 套餐类型:1-包时,2-包量
|
||||||
Live field.Int32 // 可用时长(秒)
|
Live field.Int32 // 可用时长(秒)
|
||||||
Quota field.Int32 // 配额数量
|
Quota field.Int32 // 配额数量
|
||||||
Used field.Int32 // 已用数量
|
Used field.Int32 // 已用数量
|
||||||
Expire field.Time // 过期时间
|
Expire field.Time // 过期时间
|
||||||
LimitDay field.Int32 // 每日限额
|
DailyLimit field.Int32 // 每日限制
|
||||||
LastUsed field.Time // 最后提取时间
|
DailyUsed field.Int32 // 今日已用数量
|
||||||
|
DailyLast field.Time // 今日最后使用时间
|
||||||
CreatedAt field.Time // 创建时间
|
CreatedAt field.Time // 创建时间
|
||||||
UpdatedAt field.Time // 更新时间
|
UpdatedAt field.Time // 更新时间
|
||||||
DeletedAt field.Field // 删除时间
|
DeletedAt field.Field // 删除时间
|
||||||
@@ -81,14 +81,14 @@ func (r *resourcePss) updateTableName(table string) *resourcePss {
|
|||||||
r.ALL = field.NewAsterisk(table)
|
r.ALL = field.NewAsterisk(table)
|
||||||
r.ID = field.NewInt32(table, "id")
|
r.ID = field.NewInt32(table, "id")
|
||||||
r.ResourceID = field.NewInt32(table, "resource_id")
|
r.ResourceID = field.NewInt32(table, "resource_id")
|
||||||
r.Active = field.NewBool(table, "active")
|
|
||||||
r.Type = field.NewInt32(table, "type")
|
r.Type = field.NewInt32(table, "type")
|
||||||
r.Live = field.NewInt32(table, "live")
|
r.Live = field.NewInt32(table, "live")
|
||||||
r.Quota = field.NewInt32(table, "quota")
|
r.Quota = field.NewInt32(table, "quota")
|
||||||
r.Used = field.NewInt32(table, "used")
|
r.Used = field.NewInt32(table, "used")
|
||||||
r.Expire = field.NewTime(table, "expire")
|
r.Expire = field.NewTime(table, "expire")
|
||||||
r.LimitDay = field.NewInt32(table, "limit_day")
|
r.DailyLimit = field.NewInt32(table, "daily_limit")
|
||||||
r.LastUsed = field.NewTime(table, "last_used")
|
r.DailyUsed = field.NewInt32(table, "daily_used")
|
||||||
|
r.DailyLast = field.NewTime(table, "daily_last")
|
||||||
r.CreatedAt = field.NewTime(table, "created_at")
|
r.CreatedAt = field.NewTime(table, "created_at")
|
||||||
r.UpdatedAt = field.NewTime(table, "updated_at")
|
r.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
r.DeletedAt = field.NewField(table, "deleted_at")
|
r.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
@@ -111,14 +111,14 @@ func (r *resourcePss) fillFieldMap() {
|
|||||||
r.fieldMap = make(map[string]field.Expr, 13)
|
r.fieldMap = make(map[string]field.Expr, 13)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["resource_id"] = r.ResourceID
|
r.fieldMap["resource_id"] = r.ResourceID
|
||||||
r.fieldMap["active"] = r.Active
|
|
||||||
r.fieldMap["type"] = r.Type
|
r.fieldMap["type"] = r.Type
|
||||||
r.fieldMap["live"] = r.Live
|
r.fieldMap["live"] = r.Live
|
||||||
r.fieldMap["quota"] = r.Quota
|
r.fieldMap["quota"] = r.Quota
|
||||||
r.fieldMap["used"] = r.Used
|
r.fieldMap["used"] = r.Used
|
||||||
r.fieldMap["expire"] = r.Expire
|
r.fieldMap["expire"] = r.Expire
|
||||||
r.fieldMap["limit_day"] = r.LimitDay
|
r.fieldMap["daily_limit"] = r.DailyLimit
|
||||||
r.fieldMap["last_used"] = r.LastUsed
|
r.fieldMap["daily_used"] = r.DailyUsed
|
||||||
|
r.fieldMap["daily_last"] = r.DailyLast
|
||||||
r.fieldMap["created_at"] = r.CreatedAt
|
r.fieldMap["created_at"] = r.CreatedAt
|
||||||
r.fieldMap["updated_at"] = r.UpdatedAt
|
r.fieldMap["updated_at"] = r.UpdatedAt
|
||||||
r.fieldMap["deleted_at"] = r.DeletedAt
|
r.fieldMap["deleted_at"] = r.DeletedAt
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ import (
|
|||||||
func ApplyRouters(app *fiber.App) {
|
func ApplyRouters(app *fiber.App) {
|
||||||
api := app.Group("/api")
|
api := app.Group("/api")
|
||||||
|
|
||||||
// 认证路由
|
// 认证
|
||||||
auth := api.Group("/auth")
|
auth := api.Group("/auth")
|
||||||
auth.Post("/verify/sms", Protect(), handlers.SmsCode)
|
auth.Post("/verify/sms", PermitUser(), handlers.SmsCode)
|
||||||
auth.Post("/login/sms", Protect(), handlers.Login)
|
auth.Post("/login/sms", PermitUser(), handlers.Login)
|
||||||
auth.Post("/token", handlers.Token)
|
auth.Post("/token", handlers.Token)
|
||||||
|
|
||||||
// 客户端路由
|
// 客户端
|
||||||
client := api.Group("/client")
|
client := api.Group("/client")
|
||||||
client.Get("/test/create", handlers.CreateClient)
|
client.Get("/test/create", handlers.CreateClient)
|
||||||
|
|
||||||
|
// 通道
|
||||||
|
channel := api.Group("/channel", PermitUser())
|
||||||
|
channel.Post("/create", handlers.CreateChannel)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"log/slog"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"platform/pkg/rds"
|
||||||
|
"platform/web/common"
|
||||||
"platform/web/models"
|
"platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/jxskiss/base62"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Channel = &channelService{}
|
var Channel = &channelService{}
|
||||||
@@ -12,47 +22,229 @@ var Channel = &channelService{}
|
|||||||
type channelService struct {
|
type channelService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChannel 创建连接通道,并返回连接信息,如果配额不足则返回错误
|
||||||
func (s *channelService) CreateChannel(
|
func (s *channelService) CreateChannel(
|
||||||
userID int32,
|
ctx context.Context,
|
||||||
region string,
|
auth *AuthContext,
|
||||||
provider string,
|
resourceId int32,
|
||||||
protocol Protocol,
|
protocol ChannelProtocol,
|
||||||
resourceId int,
|
authType ChannelAuthType,
|
||||||
count int,
|
count int,
|
||||||
|
nodeFilter ...NodeFilterConfig,
|
||||||
) ([]*models.Channel, error) {
|
) ([]*models.Channel, error) {
|
||||||
|
|
||||||
// 检查并扣减套餐余额
|
// 创建通道
|
||||||
var resourceInfo = struct {
|
var channels []*models.Channel
|
||||||
models.Resource
|
err := q.Q.Transaction(func(tx *q.Query) error {
|
||||||
models.ResourcePss
|
// 查找套餐
|
||||||
|
var resource = struct {
|
||||||
|
data models.Resource
|
||||||
|
pss models.ResourcePss
|
||||||
}{}
|
}{}
|
||||||
err := q.Resource.
|
err := q.Resource.As("data").
|
||||||
Where(q.Resource.UserID.Eq(userID)).
|
LeftJoin(q.ResourcePss.As("pss"), q.ResourcePss.ResourceID.EqCol(q.Resource.ID)).
|
||||||
LeftJoin(q.ResourcePss, q.ResourcePss.ResourceID.EqCol(q.Resource.ID)).
|
Where(q.Resource.ID.Eq(resourceId)).
|
||||||
Scan(&resourceInfo)
|
Scan(&resource)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return ChannelServiceErr("套餐不存在")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查使用人
|
||||||
|
if auth.Payload.Type == PayloadUser && auth.Payload.Id != resource.data.UserID {
|
||||||
|
return common.AuthForbiddenErr("无权限访问")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查套餐状态
|
||||||
|
if !resource.data.Active {
|
||||||
|
return ChannelServiceErr("套餐已失效")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查每日限额
|
||||||
|
today := time.Now().Format("2006-01-02") == resource.pss.DailyLast.Format("2006-01-02")
|
||||||
|
dailyRemain := int(math.Max(float64(resource.pss.DailyLimit-resource.pss.DailyUsed), 0))
|
||||||
|
if today && dailyRemain < count {
|
||||||
|
return ChannelServiceErr("套餐每日配额不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查时间或配额
|
||||||
|
if resource.pss.Type == 1 { // 包时
|
||||||
|
if resource.pss.Expire.Before(time.Now()) {
|
||||||
|
return ChannelServiceErr("套餐已过期")
|
||||||
|
}
|
||||||
|
} else { // 包量
|
||||||
|
remain := int(math.Max(float64(resource.pss.Quota-resource.pss.Used), 0))
|
||||||
|
if remain < count {
|
||||||
|
return ChannelServiceErr("套餐配额不足")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选可用节点
|
||||||
|
nodes, err := Node.Filter(auth.Payload.Id, protocol, count, nodeFilter...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户配置白名单
|
||||||
|
whitelist, err := q.Whitelist.Where(
|
||||||
|
q.Whitelist.UserID.Eq(auth.Payload.Id),
|
||||||
|
).Find()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建连接通道
|
||||||
|
channels = make([]*models.Channel, 0, len(nodes)*len(whitelist))
|
||||||
|
for _, node := range nodes {
|
||||||
|
for _, allowed := range whitelist {
|
||||||
|
username, password := genPassPair()
|
||||||
|
channels = append(channels, &models.Channel{
|
||||||
|
UserID: auth.Payload.Id,
|
||||||
|
NodeID: node.ID,
|
||||||
|
NodePort: node.FwdPort,
|
||||||
|
Protocol: string(protocol),
|
||||||
|
AuthIP: authType == ChannelAuthTypeIp,
|
||||||
|
UserHost: allowed.Host,
|
||||||
|
AuthPass: authType == ChannelAuthTypePass,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
Expiration: time.Now().Add(time.Duration(resource.pss.Live) * time.Second),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到数据库
|
||||||
|
err = tx.Channel.Create(channels...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新套餐使用记录
|
||||||
|
if today {
|
||||||
|
resource.pss.DailyUsed += int32(count)
|
||||||
|
resource.pss.Used += int32(count)
|
||||||
|
} else {
|
||||||
|
resource.pss.DailyLast = time.Now()
|
||||||
|
resource.pss.DailyUsed = int32(count)
|
||||||
|
resource.pss.Used += int32(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.ResourcePss.
|
||||||
|
Where(q.ResourcePss.ID.Eq(resource.pss.ID)).
|
||||||
|
Omit(q.ResourcePss.ResourceID).
|
||||||
|
Save(&resource.pss)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Debug("查询资源", slog.Any("info", resourceInfo))
|
// 缓存通道信息与异步删除任务
|
||||||
|
pipe := rds.Client.TxPipeline()
|
||||||
// 创建连接通道
|
zList := make([]redis.Z, 0, len(channels))
|
||||||
|
for _, channel := range channels {
|
||||||
// 保存到数据库与缓存,以及计时关闭
|
pipe.Set(ctx, chKey(channel), channel, channel.Expiration.Sub(time.Now()))
|
||||||
|
zList = append(zList, redis.Z{
|
||||||
// 组织请求数据
|
Score: float64(channel.Expiration.Unix()),
|
||||||
|
Member: channel.ID,
|
||||||
// 发送请求到远端配置服务
|
})
|
||||||
|
}
|
||||||
// 返回连接通道列表
|
pipe.ZAdd(ctx, "tasks:channel", zList...)
|
||||||
|
_, err = pipe.Exec(ctx)
|
||||||
return nil, errors.New("not implemented")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type Protocol string
|
// 返回连接通道列表
|
||||||
|
return channels, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelAuthType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProtocolSocks5 = Protocol("socks5")
|
ChannelAuthTypeIp = iota
|
||||||
ProtocolHTTP = Protocol("http")
|
ChannelAuthTypePass
|
||||||
ProtocolHttps = Protocol("https")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ChannelProtocol string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtocolSocks5 = ChannelProtocol("socks5")
|
||||||
|
ProtocolHTTP = ChannelProtocol("http")
|
||||||
|
ProtocolHttps = ChannelProtocol("https")
|
||||||
|
)
|
||||||
|
|
||||||
|
func genPassPair() (string, string) {
|
||||||
|
usernameBytes, err := uuid.New().MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
passwordBytes, err := uuid.New().MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
username := base62.EncodeToString(usernameBytes)
|
||||||
|
password := base62.EncodeToString(passwordBytes)
|
||||||
|
return username, password
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *channelService) RemoveChannels(auth *AuthContext, id ...int32) error {
|
||||||
|
|
||||||
|
var channels []*models.Channel
|
||||||
|
|
||||||
|
// 删除通道
|
||||||
|
err := q.Q.Transaction(func(tx *q.Query) error {
|
||||||
|
// 查找通道
|
||||||
|
channels, err := tx.Channel.Where(
|
||||||
|
q.Channel.ID.In(id...),
|
||||||
|
).Find()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查权限,只有用户自己和管理员能删除
|
||||||
|
for _, channel := range channels {
|
||||||
|
if auth.Payload.Type == PayloadUser && auth.Payload.Id != channel.UserID {
|
||||||
|
return common.AuthForbiddenErr("无权限访问")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定的通道
|
||||||
|
result, err := tx.Channel.Delete(channels...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if result.RowsAffected != int64(len(channels)) {
|
||||||
|
return ChannelServiceErr("删除通道失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除缓存,异步任务直接在消费端处理删除
|
||||||
|
pipe := rds.Client.TxPipeline()
|
||||||
|
for _, channel := range channels {
|
||||||
|
pipe.Del(context.Background(), chKey(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func chKey(channel *models.Channel) string {
|
||||||
|
return fmt.Sprintf("channel:%s:%d", channel.UserHost, channel.NodePort)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelServiceErr string
|
||||||
|
|
||||||
|
func (c ChannelServiceErr) Error() string {
|
||||||
|
return string(c)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_channelService_CreateChannel(t *testing.T) {
|
|
||||||
// type args struct {
|
|
||||||
// userID int32
|
|
||||||
// region string
|
|
||||||
// provider string
|
|
||||||
// protocol Protocol
|
|
||||||
// resourceId int
|
|
||||||
// count int
|
|
||||||
// }
|
|
||||||
// tests := []struct {
|
|
||||||
// name string
|
|
||||||
// args args
|
|
||||||
// want []*models.Channel
|
|
||||||
// wantErr bool
|
|
||||||
// }{
|
|
||||||
// // TODO: Add test cases.
|
|
||||||
// }
|
|
||||||
// for _, tt := range tests {
|
|
||||||
// t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// s := &channelService{}
|
|
||||||
// got, err := s.CreateChannel(tt.args.userID, tt.args.region, tt.args.provider, tt.args.protocol, tt.args.resourceId, tt.args.count)
|
|
||||||
// if (err != nil) != tt.wantErr {
|
|
||||||
// t.Errorf("CreateChannel() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
// t.Errorf("CreateChannel() got = %v, want %v", got, tt.want)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
18
web/services/node.go
Normal file
18
web/services/node.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import "platform/web/models"
|
||||||
|
|
||||||
|
var Node = &nodeService{}
|
||||||
|
|
||||||
|
type nodeService struct{}
|
||||||
|
|
||||||
|
func (s *nodeService) Filter(userId int32, proto ChannelProtocol, count int, config ...NodeFilterConfig) ([]*models.Node, error) {
|
||||||
|
|
||||||
|
return make([]*models.Node, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeFilterConfig struct {
|
||||||
|
province string
|
||||||
|
city string
|
||||||
|
provider string
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"platform/init/rds"
|
"platform/pkg/rds"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"platform/init/rds"
|
"platform/pkg/rds"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"platform/init/rds"
|
"platform/pkg/rds"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"platform/init/rds"
|
"platform/pkg/rds"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"platform/init/env"
|
"platform/pkg/env"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
|
|||||||
Reference in New Issue
Block a user