Spaces:
Running
Running
aurorax-neo
commited on
Commit
•
a4468f1
1
Parent(s):
d578104
init
Browse files- .gitignore +133 -0
- Dockerfile +33 -0
- README.md +1 -0
- app/app.go +57 -0
- app/http.go +44 -0
- app/kpl.go +92 -0
- cmd/main.go +11 -0
- conf/conf.go +89 -0
- go.mod +47 -0
- go.sum +126 -0
- internal/app/app.go +57 -0
- internal/app/http.go +44 -0
- internal/app/kpl.go +92 -0
- internal/conf/conf.go +89 -0
- pkg/logx/logx.go +262 -0
- pkg/logx/text_formater.go +114 -0
- pkg/mapx/mapx.go +203 -0
- pkg/mapx/mapx_test.go +43 -0
- pkg/pointer/pointer.go +32 -0
- pkg/tools/stack.go +29 -0
- pkg/tools/tools.go +49 -0
- release.bat +56 -0
.gitignore
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
### Node template
|
2 |
+
# Logs
|
3 |
+
logs
|
4 |
+
*.log
|
5 |
+
npm-debug.log*
|
6 |
+
yarn-debug.log*
|
7 |
+
yarn-error.log*
|
8 |
+
lerna-debug.log*
|
9 |
+
.pnpm-debug.log*
|
10 |
+
|
11 |
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
12 |
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
13 |
+
|
14 |
+
# Runtime data
|
15 |
+
pids
|
16 |
+
*.pid
|
17 |
+
*.seed
|
18 |
+
*.pid.lock
|
19 |
+
|
20 |
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
21 |
+
lib-cov
|
22 |
+
|
23 |
+
# Coverage directory used by tools like istanbul
|
24 |
+
coverage
|
25 |
+
*.lcov
|
26 |
+
|
27 |
+
# nyc test coverage
|
28 |
+
.nyc_output
|
29 |
+
|
30 |
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
31 |
+
.grunt
|
32 |
+
|
33 |
+
# Bower dependency directory (https://bower.io/)
|
34 |
+
bower_components
|
35 |
+
|
36 |
+
# node-waf configuration
|
37 |
+
.lock-wscript
|
38 |
+
|
39 |
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
40 |
+
build/Release
|
41 |
+
|
42 |
+
# Dependency directories
|
43 |
+
node_modules/
|
44 |
+
jspm_packages/
|
45 |
+
|
46 |
+
# Snowpack dependency directory (https://snowpack.dev/)
|
47 |
+
web_modules/
|
48 |
+
|
49 |
+
# TypeScript cache
|
50 |
+
*.tsbuildinfo
|
51 |
+
|
52 |
+
# Optional npm cache directory
|
53 |
+
.npm
|
54 |
+
|
55 |
+
# Optional eslint cache
|
56 |
+
.eslintcache
|
57 |
+
|
58 |
+
# Optional stylelint cache
|
59 |
+
.stylelintcache
|
60 |
+
|
61 |
+
# Microbundle cache
|
62 |
+
.rpt2_cache/
|
63 |
+
.rts2_cache_cjs/
|
64 |
+
.rts2_cache_es/
|
65 |
+
.rts2_cache_umd/
|
66 |
+
|
67 |
+
# Optional REPL history
|
68 |
+
.node_repl_history
|
69 |
+
|
70 |
+
# Output of 'npm pack'
|
71 |
+
*.tgz
|
72 |
+
|
73 |
+
# Yarn Integrity file
|
74 |
+
.yarn-integrity
|
75 |
+
|
76 |
+
# dotenv environment variable files
|
77 |
+
.env
|
78 |
+
.env.development.local
|
79 |
+
.env.test.local
|
80 |
+
.env.production.local
|
81 |
+
.env.local
|
82 |
+
|
83 |
+
# parcel-bundler cache (https://parceljs.org/)
|
84 |
+
.cache
|
85 |
+
.parcel-cache
|
86 |
+
|
87 |
+
# Next.js build output
|
88 |
+
.next
|
89 |
+
out
|
90 |
+
|
91 |
+
# Nuxt.js build / generate output
|
92 |
+
.nuxt
|
93 |
+
dist
|
94 |
+
|
95 |
+
# Gatsby files
|
96 |
+
.cache/
|
97 |
+
# Comment in the public line in if your project uses Gatsby and not Next.js
|
98 |
+
# https://nextjs.org/blog/next-9-1#public-directory-support
|
99 |
+
# public
|
100 |
+
|
101 |
+
# vuepress build output
|
102 |
+
.vuepress/dist
|
103 |
+
|
104 |
+
# vuepress v2.x temp and cache directory
|
105 |
+
.temp
|
106 |
+
.cache
|
107 |
+
|
108 |
+
# Docusaurus cache and generated files
|
109 |
+
.docusaurus
|
110 |
+
|
111 |
+
# Serverless directories
|
112 |
+
.serverless/
|
113 |
+
|
114 |
+
# FuseBox cache
|
115 |
+
.fusebox/
|
116 |
+
|
117 |
+
# DynamoDB Local files
|
118 |
+
.dynamodb/
|
119 |
+
|
120 |
+
# TernJS port file
|
121 |
+
.tern-port
|
122 |
+
|
123 |
+
# Stores VSCode versions used for testing VSCode extensions
|
124 |
+
.vscode-test
|
125 |
+
|
126 |
+
# yarn v2
|
127 |
+
.yarn/cache
|
128 |
+
.yarn/unplugged
|
129 |
+
.yarn/build-state.yml
|
130 |
+
.yarn/install-state.gz
|
131 |
+
.pnp.*
|
132 |
+
|
133 |
+
/config.json
|
Dockerfile
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Start with a base image containing Go runtime
|
2 |
+
FROM golang:1.22.0 AS builder
|
3 |
+
|
4 |
+
# Set the working directory inside the container
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy the go mod and sum files
|
8 |
+
COPY go.mod go.sum ./
|
9 |
+
|
10 |
+
# Download dependencies
|
11 |
+
ENV GOPROXY=https://goproxy.cn,direct
|
12 |
+
RUN go mod download
|
13 |
+
|
14 |
+
# Copy the rest of the application's source code
|
15 |
+
COPY . .
|
16 |
+
|
17 |
+
# Build the Go app ensuring that the binary is statically linked
|
18 |
+
RUN CGO_ENABLED=0 go build -o /app/f99871322b15ff5007f0 /app/cmd/main.go
|
19 |
+
|
20 |
+
# Now use a smaller image to run the app
|
21 |
+
FROM alpine:latest
|
22 |
+
|
23 |
+
# Set the working directory in the new container
|
24 |
+
WORKDIR /app
|
25 |
+
|
26 |
+
# Copy the statically-linked binary into the new container
|
27 |
+
COPY --from=builder /app/f99871322b15ff5007f0 /app/f99871322b15ff5007f0
|
28 |
+
|
29 |
+
# This container exposes port 3040 to the outside world
|
30 |
+
EXPOSE 3040
|
31 |
+
|
32 |
+
# Run the binary.
|
33 |
+
CMD [ "/app/f99871322b15ff5007f0" ]
|
README.md
CHANGED
@@ -6,6 +6,7 @@ colorTo: yellow
|
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
license: mit
|
|
|
9 |
---
|
10 |
|
11 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
license: mit
|
9 |
+
app_port: 3040
|
10 |
---
|
11 |
|
12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app/app.go
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package app
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"kpl/internal/conf"
|
6 |
+
"kpl/pkg/logx"
|
7 |
+
"kpl/pkg/tools"
|
8 |
+
"os"
|
9 |
+
"time"
|
10 |
+
)
|
11 |
+
|
12 |
+
func Run(ctx context.Context) {
|
13 |
+
cleanUp := tools.Stack{}
|
14 |
+
ctx = logx.TagContext(ctx, "initial")
|
15 |
+
logx.Init(ctx)
|
16 |
+
|
17 |
+
// config
|
18 |
+
conf.Init(ctx)
|
19 |
+
|
20 |
+
// http
|
21 |
+
cleanUp.Push(Start(ctx))
|
22 |
+
|
23 |
+
// 提示 保活列表
|
24 |
+
logx.WithContext(ctx).Info("hgUrls: ")
|
25 |
+
for i, url := range conf.CONF.HgUrls {
|
26 |
+
logx.WithContext(ctx).Info(i, ": ", url)
|
27 |
+
}
|
28 |
+
|
29 |
+
logx.WithContext(ctx).Info("serv00s: ")
|
30 |
+
for i, serv00 := range conf.CONF.Serv00s {
|
31 |
+
logx.WithContext(ctx).Info(i, ": ", serv00.Username, "@", serv00.Host, ":", serv00.Port)
|
32 |
+
}
|
33 |
+
|
34 |
+
AsyncTimingTask(time.Duration(conf.CONF.HgIntervalSec)*time.Second, func() {
|
35 |
+
for _, url := range conf.CONF.HgUrls {
|
36 |
+
go DoGetRequest(ctx, url, conf.CONF.Proxy)
|
37 |
+
}
|
38 |
+
})
|
39 |
+
|
40 |
+
AsyncTimingTask(time.Duration(conf.CONF.Serv00IntervalSec)*time.Second, func() {
|
41 |
+
for _, serv00 := range conf.CONF.Serv00s {
|
42 |
+
go KplServ00(ctx, serv00.Username, serv00.Password, serv00.Host, serv00.Port, serv00.Cmd)
|
43 |
+
}
|
44 |
+
})
|
45 |
+
|
46 |
+
// Handle signals
|
47 |
+
{
|
48 |
+
exitCode := 1
|
49 |
+
exitCode = tools.HandleSignals(exitCode)
|
50 |
+
ctx = logx.TagContext(ctx, "cleanup")
|
51 |
+
for cleanUp.Next() {
|
52 |
+
cleanUp.Pop()(ctx)
|
53 |
+
}
|
54 |
+
time.Sleep(time.Second)
|
55 |
+
os.Exit(exitCode)
|
56 |
+
}
|
57 |
+
}
|
app/http.go
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package app
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"errors"
|
6 |
+
"github.com/gin-gonic/gin"
|
7 |
+
"kpl/pkg/logx"
|
8 |
+
"net/http"
|
9 |
+
"time"
|
10 |
+
)
|
11 |
+
|
12 |
+
func Start(ctx context.Context) func(ctx context.Context) {
|
13 |
+
gin.SetMode(gin.ReleaseMode)
|
14 |
+
|
15 |
+
e := gin.New()
|
16 |
+
|
17 |
+
e.Use(gin.Recovery())
|
18 |
+
|
19 |
+
e.GET("/", func(ctx *gin.Context) {
|
20 |
+
ctx.String(http.StatusOK, "Hello World!")
|
21 |
+
})
|
22 |
+
|
23 |
+
srv := &http.Server{
|
24 |
+
Addr: ":3040",
|
25 |
+
Handler: e,
|
26 |
+
}
|
27 |
+
|
28 |
+
go func() {
|
29 |
+
logx.WithContext(ctx).Infof("http server initialized successfully at \u001B[35m%v\u001B[0m", srv.Addr)
|
30 |
+
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
31 |
+
panic(err)
|
32 |
+
}
|
33 |
+
}()
|
34 |
+
|
35 |
+
return func(ctx context.Context) {
|
36 |
+
logx.WithContext(ctx).Info("http server shutdown.")
|
37 |
+
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
|
38 |
+
srv.SetKeepAlivesEnabled(false)
|
39 |
+
if err := srv.Shutdown(ctx); err != nil {
|
40 |
+
logx.WithContext(ctx).Error(err.Error())
|
41 |
+
}
|
42 |
+
cancel()
|
43 |
+
}
|
44 |
+
}
|
app/kpl.go
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package app
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"fmt"
|
6 |
+
"github.com/aurorax-neo/tls_client_httpi"
|
7 |
+
"github.com/aurorax-neo/tls_client_httpi/tls_client"
|
8 |
+
"github.com/bogdanfinn/tls-client/profiles"
|
9 |
+
"golang.org/x/crypto/ssh"
|
10 |
+
"kpl/pkg/logx"
|
11 |
+
"time"
|
12 |
+
)
|
13 |
+
|
14 |
+
func AsyncTimingTask(nanosecond time.Duration, fun func()) {
|
15 |
+
go func() {
|
16 |
+
timerChan := time.After(nanosecond)
|
17 |
+
// 使用for循环阻塞等待定时器的信号
|
18 |
+
for {
|
19 |
+
// 通过select语句监听定时器通道和其他事件
|
20 |
+
select {
|
21 |
+
case <-timerChan:
|
22 |
+
fun()
|
23 |
+
// 重新设置定时器,以便下一次执行
|
24 |
+
timerChan = time.After(nanosecond)
|
25 |
+
}
|
26 |
+
time.Sleep(time.Millisecond * 100)
|
27 |
+
}
|
28 |
+
}()
|
29 |
+
}
|
30 |
+
|
31 |
+
// DoGetRequest 定义一个函数,用于发送GET请求
|
32 |
+
func DoGetRequest(ctx context.Context, rawUrl string, proxy string) {
|
33 |
+
opts := tls_client.NewClientOptions(5, profiles.Chrome_124)
|
34 |
+
client := tls_client.NewClient(opts)
|
35 |
+
_ = client.SetProxy(proxy)
|
36 |
+
res, err := client.Request(tls_client_httpi.GET, rawUrl, nil, nil, nil)
|
37 |
+
if err != nil {
|
38 |
+
logx.WithContext(ctx).Error(err)
|
39 |
+
return
|
40 |
+
}
|
41 |
+
logx.WithContext(ctx).Info(fmt.Sprint("GET ", rawUrl, " ", res.Status))
|
42 |
+
}
|
43 |
+
|
44 |
+
// KplServ00 serv00
|
45 |
+
func KplServ00(ctx context.Context, user string, password string, host string, port int, cmd string) {
|
46 |
+
// SSH 连接配置
|
47 |
+
sshConfig := &ssh.ClientConfig{
|
48 |
+
User: user,
|
49 |
+
Auth: []ssh.AuthMethod{
|
50 |
+
ssh.Password(password),
|
51 |
+
ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
52 |
+
answers = make([]string, len(questions))
|
53 |
+
for i := range questions {
|
54 |
+
answers[i] = password
|
55 |
+
}
|
56 |
+
return answers, nil
|
57 |
+
}),
|
58 |
+
},
|
59 |
+
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
60 |
+
}
|
61 |
+
|
62 |
+
// 连接到远程服务器
|
63 |
+
client, err := ssh.Dial("tcp", fmt.Sprint(host, ":", port), sshConfig)
|
64 |
+
if err != nil {
|
65 |
+
logx.WithContext(ctx).Error(err)
|
66 |
+
return
|
67 |
+
}
|
68 |
+
defer func(client *ssh.Client) {
|
69 |
+
_ = client.Close()
|
70 |
+
}(client)
|
71 |
+
|
72 |
+
// 创建一个会话
|
73 |
+
session1, err := client.NewSession()
|
74 |
+
if err != nil {
|
75 |
+
logx.WithContext(ctx).Error(err)
|
76 |
+
return
|
77 |
+
}
|
78 |
+
// 关闭会话
|
79 |
+
defer func(session *ssh.Session) {
|
80 |
+
_ = session.Close()
|
81 |
+
}(session1)
|
82 |
+
|
83 |
+
// 执行命令
|
84 |
+
output1, err := session1.CombinedOutput(cmd)
|
85 |
+
if err != nil {
|
86 |
+
errMsg := fmt.Sprintf("%s@%s:%d - 执行命令失败: \n%s", user, host, port, err)
|
87 |
+
logx.WithContext(ctx).Error(errMsg)
|
88 |
+
} else {
|
89 |
+
msg := fmt.Sprintf("%s@%s:%d - 执行命令成功: \n%s", user, host, port, output1)
|
90 |
+
logx.WithContext(ctx).Info(msg)
|
91 |
+
}
|
92 |
+
}
|
cmd/main.go
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package main
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"kpl/internal/app"
|
6 |
+
)
|
7 |
+
|
8 |
+
func main() {
|
9 |
+
ctx := context.Background()
|
10 |
+
app.Run(ctx)
|
11 |
+
}
|
conf/conf.go
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package conf
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"fmt"
|
6 |
+
"github.com/joho/godotenv"
|
7 |
+
"kpl/pkg/logx"
|
8 |
+
"os"
|
9 |
+
"strconv"
|
10 |
+
)
|
11 |
+
|
12 |
+
var CONF = &conf{}
|
13 |
+
|
14 |
+
type Serv00 struct {
|
15 |
+
Host string
|
16 |
+
Port int
|
17 |
+
Username string
|
18 |
+
Password string
|
19 |
+
Cmd string
|
20 |
+
}
|
21 |
+
|
22 |
+
type conf struct {
|
23 |
+
Proxy string
|
24 |
+
Serv00s []Serv00
|
25 |
+
Serv00IntervalSec int
|
26 |
+
HgUrls []string
|
27 |
+
HgIntervalSec int
|
28 |
+
}
|
29 |
+
|
30 |
+
func Init(ctx context.Context) func(context.Context) {
|
31 |
+
_ = godotenv.Load()
|
32 |
+
index := 1
|
33 |
+
for {
|
34 |
+
url := os.Getenv(fmt.Sprint("HG_URL", index))
|
35 |
+
if url == "" {
|
36 |
+
break
|
37 |
+
}
|
38 |
+
CONF.HgUrls = append(CONF.HgUrls, url)
|
39 |
+
index++
|
40 |
+
}
|
41 |
+
index = 1
|
42 |
+
for {
|
43 |
+
host := os.Getenv(fmt.Sprint("SERV00_HOST", index))
|
44 |
+
username := os.Getenv(fmt.Sprint("SERV00_USERNAME", index))
|
45 |
+
password := os.Getenv(fmt.Sprint("SERV00_PASSWORD", index))
|
46 |
+
cmd := os.Getenv(fmt.Sprint("SERV00_CMD", index))
|
47 |
+
if host == "" {
|
48 |
+
break
|
49 |
+
}
|
50 |
+
if username == "" || password == "" || cmd == "" {
|
51 |
+
continue
|
52 |
+
}
|
53 |
+
CONF.Serv00s = append(CONF.Serv00s, Serv00{
|
54 |
+
Host: host,
|
55 |
+
Port: 22,
|
56 |
+
Username: username,
|
57 |
+
Password: password,
|
58 |
+
Cmd: cmd,
|
59 |
+
})
|
60 |
+
index++
|
61 |
+
}
|
62 |
+
CONF.Proxy = os.Getenv("PROXY")
|
63 |
+
hgIntervalSec := os.Getenv("HG_INTERVAL_SEC")
|
64 |
+
if hgIntervalSec == "" {
|
65 |
+
CONF.HgIntervalSec = 8
|
66 |
+
} else {
|
67 |
+
parseInt, err := strconv.ParseInt(hgIntervalSec, 10, 64)
|
68 |
+
if err != nil {
|
69 |
+
CONF.HgIntervalSec = 8
|
70 |
+
} else {
|
71 |
+
CONF.HgIntervalSec = int(parseInt)
|
72 |
+
}
|
73 |
+
}
|
74 |
+
serv00IntervalSec := os.Getenv("SERV00_INTERVAL_SEC")
|
75 |
+
if serv00IntervalSec == "" {
|
76 |
+
CONF.Serv00IntervalSec = 300
|
77 |
+
} else {
|
78 |
+
parseInt, err := strconv.ParseInt(serv00IntervalSec, 10, 64)
|
79 |
+
if err != nil {
|
80 |
+
CONF.Serv00IntervalSec = 300
|
81 |
+
} else {
|
82 |
+
CONF.Serv00IntervalSec = int(parseInt)
|
83 |
+
}
|
84 |
+
}
|
85 |
+
logx.WithContext(ctx).Infof("config loaded successfully")
|
86 |
+
|
87 |
+
return func(ctx context.Context) {
|
88 |
+
}
|
89 |
+
}
|
go.mod
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module kpl
|
2 |
+
|
3 |
+
go 1.21
|
4 |
+
|
5 |
+
require (
|
6 |
+
github.com/aurorax-neo/tls_client_httpi v0.0.0-20240621113219-1c6faee7681b
|
7 |
+
github.com/bogdanfinn/tls-client v1.7.5
|
8 |
+
github.com/gin-gonic/gin v1.10.0
|
9 |
+
github.com/joho/godotenv v1.5.1
|
10 |
+
github.com/json-iterator/go v1.1.12
|
11 |
+
github.com/sirupsen/logrus v1.9.3
|
12 |
+
golang.org/x/crypto v0.24.0
|
13 |
+
)
|
14 |
+
|
15 |
+
require (
|
16 |
+
github.com/andybalholm/brotli v1.1.0 // indirect
|
17 |
+
github.com/bogdanfinn/fhttp v0.5.28 // indirect
|
18 |
+
github.com/bogdanfinn/utls v1.6.1 // indirect
|
19 |
+
github.com/bytedance/sonic v1.11.6 // indirect
|
20 |
+
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
21 |
+
github.com/cloudflare/circl v1.3.9 // indirect
|
22 |
+
github.com/cloudwego/base64x v0.1.4 // indirect
|
23 |
+
github.com/cloudwego/iasm v0.2.0 // indirect
|
24 |
+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
25 |
+
github.com/gin-contrib/sse v0.1.0 // indirect
|
26 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
27 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
28 |
+
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
29 |
+
github.com/goccy/go-json v0.10.2 // indirect
|
30 |
+
github.com/klauspost/compress v1.17.9 // indirect
|
31 |
+
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
32 |
+
github.com/leodido/go-urn v1.4.0 // indirect
|
33 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
34 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
35 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
36 |
+
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
37 |
+
github.com/quic-go/quic-go v0.45.0 // indirect
|
38 |
+
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
|
39 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
40 |
+
github.com/ugorji/go/codec v1.2.12 // indirect
|
41 |
+
golang.org/x/arch v0.8.0 // indirect
|
42 |
+
golang.org/x/net v0.25.0 // indirect
|
43 |
+
golang.org/x/sys v0.21.0 // indirect
|
44 |
+
golang.org/x/text v0.16.0 // indirect
|
45 |
+
google.golang.org/protobuf v1.34.1 // indirect
|
46 |
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
47 |
+
)
|
go.sum
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
2 |
+
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
3 |
+
github.com/aurorax-neo/tls_client_httpi v0.0.0-20240621113219-1c6faee7681b h1:PjtJ9Ljd2W6LRXQCZxzH47gxMVSBaOnrPbeBvTsib1g=
|
4 |
+
github.com/aurorax-neo/tls_client_httpi v0.0.0-20240621113219-1c6faee7681b/go.mod h1:IeKatJKJZ9jR4HO/rb3/OH6XpsOBBOFlw+zbxhGphPk=
|
5 |
+
github.com/bogdanfinn/fhttp v0.5.28 h1:G6thT8s8v6z1IuvXMUsX9QKy3ZHseTQTzxuIhSiaaAw=
|
6 |
+
github.com/bogdanfinn/fhttp v0.5.28/go.mod h1:oJiYPG3jQTKzk/VFmogH8jxjH5yiv2rrOH48Xso2lrE=
|
7 |
+
github.com/bogdanfinn/tls-client v1.7.5 h1:R1aTwe5oja5niLnQggzbWnzJEssw9n+3O4kR0H/Tjl4=
|
8 |
+
github.com/bogdanfinn/tls-client v1.7.5/go.mod h1:pQwF0eqfL0gf0mu8hikvu6deZ3ijSPruJDzEKEnnXjU=
|
9 |
+
github.com/bogdanfinn/utls v1.6.1 h1:dKDYAcXEyFFJ3GaWaN89DEyjyRraD1qb4osdEK89ass=
|
10 |
+
github.com/bogdanfinn/utls v1.6.1/go.mod h1:VXIbRZaiY/wHZc6Hu+DZ4O2CgTzjhjCg/Ou3V4r/39Y=
|
11 |
+
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
12 |
+
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
13 |
+
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
14 |
+
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
15 |
+
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
16 |
+
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
17 |
+
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
18 |
+
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
19 |
+
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
20 |
+
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
21 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
22 |
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
23 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
24 |
+
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
25 |
+
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
26 |
+
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
27 |
+
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
28 |
+
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
29 |
+
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
30 |
+
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
31 |
+
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
32 |
+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
33 |
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
34 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
35 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
36 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
37 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
38 |
+
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
39 |
+
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
40 |
+
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
41 |
+
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
42 |
+
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
43 |
+
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
44 |
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
45 |
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
46 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
47 |
+
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
48 |
+
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
49 |
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
50 |
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
51 |
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
52 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
53 |
+
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
54 |
+
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
55 |
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
56 |
+
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
57 |
+
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
58 |
+
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
59 |
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
60 |
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
61 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
62 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
63 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
64 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
65 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
66 |
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
67 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
68 |
+
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
69 |
+
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
70 |
+
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
71 |
+
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
72 |
+
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
73 |
+
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
74 |
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
75 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
76 |
+
github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
|
77 |
+
github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
78 |
+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
79 |
+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
80 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
81 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
82 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
83 |
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
84 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
85 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
86 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
87 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
88 |
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
89 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
90 |
+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
91 |
+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
92 |
+
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc=
|
93 |
+
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng=
|
94 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
95 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
96 |
+
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
97 |
+
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
98 |
+
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
99 |
+
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
100 |
+
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
101 |
+
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
102 |
+
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
103 |
+
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
104 |
+
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
105 |
+
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
106 |
+
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
107 |
+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
108 |
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
109 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
110 |
+
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
111 |
+
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
112 |
+
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
113 |
+
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
114 |
+
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
115 |
+
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
116 |
+
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
117 |
+
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
118 |
+
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
119 |
+
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
120 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
121 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
122 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
123 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
124 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
125 |
+
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
126 |
+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
internal/app/app.go
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package app
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"kpl/internal/conf"
|
6 |
+
"kpl/pkg/logx"
|
7 |
+
"kpl/pkg/tools"
|
8 |
+
"os"
|
9 |
+
"time"
|
10 |
+
)
|
11 |
+
|
12 |
+
func Run(ctx context.Context) {
|
13 |
+
cleanUp := tools.Stack{}
|
14 |
+
ctx = logx.TagContext(ctx, "initial")
|
15 |
+
logx.Init(ctx)
|
16 |
+
|
17 |
+
// config
|
18 |
+
conf.Init(ctx)
|
19 |
+
|
20 |
+
// http
|
21 |
+
cleanUp.Push(Start(ctx))
|
22 |
+
|
23 |
+
// 提示 保活列表
|
24 |
+
logx.WithContext(ctx).Info("hgUrls: ")
|
25 |
+
for i, url := range conf.CONF.HgUrls {
|
26 |
+
logx.WithContext(ctx).Info(i, ": ", url)
|
27 |
+
}
|
28 |
+
|
29 |
+
logx.WithContext(ctx).Info("serv00s: ")
|
30 |
+
for i, serv00 := range conf.CONF.Serv00s {
|
31 |
+
logx.WithContext(ctx).Info(i, ": ", serv00.Username, "@", serv00.Host, ":", serv00.Port)
|
32 |
+
}
|
33 |
+
|
34 |
+
AsyncTimingTask(time.Duration(conf.CONF.HgIntervalSec)*time.Second, func() {
|
35 |
+
for _, url := range conf.CONF.HgUrls {
|
36 |
+
go DoGetRequest(ctx, url, conf.CONF.Proxy)
|
37 |
+
}
|
38 |
+
})
|
39 |
+
|
40 |
+
AsyncTimingTask(time.Duration(conf.CONF.Serv00IntervalSec)*time.Second, func() {
|
41 |
+
for _, serv00 := range conf.CONF.Serv00s {
|
42 |
+
go KplServ00(ctx, serv00.Username, serv00.Password, serv00.Host, serv00.Port, serv00.Cmd)
|
43 |
+
}
|
44 |
+
})
|
45 |
+
|
46 |
+
// Handle signals
|
47 |
+
{
|
48 |
+
exitCode := 1
|
49 |
+
exitCode = tools.HandleSignals(exitCode)
|
50 |
+
ctx = logx.TagContext(ctx, "cleanup")
|
51 |
+
for cleanUp.Next() {
|
52 |
+
cleanUp.Pop()(ctx)
|
53 |
+
}
|
54 |
+
time.Sleep(time.Second)
|
55 |
+
os.Exit(exitCode)
|
56 |
+
}
|
57 |
+
}
|
internal/app/http.go
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package app
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"errors"
|
6 |
+
"github.com/gin-gonic/gin"
|
7 |
+
"kpl/pkg/logx"
|
8 |
+
"net/http"
|
9 |
+
"time"
|
10 |
+
)
|
11 |
+
|
12 |
+
func Start(ctx context.Context) func(ctx context.Context) {
|
13 |
+
gin.SetMode(gin.ReleaseMode)
|
14 |
+
|
15 |
+
e := gin.New()
|
16 |
+
|
17 |
+
e.Use(gin.Recovery())
|
18 |
+
|
19 |
+
e.GET("/", func(ctx *gin.Context) {
|
20 |
+
ctx.String(http.StatusOK, "Hello World!")
|
21 |
+
})
|
22 |
+
|
23 |
+
srv := &http.Server{
|
24 |
+
Addr: ":3040",
|
25 |
+
Handler: e,
|
26 |
+
}
|
27 |
+
|
28 |
+
go func() {
|
29 |
+
logx.WithContext(ctx).Infof("http server initialized successfully at \u001B[35m%v\u001B[0m", srv.Addr)
|
30 |
+
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
31 |
+
panic(err)
|
32 |
+
}
|
33 |
+
}()
|
34 |
+
|
35 |
+
return func(ctx context.Context) {
|
36 |
+
logx.WithContext(ctx).Info("http server shutdown.")
|
37 |
+
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
|
38 |
+
srv.SetKeepAlivesEnabled(false)
|
39 |
+
if err := srv.Shutdown(ctx); err != nil {
|
40 |
+
logx.WithContext(ctx).Error(err.Error())
|
41 |
+
}
|
42 |
+
cancel()
|
43 |
+
}
|
44 |
+
}
|
internal/app/kpl.go
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package app
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"fmt"
|
6 |
+
"github.com/aurorax-neo/tls_client_httpi"
|
7 |
+
"github.com/aurorax-neo/tls_client_httpi/tls_client"
|
8 |
+
"github.com/bogdanfinn/tls-client/profiles"
|
9 |
+
"golang.org/x/crypto/ssh"
|
10 |
+
"kpl/pkg/logx"
|
11 |
+
"time"
|
12 |
+
)
|
13 |
+
|
14 |
+
func AsyncTimingTask(nanosecond time.Duration, fun func()) {
|
15 |
+
go func() {
|
16 |
+
timerChan := time.After(nanosecond)
|
17 |
+
// 使用for循环阻塞等待定时器的信号
|
18 |
+
for {
|
19 |
+
// 通过select语句监听定时器通道和其他事件
|
20 |
+
select {
|
21 |
+
case <-timerChan:
|
22 |
+
fun()
|
23 |
+
// 重新设置定时器,以便下一次执行
|
24 |
+
timerChan = time.After(nanosecond)
|
25 |
+
}
|
26 |
+
time.Sleep(time.Millisecond * 100)
|
27 |
+
}
|
28 |
+
}()
|
29 |
+
}
|
30 |
+
|
31 |
+
// DoGetRequest 定义一个函数,用于发送GET请求
|
32 |
+
func DoGetRequest(ctx context.Context, rawUrl string, proxy string) {
|
33 |
+
opts := tls_client.NewClientOptions(5, profiles.Chrome_124)
|
34 |
+
client := tls_client.NewClient(opts)
|
35 |
+
_ = client.SetProxy(proxy)
|
36 |
+
res, err := client.Request(tls_client_httpi.GET, rawUrl, nil, nil, nil)
|
37 |
+
if err != nil {
|
38 |
+
logx.WithContext(ctx).Error(err)
|
39 |
+
return
|
40 |
+
}
|
41 |
+
logx.WithContext(ctx).Info(fmt.Sprint("GET ", rawUrl, " ", res.Status))
|
42 |
+
}
|
43 |
+
|
44 |
+
// KplServ00 serv00
|
45 |
+
func KplServ00(ctx context.Context, user string, password string, host string, port int, cmd string) {
|
46 |
+
// SSH 连接配置
|
47 |
+
sshConfig := &ssh.ClientConfig{
|
48 |
+
User: user,
|
49 |
+
Auth: []ssh.AuthMethod{
|
50 |
+
ssh.Password(password),
|
51 |
+
ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
52 |
+
answers = make([]string, len(questions))
|
53 |
+
for i := range questions {
|
54 |
+
answers[i] = password
|
55 |
+
}
|
56 |
+
return answers, nil
|
57 |
+
}),
|
58 |
+
},
|
59 |
+
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
60 |
+
}
|
61 |
+
|
62 |
+
// 连接到远程服务器
|
63 |
+
client, err := ssh.Dial("tcp", fmt.Sprint(host, ":", port), sshConfig)
|
64 |
+
if err != nil {
|
65 |
+
logx.WithContext(ctx).Error(err)
|
66 |
+
return
|
67 |
+
}
|
68 |
+
defer func(client *ssh.Client) {
|
69 |
+
_ = client.Close()
|
70 |
+
}(client)
|
71 |
+
|
72 |
+
// 创建一个会话
|
73 |
+
session1, err := client.NewSession()
|
74 |
+
if err != nil {
|
75 |
+
logx.WithContext(ctx).Error(err)
|
76 |
+
return
|
77 |
+
}
|
78 |
+
// 关闭会话
|
79 |
+
defer func(session *ssh.Session) {
|
80 |
+
_ = session.Close()
|
81 |
+
}(session1)
|
82 |
+
|
83 |
+
// 执行命令
|
84 |
+
output1, err := session1.CombinedOutput(cmd)
|
85 |
+
if err != nil {
|
86 |
+
errMsg := fmt.Sprintf("%s@%s:%d - 执行命令失败: \n%s", user, host, port, err)
|
87 |
+
logx.WithContext(ctx).Error(errMsg)
|
88 |
+
} else {
|
89 |
+
msg := fmt.Sprintf("%s@%s:%d - 执行命令成功: \n%s", user, host, port, output1)
|
90 |
+
logx.WithContext(ctx).Info(msg)
|
91 |
+
}
|
92 |
+
}
|
internal/conf/conf.go
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package conf
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"fmt"
|
6 |
+
"github.com/joho/godotenv"
|
7 |
+
"kpl/pkg/logx"
|
8 |
+
"os"
|
9 |
+
"strconv"
|
10 |
+
)
|
11 |
+
|
12 |
+
var CONF = &conf{}
|
13 |
+
|
14 |
+
type Serv00 struct {
|
15 |
+
Host string
|
16 |
+
Port int
|
17 |
+
Username string
|
18 |
+
Password string
|
19 |
+
Cmd string
|
20 |
+
}
|
21 |
+
|
22 |
+
type conf struct {
|
23 |
+
Proxy string
|
24 |
+
Serv00s []Serv00
|
25 |
+
Serv00IntervalSec int
|
26 |
+
HgUrls []string
|
27 |
+
HgIntervalSec int
|
28 |
+
}
|
29 |
+
|
30 |
+
func Init(ctx context.Context) func(context.Context) {
|
31 |
+
_ = godotenv.Load()
|
32 |
+
index := 1
|
33 |
+
for {
|
34 |
+
url := os.Getenv(fmt.Sprint("HG_URL", index))
|
35 |
+
if url == "" {
|
36 |
+
break
|
37 |
+
}
|
38 |
+
CONF.HgUrls = append(CONF.HgUrls, url)
|
39 |
+
index++
|
40 |
+
}
|
41 |
+
index = 1
|
42 |
+
for {
|
43 |
+
host := os.Getenv(fmt.Sprint("SERV00_HOST", index))
|
44 |
+
username := os.Getenv(fmt.Sprint("SERV00_USERNAME", index))
|
45 |
+
password := os.Getenv(fmt.Sprint("SERV00_PASSWORD", index))
|
46 |
+
cmd := os.Getenv(fmt.Sprint("SERV00_CMD", index))
|
47 |
+
if host == "" {
|
48 |
+
break
|
49 |
+
}
|
50 |
+
if username == "" || password == "" || cmd == "" {
|
51 |
+
continue
|
52 |
+
}
|
53 |
+
CONF.Serv00s = append(CONF.Serv00s, Serv00{
|
54 |
+
Host: host,
|
55 |
+
Port: 22,
|
56 |
+
Username: username,
|
57 |
+
Password: password,
|
58 |
+
Cmd: cmd,
|
59 |
+
})
|
60 |
+
index++
|
61 |
+
}
|
62 |
+
CONF.Proxy = os.Getenv("PROXY")
|
63 |
+
hgIntervalSec := os.Getenv("HG_INTERVAL_SEC")
|
64 |
+
if hgIntervalSec == "" {
|
65 |
+
CONF.HgIntervalSec = 8
|
66 |
+
} else {
|
67 |
+
parseInt, err := strconv.ParseInt(hgIntervalSec, 10, 64)
|
68 |
+
if err != nil {
|
69 |
+
CONF.HgIntervalSec = 8
|
70 |
+
} else {
|
71 |
+
CONF.HgIntervalSec = int(parseInt)
|
72 |
+
}
|
73 |
+
}
|
74 |
+
serv00IntervalSec := os.Getenv("SERV00_INTERVAL_SEC")
|
75 |
+
if serv00IntervalSec == "" {
|
76 |
+
CONF.Serv00IntervalSec = 300
|
77 |
+
} else {
|
78 |
+
parseInt, err := strconv.ParseInt(serv00IntervalSec, 10, 64)
|
79 |
+
if err != nil {
|
80 |
+
CONF.Serv00IntervalSec = 300
|
81 |
+
} else {
|
82 |
+
CONF.Serv00IntervalSec = int(parseInt)
|
83 |
+
}
|
84 |
+
}
|
85 |
+
logx.WithContext(ctx).Infof("config loaded successfully")
|
86 |
+
|
87 |
+
return func(ctx context.Context) {
|
88 |
+
}
|
89 |
+
}
|
pkg/logx/logx.go
ADDED
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package logx
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"encoding/json"
|
6 |
+
"fmt"
|
7 |
+
"github.com/sirupsen/logrus"
|
8 |
+
"os"
|
9 |
+
)
|
10 |
+
|
11 |
+
type (
|
12 |
+
Logger = logrus.Logger
|
13 |
+
Entry = logrus.Entry
|
14 |
+
Hook = logrus.Hook
|
15 |
+
Level = logrus.Level
|
16 |
+
)
|
17 |
+
|
18 |
+
func Init(ctx context.Context) {
|
19 |
+
SetLevel(logrus.DebugLevel)
|
20 |
+
SetFormatter(func() logrus.Formatter {
|
21 |
+
return &TextFormatter{}
|
22 |
+
}())
|
23 |
+
WithContext(ctx).Infof("application process in PID: %v", os.Getpid())
|
24 |
+
}
|
25 |
+
|
26 |
+
func SetLevel(level Level) {
|
27 |
+
logrus.SetLevel(level)
|
28 |
+
}
|
29 |
+
|
30 |
+
func SetFormatter(format logrus.Formatter) {
|
31 |
+
logrus.SetFormatter(format)
|
32 |
+
}
|
33 |
+
|
34 |
+
//
|
35 |
+
//func SetFormatter(format string) {
|
36 |
+
// switch format {
|
37 |
+
// case "json":
|
38 |
+
// logrus.SetFormatter(&logrus.JSONFormatter{})
|
39 |
+
// case "text":
|
40 |
+
// logrus.SetFormatter(&TextFormatter{})
|
41 |
+
// default:
|
42 |
+
// logrus.SetFormatter(&logrus.TextFormatter{})
|
43 |
+
// }
|
44 |
+
//}
|
45 |
+
|
46 |
+
func AddHook(hook Hook) {
|
47 |
+
logrus.AddHook(hook)
|
48 |
+
}
|
49 |
+
|
50 |
+
const (
|
51 |
+
TraceIdKey = "trace"
|
52 |
+
UserIDKey = "uid"
|
53 |
+
GuuIDKey = "guid"
|
54 |
+
SchoolIDKey = "school"
|
55 |
+
UsernameKey = "username"
|
56 |
+
TagKey = "tag"
|
57 |
+
StackKey = "stack"
|
58 |
+
)
|
59 |
+
|
60 |
+
type (
|
61 |
+
traceIdKey struct{}
|
62 |
+
userIDKey struct{}
|
63 |
+
guuIDKey struct{}
|
64 |
+
usernameKey struct{}
|
65 |
+
schoolIdKey struct{}
|
66 |
+
tagKey struct{}
|
67 |
+
stackKey struct{}
|
68 |
+
requestKey struct{}
|
69 |
+
responseKey struct{}
|
70 |
+
diff1Key struct{}
|
71 |
+
diff2Key struct{}
|
72 |
+
actionKey struct{}
|
73 |
+
)
|
74 |
+
|
75 |
+
func TraceIdContext(ctx context.Context, traceId string) context.Context {
|
76 |
+
return context.WithValue(ctx, traceIdKey{}, traceId)
|
77 |
+
}
|
78 |
+
|
79 |
+
func FromTraceIdContext(ctx context.Context) string {
|
80 |
+
if v := ctx.Value(traceIdKey{}); v != nil {
|
81 |
+
if s, ok := v.(string); ok {
|
82 |
+
return s
|
83 |
+
}
|
84 |
+
}
|
85 |
+
return ""
|
86 |
+
}
|
87 |
+
|
88 |
+
func UserIDContext(ctx context.Context, userId int) context.Context {
|
89 |
+
return context.WithValue(ctx, userIDKey{}, userId)
|
90 |
+
}
|
91 |
+
|
92 |
+
func FromUserIDContext(ctx context.Context) int {
|
93 |
+
if v := ctx.Value(userIDKey{}); v != nil {
|
94 |
+
if s, ok := v.(int); ok {
|
95 |
+
return s
|
96 |
+
}
|
97 |
+
}
|
98 |
+
return 0
|
99 |
+
}
|
100 |
+
|
101 |
+
func GuuIDContext(ctx context.Context, userId string) context.Context {
|
102 |
+
return context.WithValue(ctx, guuIDKey{}, userId)
|
103 |
+
}
|
104 |
+
|
105 |
+
func FromGuuIDContext(ctx context.Context) string {
|
106 |
+
if v := ctx.Value(guuIDKey{}); v != nil {
|
107 |
+
if s, ok := v.(string); ok {
|
108 |
+
return s
|
109 |
+
}
|
110 |
+
}
|
111 |
+
return ""
|
112 |
+
}
|
113 |
+
|
114 |
+
func SchoolIDContext(ctx context.Context, userId int) context.Context {
|
115 |
+
return context.WithValue(ctx, schoolIdKey{}, userId)
|
116 |
+
}
|
117 |
+
|
118 |
+
func FromSchoolIDContext(ctx context.Context) int {
|
119 |
+
if v := ctx.Value(schoolIdKey{}); v != nil {
|
120 |
+
if s, ok := v.(int); ok {
|
121 |
+
return s
|
122 |
+
}
|
123 |
+
}
|
124 |
+
return 0
|
125 |
+
}
|
126 |
+
|
127 |
+
func UsernameContext(ctx context.Context, username string) context.Context {
|
128 |
+
return context.WithValue(ctx, usernameKey{}, username)
|
129 |
+
}
|
130 |
+
|
131 |
+
func FromUsernameContext(ctx context.Context) string {
|
132 |
+
if v := ctx.Value(usernameKey{}); v != nil {
|
133 |
+
if s, ok := v.(string); ok {
|
134 |
+
return s
|
135 |
+
}
|
136 |
+
}
|
137 |
+
return ""
|
138 |
+
}
|
139 |
+
|
140 |
+
func TagContext(ctx context.Context, tag string) context.Context {
|
141 |
+
return context.WithValue(ctx, tagKey{}, tag)
|
142 |
+
}
|
143 |
+
|
144 |
+
func FromTagContext(ctx context.Context) string {
|
145 |
+
if v := ctx.Value(tagKey{}); v != nil {
|
146 |
+
if s, ok := v.(string); ok {
|
147 |
+
return s
|
148 |
+
}
|
149 |
+
}
|
150 |
+
return ""
|
151 |
+
}
|
152 |
+
|
153 |
+
func ActionContext(ctx context.Context, action string) context.Context {
|
154 |
+
return context.WithValue(ctx, actionKey{}, action)
|
155 |
+
}
|
156 |
+
|
157 |
+
func FromActionContext(ctx context.Context) string {
|
158 |
+
if v := ctx.Value(actionKey{}); v != nil {
|
159 |
+
if s, ok := v.(string); ok {
|
160 |
+
return s
|
161 |
+
}
|
162 |
+
}
|
163 |
+
return ""
|
164 |
+
}
|
165 |
+
|
166 |
+
func StackContext(ctx context.Context, stack error) context.Context {
|
167 |
+
return context.WithValue(ctx, stackKey{}, stack)
|
168 |
+
}
|
169 |
+
|
170 |
+
func FromStackContext(ctx context.Context) error {
|
171 |
+
if v := ctx.Value(tagKey{}); v != nil {
|
172 |
+
if e, ok := v.(error); ok {
|
173 |
+
return e
|
174 |
+
}
|
175 |
+
}
|
176 |
+
return nil
|
177 |
+
}
|
178 |
+
|
179 |
+
func RequestContext(ctx context.Context, data []byte) context.Context {
|
180 |
+
return context.WithValue(ctx, requestKey{}, string(data))
|
181 |
+
}
|
182 |
+
|
183 |
+
func FromRequestContext(ctx context.Context) string {
|
184 |
+
if v := ctx.Value(requestKey{}); v != "" {
|
185 |
+
if e, ok := v.(string); ok {
|
186 |
+
return e
|
187 |
+
}
|
188 |
+
}
|
189 |
+
return ""
|
190 |
+
}
|
191 |
+
|
192 |
+
func ResponseContext(ctx context.Context, data any) context.Context {
|
193 |
+
bts, _ := json.Marshal(&data)
|
194 |
+
return context.WithValue(ctx, responseKey{}, string(bts))
|
195 |
+
}
|
196 |
+
|
197 |
+
func FromResponseContext(ctx context.Context) string {
|
198 |
+
if v := ctx.Value(responseKey{}); v != "" {
|
199 |
+
if e, ok := v.(string); ok {
|
200 |
+
return e
|
201 |
+
}
|
202 |
+
}
|
203 |
+
return ""
|
204 |
+
}
|
205 |
+
|
206 |
+
func DiffContext(ctx context.Context, data1, data2 any) context.Context {
|
207 |
+
ctx = context.WithValue(ctx, diff1Key{}, data1)
|
208 |
+
ctx = context.WithValue(ctx, diff2Key{}, data2)
|
209 |
+
return ctx
|
210 |
+
}
|
211 |
+
|
212 |
+
func FromDiffContext(ctx context.Context) (any, any) {
|
213 |
+
return ctx.Value(diff1Key{}), ctx.Value(diff2Key{})
|
214 |
+
}
|
215 |
+
|
216 |
+
func WithContext(ctx context.Context) *Entry {
|
217 |
+
fields := logrus.Fields{}
|
218 |
+
if v := FromTraceIdContext(ctx); v != "" {
|
219 |
+
fields[TraceIdKey] = v
|
220 |
+
}
|
221 |
+
|
222 |
+
if v := FromUserIDContext(ctx); v != 0 {
|
223 |
+
fields[UserIDKey] = v
|
224 |
+
}
|
225 |
+
|
226 |
+
if v := FromGuuIDContext(ctx); v != "" {
|
227 |
+
fields[GuuIDKey] = v
|
228 |
+
}
|
229 |
+
|
230 |
+
if v := FromSchoolIDContext(ctx); v != 0 {
|
231 |
+
fields[SchoolIDKey] = v
|
232 |
+
}
|
233 |
+
|
234 |
+
if v := FromUsernameContext(ctx); v != "" {
|
235 |
+
fields[UsernameKey] = v
|
236 |
+
}
|
237 |
+
|
238 |
+
if v := FromTagContext(ctx); v != "" {
|
239 |
+
fields[TagKey] = v
|
240 |
+
}
|
241 |
+
|
242 |
+
if v := FromStackContext(ctx); v != nil {
|
243 |
+
fields[StackKey] = fmt.Sprintf("%+v", v)
|
244 |
+
}
|
245 |
+
|
246 |
+
return logrus.WithContext(ctx).WithFields(fields)
|
247 |
+
}
|
248 |
+
|
249 |
+
var (
|
250 |
+
Tracef = logrus.Tracef
|
251 |
+
Debugf = logrus.Debugf
|
252 |
+
Infof = logrus.Infof
|
253 |
+
Warnf = logrus.Warnf
|
254 |
+
Errorf = logrus.Errorf
|
255 |
+
Fatalf = logrus.Fatalf
|
256 |
+
Panicf = logrus.Panicf
|
257 |
+
Printf = logrus.Printf
|
258 |
+
SetOutput = logrus.SetOutput
|
259 |
+
SetReportCaller = logrus.SetReportCaller
|
260 |
+
StandardLogger = logrus.StandardLogger
|
261 |
+
ParseLevel = logrus.ParseLevel
|
262 |
+
)
|
pkg/logx/text_formater.go
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package logx
|
2 |
+
|
3 |
+
import (
|
4 |
+
"bytes"
|
5 |
+
"encoding/json"
|
6 |
+
"fmt"
|
7 |
+
"github.com/sirupsen/logrus"
|
8 |
+
"strings"
|
9 |
+
)
|
10 |
+
|
11 |
+
type TextFormatter struct {
|
12 |
+
}
|
13 |
+
|
14 |
+
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
15 |
+
//前景色 背景色
|
16 |
+
// 30 40 黑色
|
17 |
+
// 31 41 红色
|
18 |
+
// 32 42 绿色
|
19 |
+
// 33 43 黄色
|
20 |
+
// 34 44 蓝色
|
21 |
+
// 35 45 紫色
|
22 |
+
// 36 46 青色
|
23 |
+
// 37 47 白色
|
24 |
+
var color int
|
25 |
+
switch entry.Level {
|
26 |
+
case logrus.DebugLevel, logrus.TraceLevel:
|
27 |
+
color = 37
|
28 |
+
case logrus.WarnLevel:
|
29 |
+
color = 33
|
30 |
+
case logrus.ErrorLevel:
|
31 |
+
color = 31
|
32 |
+
case logrus.InfoLevel:
|
33 |
+
color = 36
|
34 |
+
case logrus.FatalLevel:
|
35 |
+
color = 41
|
36 |
+
case logrus.PanicLevel:
|
37 |
+
color = 44
|
38 |
+
default:
|
39 |
+
color = 36
|
40 |
+
}
|
41 |
+
|
42 |
+
var b *bytes.Buffer
|
43 |
+
if entry.Buffer != nil {
|
44 |
+
b = entry.Buffer
|
45 |
+
} else {
|
46 |
+
b = &bytes.Buffer{}
|
47 |
+
}
|
48 |
+
|
49 |
+
if entry.HasCaller() {
|
50 |
+
_, _ = fmt.Fprintf(b, "\u001B[%dmFunc\u001B[0m %v():%d\n", color, entry.Caller.Function, entry.Caller.Line)
|
51 |
+
}
|
52 |
+
//entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
53 |
+
_, _ = fmt.Fprintf(b, "%s \x1b[%dm%s\x1b[0m", entry.Time.Format("2006-01-02 15:04:05:06"),
|
54 |
+
color, func() string {
|
55 |
+
level := strings.ToUpper(entry.Level.String())
|
56 |
+
if len(level) < 7 {
|
57 |
+
for i := len(level); i < 7; i++ {
|
58 |
+
level += " "
|
59 |
+
}
|
60 |
+
}
|
61 |
+
return level
|
62 |
+
}())
|
63 |
+
|
64 |
+
if tag, ok := entry.Data[TagKey]; ok {
|
65 |
+
// TAG:
|
66 |
+
_, _ = fmt.Fprintf(b, "\u001B[%dm%s\u001B[0m\t", 31, strings.ToUpper(tag.(string)))
|
67 |
+
delete(entry.Data, TagKey)
|
68 |
+
}
|
69 |
+
|
70 |
+
_, _ = fmt.Fprintf(b, " %-44s", entry.Message)
|
71 |
+
|
72 |
+
if schoolId, ok := entry.Data[SchoolIDKey]; ok {
|
73 |
+
_, _ = fmt.Fprintf(b, "[\u001B[%dm%v\u001B[0m]\t", 35, schoolId)
|
74 |
+
delete(entry.Data, SchoolIDKey)
|
75 |
+
}
|
76 |
+
|
77 |
+
{
|
78 |
+
userId, uidOk := entry.Data[UserIDKey]
|
79 |
+
username, unameOk := entry.Data[UsernameKey]
|
80 |
+
if uidOk && unameOk {
|
81 |
+
// USER_ID: USERNAME:
|
82 |
+
_, _ = fmt.Fprintf(b, "[\u001B[%dm%v(%v)\u001B[0m]\t", 36, username, userId)
|
83 |
+
delete(entry.Data, UserIDKey)
|
84 |
+
delete(entry.Data, UsernameKey)
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
if gUuid, ok := entry.Data[GuuIDKey]; ok {
|
89 |
+
// TAG:
|
90 |
+
_, _ = fmt.Fprintf(b, "\u001B[%dm%s\u001B[0m\t", 36, gUuid)
|
91 |
+
delete(entry.Data, GuuIDKey)
|
92 |
+
}
|
93 |
+
|
94 |
+
if traceId, ok := entry.Data[TraceIdKey]; ok {
|
95 |
+
// TRACE_ID:
|
96 |
+
_, _ = fmt.Fprintf(b, "\u001B[%dm%s\u001B[0m\t", 34, traceId)
|
97 |
+
delete(entry.Data, TraceIdKey)
|
98 |
+
}
|
99 |
+
|
100 |
+
if stack, ok := entry.Data[StackKey]; ok {
|
101 |
+
_, _ = fmt.Fprintf(b, "\u001B[%dm%s\u001B[0m\t", 33, stack)
|
102 |
+
delete(entry.Data, StackKey)
|
103 |
+
}
|
104 |
+
|
105 |
+
if len(entry.Data) > 0 {
|
106 |
+
// DATA:
|
107 |
+
var buf bytes.Buffer
|
108 |
+
_ = json.NewEncoder(&buf).Encode(&entry.Data)
|
109 |
+
_, _ = fmt.Fprintf(b, "\u001B[%dm%v\u001B[0m\t", 34, strings.TrimSuffix(buf.String(), "\n"))
|
110 |
+
}
|
111 |
+
|
112 |
+
b.WriteByte('\n')
|
113 |
+
return b.Bytes(), nil
|
114 |
+
}
|
pkg/mapx/mapx.go
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package mapx
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/json"
|
5 |
+
"fmt"
|
6 |
+
"net/url"
|
7 |
+
"reflect"
|
8 |
+
"sort"
|
9 |
+
"strconv"
|
10 |
+
"strings"
|
11 |
+
"time"
|
12 |
+
)
|
13 |
+
|
14 |
+
const dot = "."
|
15 |
+
|
16 |
+
type Mapx map[string]interface{}
|
17 |
+
|
18 |
+
func (m *Mapx) IsNil(key string) bool {
|
19 |
+
return m.Get(key) == nil
|
20 |
+
}
|
21 |
+
|
22 |
+
func (m *Mapx) GetString(key string) string {
|
23 |
+
return fmt.Sprintf("%v", m.Get(key))
|
24 |
+
}
|
25 |
+
|
26 |
+
func (m *Mapx) GetTime(key string) time.Time {
|
27 |
+
t, _ := time.ParseInLocation(time.RFC3339, m.GetString(key), time.Local)
|
28 |
+
return t
|
29 |
+
}
|
30 |
+
|
31 |
+
func (m *Mapx) GetInt(key string) int64 {
|
32 |
+
i, _ := strconv.ParseInt(m.GetString(key), 10, 64)
|
33 |
+
return i
|
34 |
+
}
|
35 |
+
|
36 |
+
func (m *Mapx) GetFloat(key string) float64 {
|
37 |
+
f, _ := strconv.ParseFloat(fmt.Sprintf("%v", m.Get(key)), 64)
|
38 |
+
return f
|
39 |
+
}
|
40 |
+
|
41 |
+
func (m *Mapx) GetBool(key string) bool {
|
42 |
+
b, _ := strconv.ParseBool(fmt.Sprintf("%v", m.Get(key)))
|
43 |
+
return b
|
44 |
+
}
|
45 |
+
|
46 |
+
func (m *Mapx) GetMapx(key string) *Mapx {
|
47 |
+
v, _ := m.Get(key).(map[string]interface{})
|
48 |
+
return (*Mapx)(&v)
|
49 |
+
}
|
50 |
+
|
51 |
+
func (m *Mapx) GetSlice(key string, fn func(item *Mapx, index int) bool) {
|
52 |
+
value := reflect.ValueOf(m.Get(key))
|
53 |
+
if value.Kind() != reflect.Slice {
|
54 |
+
return
|
55 |
+
}
|
56 |
+
for i := 0; i < value.Len(); i++ {
|
57 |
+
item := value.Index(i)
|
58 |
+
if item.Kind() != reflect.Invalid {
|
59 |
+
v, _ := item.Interface().(map[string]interface{})
|
60 |
+
if !fn((*Mapx)(&v), i) {
|
61 |
+
break
|
62 |
+
}
|
63 |
+
}
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
func (m *Mapx) Set(key string, val any) *Mapx {
|
68 |
+
(*m)[key] = val
|
69 |
+
return m
|
70 |
+
}
|
71 |
+
|
72 |
+
func (m *Mapx) Get(key string) any {
|
73 |
+
if m == nil {
|
74 |
+
return nil
|
75 |
+
}
|
76 |
+
var v any = map[string]interface{}(*m)
|
77 |
+
keys := strings.Split(key, dot)
|
78 |
+
for i := 0; i < len(keys); i++ {
|
79 |
+
if v == nil {
|
80 |
+
return nil
|
81 |
+
}
|
82 |
+
k := keys[i]
|
83 |
+
switch kind := reflect.TypeOf(v).Kind(); kind {
|
84 |
+
case reflect.Map:
|
85 |
+
_v := reflect.ValueOf(v).MapIndex(reflect.ValueOf(k))
|
86 |
+
if _v.Kind() != reflect.Invalid {
|
87 |
+
v = _v.Interface()
|
88 |
+
} else {
|
89 |
+
v = nil
|
90 |
+
}
|
91 |
+
case reflect.Slice:
|
92 |
+
if len(k) > 2 && k[0] == '[' && k[len(k)-1] == ']' {
|
93 |
+
idx, err := strconv.Atoi(k[1 : len(k)-1])
|
94 |
+
if err != nil {
|
95 |
+
return nil
|
96 |
+
}
|
97 |
+
_v := reflect.ValueOf(v).Index(idx)
|
98 |
+
if _v.Kind() != reflect.Invalid {
|
99 |
+
v = _v.Interface()
|
100 |
+
} else {
|
101 |
+
v = nil
|
102 |
+
}
|
103 |
+
}
|
104 |
+
case reflect.Struct:
|
105 |
+
_v := reflect.ValueOf(v).FieldByName(k)
|
106 |
+
if _v.Kind() != reflect.Invalid {
|
107 |
+
v = _v.Interface()
|
108 |
+
} else {
|
109 |
+
v = nil
|
110 |
+
}
|
111 |
+
|
112 |
+
case reflect.Ptr:
|
113 |
+
v = reflect.ValueOf(v).Elem().Interface()
|
114 |
+
i--
|
115 |
+
case reflect.Invalid:
|
116 |
+
return nil
|
117 |
+
default:
|
118 |
+
return nil
|
119 |
+
}
|
120 |
+
}
|
121 |
+
return v
|
122 |
+
}
|
123 |
+
|
124 |
+
func (m *Mapx) Remove(key string) {
|
125 |
+
delete(*m, key)
|
126 |
+
}
|
127 |
+
|
128 |
+
func (m *Mapx) Json() string {
|
129 |
+
b, _ := json.Marshal(&m)
|
130 |
+
return string(b)
|
131 |
+
}
|
132 |
+
|
133 |
+
func UnmarshalJSON(data []byte) (m *Mapx) {
|
134 |
+
_ = json.Unmarshal(data, &m)
|
135 |
+
return
|
136 |
+
}
|
137 |
+
|
138 |
+
// EncodeAliPaySignParams ("bar=baz&foo=quux") sorted by key.
|
139 |
+
func (m *Mapx) EncodeAliPaySignParams() string {
|
140 |
+
if m == nil {
|
141 |
+
return ""
|
142 |
+
}
|
143 |
+
var (
|
144 |
+
buf strings.Builder
|
145 |
+
keyList []string
|
146 |
+
)
|
147 |
+
for k := range *m {
|
148 |
+
keyList = append(keyList, k)
|
149 |
+
}
|
150 |
+
sort.Strings(keyList)
|
151 |
+
for _, k := range keyList {
|
152 |
+
if v := m.GetString(k); v != "" {
|
153 |
+
buf.WriteString(k)
|
154 |
+
buf.WriteByte('=')
|
155 |
+
buf.WriteString(v)
|
156 |
+
buf.WriteByte('&')
|
157 |
+
}
|
158 |
+
}
|
159 |
+
if buf.Len() <= 0 {
|
160 |
+
return ""
|
161 |
+
}
|
162 |
+
return buf.String()[:buf.Len()-1]
|
163 |
+
}
|
164 |
+
|
165 |
+
// EncodeURLParams ("bar=baz&foo=quux") sorted by key.
|
166 |
+
func (m *Mapx) EncodeURLParams() string {
|
167 |
+
if m == nil {
|
168 |
+
return ""
|
169 |
+
}
|
170 |
+
var (
|
171 |
+
buf strings.Builder
|
172 |
+
keys []string
|
173 |
+
)
|
174 |
+
for k := range *m {
|
175 |
+
keys = append(keys, k)
|
176 |
+
}
|
177 |
+
sort.Strings(keys)
|
178 |
+
for _, k := range keys {
|
179 |
+
if v := m.GetString(k); v != "" {
|
180 |
+
buf.WriteString(url.QueryEscape(k))
|
181 |
+
buf.WriteByte('=')
|
182 |
+
buf.WriteString(url.QueryEscape(v))
|
183 |
+
buf.WriteByte('&')
|
184 |
+
}
|
185 |
+
}
|
186 |
+
if buf.Len() <= 0 {
|
187 |
+
return ""
|
188 |
+
}
|
189 |
+
return buf.String()[:buf.Len()-1]
|
190 |
+
}
|
191 |
+
|
192 |
+
func (m *Mapx) ValidateEmpty(keys ...string) error {
|
193 |
+
var emptyKeys []string
|
194 |
+
for _, k := range keys {
|
195 |
+
if m.IsNil(k) {
|
196 |
+
emptyKeys = append(emptyKeys, k)
|
197 |
+
}
|
198 |
+
}
|
199 |
+
if len(emptyKeys) > 0 {
|
200 |
+
return fmt.Errorf("[miss param error], %v", strings.Join(emptyKeys, ", "))
|
201 |
+
}
|
202 |
+
return nil
|
203 |
+
}
|
pkg/mapx/mapx_test.go
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package mapx
|
2 |
+
|
3 |
+
import (
|
4 |
+
"fmt"
|
5 |
+
"testing"
|
6 |
+
)
|
7 |
+
|
8 |
+
func TestMapx(t *testing.T) {
|
9 |
+
mapx := Mapx{
|
10 |
+
"string": "abc",
|
11 |
+
"int": 1234,
|
12 |
+
"float": 12.34,
|
13 |
+
"slice": []any{
|
14 |
+
"a",
|
15 |
+
map[string]any{
|
16 |
+
"aa": map[string]interface{}{
|
17 |
+
"qwe": 123,
|
18 |
+
},
|
19 |
+
},
|
20 |
+
"c",
|
21 |
+
},
|
22 |
+
"map": map[string]any{
|
23 |
+
"map": map[string]any{
|
24 |
+
"bool": true,
|
25 |
+
"aa": map[string]interface{}{
|
26 |
+
"qwe": 123,
|
27 |
+
},
|
28 |
+
},
|
29 |
+
},
|
30 |
+
"m2": Mapx{
|
31 |
+
//"a": model.PayOrder{OrderNo: "1234"},
|
32 |
+
//"b": &model.PayOrder{OrderNo: "123444"},
|
33 |
+
},
|
34 |
+
}
|
35 |
+
|
36 |
+
//v := mapx.ListByIDs("map.map.aa.qwe")
|
37 |
+
//v := mapx.ListByIDs("slice.[1].aa.qwe")
|
38 |
+
v := mapx.Get("m2.b.OrderNo")
|
39 |
+
//v := mapx.ListByIDs("int")
|
40 |
+
//vv := v.(int)
|
41 |
+
fmt.Printf("type:%T value:%v\n", v, v)
|
42 |
+
|
43 |
+
}
|
pkg/pointer/pointer.go
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package pointer
|
2 |
+
|
3 |
+
import "reflect"
|
4 |
+
|
5 |
+
func To[T any](v T) *T {
|
6 |
+
vo := reflect.ValueOf(v)
|
7 |
+
for {
|
8 |
+
if vo.Kind() == reflect.Ptr {
|
9 |
+
vo = vo.Elem()
|
10 |
+
} else {
|
11 |
+
break
|
12 |
+
}
|
13 |
+
}
|
14 |
+
if vo.IsZero() {
|
15 |
+
return nil
|
16 |
+
}
|
17 |
+
|
18 |
+
return &v
|
19 |
+
}
|
20 |
+
|
21 |
+
func Take[T any](v *T) T {
|
22 |
+
if v == nil {
|
23 |
+
t := reflect.TypeOf(v)
|
24 |
+
_v := reflect.New(t)
|
25 |
+
for t.Kind() == reflect.Ptr {
|
26 |
+
t = t.Elem()
|
27 |
+
_v = _v.Elem()
|
28 |
+
}
|
29 |
+
return _v.Interface().(T)
|
30 |
+
}
|
31 |
+
return *v
|
32 |
+
}
|
pkg/tools/stack.go
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package tools
|
2 |
+
|
3 |
+
import (
|
4 |
+
"context"
|
5 |
+
"sync"
|
6 |
+
)
|
7 |
+
|
8 |
+
type Stack struct {
|
9 |
+
sync.Mutex
|
10 |
+
items []func(context.Context)
|
11 |
+
}
|
12 |
+
|
13 |
+
func (s *Stack) Pop() func(context.Context) {
|
14 |
+
s.Lock()
|
15 |
+
defer s.Unlock()
|
16 |
+
item := s.items[len(s.items)-1]
|
17 |
+
s.items = s.items[:len(s.items)-1]
|
18 |
+
return item
|
19 |
+
}
|
20 |
+
|
21 |
+
func (s *Stack) Push(item func(context.Context)) {
|
22 |
+
s.Lock()
|
23 |
+
defer s.Unlock()
|
24 |
+
s.items = append(s.items, item)
|
25 |
+
}
|
26 |
+
|
27 |
+
func (s *Stack) Next() bool {
|
28 |
+
return len(s.items) > 0
|
29 |
+
}
|
pkg/tools/tools.go
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package tools
|
2 |
+
|
3 |
+
import (
|
4 |
+
jsoniter "github.com/json-iterator/go"
|
5 |
+
"os"
|
6 |
+
"os/signal"
|
7 |
+
"syscall"
|
8 |
+
)
|
9 |
+
|
10 |
+
func HandleSignals(exitCode int) int {
|
11 |
+
s := make(chan os.Signal)
|
12 |
+
signal.Notify(s, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
13 |
+
EXIT:
|
14 |
+
for {
|
15 |
+
switch <-s {
|
16 |
+
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
|
17 |
+
exitCode = 0
|
18 |
+
break EXIT
|
19 |
+
case syscall.SIGHUP:
|
20 |
+
default:
|
21 |
+
break EXIT
|
22 |
+
}
|
23 |
+
}
|
24 |
+
return exitCode
|
25 |
+
}
|
26 |
+
|
27 |
+
// Struct2Bytes 将结构体转换为字节数组
|
28 |
+
func Struct2Bytes(v interface{}) ([]byte, error) {
|
29 |
+
// 创建一个jsonIter的Encoder
|
30 |
+
configCompatibleWithStandardLibrary := jsoniter.ConfigCompatibleWithStandardLibrary
|
31 |
+
// 将结构体转换为JSON文本并保持顺序
|
32 |
+
bytes_, err := configCompatibleWithStandardLibrary.Marshal(v)
|
33 |
+
if err != nil {
|
34 |
+
return nil, err
|
35 |
+
}
|
36 |
+
return bytes_, nil
|
37 |
+
}
|
38 |
+
|
39 |
+
// Bytes2Struct 将字节数组转换为结构体
|
40 |
+
func Bytes2Struct(data []byte, v interface{}) error {
|
41 |
+
// 创建一个jsonIter的Decoder
|
42 |
+
configCompatibleWithStandardLibrary := jsoniter.ConfigCompatibleWithStandardLibrary
|
43 |
+
// 将JSON字节数组解码为结构体
|
44 |
+
err := configCompatibleWithStandardLibrary.Unmarshal(data, v)
|
45 |
+
if err != nil {
|
46 |
+
return err
|
47 |
+
}
|
48 |
+
return nil
|
49 |
+
}
|
release.bat
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@echo off
|
2 |
+
SETLOCAL
|
3 |
+
|
4 |
+
REM 指定编码为 UTF-8
|
5 |
+
chcp 65001
|
6 |
+
|
7 |
+
REM 设置要生成的可执行文件的名称
|
8 |
+
set OUTPUT_NAME=hg_kpl
|
9 |
+
|
10 |
+
REM 设置 Go 源文件的名称
|
11 |
+
SET GOFILE=cmd/main.go
|
12 |
+
|
13 |
+
REM 设置输出目录
|
14 |
+
SET OUTPUTDIR=target
|
15 |
+
|
16 |
+
REM 确保输出目录存在
|
17 |
+
IF NOT EXIST %OUTPUTDIR% MKDIR %OUTPUTDIR%
|
18 |
+
|
19 |
+
REM 编译为 Windows/amd64
|
20 |
+
echo 开始编译 Windows/amd64
|
21 |
+
SET GOOS=windows
|
22 |
+
SET GOARCH=amd64
|
23 |
+
go build -o %OUTPUTDIR%/%OUTPUT_NAME%_windows_amd64.exe %GOFILE%
|
24 |
+
echo 编译完成 Windows/amd64
|
25 |
+
|
26 |
+
REM 编译为 Windows/386
|
27 |
+
echo 开始编译 Windows/386
|
28 |
+
SET GOOS=windows
|
29 |
+
SET GOARCH=386
|
30 |
+
go build -o %OUTPUTDIR%/%OUTPUT_NAME%_windows_386.exe %GOFILE%
|
31 |
+
echo 编译完成 Windows/386
|
32 |
+
|
33 |
+
REM 编译为 Linux/amd64
|
34 |
+
echo 开始编译 Linux/amd64
|
35 |
+
SET GOOS=linux
|
36 |
+
SET GOARCH=amd64
|
37 |
+
go build -o %OUTPUTDIR%/%OUTPUT_NAME%_linux_amd64 %GOFILE%
|
38 |
+
echo 编译完成 Linux/amd64
|
39 |
+
|
40 |
+
REM 编译为 macOS/amd64
|
41 |
+
echo 开始编译 macOS/amd64
|
42 |
+
SET GOOS=darwin
|
43 |
+
SET GOARCH=amd64
|
44 |
+
go build -o %OUTPUTDIR%/%OUTPUT_NAME%_macos_amd64 %GOFILE%
|
45 |
+
echo 编译完成 macOS/amd64
|
46 |
+
|
47 |
+
REM 编译为 freebsd/amd64
|
48 |
+
echo 开始编译 freebsd/amd64
|
49 |
+
SET GOOS=freebsd
|
50 |
+
SET GOARCH=amd64
|
51 |
+
go build -o %OUTPUTDIR%/%OUTPUT_NAME%_freebsd_amd64 %GOFILE%
|
52 |
+
echo 编译完成 freebsd/amd64
|
53 |
+
|
54 |
+
REM 结束批处理脚本
|
55 |
+
ENDLOCAL
|
56 |
+
echo 编译完成!
|