Compare commits
300 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffac080611 | ||
|
|
f7a1e60735 | ||
|
|
f22651821c | ||
|
|
b2a5a9ac9b | ||
|
|
245af2bd77 | ||
|
|
9dd2b1d35c | ||
|
|
bdbfd3e055 | ||
|
|
1518c03d2b | ||
|
|
45920d4df2 | ||
|
|
1c106ac6eb | ||
|
|
9f4a32b4ac | ||
|
|
2565b10491 | ||
|
|
34d632a401 | ||
|
|
a9df27c0e1 | ||
|
|
313011a168 | ||
|
|
5c595aad83 | ||
|
|
2c67eac63c | ||
|
|
d20c20cb33 | ||
|
|
fa76f032be | ||
|
|
3f74910640 | ||
|
|
22bd2a57e9 | ||
|
|
e45a1d0367 | ||
|
|
ee7acdc6a0 | ||
|
|
32264f2700 | ||
|
|
249ea32973 | ||
|
|
9c31b69a58 | ||
|
|
6e985cf6cc | ||
|
|
0f35b45704 | ||
|
|
ba9245e724 | ||
|
|
6e806f2822 | ||
|
|
7c6842a859 | ||
|
|
39f9fa0e6b | ||
|
|
0c8ae8960d | ||
|
|
ffd6a20829 | ||
|
|
2ee3e2782c | ||
|
|
61324c28c6 | ||
|
|
e9f70ecb90 | ||
|
|
cc3b802575 | ||
|
|
9b780ff4a3 | ||
|
|
deb1a33e78 | ||
|
|
5021350aa0 | ||
|
|
41b3dab135 | ||
|
|
ecf94170c8 | ||
|
|
56acdf3ce3 | ||
|
|
5d6d970d49 | ||
|
|
b501cf3c88 | ||
|
|
deac2baa18 | ||
|
|
b1e3f54055 | ||
|
|
8ebf79ffbe | ||
|
|
8d7307cddf | ||
|
|
c547837ae3 | ||
|
|
b408996e72 | ||
|
|
6b5866524d | ||
|
|
4994dfb488 | ||
|
|
5d5040651e | ||
|
|
5252188a68 | ||
|
|
3f664882d5 | ||
|
|
15f2a2d517 | ||
|
|
6637b0fb8f | ||
|
|
a06eeb9129 | ||
|
|
a24fce2c22 | ||
|
|
ddbdb64fc4 | ||
|
|
38c6d545ec | ||
|
|
fe880abbb0 | ||
|
|
081ed76c39 | ||
|
|
613a00686f | ||
|
|
11739ee671 | ||
|
|
d06951e3b5 | ||
|
|
63bbacd4d8 | ||
|
|
6ba130b62c | ||
|
|
5529d644ac | ||
|
|
7e5e6fb5b4 | ||
|
|
f5fb1911f7 | ||
|
|
e26e129a63 | ||
|
|
280cedad8d | ||
|
|
7c3d5aa69c | ||
|
|
7224ba1123 | ||
|
|
0f7d5057fd | ||
|
|
43f2804ced | ||
|
|
5bb83555a4 | ||
|
|
af4d14364c | ||
|
|
105e717eb2 | ||
|
|
697ec6f6e3 | ||
|
|
e07c99f34d | ||
|
|
c047bd5896 | ||
|
|
d97ac01d79 | ||
|
|
df19a04ad1 | ||
|
|
cbce8a9934 | ||
|
|
7739a84828 | ||
|
|
96f711b43f | ||
|
|
96b3ae5e7d | ||
|
|
d0508e257a | ||
|
|
ec8548f133 | ||
|
|
f217b4190b | ||
|
|
9a3b9ee8f8 | ||
|
|
1c299c767b | ||
|
|
03b46f4c24 | ||
|
|
0f40b6438a | ||
|
|
4549d93e0d | ||
|
|
844cec1011 | ||
|
|
0464c6ce43 | ||
|
|
0cdaa9bb70 | ||
|
|
dade2eb4ff | ||
|
|
42e1cd560e | ||
|
|
5db14ec84a | ||
|
|
22aca2e80c | ||
|
|
4f105f9327 | ||
|
|
e2a218dd63 | ||
|
|
f9c0226fa0 | ||
|
|
fcccd69cee | ||
|
|
cc60c827da | ||
|
|
3c44e14386 | ||
|
|
7f765f4267 | ||
|
|
2262e6f3ca | ||
|
|
4349d165ac | ||
|
|
9d396af447 | ||
|
|
a665457014 | ||
|
|
92ddd04b07 | ||
|
|
b5f93d6fbc | ||
|
|
374b75b9f1 | ||
|
|
47b7d283a3 | ||
|
|
03a3b8b38f | ||
|
|
d2a27f16d7 | ||
|
|
b9837f526b | ||
|
|
2393f3a701 | ||
|
|
b910339e6e | ||
|
|
438445dab8 | ||
|
|
dc7e62c388 | ||
|
|
3ed8f9ca55 | ||
|
|
0282b5e75f | ||
|
|
f5f8218940 | ||
|
|
a634046757 | ||
|
|
6a4d86e084 | ||
|
|
9f9b41c63d | ||
|
|
d813f6515b | ||
|
|
730a445ef6 | ||
|
|
938947548b | ||
|
|
2d5085e652 | ||
|
|
e665e299f2 | ||
|
|
30b9c5eda5 | ||
|
|
fbf29e774a | ||
|
|
5429971553 | ||
|
|
df81bde6fd | ||
|
|
cb8b56ceb8 | ||
|
|
1886c9954b | ||
|
|
c942503351 | ||
|
|
806fc42379 | ||
|
|
06a49f0272 | ||
|
|
a99bcdc437 | ||
|
|
2d4378423b | ||
|
|
6aaf620f18 | ||
|
|
8e38cb2149 | ||
|
|
49ea05c890 | ||
|
|
54b479fb3a | ||
|
|
182fbd3e23 | ||
|
|
6819e33510 | ||
|
|
d547c657a0 | ||
|
|
62172db59b | ||
|
|
fc507b69ab | ||
|
|
cfde05f5af | ||
|
|
15618ad03b | ||
|
|
d54fec069c | ||
|
|
f3cbb9b9d1 | ||
|
|
01b510f7f8 | ||
|
|
2c1463e071 | ||
|
|
9f90ab1b6b | ||
|
|
ca5bf930d0 | ||
|
|
7eb0ab342a | ||
|
|
f400916351 | ||
|
|
858cf49966 | ||
|
|
a5e97fc425 | ||
|
|
981d1c626b | ||
|
|
ca2b1a6612 | ||
|
|
fc749b6ef8 | ||
|
|
7c4e5b1a57 | ||
|
|
4e0e5e955c | ||
|
|
a07568f412 | ||
|
|
092735680e | ||
|
|
5d43125284 | ||
|
|
05d51a5447 | ||
|
|
dd6b8b7157 | ||
|
|
3218760f84 | ||
|
|
3fa2cdfe44 | ||
|
|
60eef31490 | ||
|
|
3e83e89c2b | ||
|
|
949fd9092d | ||
|
|
4276f3436d | ||
|
|
4735aa12a9 | ||
|
|
5e38b1fbca | ||
|
|
65555e1fe3 | ||
|
|
a5e53df998 | ||
|
|
70452428ab | ||
|
|
a431c57e25 | ||
|
|
a575afdb05 | ||
|
|
40d6c7f87b | ||
|
|
8feae0a80f | ||
|
|
0a5b91f0cf | ||
|
|
08aae4df39 | ||
|
|
0ff8aecbea | ||
|
|
e6cae84764 | ||
|
|
2d2d2931f1 | ||
|
|
69ad5e7d1f | ||
|
|
97abc6aeb9 | ||
|
|
8cf37dd37c | ||
|
|
e11d091ac5 | ||
|
|
e952dfcedd | ||
|
|
2565d5cab2 | ||
|
|
e4e66c74bc | ||
|
|
6607c41823 | ||
|
|
43570f20b5 | ||
|
|
9ea7a93206 | ||
|
|
dec4922fe5 | ||
|
|
ef1f95b347 | ||
|
|
7317312f68 | ||
|
|
2965e8df65 | ||
|
|
4fdcc7fae9 | ||
|
|
6be8f7539d | ||
|
|
a6de65fe39 | ||
|
|
fc5dae5a8e | ||
|
|
db69454d32 | ||
|
|
c824335b0e | ||
|
|
1f1ce11078 | ||
|
|
0ec1571d37 | ||
|
|
407e8b52a5 | ||
|
|
1be13a151d | ||
|
|
4d9d417d7a | ||
|
|
aaff8f0c4a | ||
|
|
8c064daf3f | ||
|
|
b0b3dae147 | ||
|
|
2ea2be43d3 | ||
|
|
81f7364e93 | ||
|
|
c530869530 | ||
|
|
586ccd90ec | ||
|
|
ece237c49f | ||
|
|
11da5ca98c | ||
|
|
276a68bda6 | ||
|
|
fba89d197a | ||
|
|
b3259e2489 | ||
|
|
880704dda0 | ||
|
|
d5e92e9c09 | ||
|
|
df2f5692bd | ||
|
|
dacc99b8b6 | ||
|
|
0e8ade3b61 | ||
|
|
fe2d8f6261 | ||
|
|
927381039b | ||
|
|
6b95947acb | ||
|
|
b05e53ce58 | ||
|
|
a2546582f2 | ||
|
|
a023b3401e | ||
|
|
07142e9872 | ||
|
|
f5fea6c34d | ||
|
|
5eb0fb3422 | ||
|
|
fa915e7c35 | ||
|
|
2b7905d31c | ||
|
|
9cbbf37add | ||
|
|
642a0c3b0d | ||
|
|
ff47a19250 | ||
|
|
1fc1a3fbe3 | ||
|
|
996172bd69 | ||
|
|
df038314ef | ||
|
|
67881c2e34 | ||
|
|
9c691a17b2 | ||
|
|
54df86a4a2 | ||
|
|
70aff759d7 | ||
|
|
d903cc6e3b | ||
|
|
c520271ab5 | ||
|
|
f6936224d9 | ||
|
|
283984a6a6 | ||
|
|
c4fc09f72a | ||
|
|
efdbabfa04 | ||
|
|
a43af333fd | ||
|
|
1f5894ff82 | ||
|
|
79b4054e31 | ||
|
|
376d7d0c78 | ||
|
|
c8b85330ed | ||
|
|
c65dffee13 | ||
|
|
275320ad9b | ||
|
|
f71cdf9065 | ||
|
|
8e0a239de9 | ||
|
|
ed2b95bc3f | ||
|
|
2e23ee4c66 | ||
|
|
58def52e30 | ||
|
|
aea5ef3b68 | ||
|
|
f2e14cf0a6 | ||
|
|
bc9b6d1595 | ||
|
|
fd11b62390 | ||
|
|
8db00a5ab5 | ||
|
|
a6757e9374 | ||
|
|
e0fe385404 | ||
|
|
f9f4258ec1 | ||
|
|
f939d563ad | ||
|
|
fad03add54 | ||
|
|
9ab6dab081 | ||
|
|
5a55268830 | ||
|
|
fc3769239d | ||
|
|
c9fb3153eb | ||
|
|
a31f9ed9c5 | ||
|
|
2f75828ba4 | ||
|
|
89679ec358 | ||
|
|
98f77f52df |
74
.golangci.yaml
Normal file
74
.golangci.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
# https://golangci-lint.run/usage/configuration/
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- ifshort
|
||||
- exhaustivestruct
|
||||
- golint
|
||||
- nosnakecase
|
||||
- scopelint
|
||||
- varcheck
|
||||
- structcheck
|
||||
- interfacer
|
||||
- maligned
|
||||
- deadcode
|
||||
- dogsled
|
||||
- wrapcheck
|
||||
- wastedassign
|
||||
- varnamelen
|
||||
- testpackage
|
||||
- thelper
|
||||
- nilerr
|
||||
- sqlclosecheck
|
||||
- paralleltest
|
||||
- nonamedreturns
|
||||
- nlreturn
|
||||
- nakedret
|
||||
- ireturn
|
||||
- interfacebloat
|
||||
- gosmopolitan
|
||||
- gomnd
|
||||
- goerr113
|
||||
- gochecknoglobals
|
||||
- exhaustruct
|
||||
- errorlint
|
||||
- depguard
|
||||
- exhaustive
|
||||
- containedctx
|
||||
- wsl
|
||||
- cyclop
|
||||
- dupword
|
||||
- errchkjson
|
||||
- contextcheck
|
||||
- tagalign
|
||||
- dupl
|
||||
- forbidigo
|
||||
- funlen
|
||||
- goconst
|
||||
- godox
|
||||
- gosec
|
||||
- lll
|
||||
- nestif
|
||||
- revive
|
||||
- unparam
|
||||
- stylecheck
|
||||
- gocritic
|
||||
- gofumpt
|
||||
- gomoddirectives
|
||||
- godot
|
||||
- gofmt
|
||||
- gocognit
|
||||
- mirror
|
||||
- gocyclo
|
||||
- gochecknoinits
|
||||
- gci
|
||||
- maintidx
|
||||
- prealloc
|
||||
- goimports
|
||||
- errname
|
||||
- musttag
|
||||
- forcetypeassert
|
||||
- whitespace
|
||||
- noctx
|
||||
- reassign
|
||||
@@ -46,7 +46,7 @@
|
||||
* [开发者指南](https://goedge.cn/docs/Developer/Build.md)
|
||||
|
||||
## 架构
|
||||

|
||||

|
||||
|
||||
其中的组件源码地址如下:
|
||||
* [边缘节点](https://github.com/TeaOSLab/EdgeNode)
|
||||
@@ -54,7 +54,7 @@
|
||||
* [管理平台](https://github.com/TeaOSLab/EdgeAdmin)
|
||||
|
||||
## 联系我们
|
||||
有什么问题和建议都可以加入QQ群 `659832182` 或者 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9) 。
|
||||
有什么问题和建议都可以加入 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9)
|
||||
|
||||
## 企业版
|
||||
* [GoEdge企业版](https://goedge.cn/commercial) - 功能更强大的CDN系统
|
||||
|
||||
@@ -57,9 +57,11 @@ function build() {
|
||||
if [ "$(which uglifyjs)" ]; then
|
||||
echo "compress to component.js ..."
|
||||
uglifyjs --compress --mangle -- "${JS_ROOT}"/components.src.js > "${JS_ROOT}"/components.js
|
||||
uglifyjs --compress --mangle -- "${JS_ROOT}"/utils.js > "${JS_ROOT}"/utils.min.js
|
||||
else
|
||||
echo "copy to component.js ..."
|
||||
cp "${JS_ROOT}"/components.src.js "${JS_ROOT}"/components.js
|
||||
cp "${JS_ROOT}"/utils.js "${JS_ROOT}"/utils.min.js
|
||||
fi
|
||||
|
||||
# create dir & copy files
|
||||
|
||||
1
build/configs/.gitignore
vendored
1
build/configs/.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
api.yaml
|
||||
api_admin.yaml
|
||||
server.yaml
|
||||
api_db.yaml
|
||||
*.pem
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
rpc:
|
||||
endpoints: [ "http://127.0.0.1:8003" ]
|
||||
nodeId: ""
|
||||
secret: ""
|
||||
3
build/configs/api_admin.template.yaml
Normal file
3
build/configs/api_admin.template.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
rpc.endpoints: [ "http://127.0.0.1:8003" ]
|
||||
nodeId: ""
|
||||
secret: ""
|
||||
@@ -2,15 +2,21 @@
|
||||
|
||||
JS_ROOT=../web/public/js
|
||||
|
||||
echo "generate component.src.js ..."
|
||||
echo "generating component.src.js ..."
|
||||
go run -tags=community ../cmd/edge-admin/main.go generate
|
||||
|
||||
if [ `which uglifyjs` ]; then
|
||||
if [ "$(which uglifyjs)" ]; then
|
||||
echo "compress to component.js ..."
|
||||
uglifyjs --compress --mangle -- ${JS_ROOT}/components.src.js > ${JS_ROOT}/components.js
|
||||
|
||||
echo "compress to utils.min.js ..."
|
||||
uglifyjs --compress --mangle -- ${JS_ROOT}/utils.js > ${JS_ROOT}/utils.min.js
|
||||
else
|
||||
echo "copy to component.js ..."
|
||||
cp ${JS_ROOT}/components.src.js ${JS_ROOT}/components.js
|
||||
|
||||
echo "copy to utils.min.js ..."
|
||||
cp ${JS_ROOT}/utils.js ${JS_ROOT}/utils.min.js
|
||||
fi
|
||||
|
||||
echo "ok"
|
||||
@@ -1 +1 @@
|
||||
这个目录下我们列举了所有需要公开声明的第三方License,如果有遗漏,烦请告知 iwind.liu@gmail.com。再次感谢这些开源软件项目和贡献人员!
|
||||
这个目录下我们列举了所有需要公开声明的第三方License,如果有遗漏,烦请告知 goedge.cdn@gmail.com。再次感谢这些开源软件项目和贡献人员!
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
|
||||
_ "github.com/TeaOSLab/EdgeCommon/pkg/langs/messages"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -160,7 +161,7 @@ func main() {
|
||||
if progress >= 0 {
|
||||
if progress == 0 || progress == 1 || progress-lastProgress >= 0.1 {
|
||||
lastProgress = progress
|
||||
log.Println(fmt.Sprintf("%.2f%%", manager.Progress()*100))
|
||||
log.Printf("%.2f%%", manager.Progress()*100)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB |
BIN
doc/architect-zh.png
Normal file
BIN
doc/architect-zh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
@@ -1,11 +1,15 @@
|
||||
FROM alpine:latest
|
||||
LABEL maintainer="iwind.liu@gmail.com"
|
||||
FROM --platform=linux/amd64 alpine:latest
|
||||
LABEL maintainer="goedge.cdn@gmail.com"
|
||||
ENV TZ "Asia/Shanghai"
|
||||
ENV VERSION 1.0.0
|
||||
ENV VERSION 1.2.11
|
||||
ENV ROOT_DIR /usr/local/goedge
|
||||
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
|
||||
|
||||
# remote official repository
|
||||
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"
|
||||
#ENV TAR_URL "http://192.168.2.60:8080/edge-admin-linux-amd64-plus-v${VERSION}.zip" # your local repository
|
||||
|
||||
# your local repository
|
||||
#ENV TAR_URL "http://192.168.2.61:8080/edge-admin-linux-amd64-plus-v${VERSION}.zip"
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
|
||||
|
||||
29
go.mod
29
go.mod
@@ -8,15 +8,16 @@ require (
|
||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/quic-go/quic-go v0.36.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.5
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/tealeg/xlsx/v3 v3.2.3
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/sys v0.6.0
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/sys v0.9.0
|
||||
google.golang.org/grpc v1.45.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -24,19 +25,31 @@ require (
|
||||
require (
|
||||
github.com/frankban/quicktest v1.11.3 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/rogpeppe/fastuuid v1.2.0 // indirect
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
|
||||
github.com/tdewolff/minify/v2 v2.12.7 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.6 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mod v0.11.0 // indirect
|
||||
golang.org/x/net v0.11.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/tools v0.10.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
||||
78
go.sum
78
go.sum
@@ -12,6 +12,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -21,8 +22,11 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -34,15 +38,21 @@ github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebP
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
@@ -55,8 +65,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -67,15 +78,18 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470 h1:TuRxvKRv9PxKVijWOkUnZm5TeanQqWGUJyPx9u6cra4=
|
||||
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f h1:xo6XmXLtveKcwcZAXV6VMxkWNzy/2dStfHEnyowsGAE=
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -87,6 +101,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -97,18 +112,30 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
|
||||
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.36.0 h1:JIrO7p7Ug6hssFcARjWDiqS2RAKJHCiwPxBAA989rbI=
|
||||
github.com/quic-go/quic-go v0.36.0/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
|
||||
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
|
||||
@@ -119,18 +146,28 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/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 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA=
|
||||
github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws=
|
||||
github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo=
|
||||
github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
|
||||
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
|
||||
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||
@@ -138,11 +175,13 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -151,6 +190,9 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -164,16 +206,16 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -193,22 +235,29 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -241,8 +290,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
@@ -9,8 +10,10 @@ import (
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -186,13 +189,16 @@ func (this *AppCmd) runStart() {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0])
|
||||
var cmd = exec.Command(this.exe())
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(this.product+" start failed:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// create symbolic links
|
||||
_ = this.createSymLinks()
|
||||
|
||||
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
|
||||
}
|
||||
|
||||
@@ -239,3 +245,58 @@ func (this *AppCmd) getPID() int {
|
||||
}
|
||||
return maps.NewMap(reply.Params).GetInt("pid")
|
||||
}
|
||||
|
||||
func (this *AppCmd) exe() string {
|
||||
var exe, _ = os.Executable()
|
||||
if len(exe) == 0 {
|
||||
exe = os.Args[0]
|
||||
}
|
||||
return exe
|
||||
}
|
||||
|
||||
// 创建软链接
|
||||
func (this *AppCmd) createSymLinks() error {
|
||||
if runtime.GOOS != "linux" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var exe, _ = os.Executable()
|
||||
if len(exe) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errorList = []string{}
|
||||
|
||||
// bin
|
||||
{
|
||||
var target = "/usr/bin/" + teaconst.ProcessName
|
||||
old, _ := filepath.EvalSymlinks(target)
|
||||
if old != exe {
|
||||
_ = os.Remove(target)
|
||||
err := os.Symlink(exe, target)
|
||||
if err != nil {
|
||||
errorList = append(errorList, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log
|
||||
{
|
||||
var realPath = filepath.Dir(filepath.Dir(exe)) + "/logs/run.log"
|
||||
var target = "/var/log/" + teaconst.ProcessName + ".log"
|
||||
old, _ := filepath.EvalSymlinks(target)
|
||||
if old != realPath {
|
||||
_ = os.Remove(target)
|
||||
err := os.Symlink(realPath, target)
|
||||
if err != nil {
|
||||
errorList = append(errorList, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errorList) > 0 {
|
||||
return errors.New(strings.Join(errorList, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ package configloaders
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
@@ -47,6 +50,7 @@ func loadAdminModuleMapping() (map[int64]*AdminModuleList, error) {
|
||||
IsSuper: m.IsSuper,
|
||||
Fullname: m.Fullname,
|
||||
Theme: m.Theme,
|
||||
Lang: m.Lang,
|
||||
}
|
||||
|
||||
for _, pbModule := range m.Modules {
|
||||
@@ -158,50 +162,80 @@ func UpdateAdminTheme(adminId int64, theme string) {
|
||||
}
|
||||
}
|
||||
|
||||
// FindAdminLang 查找某个管理员选择的语言
|
||||
func FindAdminLang(adminId int64) string {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
list, ok := sharedAdminModuleMapping[adminId]
|
||||
if ok {
|
||||
return list.Lang
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func FindAdminLangForAction(actionPtr actions.ActionWrapper) (langCode langs.LangCode) {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
var adminId = actionPtr.Object().Session().GetInt64(teaconst.SessionAdminId)
|
||||
list, ok := sharedAdminModuleMapping[adminId]
|
||||
var result = ""
|
||||
if ok {
|
||||
result = list.Lang
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
result = langs.ParseLangFromAction(actionPtr)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// AllModuleMaps 所有权限列表
|
||||
func AllModuleMaps() []maps.Map {
|
||||
func AllModuleMaps(langCode string) []maps.Map {
|
||||
var m = []maps.Map{
|
||||
{
|
||||
"name": "看板",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Dashboard),
|
||||
"code": AdminModuleCodeDashboard,
|
||||
"url": "/dashboard",
|
||||
},
|
||||
{
|
||||
"name": "网站服务",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Servers),
|
||||
"code": AdminModuleCodeServer,
|
||||
"url": "/servers",
|
||||
},
|
||||
{
|
||||
"name": "边缘节点",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Nodes),
|
||||
"code": AdminModuleCodeNode,
|
||||
"url": "/clusters",
|
||||
},
|
||||
{
|
||||
"name": "域名解析",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_DNS),
|
||||
"code": AdminModuleCodeDNS,
|
||||
"url": "/dns",
|
||||
},
|
||||
}
|
||||
if teaconst.IsPlus {
|
||||
m = append(m, maps.Map{
|
||||
"name": "智能DNS",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_NS),
|
||||
"code": AdminModuleCodeNS,
|
||||
"url": "/ns",
|
||||
})
|
||||
}
|
||||
m = append(m, []maps.Map{
|
||||
{
|
||||
"name": "平台用户",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Users),
|
||||
"code": AdminModuleCodeUser,
|
||||
"url": "/users",
|
||||
},
|
||||
{
|
||||
"name": "系统用户",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Admins),
|
||||
"code": AdminModuleCodeAdmin,
|
||||
"url": "/admins",
|
||||
},
|
||||
{
|
||||
"name": "财务管理",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Finance),
|
||||
"code": AdminModuleCodeFinance,
|
||||
"url": "/finance",
|
||||
},
|
||||
@@ -210,12 +244,12 @@ func AllModuleMaps() []maps.Map {
|
||||
if teaconst.IsPlus {
|
||||
m = append(m, []maps.Map{
|
||||
{
|
||||
"name": "套餐管理",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Plans),
|
||||
"code": AdminModuleCodePlan,
|
||||
"url": "/plans",
|
||||
},
|
||||
{
|
||||
"name": "工单系统",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Tickets),
|
||||
"code": AdminModuleCodeTicket,
|
||||
"url": "/tickets",
|
||||
},
|
||||
@@ -224,12 +258,12 @@ func AllModuleMaps() []maps.Map {
|
||||
|
||||
m = append(m, []maps.Map{
|
||||
{
|
||||
"name": "日志审计",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Logs),
|
||||
"code": AdminModuleCodeLog,
|
||||
"url": "/log",
|
||||
},
|
||||
{
|
||||
"name": "系统设置",
|
||||
"name": langs.Message(langCode, codes.AdminMenu_Settings),
|
||||
"code": AdminModuleCodeSetting,
|
||||
"url": "/settings",
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ type AdminModuleList struct {
|
||||
Modules []*systemconfigs.AdminModule
|
||||
Fullname string
|
||||
Theme string
|
||||
Lang string
|
||||
}
|
||||
|
||||
func (this *AdminModuleList) Allow(module string) bool {
|
||||
|
||||
@@ -3,6 +3,8 @@ package configloaders
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
@@ -107,8 +109,8 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
|
||||
|
||||
func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
|
||||
return &systemconfigs.AdminUIConfig{
|
||||
ProductName: "GoEdge",
|
||||
AdminSystemName: "GoEdge管理员系统",
|
||||
ProductName: langs.DefaultMessage(codes.AdminUI_DefaultProductName),
|
||||
AdminSystemName: langs.DefaultMessage(codes.AdminUI_DefaultSystemName),
|
||||
ShowOpenSourceInfo: true,
|
||||
ShowVersion: true,
|
||||
ShowFinance: true,
|
||||
|
||||
@@ -29,7 +29,7 @@ func LoadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
|
||||
var v = reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
@@ -83,7 +83,12 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
||||
return sharedSecurityConfig, nil
|
||||
}
|
||||
|
||||
config := &systemconfigs.SecurityConfig{}
|
||||
var config = &systemconfigs.SecurityConfig{
|
||||
Frame: FrameSameOrigin,
|
||||
AllowLocal: true,
|
||||
CheckClientFingerprint: false,
|
||||
CheckClientRegion: true,
|
||||
}
|
||||
err = json.Unmarshal(resp.ValueJSON, config)
|
||||
if err != nil {
|
||||
logs.Println("[SECURITY_MANAGER]" + err.Error())
|
||||
@@ -100,7 +105,9 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
|
||||
|
||||
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
|
||||
return &systemconfigs.SecurityConfig{
|
||||
Frame: FrameSameOrigin,
|
||||
AllowLocal: true,
|
||||
Frame: FrameSameOrigin,
|
||||
AllowLocal: true,
|
||||
CheckClientFingerprint: false,
|
||||
CheckClientRegion: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -8,12 +9,19 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const ConfigFileName = "api_admin.yaml"
|
||||
const oldConfigFileName = "api.yaml"
|
||||
|
||||
// APIConfig API配置
|
||||
type APIConfig struct {
|
||||
RPC struct {
|
||||
OldRPC struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
DisableUpdate bool `yaml:"disableUpdate"`
|
||||
} `yaml:"rpc"`
|
||||
} `yaml:"rpc,omitempty"`
|
||||
|
||||
RPCEndpoints []string `yaml:"rpc.endpoints,flow" json:"rpc.endpoints"`
|
||||
RPCDisableUpdate bool `yaml:"rpc.disableUpdate" json:"rpc.disableUpdate"`
|
||||
|
||||
NodeId string `yaml:"nodeId"`
|
||||
Secret string `yaml:"secret"`
|
||||
}
|
||||
@@ -21,23 +29,29 @@ type APIConfig struct {
|
||||
// LoadAPIConfig 加载API配置
|
||||
func LoadAPIConfig() (*APIConfig, error) {
|
||||
// 候选文件
|
||||
var localFile = Tea.ConfigFile("api.yaml")
|
||||
var realFile = Tea.ConfigFile(ConfigFileName)
|
||||
var oldRealFile = Tea.ConfigFile(oldConfigFileName)
|
||||
var isFromLocal = false
|
||||
var paths = []string{localFile}
|
||||
var paths = []string{realFile, oldRealFile}
|
||||
homeDir, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
|
||||
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/"+ConfigFileName)
|
||||
}
|
||||
paths = append(paths, "/etc/"+teaconst.ProcessName+"/api.yaml")
|
||||
paths = append(paths, "/etc/"+teaconst.ProcessName+"/"+ConfigFileName)
|
||||
|
||||
var data []byte
|
||||
var err error
|
||||
var isFromOld = false
|
||||
for _, path := range paths {
|
||||
data, err = os.ReadFile(path)
|
||||
if err == nil {
|
||||
if path == localFile {
|
||||
if path == realFile || path == oldRealFile {
|
||||
isFromLocal = true
|
||||
}
|
||||
|
||||
// 自动生成新的配置文件
|
||||
isFromOld = path == oldRealFile
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -51,9 +65,20 @@ func LoadAPIConfig() (*APIConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = config.Init()
|
||||
if err != nil {
|
||||
return nil, errors.New("init error: " + err.Error())
|
||||
}
|
||||
|
||||
if !isFromLocal {
|
||||
// 恢复文件
|
||||
_ = os.WriteFile(localFile, data, 0666)
|
||||
_ = os.WriteFile(realFile, data, 0666)
|
||||
}
|
||||
|
||||
// 自动生成新配置文件
|
||||
if isFromOld {
|
||||
config.OldRPC.Endpoints = nil
|
||||
_ = config.WriteFile(Tea.ConfigFile(ConfigFileName))
|
||||
}
|
||||
|
||||
return config, nil
|
||||
@@ -61,9 +86,9 @@ func LoadAPIConfig() (*APIConfig, error) {
|
||||
|
||||
// ResetAPIConfig 重置配置
|
||||
func ResetAPIConfig() error {
|
||||
var filename = "api.yaml"
|
||||
var filename = ConfigFileName
|
||||
|
||||
// 重置 configs/api.yaml
|
||||
// 重置 configs/api_admin.yaml
|
||||
{
|
||||
var configFile = Tea.ConfigFile(filename)
|
||||
stat, err := os.Stat(configFile)
|
||||
@@ -75,7 +100,7 @@ func ResetAPIConfig() error {
|
||||
}
|
||||
}
|
||||
|
||||
// 重置 ~/.edge-admin/api.yaml
|
||||
// 重置 ~/.edge-admin/api_admin.yaml
|
||||
homeDir, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
|
||||
@@ -88,7 +113,7 @@ func ResetAPIConfig() error {
|
||||
}
|
||||
}
|
||||
|
||||
// 重置 /etc/edge-admin/api.yaml
|
||||
// 重置 /etc/edge-admin/api_admin.yaml
|
||||
{
|
||||
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
|
||||
stat, err := os.Stat(configFile)
|
||||
@@ -103,6 +128,22 @@ func ResetAPIConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsNewInstalled() bool {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, filename := range []string{ConfigFileName, oldConfigFileName} {
|
||||
_, err = os.Stat(homeDir + "/." + teaconst.ProcessName + "/" + filename)
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// WriteFile 写入API配置
|
||||
func (this *APIConfig) WriteFile(path string) error {
|
||||
data, err := yaml.Marshal(this)
|
||||
@@ -159,11 +200,27 @@ func (this *APIConfig) WriteFile(path string) error {
|
||||
// Clone 克隆当前配置
|
||||
func (this *APIConfig) Clone() *APIConfig {
|
||||
return &APIConfig{
|
||||
RPC: struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
DisableUpdate bool `yaml:"disableUpdate"`
|
||||
}{},
|
||||
NodeId: this.NodeId,
|
||||
Secret: this.Secret,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *APIConfig) Init() error {
|
||||
// compatible with old
|
||||
if len(this.RPCEndpoints) == 0 && len(this.OldRPC.Endpoints) > 0 {
|
||||
this.RPCEndpoints = this.OldRPC.Endpoints
|
||||
this.RPCDisableUpdate = this.OldRPC.DisableUpdate
|
||||
}
|
||||
|
||||
if len(this.RPCEndpoints) == 0 {
|
||||
return errors.New("no valid 'rpc.endpoints'")
|
||||
}
|
||||
|
||||
if len(this.NodeId) == 0 {
|
||||
return errors.New("'nodeId' required")
|
||||
}
|
||||
if len(this.Secret) == 0 {
|
||||
return errors.New("'secret' required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package configs
|
||||
|
||||
import (
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -11,10 +12,16 @@ func TestLoadAPIConfig(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(config)
|
||||
|
||||
configData, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(configData))
|
||||
}
|
||||
|
||||
func TestAPIConfig_WriteFile(t *testing.T) {
|
||||
config := &APIConfig{}
|
||||
var config = &APIConfig{}
|
||||
err := config.WriteFile("/tmp/api_config.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "1.0.1"
|
||||
Version = "1.2.11"
|
||||
|
||||
APINodeVersion = "1.0.1"
|
||||
APINodeVersion = "1.2.11"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
@@ -14,8 +14,8 @@ const (
|
||||
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
|
||||
EncryptMethod = "aes-256-cfb"
|
||||
|
||||
ErrServer = "服务器出了点小问题,请联系技术人员处理。"
|
||||
CookieSID = "edgesid"
|
||||
CookieSID = "edgesid"
|
||||
SessionAdminId = "adminId"
|
||||
|
||||
SystemdServiceName = "edge-admin"
|
||||
UpdatesURL = "https://goedge.cn/api/boot/versions?os=${os}&arch=${arch}&version=${version}"
|
||||
|
||||
@@ -19,7 +19,6 @@ func TestAES128CFBMethod_Encrypt(t *testing.T) {
|
||||
dst = dst[:len(src)]
|
||||
t.Log("dst:", string(dst))
|
||||
|
||||
src = make([]byte, len(src))
|
||||
src, err = method.Decrypt(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -64,7 +63,6 @@ func TestAES128CFBMethod_Encrypt2(t *testing.T) {
|
||||
|
||||
for _, dst := range sources {
|
||||
dst2 := append([]byte{}, dst...)
|
||||
src2 := make([]byte, len(dst2))
|
||||
src2, err := method.Decrypt(dst2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -5,22 +5,22 @@ import "sync"
|
||||
var eventsMap = map[string][]func(){} // event => []callbacks
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
// 增加事件回调
|
||||
// On 增加事件回调
|
||||
func On(event string, callback func()) {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
callbacks, _ := eventsMap[event]
|
||||
var callbacks = eventsMap[event]
|
||||
callbacks = append(callbacks, callback)
|
||||
eventsMap[event] = callbacks
|
||||
}
|
||||
|
||||
// 通知事件
|
||||
// Notify 通知事件
|
||||
func Notify(event string) {
|
||||
locker.Lock()
|
||||
callbacks, _ := eventsMap[event]
|
||||
var callbacks = eventsMap[event]
|
||||
locker.Unlock()
|
||||
|
||||
|
||||
for _, callback := range callbacks {
|
||||
callback()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package gen
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/conds/condutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
func Generate() error {
|
||||
err := generateComponentsJSFile()
|
||||
if err != nil {
|
||||
return errors.New("generate 'components.src.js' failed: " + err.Error())
|
||||
return fmt.Errorf("generate 'components.src.js' failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package nodes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
||||
@@ -65,7 +66,7 @@ func (this *AdminNode) Run() {
|
||||
this.addPortsToFirewall()
|
||||
|
||||
// 监听信号
|
||||
sigQueue := make(chan os.Signal)
|
||||
var sigQueue = make(chan os.Signal, 8)
|
||||
signal.Notify(sigQueue, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT)
|
||||
go func() {
|
||||
for range sigQueue {
|
||||
@@ -85,6 +86,9 @@ func (this *AdminNode) Run() {
|
||||
// 启动API节点
|
||||
this.startAPINode()
|
||||
|
||||
// 启动IP库
|
||||
this.startIPLibrary()
|
||||
|
||||
// 启动Web服务
|
||||
sessionManager, err := NewSessionManager()
|
||||
if err != nil {
|
||||
@@ -103,8 +107,7 @@ func (this *AdminNode) Run() {
|
||||
// Daemon 实现守护进程
|
||||
func (this *AdminNode) Daemon() {
|
||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||
isDebug := lists.ContainsString(os.Args, "debug")
|
||||
isDebug = true
|
||||
var isDebug = lists.ContainsString(os.Args, "debug")
|
||||
for {
|
||||
conn, err := sock.Dial()
|
||||
if err != nil {
|
||||
@@ -182,7 +185,7 @@ func (this *AdminNode) checkServer() error {
|
||||
if err == nil {
|
||||
err = os.WriteFile(configFile, data, 0666)
|
||||
if err != nil {
|
||||
return errors.New("create config file failed: " + err.Error())
|
||||
return fmt.Errorf("create config file failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
templateYAML := `# environment code
|
||||
@@ -202,11 +205,11 @@ https:
|
||||
`
|
||||
err = os.WriteFile(configFile, []byte(templateYAML), 0666)
|
||||
if err != nil {
|
||||
return errors.New("create config file failed: " + err.Error())
|
||||
return fmt.Errorf("create config file failed: %w", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New("can not read config from 'configs/server.yaml': " + err.Error())
|
||||
return fmt.Errorf("can not read config from 'configs/server.yaml': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -252,7 +255,7 @@ func (this *AdminNode) addPortsToFirewall() {
|
||||
|
||||
// 启动API节点
|
||||
func (this *AdminNode) startAPINode() {
|
||||
configPath := Tea.Root + "/edge-api/configs/api.yaml"
|
||||
var configPath = Tea.Root + "/edge-api/configs/api.yaml"
|
||||
_, err := os.Stat(configPath)
|
||||
canStart := false
|
||||
if err == nil {
|
||||
|
||||
18
internal/nodes/admin_node_ext.go
Normal file
18
internal/nodes/admin_node_ext.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
)
|
||||
|
||||
// 启动IP库
|
||||
func (this *AdminNode) startIPLibrary() {
|
||||
logs.Println("[NODE]initializing ip library ...")
|
||||
err := iplibrary.InitDefault()
|
||||
if err != nil {
|
||||
logs.Println("[NODE]initialize ip library failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,12 @@ package nodes
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SessionManager SESSION管理
|
||||
@@ -32,6 +34,15 @@ func (this *SessionManager) Read(sid string) map[string]string {
|
||||
|
||||
var result = map[string]string{}
|
||||
|
||||
var cacheKey = "SESSION@" + sid
|
||||
var item = ttlcache.DefaultCache.Read(cacheKey)
|
||||
if item != nil && item.Value != nil {
|
||||
itemMap, ok := item.Value.(map[string]string)
|
||||
if ok {
|
||||
return itemMap
|
||||
}
|
||||
}
|
||||
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return map[string]string{}
|
||||
@@ -40,6 +51,7 @@ func (this *SessionManager) Read(sid string) map[string]string {
|
||||
resp, err := rpcClient.LoginSessionRPC().FindLoginSession(rpcClient.Context(0), &pb.FindLoginSessionRequest{Sid: sid})
|
||||
if err != nil {
|
||||
logs.Println("SESSION", "read '"+sid+"' failed: "+err.Error())
|
||||
result["@error"] = err.Error()
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -53,6 +65,9 @@ func (this *SessionManager) Read(sid string) map[string]string {
|
||||
logs.Println("SESSION", "decode '"+sid+"' values failed: "+err.Error())
|
||||
}
|
||||
|
||||
// Write to cache
|
||||
ttlcache.DefaultCache.Write(cacheKey, result, time.Now().Unix()+300 /** must not be too long **/)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/encrypt"
|
||||
@@ -279,34 +280,6 @@ func (this *RPCClient) MessageRPC() pb.MessageServiceClient {
|
||||
return pb.NewMessageServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageRecipientGroupRPC() pb.MessageRecipientGroupServiceClient {
|
||||
return pb.NewMessageRecipientGroupServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageRecipientRPC() pb.MessageRecipientServiceClient {
|
||||
return pb.NewMessageRecipientServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageMediaRPC() pb.MessageMediaServiceClient {
|
||||
return pb.NewMessageMediaServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageMediaInstanceRPC() pb.MessageMediaInstanceServiceClient {
|
||||
return pb.NewMessageMediaInstanceServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageTaskRPC() pb.MessageTaskServiceClient {
|
||||
return pb.NewMessageTaskServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageTaskLogRPC() pb.MessageTaskLogServiceClient {
|
||||
return pb.NewMessageTaskLogServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MessageReceiverRPC() pb.MessageReceiverServiceClient {
|
||||
return pb.NewMessageReceiverServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) IPLibraryRPC() pb.IPLibraryServiceClient {
|
||||
return pb.NewIPLibraryServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -415,14 +388,6 @@ func (this *RPCClient) NodeTaskRPC() pb.NodeTaskServiceClient {
|
||||
return pb.NewNodeTaskServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) AuthorityKeyRPC() pb.AuthorityKeyServiceClient {
|
||||
return pb.NewAuthorityKeyServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) AuthorityNodeRPC() pb.AuthorityNodeServiceClient {
|
||||
return pb.NewAuthorityNodeServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) LatestItemRPC() pb.LatestItemServiceClient {
|
||||
return pb.NewLatestItemServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -522,10 +487,10 @@ func (this *RPCClient) init() error {
|
||||
|
||||
// 重新连接
|
||||
var conns = []*grpc.ClientConn{}
|
||||
for _, endpoint := range this.apiConfig.RPC.Endpoints {
|
||||
for _, endpoint := range this.apiConfig.RPCEndpoints {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return errors.New("parse endpoint failed: " + err.Error())
|
||||
return fmt.Errorf("parse endpoint failed: %w", err)
|
||||
}
|
||||
|
||||
var apiHost = u.Host
|
||||
|
||||
@@ -2,6 +2,9 @@ package rpc
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -28,3 +31,39 @@ func SharedRPC() (*RPCClient, error) {
|
||||
sharedRPC = client
|
||||
return sharedRPC, nil
|
||||
}
|
||||
|
||||
// IsConnError 是否为连接错误
|
||||
func IsConnError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否为连接错误
|
||||
statusErr, ok := status.FromError(err)
|
||||
if ok {
|
||||
var errorCode = statusErr.Code()
|
||||
return errorCode == codes.Unavailable || errorCode == codes.Canceled
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "code = Canceled") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUnimplementedError 检查是否为未实现错误
|
||||
func IsUnimplementedError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
statusErr, ok := status.FromError(err)
|
||||
if ok {
|
||||
if statusErr.Code() == codes.Unimplemented {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package setup
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"os"
|
||||
)
|
||||
|
||||
var isConfigured bool
|
||||
@@ -21,13 +19,5 @@ func IsConfigured() bool {
|
||||
|
||||
// IsNewInstalled IsNew 检查是否新安装
|
||||
func IsNewInstalled() bool {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = os.Stat(homeDir + "/." + teaconst.ProcessName + "/api.yaml")
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return configs.IsNewInstalled()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package tasks
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
|
||||
@@ -59,11 +60,11 @@ func (this *CheckUpdatesTask) Loop() error {
|
||||
return err
|
||||
}
|
||||
var valueJSON = valueResp.ValueJSON
|
||||
var config = &systemconfigs.CheckUpdatesConfig{AutoCheck: false}
|
||||
var config = systemconfigs.NewCheckUpdatesConfig()
|
||||
if len(valueJSON) > 0 {
|
||||
err = json.Unmarshal(valueJSON, config)
|
||||
if err != nil {
|
||||
return errors.New("decode config failed: " + err.Error())
|
||||
return fmt.Errorf("decode config failed: %w", err)
|
||||
}
|
||||
if !config.AutoCheck {
|
||||
return nil
|
||||
@@ -90,7 +91,7 @@ func (this *CheckUpdatesTask) Loop() error {
|
||||
apiURL = strings.ReplaceAll(apiURL, "${version}", teaconst.Version)
|
||||
resp, err := http.Get(apiURL)
|
||||
if err != nil {
|
||||
return errors.New("read api failed: " + err.Error())
|
||||
return fmt.Errorf("read api failed: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -98,13 +99,13 @@ func (this *CheckUpdatesTask) Loop() error {
|
||||
}()
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.New("read api failed: " + err.Error())
|
||||
return fmt.Errorf("read api failed: %w", err)
|
||||
}
|
||||
|
||||
var apiResponse = &Response{}
|
||||
err = json.Unmarshal(data, apiResponse)
|
||||
if err != nil {
|
||||
return errors.New("decode version data failed: " + err.Error())
|
||||
return fmt.Errorf("decode version data failed: %w", err)
|
||||
}
|
||||
|
||||
if apiResponse.Code != 200 {
|
||||
@@ -119,7 +120,7 @@ func (this *CheckUpdatesTask) Loop() error {
|
||||
var vMap = maps.NewMap(version)
|
||||
if vMap.GetString("code") == "admin" {
|
||||
var latestVersion = vMap.GetString("version")
|
||||
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 {
|
||||
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 && (len(config.IgnoredVersion) == 0 || stringutil.VersionCompare(latestVersion, config.IgnoredVersion) > 0) {
|
||||
teaconst.NewVersionCode = latestVersion
|
||||
teaconst.NewVersionDownloadURL = dlHost + vMap.GetString("url")
|
||||
return nil
|
||||
|
||||
@@ -65,7 +65,7 @@ func (this *SyncAPINodesTask) Loop() error {
|
||||
}
|
||||
|
||||
// 是否禁止自动升级
|
||||
if config.RPC.DisableUpdate {
|
||||
if config.RPCDisableUpdate {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func (this *SyncAPINodesTask) Loop() error {
|
||||
}
|
||||
|
||||
// 和现有的对比
|
||||
if this.isSame(newEndpoints, config.RPC.Endpoints) {
|
||||
if this.isSame(newEndpoints, config.RPCEndpoints) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -99,14 +99,14 @@ func (this *SyncAPINodesTask) Loop() error {
|
||||
}
|
||||
|
||||
// 修改RPC对象配置
|
||||
config.RPC.Endpoints = newEndpoints
|
||||
config.RPCEndpoints = newEndpoints
|
||||
err = rpcClient.UpdateConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存到文件
|
||||
err = config.WriteFile(Tea.ConfigFile("api.yaml"))
|
||||
err = config.WriteFile(Tea.ConfigFile(configs.ConfigFileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@ var DefaultCache = NewCache()
|
||||
// TTL缓存
|
||||
// 最大的缓存时间为30 * 86400
|
||||
// Piece数据结构:
|
||||
// Piece1 | Piece2 | Piece3 | ...
|
||||
// [ Item1, Item2, ... | ...
|
||||
//
|
||||
// Piece1 | Piece2 | Piece3 | ...
|
||||
// [ Item1, Item2, ... | ...
|
||||
//
|
||||
// KeyMap列表数据结构
|
||||
// { timestamp1 => [key1, key2, ...] }, ...
|
||||
type Cache struct {
|
||||
@@ -109,19 +111,11 @@ func (this *Cache) Read(key string) (item *Item) {
|
||||
return this.pieces[uint64Key%this.countPieces].Read(uint64Key)
|
||||
}
|
||||
|
||||
func (this *Cache) readIntKey(key uint64) (value *Item) {
|
||||
return this.pieces[key%this.countPieces].Read(key)
|
||||
}
|
||||
|
||||
func (this *Cache) Delete(key string) {
|
||||
uint64Key := HashKey([]byte(key))
|
||||
this.pieces[uint64Key%this.countPieces].Delete(uint64Key)
|
||||
}
|
||||
|
||||
func (this *Cache) deleteIntKey(key uint64) {
|
||||
this.pieces[key%this.countPieces].Delete(key)
|
||||
}
|
||||
|
||||
func (this *Cache) Count() (count int) {
|
||||
for _, piece := range this.pieces {
|
||||
count += piece.Count()
|
||||
|
||||
79
internal/utils/apinodeutils/deploy_file.go
Normal file
79
internal/utils/apinodeutils/deploy_file.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DeployFile 部署文件描述
|
||||
type DeployFile struct {
|
||||
OS string
|
||||
Arch string
|
||||
Version string
|
||||
Path string
|
||||
}
|
||||
|
||||
// Sum 计算概要
|
||||
func (this *DeployFile) Sum() (string, error) {
|
||||
fp, err := os.Open(this.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
m := md5.New()
|
||||
buffer := make([]byte, 128*1024)
|
||||
for {
|
||||
n, err := fp.Read(buffer)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
_, err = m.Write(buffer[:n])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
sum := m.Sum(nil)
|
||||
return fmt.Sprintf("%x", sum), nil
|
||||
}
|
||||
|
||||
// Read 读取一个片段数据
|
||||
func (this *DeployFile) Read(offset int64) (data []byte, newOffset int64, err error) {
|
||||
fp, err := os.Open(this.Path)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
stat, err := fp.Stat()
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
if offset >= stat.Size() {
|
||||
return nil, offset, io.EOF
|
||||
}
|
||||
|
||||
_, err = fp.Seek(offset, io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
|
||||
buffer := make([]byte, 128*1024)
|
||||
n, err := fp.Read(buffer)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
|
||||
return buffer[:n], offset + int64(n), nil
|
||||
}
|
||||
96
internal/utils/apinodeutils/deploy_manager.go
Normal file
96
internal/utils/apinodeutils/deploy_manager.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// DeployManager 节点部署文件管理器
|
||||
// 如果节点部署文件有变化,需要重启API节点以便于生效
|
||||
type DeployManager struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
// NewDeployManager 获取新节点部署文件管理器
|
||||
func NewDeployManager() *DeployManager {
|
||||
var manager = &DeployManager{
|
||||
dir: Tea.Root + "/edge-api/deploy",
|
||||
}
|
||||
manager.LoadNodeFiles()
|
||||
manager.LoadNSNodeFiles()
|
||||
return manager
|
||||
}
|
||||
|
||||
// LoadNodeFiles 加载所有边缘节点文件
|
||||
func (this *DeployManager) LoadNodeFiles() []*DeployFile {
|
||||
var keyMap = map[string]*DeployFile{} // key => File
|
||||
|
||||
var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
||||
for _, file := range files.NewFile(this.dir).List() {
|
||||
var name = file.Name()
|
||||
if !reg.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
var matches = reg.FindStringSubmatch(name)
|
||||
var osName = matches[1]
|
||||
var arch = matches[2]
|
||||
var version = matches[3]
|
||||
|
||||
var key = osName + "_" + arch
|
||||
oldFile, ok := keyMap[key]
|
||||
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
||||
continue
|
||||
}
|
||||
keyMap[key] = &DeployFile{
|
||||
OS: osName,
|
||||
Arch: arch,
|
||||
Version: version,
|
||||
Path: file.Path(),
|
||||
}
|
||||
}
|
||||
|
||||
var result = []*DeployFile{}
|
||||
for _, v := range keyMap {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// LoadNSNodeFiles 加载所有NS节点安装文件
|
||||
func (this *DeployManager) LoadNSNodeFiles() []*DeployFile {
|
||||
var keyMap = map[string]*DeployFile{} // key => File
|
||||
|
||||
var reg = regexp.MustCompile(`^edge-dns-(\w+)-(\w+)-v([0-9.]+)\.zip$`)
|
||||
for _, file := range files.NewFile(this.dir).List() {
|
||||
var name = file.Name()
|
||||
if !reg.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
var matches = reg.FindStringSubmatch(name)
|
||||
var osName = matches[1]
|
||||
var arch = matches[2]
|
||||
var version = matches[3]
|
||||
|
||||
var key = osName + "_" + arch
|
||||
oldFile, ok := keyMap[key]
|
||||
if ok && stringutil.VersionCompare(oldFile.Version, version) > 0 {
|
||||
continue
|
||||
}
|
||||
keyMap[key] = &DeployFile{
|
||||
OS: osName,
|
||||
Arch: arch,
|
||||
Version: version,
|
||||
Path: file.Path(),
|
||||
}
|
||||
}
|
||||
|
||||
var result = []*DeployFile{}
|
||||
for _, v := range keyMap {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package apinodeutils
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -56,13 +57,42 @@ func (this *Upgrader) Upgrade() error {
|
||||
return err
|
||||
}
|
||||
var newAPIConfig = apiConfig.Clone()
|
||||
newAPIConfig.RPC.Endpoints = apiNode.AccessAddrs
|
||||
newAPIConfig.RPCEndpoints = apiNode.AccessAddrs
|
||||
|
||||
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(sharedClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
|
||||
|
||||
// 升级边缘节点
|
||||
err = this.upgradeNodes(sharedClient.Context(0), rpcClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级NS节点
|
||||
err = this.upgradeNSNodes(sharedClient.Context(0), rpcClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级API节点
|
||||
err = this.upgradeAPINode(sharedClient.Context(0), rpcClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upgrade api node failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Progress 查看升级进程
|
||||
func (this *Upgrader) Progress() *Progress {
|
||||
return this.progress
|
||||
}
|
||||
|
||||
// 升级API节点
|
||||
func (this *Upgrader) upgradeAPINode(ctx context.Context, rpcClient *rpc.RPCClient) error {
|
||||
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(ctx, &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,9 +107,9 @@ func (this *Upgrader) Upgrade() error {
|
||||
return errors.New(reason)
|
||||
}
|
||||
|
||||
localVersion, err := localVersion()
|
||||
localVersion, err := lookupLocalVersion()
|
||||
if err != nil {
|
||||
return errors.New("lookup version failed: " + err.Error())
|
||||
return fmt.Errorf("lookup version failed: %w", err)
|
||||
}
|
||||
|
||||
// 检查要升级的文件
|
||||
@@ -196,6 +226,124 @@ func (this *Upgrader) Upgrade() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Upgrader) Progress() *Progress {
|
||||
return this.progress
|
||||
// 升级边缘节点
|
||||
func (this *Upgrader) upgradeNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
|
||||
// 本地的
|
||||
var manager = NewDeployManager()
|
||||
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
|
||||
for _, deployFile := range manager.LoadNodeFiles() {
|
||||
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
|
||||
}
|
||||
|
||||
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
|
||||
for _, nodeFile := range remoteFilesResp.NodeDeployFiles {
|
||||
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
|
||||
}
|
||||
|
||||
// 对比版本
|
||||
for key, deployFile := range localFileMap {
|
||||
remoteDeployFile, ok := remoteFileMap[key]
|
||||
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
|
||||
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload deploy file '%s' failed: %w", filepath.Base(deployFile.Path), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 升级NS节点
|
||||
func (this *Upgrader) upgradeNSNodes(ctx context.Context, rpcClient *rpc.RPCClient) error {
|
||||
// 本地的
|
||||
var manager = NewDeployManager()
|
||||
var localFileMap = map[string]*DeployFile{} // os_arch => *DeployFile
|
||||
for _, deployFile := range manager.LoadNSNodeFiles() {
|
||||
localFileMap[deployFile.OS+"_"+deployFile.Arch] = deployFile
|
||||
}
|
||||
|
||||
remoteFilesResp, err := rpcClient.APINodeRPC().FindLatestDeployFiles(ctx, &pb.FindLatestDeployFilesRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var remoteFileMap = map[string]*pb.FindLatestDeployFilesResponse_DeployFile{} // os_arch => *DeployFile
|
||||
for _, nodeFile := range remoteFilesResp.NsNodeDeployFiles {
|
||||
remoteFileMap[nodeFile.Os+"_"+nodeFile.Arch] = nodeFile
|
||||
}
|
||||
|
||||
// 对比版本
|
||||
for key, deployFile := range localFileMap {
|
||||
remoteDeployFile, ok := remoteFileMap[key]
|
||||
if !ok || stringutil.VersionCompare(remoteDeployFile.Version, deployFile.Version) < 0 {
|
||||
err = this.uploadNodeDeployFile(ctx, rpcClient, deployFile.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload deploy file '%s' failed: %w", filepath.Base(deployFile.Path), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 上传节点文件
|
||||
func (this *Upgrader) uploadNodeDeployFile(ctx context.Context, rpcClient *rpc.RPCClient, path string) error {
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
var buf = make([]byte, 128*4096)
|
||||
var isFirst = true
|
||||
|
||||
var hash = md5.New()
|
||||
|
||||
for {
|
||||
n, err := fp.Read(buf)
|
||||
if n > 0 {
|
||||
hash.Write(buf[:n])
|
||||
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
|
||||
Filename: filepath.Base(path),
|
||||
Sum: "",
|
||||
ChunkData: buf[:n],
|
||||
IsFirstChunk: isFirst,
|
||||
IsLastChunk: false,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
isFirst = false
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadDeployFileToAPINode(ctx, &pb.UploadDeployFileToAPINodeRequest{
|
||||
Filename: filepath.Base(path),
|
||||
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
|
||||
ChunkData: nil,
|
||||
IsFirstChunk: false,
|
||||
IsLastChunk: true,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool,
|
||||
return false, "is directory"
|
||||
}
|
||||
|
||||
localVersion, err := localVersion()
|
||||
localVersion, err := lookupLocalVersion()
|
||||
if err != nil {
|
||||
return false, "lookup version failed: " + err.Error()
|
||||
}
|
||||
@@ -53,9 +53,7 @@ func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool,
|
||||
return true, ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
func localVersion() (string, error) {
|
||||
func lookupLocalVersion() (string, error) {
|
||||
var cmd = exec.Command(apiExe(), "-V")
|
||||
var output = &bytes.Buffer{}
|
||||
cmd.Stdout = output
|
||||
@@ -74,7 +72,6 @@ func localVersion() (string, error) {
|
||||
return localVersion, nil
|
||||
}
|
||||
|
||||
|
||||
func apiExe() string {
|
||||
return Tea.Root + "/edge-api/bin/edge-api"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
@@ -22,3 +24,37 @@ func JSONClone(v interface{}) (interface{}, error) {
|
||||
|
||||
return nv, nil
|
||||
}
|
||||
|
||||
// JSONIsNull 判断JSON数据是否为null
|
||||
func JSONIsNull(jsonData []byte) bool {
|
||||
return len(jsonData) == 0 || bytes.Equal(jsonData, []byte("null"))
|
||||
}
|
||||
|
||||
// JSONDecodeConfig 解码并重新编码
|
||||
// 是为了去除原有JSON中不需要的数据
|
||||
func JSONDecodeConfig(data []byte, ptr any) (encodeJSON []byte, err error) {
|
||||
err = json.Unmarshal(data, ptr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
encodeJSON, err = json.Marshal(ptr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// validate config
|
||||
if ptr != nil {
|
||||
config, ok := ptr.(interface {
|
||||
Init() error
|
||||
})
|
||||
if ok {
|
||||
initErr := config.Init()
|
||||
if initErr != nil {
|
||||
err = errors.New("validate config failed: " + initErr.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package utils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -23,3 +24,12 @@ func TestJSONClone(t *testing.T) {
|
||||
t.Logf("%p, %#v", c, c)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestJSONIsNull(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsTrue(utils.JSONIsNull(nil))
|
||||
a.IsTrue(utils.JSONIsNull([]byte{}))
|
||||
a.IsTrue(utils.JSONIsNull([]byte("null")))
|
||||
a.IsFalse(utils.JSONIsNull([]byte{1, 2, 3}))
|
||||
}
|
||||
@@ -4,22 +4,24 @@
|
||||
package nodelogutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// FindCommonTags 查找常用的标签
|
||||
func FindNodeCommonTags() []maps.Map {
|
||||
func FindNodeCommonTags(langCode langs.LangCode) []maps.Map {
|
||||
return []maps.Map{
|
||||
{
|
||||
"name": "端口监听",
|
||||
"name": langs.Message(langCode, codes.Log_TagListener),
|
||||
"code": "LISTENER",
|
||||
},
|
||||
{
|
||||
"name": "WAF",
|
||||
"name": langs.Message(langCode, codes.Log_TagWAF),
|
||||
"code": "WAF",
|
||||
},
|
||||
{
|
||||
"name": "访问日志",
|
||||
"name": langs.Message(langCode, codes.Log_TagAccessLog),
|
||||
"code": "ACCESS_LOG",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func FormatCount(count int64) string {
|
||||
return fmt.Sprintf("%.1fB", float32(count)/1000/1000/1000)
|
||||
}
|
||||
|
||||
func FormatFloat(f interface{}, decimal int) string {
|
||||
func FormatFloat(f any, decimal int) string {
|
||||
if f == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -101,10 +101,29 @@ func FormatFloat(f interface{}, decimal int) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func FormatFloat2(f interface{}) string {
|
||||
func FormatFloat2(f any) string {
|
||||
return FormatFloat(f, 2)
|
||||
}
|
||||
|
||||
// PadFloatZero 为浮点型数字字符串填充足够的0
|
||||
func PadFloatZero(s string, countZero int) string {
|
||||
if countZero <= 0 {
|
||||
return s
|
||||
}
|
||||
if len(s) == 0 {
|
||||
s = "0"
|
||||
}
|
||||
var index = strings.Index(s, ".")
|
||||
if index < 0 {
|
||||
return s + "." + strings.Repeat("0", countZero)
|
||||
}
|
||||
var decimalLen = len(s) - 1 - index
|
||||
if decimalLen < countZero {
|
||||
return s + strings.Repeat("0", countZero-decimalLen)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var decimalReg = regexp.MustCompile(`^(\d+\.\d+)([a-zA-Z]+)?$`)
|
||||
|
||||
// TrimZeroSuffix 去除小数数字尾部多余的0
|
||||
|
||||
@@ -4,6 +4,7 @@ package numberutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -49,6 +50,24 @@ func TestFormatFloat(t *testing.T) {
|
||||
t.Log(numberutils.FormatFloat(-221745.12, 2))
|
||||
}
|
||||
|
||||
func TestFormatFloat2(t *testing.T) {
|
||||
t.Log(numberutils.FormatFloat2(0))
|
||||
t.Log(numberutils.FormatFloat2(0.0))
|
||||
t.Log(numberutils.FormatFloat2(1.23456))
|
||||
t.Log(numberutils.FormatFloat2(1.0))
|
||||
}
|
||||
|
||||
func TestPadFloatZero(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsTrue(numberutils.PadFloatZero("1", 0) == "1")
|
||||
a.IsTrue(numberutils.PadFloatZero("1", 2) == "1.00")
|
||||
a.IsTrue(numberutils.PadFloatZero("1.1", 2) == "1.10")
|
||||
a.IsTrue(numberutils.PadFloatZero("1.12", 2) == "1.12")
|
||||
a.IsTrue(numberutils.PadFloatZero("1.123", 2) == "1.123")
|
||||
a.IsTrue(numberutils.PadFloatZero("10000.123", 2) == "10000.123")
|
||||
a.IsTrue(numberutils.PadFloatZero("", 2) == "0.00")
|
||||
}
|
||||
|
||||
func TestTrimZeroSuffix(t *testing.T) {
|
||||
for _, s := range []string{
|
||||
"1",
|
||||
|
||||
@@ -39,7 +39,7 @@ func (this *ServiceManager) setup() {
|
||||
this.onceLocker.Do(func() {
|
||||
logFile := files.NewFile(Tea.Root + "/logs/service.log")
|
||||
if logFile.Exists() {
|
||||
logFile.Delete()
|
||||
_ = logFile.Delete()
|
||||
}
|
||||
|
||||
//logger
|
||||
|
||||
@@ -59,10 +59,10 @@ func (this *ServiceManager) Uninstall() error {
|
||||
}
|
||||
|
||||
// disable service
|
||||
exec.Command(systemd, "disable", teaconst.SystemdServiceName+".service").Start()
|
||||
_ = exec.Command(systemd, "disable", teaconst.SystemdServiceName+".service").Start()
|
||||
|
||||
// reload
|
||||
exec.Command(systemd, "daemon-reload")
|
||||
_ = exec.Command(systemd, "daemon-reload").Start()
|
||||
|
||||
return files.NewFile(systemdServiceFile).Delete()
|
||||
}
|
||||
@@ -143,10 +143,10 @@ WantedBy=multi-user.target`
|
||||
}
|
||||
|
||||
// stop current systemd service if running
|
||||
exec.Command(systemd, "stop", shortName+".service")
|
||||
_ = exec.Command(systemd, "stop", shortName+".service").Start()
|
||||
|
||||
// reload
|
||||
exec.Command(systemd, "daemon-reload")
|
||||
_ = exec.Command(systemd, "daemon-reload").Start()
|
||||
|
||||
// enable
|
||||
cmd := exec.Command(systemd, "enable", shortName+".service")
|
||||
|
||||
@@ -4,6 +4,7 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
func (this *ServiceManager) Install(exePath string, args []string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connecting: %s please 'Run as administrator' again", err.Error())
|
||||
return fmt.Errorf("connecting: %w please 'Run as administrator' again", err)
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(this.Name)
|
||||
@@ -30,7 +31,7 @@ func (this *ServiceManager) Install(exePath string, args []string) error {
|
||||
StartType: windows.SERVICE_AUTO_START,
|
||||
}, args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating: %s", err.Error())
|
||||
return fmt.Errorf("creating: %w", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
@@ -46,12 +47,12 @@ func (this *ServiceManager) Start() error {
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(this.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
return fmt.Errorf("could not access service: %w", err)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Start("service")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start service: %v", err)
|
||||
return fmt.Errorf("could not start service: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -61,12 +62,12 @@ func (this *ServiceManager) Start() error {
|
||||
func (this *ServiceManager) Uninstall() error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connecting: %s please 'Run as administrator' again", err.Error())
|
||||
return fmt.Errorf("connecting: %w please 'Run as administrator' again", err)
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(this.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open service: %s", err.Error())
|
||||
return fmt.Errorf("open service: %w", err)
|
||||
}
|
||||
|
||||
// shutdown service
|
||||
@@ -78,7 +79,7 @@ func (this *ServiceManager) Uninstall() error {
|
||||
defer s.Close()
|
||||
err = s.Delete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting: %s", err.Error())
|
||||
return fmt.Errorf("deleting: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
56
internal/utils/taskutils/concurrent.go
Normal file
56
internal/utils/taskutils/concurrent.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package taskutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func RunConcurrent(tasks any, concurrent int, f func(task any)) error {
|
||||
if tasks == nil {
|
||||
return nil
|
||||
}
|
||||
var tasksValue = reflect.ValueOf(tasks)
|
||||
if tasksValue.Type().Kind() != reflect.Slice {
|
||||
return errors.New("ony works for slice")
|
||||
}
|
||||
|
||||
var countTasks = tasksValue.Len()
|
||||
if countTasks == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if concurrent <= 0 {
|
||||
concurrent = 8
|
||||
}
|
||||
if concurrent > countTasks {
|
||||
concurrent = countTasks
|
||||
}
|
||||
|
||||
var taskChan = make(chan any, countTasks)
|
||||
for i := 0; i < countTasks; i++ {
|
||||
taskChan <- tasksValue.Index(i).Interface()
|
||||
}
|
||||
|
||||
var wg = &sync.WaitGroup{}
|
||||
wg.Add(concurrent)
|
||||
for i := 0; i < concurrent; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case task := <-taskChan:
|
||||
f(task)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
17
internal/utils/taskutils/concurrent_test.go
Normal file
17
internal/utils/taskutils/concurrent_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package taskutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/taskutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunConcurrent(t *testing.T) {
|
||||
err := taskutils.RunConcurrent([]string{"a", "b", "c", "d", "e"}, 3, func(task any) {
|
||||
t.Log("run", task)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
95
internal/utils/unzip.go
Normal file
95
internal/utils/unzip.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Unzip struct {
|
||||
zipFile string
|
||||
targetDir string
|
||||
}
|
||||
|
||||
func NewUnzip(zipFile string, targetDir string) *Unzip {
|
||||
return &Unzip{
|
||||
zipFile: zipFile,
|
||||
targetDir: targetDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Unzip) Run() error {
|
||||
if len(this.zipFile) == 0 {
|
||||
return errors.New("zip file should not be empty")
|
||||
}
|
||||
if len(this.targetDir) == 0 {
|
||||
return errors.New("target dir should not be empty")
|
||||
}
|
||||
|
||||
reader, err := zip.OpenReader(this.zipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = reader.Close()
|
||||
}()
|
||||
|
||||
for _, file := range reader.File {
|
||||
var info = file.FileInfo()
|
||||
var target = this.targetDir + "/" + file.Name
|
||||
|
||||
// 目录
|
||||
if info.IsDir() {
|
||||
stat, err := os.Stat(target)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
} else {
|
||||
err = os.MkdirAll(target, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if !stat.IsDir() {
|
||||
err = os.MkdirAll(target, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 文件
|
||||
err = func(file *zip.File, target string) error {
|
||||
fileReader, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = fileReader.Close()
|
||||
}()
|
||||
|
||||
// remove old
|
||||
_ = os.Remove(target)
|
||||
|
||||
// create new
|
||||
fileWriter, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, file.FileInfo().Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = fileWriter.Close()
|
||||
}()
|
||||
|
||||
_, err = io.Copy(fileWriter, fileReader)
|
||||
return err
|
||||
}(file, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -87,10 +88,6 @@ func (this *UpgradeManager) Start() error {
|
||||
|
||||
// 检查unzip
|
||||
unzipExe, _ := exec.LookPath("unzip")
|
||||
if len(unzipExe) == 0 {
|
||||
// TODO install unzip automatically or pack with a static 'unzip' file
|
||||
return errors.New("can not find 'unzip' command")
|
||||
}
|
||||
|
||||
// 检查cp
|
||||
cpExe, _ := exec.LookPath("cp")
|
||||
@@ -111,13 +108,13 @@ func (this *UpgradeManager) Start() error {
|
||||
url = strings.ReplaceAll(url, "${version}", teaconst.Version)
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return errors.New("create url request failed: " + err.Error())
|
||||
return fmt.Errorf("create url request failed: %w", err)
|
||||
}
|
||||
req.Header.Set("User-Agent", "Edge-Admin/"+teaconst.Version)
|
||||
|
||||
resp, err := this.client.Do(req)
|
||||
if err != nil {
|
||||
return errors.New("read latest version failed: " + err.Error())
|
||||
return fmt.Errorf("read latest version failed: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -130,13 +127,13 @@ func (this *UpgradeManager) Start() error {
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.New("read latest version failed: " + err.Error())
|
||||
return fmt.Errorf("read latest version failed: %w", err)
|
||||
}
|
||||
|
||||
var m = maps.Map{}
|
||||
err = json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return errors.New("invalid response data: " + err.Error() + ", origin data: " + string(data))
|
||||
return fmt.Errorf("invalid response data: %w, origin data: %s", err, string(data))
|
||||
}
|
||||
|
||||
var code = m.GetInt("code")
|
||||
@@ -172,13 +169,13 @@ func (this *UpgradeManager) Start() error {
|
||||
{
|
||||
req, err := http.NewRequest(http.MethodGet, downloadURL, nil)
|
||||
if err != nil {
|
||||
return errors.New("create download request failed: " + err.Error())
|
||||
return fmt.Errorf("create download request failed: %w", err)
|
||||
}
|
||||
req.Header.Set("User-Agent", "Edge-Admin/"+teaconst.Version)
|
||||
|
||||
resp, err := this.client.Do(req)
|
||||
if err != nil {
|
||||
return errors.New("download failed: " + downloadURL + ": " + err.Error())
|
||||
return fmt.Errorf("download failed: '%s': %w", downloadURL, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -201,7 +198,7 @@ func (this *UpgradeManager) Start() error {
|
||||
|
||||
fp, err := os.Create(destFile)
|
||||
if err != nil {
|
||||
return errors.New("create file failed: " + err.Error())
|
||||
return fmt.Errorf("create file failed: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -217,7 +214,7 @@ func (this *UpgradeManager) Start() error {
|
||||
if this.isCancelled {
|
||||
return nil
|
||||
}
|
||||
return errors.New("download failed: " + err.Error())
|
||||
return fmt.Errorf("download failed: %w", err)
|
||||
}
|
||||
|
||||
_ = fp.Close()
|
||||
@@ -228,26 +225,38 @@ func (this *UpgradeManager) Start() error {
|
||||
if err == nil && stat.IsDir() {
|
||||
err = os.RemoveAll(unzipDir)
|
||||
if err != nil {
|
||||
return errors.New("remove old dir '" + unzipDir + "' failed: " + err.Error())
|
||||
return fmt.Errorf("remove old dir '%s' failed: %w", unzipDir, err)
|
||||
}
|
||||
}
|
||||
var unzipCmd = exec.Command(unzipExe, "-q", "-o", destFile, "-d", unzipDir)
|
||||
var unzipStderr = &bytes.Buffer{}
|
||||
unzipCmd.Stderr = unzipStderr
|
||||
err = unzipCmd.Run()
|
||||
if err != nil {
|
||||
return errors.New("unzip installation file failed: " + err.Error() + ": " + unzipStderr.String())
|
||||
|
||||
if len(unzipExe) > 0 {
|
||||
var unzipCmd = exec.Command(unzipExe, "-q", "-o", destFile, "-d", unzipDir)
|
||||
var unzipStderr = &bytes.Buffer{}
|
||||
unzipCmd.Stderr = unzipStderr
|
||||
err = unzipCmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unzip installation file failed: %w: %s", err, unzipStderr.String())
|
||||
}
|
||||
} else {
|
||||
var unzipCmd = &Unzip{
|
||||
zipFile: destFile,
|
||||
targetDir: unzipDir,
|
||||
}
|
||||
err = unzipCmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unzip installation file failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
installationFiles, err := filepath.Glob(unzipDir + "/edge-" + this.component + "/*")
|
||||
if err != nil {
|
||||
return errors.New("lookup installation files failed: " + err.Error())
|
||||
return fmt.Errorf("lookup installation files failed: %w", err)
|
||||
}
|
||||
|
||||
// cp to target dir
|
||||
currentExe, err := os.Executable()
|
||||
if err != nil {
|
||||
return errors.New("reveal current executable file path failed: " + err.Error())
|
||||
return fmt.Errorf("reveal current executable file path failed: %w", err)
|
||||
}
|
||||
var targetDir = filepath.Dir(filepath.Dir(currentExe))
|
||||
if !Tea.IsTesting() {
|
||||
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
@@ -35,7 +38,7 @@ func (this *ParentAction) ErrorPage(err error) {
|
||||
}
|
||||
|
||||
// 日志
|
||||
this.CreateLog(oplogs.LevelError, "系统发生错误:%s", err.Error())
|
||||
this.CreateLog(oplogs.LevelError, codes.AdminCommon_LogSystemError, err.Error())
|
||||
|
||||
if this.Request.Method == http.MethodGet {
|
||||
FailPage(this, err)
|
||||
@@ -89,11 +92,12 @@ func (this *ParentAction) TinyMenu(menuItem string) {
|
||||
}
|
||||
|
||||
func (this *ParentAction) AdminId() int64 {
|
||||
return this.Context.GetInt64("adminId")
|
||||
return this.Context.GetInt64(teaconst.SessionAdminId)
|
||||
}
|
||||
|
||||
func (this *ParentAction) CreateLog(level string, description string, args ...interface{}) {
|
||||
desc := fmt.Sprintf(description, args...)
|
||||
func (this *ParentAction) CreateLog(level string, messageCode langs.MessageCode, args ...any) {
|
||||
var description = messageCode.For(this.LangCode())
|
||||
var desc = fmt.Sprintf(description, args...)
|
||||
if level == oplogs.LevelInfo {
|
||||
if this.Code != 200 {
|
||||
level = oplogs.LevelWarn
|
||||
@@ -102,14 +106,14 @@ func (this *ParentAction) CreateLog(level string, description string, args ...in
|
||||
}
|
||||
}
|
||||
}
|
||||
err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, this.RequestRemoteIP())
|
||||
err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, this.RequestRemoteIP(), messageCode, args)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ParentAction) CreateLogInfo(description string, args ...interface{}) {
|
||||
this.CreateLog(oplogs.LevelInfo, description, args...)
|
||||
func (this *ParentAction) CreateLogInfo(messageCode langs.MessageCode, args ...any) {
|
||||
this.CreateLog(oplogs.LevelInfo, messageCode, args...)
|
||||
}
|
||||
|
||||
// RPC 获取RPC
|
||||
@@ -148,3 +152,19 @@ func (this *ParentAction) AdminContext() context.Context {
|
||||
func (this *ParentAction) ViewData() maps.Map {
|
||||
return this.Data
|
||||
}
|
||||
|
||||
func (this *ParentAction) LangCode() string {
|
||||
return configloaders.FindAdminLangForAction(this)
|
||||
}
|
||||
|
||||
func (this *ParentAction) Lang(messageCode langs.MessageCode, args ...any) string {
|
||||
return langs.Message(this.LangCode(), messageCode, args...)
|
||||
}
|
||||
|
||||
func (this *ParentAction) FailLang(messageCode langs.MessageCode, args ...any) {
|
||||
this.Fail(langs.Message(this.LangCode(), messageCode, args...))
|
||||
}
|
||||
|
||||
func (this *ParentAction) FailFieldLang(field string, messageCode langs.MessageCode, args ...any) {
|
||||
this.FailField(field, langs.Message(this.LangCode(), messageCode, args...))
|
||||
}
|
||||
|
||||
@@ -2,37 +2,49 @@ package actionutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type TabItem struct {
|
||||
Name string `json:"name"`
|
||||
SubName string `json:"subName"`
|
||||
URL string `json:"url"`
|
||||
Icon string `json:"icon"`
|
||||
IsActive bool `json:"isActive"`
|
||||
IsRight bool `json:"isRight"`
|
||||
IsTitle bool `json:"isTitle"`
|
||||
IsDisabled bool `json:"isDisabled"`
|
||||
}
|
||||
|
||||
// Tabbar Tabbar定义
|
||||
type Tabbar struct {
|
||||
items []maps.Map
|
||||
items []*TabItem
|
||||
}
|
||||
|
||||
// NewTabbar 获取新对象
|
||||
func NewTabbar() *Tabbar {
|
||||
return &Tabbar{
|
||||
items: []maps.Map{},
|
||||
items: []*TabItem{},
|
||||
}
|
||||
}
|
||||
|
||||
// Add 添加菜单项
|
||||
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) maps.Map {
|
||||
m := maps.Map{
|
||||
"name": name,
|
||||
"subName": subName,
|
||||
"url": url,
|
||||
"icon": icon,
|
||||
"active": active,
|
||||
"right": false,
|
||||
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) *TabItem {
|
||||
var m = &TabItem{
|
||||
Name: name,
|
||||
SubName: subName,
|
||||
URL: url,
|
||||
Icon: icon,
|
||||
IsActive: active,
|
||||
IsRight: false,
|
||||
IsTitle: false,
|
||||
IsDisabled: false,
|
||||
}
|
||||
this.items = append(this.items, m)
|
||||
return m
|
||||
}
|
||||
|
||||
// Items 取得所有的Items
|
||||
func (this *Tabbar) Items() []maps.Map {
|
||||
func (this *Tabbar) Items() []*TabItem {
|
||||
return this.items
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
rpcerrors "github.com/TeaOSLab/EdgeCommon/pkg/rpc/errors"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -23,82 +24,42 @@ import (
|
||||
)
|
||||
|
||||
// Fail 提示服务器错误信息
|
||||
func Fail(action actions.ActionWrapper, err error) {
|
||||
if err != nil {
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
}
|
||||
action.Object().Fail(teaconst.ErrServer + "(" + err.Error() + ")")
|
||||
}
|
||||
|
||||
// FailPage 提示页面错误信息
|
||||
func FailPage(action actions.ActionWrapper, err error) {
|
||||
func Fail(actionPtr actions.ActionWrapper, err error) {
|
||||
if err == nil {
|
||||
err = errors.New("unknown error")
|
||||
}
|
||||
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
var langCode = configloaders.FindAdminLangForAction(actionPtr)
|
||||
var serverErrString = codes.AdminCommon_ServerError.For(langCode)
|
||||
|
||||
// 当前API终端地址
|
||||
var apiEndpoints = []string{}
|
||||
apiConfig, apiConfigErr := configs.LoadAPIConfig()
|
||||
if apiConfigErr == nil && apiConfig != nil {
|
||||
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
|
||||
}
|
||||
logs.Println("[" + reflect.TypeOf(actionPtr).String() + "]" + findStack(err.Error()))
|
||||
|
||||
var isRPCConnError bool
|
||||
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
|
||||
var apiNodeIsStarting = false
|
||||
var apiNodeProgress = ""
|
||||
if isRPCConnError {
|
||||
// API节点是否正在启动
|
||||
var sock = gosock.NewTmpSock("edge-api")
|
||||
reply, err := sock.SendTimeout(&gosock.Command{
|
||||
Code: "starting",
|
||||
Params: nil,
|
||||
}, 1*time.Second)
|
||||
if err == nil && reply != nil {
|
||||
var params = maps.NewMap(reply.Params)
|
||||
if params.GetBool("isStarting") {
|
||||
apiNodeIsStarting = true
|
||||
|
||||
var progressMap = params.GetMap("progress")
|
||||
apiNodeProgress = progressMap.GetString("description")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action.Object().ResponseWriter.WriteHeader(http.StatusInternalServerError)
|
||||
if len(action.Object().Request.Header.Get("X-Requested-With")) > 0 {
|
||||
action.Object().WriteString(teaconst.ErrServer)
|
||||
_, _, isLocalAPI, issuesHTML := parseAPIErr(actionPtr, err)
|
||||
if isLocalAPI && len(issuesHTML) > 0 {
|
||||
actionPtr.Object().Fail(serverErrString + "(" + err.Error() + ";最近一次错误提示:" + issuesHTML + ")")
|
||||
} else {
|
||||
// 本地的一些错误提示
|
||||
var isLocalAPI = false
|
||||
if isRPCConnError {
|
||||
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
|
||||
if hostErr == nil {
|
||||
for _, endpoint := range apiEndpoints {
|
||||
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
|
||||
isLocalAPI = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
actionPtr.Object().Fail(serverErrString + "(" + err.Error() + ")")
|
||||
}
|
||||
|
||||
var issuesHTML = ""
|
||||
if isLocalAPI {
|
||||
// 读取本地API节点的issues
|
||||
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
|
||||
if issuesErr == nil {
|
||||
var issueMaps = []maps.Map{}
|
||||
issuesErr = json.Unmarshal(issuesData, &issueMaps)
|
||||
if issuesErr == nil && len(issueMaps) > 0 {
|
||||
var issueMap = issueMaps[0]
|
||||
issuesHTML = "本地API节点启动错误:" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FailPage 提示页面错误信息
|
||||
func FailPage(actionPtr actions.ActionWrapper, err error) {
|
||||
if err == nil {
|
||||
err = errors.New("unknown error")
|
||||
}
|
||||
|
||||
var langCode = configloaders.FindAdminLangForAction(actionPtr)
|
||||
var serverErrString = codes.AdminCommon_ServerError.For(langCode)
|
||||
|
||||
logs.Println("[" + reflect.TypeOf(actionPtr).String() + "]" + findStack(err.Error()))
|
||||
|
||||
actionPtr.Object().ResponseWriter.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
if len(actionPtr.Object().Request.Header.Get("X-Requested-With")) > 0 {
|
||||
actionPtr.Object().WriteString(serverErrString)
|
||||
} else {
|
||||
apiNodeIsStarting, apiNodeProgress, _, issuesHTML := parseAPIErr(actionPtr, err)
|
||||
var html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -121,7 +82,7 @@ func FailPage(action actions.ActionWrapper, err error) {
|
||||
|
||||
html += "</div>"
|
||||
} else {
|
||||
html += teaconst.ErrServer + `
|
||||
html += serverErrString + `
|
||||
<div>可以通过查看 <strong><em>$安装目录/logs/run.log</em></strong> 日志文件查看具体的错误提示。</div>
|
||||
<hr/>
|
||||
<div class="red">Error: ` + err.Error() + `</div>`
|
||||
@@ -132,7 +93,7 @@ func FailPage(action actions.ActionWrapper, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
action.Object().WriteString(html + `
|
||||
actionPtr.Object().WriteString(html + `
|
||||
</div>
|
||||
</body>
|
||||
</html>`)
|
||||
@@ -146,13 +107,13 @@ func MatchPath(action *actions.ActionObject, path string) bool {
|
||||
|
||||
// FindParentAction 查找父级Action
|
||||
func FindParentAction(actionPtr actions.ActionWrapper) *ParentAction {
|
||||
parentActionValue := reflect.ValueOf(actionPtr).Elem().FieldByName("ParentAction")
|
||||
if parentActionValue.IsValid() {
|
||||
parentAction, isOk := parentActionValue.Interface().(ParentAction)
|
||||
if isOk {
|
||||
return &parentAction
|
||||
}
|
||||
action, ok := actionPtr.(interface {
|
||||
Parent() *ParentAction
|
||||
})
|
||||
if ok {
|
||||
return action.Parent()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -179,7 +140,7 @@ func findStack(err string) string {
|
||||
filename = filename[strings.Index(filename, "src"):]
|
||||
}
|
||||
|
||||
err += "\n\t\t" + string(filename) + ":" + fmt.Sprintf("%d", lineNo)
|
||||
err += "\n\t\t" + filename + ":" + fmt.Sprintf("%d", lineNo)
|
||||
|
||||
break
|
||||
}
|
||||
@@ -187,3 +148,61 @@ func findStack(err string) string {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 分析API节点的错误信息
|
||||
func parseAPIErr(action actions.ActionWrapper, err error) (apiNodeIsStarting bool, apiNodeProgress string, isLocalAPI bool, issuesHTML string) {
|
||||
// 当前API终端地址
|
||||
var apiEndpoints = []string{}
|
||||
apiConfig, apiConfigErr := configs.LoadAPIConfig()
|
||||
if apiConfigErr == nil && apiConfig != nil {
|
||||
apiEndpoints = append(apiEndpoints, apiConfig.RPCEndpoints...)
|
||||
}
|
||||
|
||||
var isRPCConnError bool
|
||||
_, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile(configs.ConfigFileName))
|
||||
if isRPCConnError {
|
||||
// API节点是否正在启动
|
||||
var sock = gosock.NewTmpSock("edge-api")
|
||||
reply, err := sock.SendTimeout(&gosock.Command{
|
||||
Code: "starting",
|
||||
Params: nil,
|
||||
}, 1*time.Second)
|
||||
if err == nil && reply != nil {
|
||||
var params = maps.NewMap(reply.Params)
|
||||
if params.GetBool("isStarting") {
|
||||
apiNodeIsStarting = true
|
||||
|
||||
var progressMap = params.GetMap("progress")
|
||||
apiNodeProgress = progressMap.GetString("description")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 本地的一些错误提示
|
||||
if isRPCConnError {
|
||||
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
|
||||
if hostErr == nil {
|
||||
for _, endpoint := range apiEndpoints {
|
||||
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
|
||||
isLocalAPI = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isLocalAPI {
|
||||
// 读取本地API节点的issues
|
||||
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
|
||||
if issuesErr == nil {
|
||||
var issueMaps = []maps.Map{}
|
||||
issuesErr = json.Unmarshal(issuesData, &issueMaps)
|
||||
if issuesErr == nil && len(issueMaps) > 0 {
|
||||
var issueMap = issueMaps[0]
|
||||
issuesHTML = "本地API节点启动错误:" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package accesskeys
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
@@ -43,7 +44,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
defer this.CreateLogInfo("创建AccessKey %d", accessKeyIdResp.UserAccessKeyId)
|
||||
defer this.CreateLogInfo(codes.UserAccessKey_LogCreateUserAccessKey, accessKeyIdResp.UserAccessKeyId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package accesskeys
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
@@ -12,7 +13,7 @@ type DeleteAction struct {
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
AccessKeyId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除AccessKey %d", params.AccessKeyId)
|
||||
defer this.CreateLogInfo(codes.UserAccessKey_LogDeleteUserAccessKey, params.AccessKeyId)
|
||||
|
||||
_, err := this.RPC().UserAccessKeyRPC().DeleteUserAccessKey(this.AdminContext(), &pb.DeleteUserAccessKeyRequest{UserAccessKeyId: params.AccessKeyId})
|
||||
if err != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package accesskeys
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
@@ -13,7 +14,7 @@ func (this *UpdateIsOnAction) RunPost(params struct {
|
||||
AccessKeyId int64
|
||||
IsOn bool
|
||||
}) {
|
||||
defer this.CreateLogInfo("设置AccessKey %d 启用状态", params.AccessKeyId)
|
||||
defer this.CreateLogInfo(codes.UserAccessKey_LogUpdateUserAccessKeyIsOn, params.AccessKeyId)
|
||||
|
||||
_, err := this.RPC().UserAccessKeyRPC().UpdateUserAccessKeyIsOn(this.AdminContext(), &pb.UpdateUserAccessKeyIsOnRequest{
|
||||
UserAccessKeyId: params.AccessKeyId,
|
||||
|
||||
@@ -51,7 +51,7 @@ func (this *AdminAction) RunGet(params struct {
|
||||
|
||||
// 权限
|
||||
moduleMaps := []maps.Map{}
|
||||
for _, m := range configloaders.AllModuleMaps() {
|
||||
for _, m := range configloaders.AllModuleMaps(this.LangCode()) {
|
||||
code := m.GetString("code")
|
||||
isChecked := false
|
||||
for _, module := range admin.Modules {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -20,7 +21,7 @@ func (this *CreatePopupAction) Init() {
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Data["modules"] = configloaders.AllModuleMaps()
|
||||
this.Data["modules"] = configloaders.AllModuleMaps(this.LangCode())
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
defer this.CreateLogInfo("创建系统用户 %d", createResp.AdminId)
|
||||
defer this.CreateLogInfo(codes.Admin_LogCreateAdmin, createResp.AdminId)
|
||||
|
||||
// 通知更改
|
||||
err = configloaders.NotifyAdminModuleMappingChange()
|
||||
|
||||
@@ -3,6 +3,7 @@ package admins
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
@@ -13,7 +14,7 @@ type DeleteAction struct {
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
AdminId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除系统用户 %d", params.AdminId)
|
||||
defer this.CreateLogInfo(codes.Admin_LogDeleteAdmin, params.AdminId)
|
||||
|
||||
_, err := this.RPC().AdminRPC().DeleteAdmin(this.AdminContext(), &pb.DeleteAdminRequest{AdminId: params.AdminId})
|
||||
if err != nil {
|
||||
|
||||
@@ -67,5 +67,5 @@ func (this *OtpQrcodeAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
this.AddHeader("Content-Type", "image/png")
|
||||
this.Write(data)
|
||||
_, _ = this.Write(data)
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
AdminId int64
|
||||
InstanceId int64
|
||||
User string
|
||||
|
||||
TelegramToken string
|
||||
|
||||
GroupIds string
|
||||
Description string
|
||||
|
||||
TimeFromHour string
|
||||
TimeFromMinute string
|
||||
TimeFromSecond string
|
||||
|
||||
TimeToHour string
|
||||
TimeToMinute string
|
||||
TimeToSecond string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("adminId", params.AdminId).
|
||||
Gt(0, "请选择系统用户").
|
||||
Field("instanceId", params.InstanceId).
|
||||
Gt(0, "请选择媒介")
|
||||
|
||||
groupIds := utils.SplitNumbers(params.GroupIds)
|
||||
|
||||
var digitReg = regexp.MustCompile(`^\d+$`)
|
||||
|
||||
var timeFrom = ""
|
||||
if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
|
||||
timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
|
||||
}
|
||||
|
||||
var timeTo = ""
|
||||
if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
|
||||
timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
|
||||
}
|
||||
|
||||
resp, err := this.RPC().MessageRecipientRPC().CreateMessageRecipient(this.AdminContext(), &pb.CreateMessageRecipientRequest{
|
||||
AdminId: params.AdminId,
|
||||
MessageMediaInstanceId: params.InstanceId,
|
||||
User: params.User,
|
||||
MessageRecipientGroupIds: groupIds,
|
||||
Description: params.Description,
|
||||
TimeFrom: timeFrom,
|
||||
TimeTo: timeTo,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer this.CreateLogInfo("创建媒介接收人 %d", resp.MessageRecipientId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
RecipientId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除媒介接收人 %d", params.RecipientId)
|
||||
|
||||
_, err := this.RPC().MessageRecipientRPC().DeleteMessageRecipient(this.AdminContext(), &pb.DeleteMessageRecipientRequest{MessageRecipientId: params.RecipientId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
Name string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入分组名称")
|
||||
|
||||
_, err := this.RPC().MessageRecipientGroupRPC().CreateMessageRecipientGroup(this.AdminContext(), &pb.CreateMessageRecipientGroupRequest{Name: params.Name})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
}) {
|
||||
_, err := this.RPC().MessageRecipientGroupRPC().DeleteMessageRecipientGroup(this.AdminContext(), &pb.DeleteMessageRecipientGroupRequest{MessageRecipientGroupId: params.GroupId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "group")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
groupsResp, err := this.RPC().MessageRecipientGroupRPC().FindAllEnabledMessageRecipientGroups(this.AdminContext(), &pb.FindAllEnabledMessageRecipientGroupsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
groupMaps := []maps.Map{}
|
||||
for _, group := range groupsResp.MessageRecipientGroups {
|
||||
groupMaps = append(groupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
"isOn": group.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeAdmin)).
|
||||
Data("teaMenu", "admins").
|
||||
Data("teaSubMenu", "recipients").
|
||||
Prefix("/admins/recipients/groups").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Get("/selectPopup", new(SelectPopupAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type SelectPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunGet(params struct {
|
||||
GroupIds string
|
||||
}) {
|
||||
selectedGroupIds := utils.SplitNumbers(params.GroupIds)
|
||||
|
||||
groupsResp, err := this.RPC().MessageRecipientGroupRPC().FindAllEnabledMessageRecipientGroups(this.AdminContext(), &pb.FindAllEnabledMessageRecipientGroupsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
groupMaps := []maps.Map{}
|
||||
for _, group := range groupsResp.MessageRecipientGroups {
|
||||
if lists.ContainsInt64(selectedGroupIds, group.Id) {
|
||||
continue
|
||||
}
|
||||
groupMaps = append(groupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
"isOn": group.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
GroupId int64
|
||||
}) {
|
||||
groupResp, err := this.RPC().MessageRecipientGroupRPC().FindEnabledMessageRecipientGroup(this.AdminContext(), &pb.FindEnabledMessageRecipientGroupRequest{MessageRecipientGroupId: params.GroupId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
group := groupResp.MessageRecipientGroup
|
||||
if group == nil {
|
||||
this.NotFound("messageRecipientGroup", params.GroupId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
"isOn": group.IsOn,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
Name string
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入分组名称")
|
||||
|
||||
_, err := this.RPC().MessageRecipientGroupRPC().UpdateMessageRecipientGroup(this.AdminContext(), &pb.UpdateMessageRecipientGroupRequest{
|
||||
MessageRecipientGroupId: params.GroupId,
|
||||
Name: params.Name,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "recipient")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
}) {
|
||||
// TODO 增加系统用户、媒介类型等条件搜索
|
||||
countResp, err := this.RPC().MessageRecipientRPC().CountAllEnabledMessageRecipients(this.AdminContext(), &pb.CountAllEnabledMessageRecipientsRequest{
|
||||
AdminId: 0,
|
||||
MediaType: "",
|
||||
MessageRecipientGroupId: 0,
|
||||
Keyword: "",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
recipientsResp, err := this.RPC().MessageRecipientRPC().ListEnabledMessageRecipients(this.AdminContext(), &pb.ListEnabledMessageRecipientsRequest{
|
||||
AdminId: 0,
|
||||
MediaType: "",
|
||||
MessageRecipientGroupId: 0,
|
||||
Keyword: "",
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
recipientMaps := []maps.Map{}
|
||||
for _, recipient := range recipientsResp.MessageRecipients {
|
||||
if recipient.Admin == nil {
|
||||
continue
|
||||
}
|
||||
if recipient.MessageMediaInstance == nil {
|
||||
continue
|
||||
}
|
||||
recipientMaps = append(recipientMaps, maps.Map{
|
||||
"id": recipient.Id,
|
||||
"admin": maps.Map{
|
||||
"id": recipient.Admin.Id,
|
||||
"fullname": recipient.Admin.Fullname,
|
||||
"username": recipient.Admin.Username,
|
||||
},
|
||||
"groups": recipient.MessageRecipientGroups,
|
||||
"isOn": recipient.IsOn,
|
||||
"instance": maps.Map{
|
||||
"name": recipient.MessageMediaInstance.Name,
|
||||
},
|
||||
"user": recipient.User,
|
||||
"description": recipient.Description,
|
||||
})
|
||||
}
|
||||
this.Data["recipients"] = recipientMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeAdmin)).
|
||||
Data("teaMenu", "admins").
|
||||
Data("teaSubMenu", "recipients").
|
||||
Prefix("/admins/recipients").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Post("/mediaOptions", new(MediaOptionsAction)).
|
||||
Get("/recipient", new(RecipientAction)).
|
||||
GetPost("/test", new(TestAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
Name string
|
||||
MediaType string
|
||||
|
||||
EmailSmtp string
|
||||
EmailUsername string
|
||||
EmailPassword string
|
||||
EmailFrom string
|
||||
|
||||
WebHookURL string
|
||||
WebHookMethod string
|
||||
WebHookHeaderNames []string
|
||||
WebHookHeaderValues []string
|
||||
WebHookContentType string
|
||||
WebHookParamNames []string
|
||||
WebHookParamValues []string
|
||||
WebHookBody string
|
||||
|
||||
ScriptType string
|
||||
ScriptPath string
|
||||
ScriptLang string
|
||||
ScriptCode string
|
||||
ScriptCwd string
|
||||
ScriptEnvNames []string
|
||||
ScriptEnvValues []string
|
||||
|
||||
DingTalkWebHookURL string
|
||||
|
||||
QyWeixinCorporateId string
|
||||
QyWeixinAgentId string
|
||||
QyWeixinAppSecret string
|
||||
QyWeixinTextFormat string
|
||||
|
||||
QyWeixinRobotWebHookURL string
|
||||
QyWeixinRobotTextFormat string
|
||||
|
||||
AliyunSmsSign string
|
||||
AliyunSmsTemplateCode string
|
||||
AliyunSmsTemplateVarNames []string
|
||||
AliyunSmsTemplateVarValues []string
|
||||
AliyunSmsAccessKeyId string
|
||||
AliyunSmsAccessKeySecret string
|
||||
|
||||
TelegramToken string
|
||||
|
||||
RateMinutes int32
|
||||
RateCount int32
|
||||
|
||||
HashLife int32
|
||||
|
||||
Description string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入媒介名称").
|
||||
Field("mediaType", params.MediaType).
|
||||
Require("请选择媒介类型")
|
||||
|
||||
options := maps.Map{}
|
||||
|
||||
switch params.MediaType {
|
||||
case "email":
|
||||
params.Must.
|
||||
Field("emailSmtp", params.EmailSmtp).
|
||||
Require("请输入SMTP地址").
|
||||
Field("emailUsername", params.EmailUsername).
|
||||
Require("请输入邮箱账号").
|
||||
Field("emailPassword", params.EmailPassword).
|
||||
Require("请输入密码或授权码")
|
||||
|
||||
options["smtp"] = params.EmailSmtp
|
||||
options["username"] = params.EmailUsername
|
||||
options["password"] = params.EmailPassword
|
||||
options["from"] = params.EmailFrom
|
||||
case "webHook":
|
||||
params.Must.
|
||||
Field("webHookURL", params.WebHookURL).
|
||||
Require("请输入URL地址").
|
||||
Match("(?i)^(http|https)://", "URL地址必须以http或https开头").
|
||||
Field("webHookMethod", params.WebHookMethod).
|
||||
Require("请选择请求方法")
|
||||
|
||||
options["url"] = params.WebHookURL
|
||||
options["method"] = params.WebHookMethod
|
||||
options["contentType"] = params.WebHookContentType
|
||||
|
||||
headers := []maps.Map{}
|
||||
if len(params.WebHookHeaderNames) > 0 {
|
||||
for index, name := range params.WebHookHeaderNames {
|
||||
if index < len(params.WebHookHeaderValues) {
|
||||
headers = append(headers, maps.Map{
|
||||
"name": name,
|
||||
"value": params.WebHookHeaderValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
options["headers"] = headers
|
||||
|
||||
if params.WebHookContentType == "params" {
|
||||
webHookParams := []maps.Map{}
|
||||
for index, name := range params.WebHookParamNames {
|
||||
if index < len(params.WebHookParamValues) {
|
||||
webHookParams = append(webHookParams, maps.Map{
|
||||
"name": name,
|
||||
"value": params.WebHookParamValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
options["params"] = webHookParams
|
||||
} else if params.WebHookContentType == "body" {
|
||||
options["body"] = params.WebHookBody
|
||||
}
|
||||
case "script":
|
||||
if params.ScriptType == "path" {
|
||||
params.Must.
|
||||
Field("scriptPath", params.ScriptPath).
|
||||
Require("请输入脚本路径")
|
||||
} else if params.ScriptType == "code" {
|
||||
params.Must.
|
||||
Field("scriptCode", params.ScriptCode).
|
||||
Require("请输入脚本代码")
|
||||
} else {
|
||||
params.Must.
|
||||
Field("scriptPath", params.ScriptPath).
|
||||
Require("请输入脚本路径")
|
||||
}
|
||||
|
||||
options["scriptType"] = params.ScriptType
|
||||
options["path"] = params.ScriptPath
|
||||
options["scriptLang"] = params.ScriptLang
|
||||
options["script"] = params.ScriptCode
|
||||
options["cwd"] = params.ScriptCwd
|
||||
|
||||
env := []maps.Map{}
|
||||
for index, envName := range params.ScriptEnvNames {
|
||||
if index < len(params.ScriptEnvValues) {
|
||||
env = append(env, maps.Map{
|
||||
"name": envName,
|
||||
"value": params.ScriptEnvValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
options["env"] = env
|
||||
case "dingTalk":
|
||||
params.Must.
|
||||
Field("dingTalkWebHookURL", params.DingTalkWebHookURL).
|
||||
Require("请输入Hook地址").
|
||||
Match("^https:", "Hook地址必须以https://开头")
|
||||
|
||||
options["webHookURL"] = params.DingTalkWebHookURL
|
||||
case "qyWeixin":
|
||||
params.Must.
|
||||
Field("qyWeixinCorporateId", params.QyWeixinCorporateId).
|
||||
Require("请输入企业ID").
|
||||
Field("qyWeixinAgentId", params.QyWeixinAgentId).
|
||||
Require("请输入应用AgentId").
|
||||
Field("qyWeixinSecret", params.QyWeixinAppSecret).
|
||||
Require("请输入应用Secret")
|
||||
|
||||
options["corporateId"] = params.QyWeixinCorporateId
|
||||
options["agentId"] = params.QyWeixinAgentId
|
||||
options["appSecret"] = params.QyWeixinAppSecret
|
||||
options["textFormat"] = params.QyWeixinTextFormat
|
||||
case "qyWeixinRobot":
|
||||
params.Must.
|
||||
Field("qyWeixinRobotWebHookURL", params.QyWeixinRobotWebHookURL).
|
||||
Require("请输入WebHook地址").
|
||||
Match("^https:", "WebHook地址必须以https://开头")
|
||||
|
||||
options["webHookURL"] = params.QyWeixinRobotWebHookURL
|
||||
options["textFormat"] = params.QyWeixinRobotTextFormat
|
||||
case "aliyunSms":
|
||||
params.Must.
|
||||
Field("aliyunSmsSign", params.AliyunSmsSign).
|
||||
Require("请输入签名名称").
|
||||
Field("aliyunSmsTemplateCode", params.AliyunSmsTemplateCode).
|
||||
Require("请输入模板CODE").
|
||||
Field("aliyunSmsAccessKeyId", params.AliyunSmsAccessKeyId).
|
||||
Require("请输入AccessKey ID").
|
||||
Field("aliyunSmsAccessKeySecret", params.AliyunSmsAccessKeySecret).
|
||||
Require("请输入AccessKey Secret")
|
||||
|
||||
options["sign"] = params.AliyunSmsSign
|
||||
options["templateCode"] = params.AliyunSmsTemplateCode
|
||||
options["accessKeyId"] = params.AliyunSmsAccessKeyId
|
||||
options["accessKeySecret"] = params.AliyunSmsAccessKeySecret
|
||||
|
||||
variables := []maps.Map{}
|
||||
for index, name := range params.AliyunSmsTemplateVarNames {
|
||||
if index < len(params.AliyunSmsTemplateVarValues) {
|
||||
variables = append(variables, maps.Map{
|
||||
"name": name,
|
||||
"value": params.AliyunSmsTemplateVarValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
options["variables"] = variables
|
||||
case "telegram":
|
||||
params.Must.
|
||||
Field("telegramToken", params.TelegramToken).
|
||||
Require("请输入机器人Token")
|
||||
options["token"] = params.TelegramToken
|
||||
default:
|
||||
this.Fail("错误的媒介类型")
|
||||
}
|
||||
|
||||
optionsJSON, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var rateConfig = &monitorconfigs.RateConfig{
|
||||
Minutes: params.RateMinutes,
|
||||
Count: params.RateCount,
|
||||
}
|
||||
rateJSON, err := json.Marshal(rateConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := this.RPC().MessageMediaInstanceRPC().CreateMessageMediaInstance(this.AdminContext(), &pb.CreateMessageMediaInstanceRequest{
|
||||
Name: params.Name,
|
||||
MediaType: params.MediaType,
|
||||
ParamsJSON: optionsJSON,
|
||||
Description: params.Description,
|
||||
RateJSON: rateJSON,
|
||||
HashLife: params.HashLife,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer this.CreateLogInfo("创建消息媒介 %d", resp.MessageMediaInstanceId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
InstanceId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除消息媒介 %d", params.InstanceId)
|
||||
|
||||
_, err := this.RPC().MessageMediaInstanceRPC().DeleteMessageMediaInstance(this.AdminContext(), &pb.DeleteMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "instance")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
}) {
|
||||
// TODO 增加系统用户、媒介类型等条件搜索
|
||||
countResp, err := this.RPC().MessageMediaInstanceRPC().CountAllEnabledMessageMediaInstances(this.AdminContext(), &pb.CountAllEnabledMessageMediaInstancesRequest{
|
||||
MediaType: "",
|
||||
Keyword: "",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
instancesResp, err := this.RPC().MessageMediaInstanceRPC().ListEnabledMessageMediaInstances(this.AdminContext(), &pb.ListEnabledMessageMediaInstancesRequest{
|
||||
MediaType: "",
|
||||
Keyword: "",
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
instanceMaps := []maps.Map{}
|
||||
for _, instance := range instancesResp.MessageMediaInstances {
|
||||
if instance.MessageMedia == nil {
|
||||
continue
|
||||
}
|
||||
instanceMaps = append(instanceMaps, maps.Map{
|
||||
"id": instance.Id,
|
||||
"name": instance.Name,
|
||||
"isOn": instance.IsOn,
|
||||
"media": maps.Map{
|
||||
"name": instance.MessageMedia.Name,
|
||||
},
|
||||
"description": instance.Description,
|
||||
})
|
||||
}
|
||||
this.Data["instances"] = instanceMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeAdmin)).
|
||||
Data("teaMenu", "admins").
|
||||
Data("teaSubMenu", "instances").
|
||||
Prefix("/admins/recipients/instances").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Get("/instance", new(InstanceAction)).
|
||||
GetPost("/test", new(TestAction)).
|
||||
Post("/options", new(OptionsAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type InstanceAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *InstanceAction) Init() {
|
||||
this.Nav("", "", "instance")
|
||||
}
|
||||
|
||||
func (this *InstanceAction) RunGet(params struct {
|
||||
InstanceId int64
|
||||
}) {
|
||||
instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
instance := instanceResp.MessageMediaInstance
|
||||
if instance == nil || instance.MessageMedia == nil {
|
||||
this.NotFound("messageMediaInstance", params.InstanceId)
|
||||
return
|
||||
}
|
||||
|
||||
mediaParams := maps.Map{}
|
||||
if len(instance.ParamsJSON) > 0 {
|
||||
err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 频率
|
||||
var rateConfig = &monitorconfigs.RateConfig{}
|
||||
if len(instance.RateJSON) > 0 {
|
||||
err = json.Unmarshal(instance.RateJSON, rateConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["instance"] = maps.Map{
|
||||
"id": instance.Id,
|
||||
"name": instance.Name,
|
||||
"isOn": instance.IsOn,
|
||||
"media": maps.Map{
|
||||
"type": instance.MessageMedia.Type,
|
||||
"name": instance.MessageMedia.Name,
|
||||
},
|
||||
"description": instance.Description,
|
||||
"params": mediaParams,
|
||||
"rate": rateConfig,
|
||||
"hashLife": instance.HashLife,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// 媒介类型选项
|
||||
type OptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *OptionsAction) RunPost(params struct{}) {
|
||||
resp, err := this.RPC().MessageMediaInstanceRPC().ListEnabledMessageMediaInstances(this.AdminContext(), &pb.ListEnabledMessageMediaInstancesRequest{
|
||||
Offset: 0,
|
||||
Size: 1000,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
instanceMaps := []maps.Map{}
|
||||
for _, instance := range resp.MessageMediaInstances {
|
||||
instanceMaps = append(instanceMaps, maps.Map{
|
||||
"id": instance.Id,
|
||||
"name": instance.Name,
|
||||
"description": instance.Description,
|
||||
"media": maps.Map{
|
||||
"type": instance.MessageMedia.Type,
|
||||
"name": instance.MessageMedia.Name,
|
||||
"userDescription": instance.MessageMedia.UserDescription,
|
||||
},
|
||||
})
|
||||
}
|
||||
this.Data["instances"] = instanceMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type TestAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestAction) Init() {
|
||||
this.Nav("", "", "test")
|
||||
}
|
||||
|
||||
func (this *TestAction) RunGet(params struct {
|
||||
InstanceId int64
|
||||
}) {
|
||||
instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
instance := instanceResp.MessageMediaInstance
|
||||
if instance == nil || instance.MessageMedia == nil {
|
||||
this.NotFound("messageMediaInstance", params.InstanceId)
|
||||
return
|
||||
}
|
||||
|
||||
mediaParams := maps.Map{}
|
||||
if len(instance.ParamsJSON) > 0 {
|
||||
err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["instance"] = maps.Map{
|
||||
"id": instance.Id,
|
||||
"isOn": instance.IsOn,
|
||||
"media": maps.Map{
|
||||
"type": instance.MessageMedia.Type,
|
||||
"name": instance.MessageMedia.Name,
|
||||
"userDescription": instance.MessageMedia.UserDescription,
|
||||
},
|
||||
"description": instance.Description,
|
||||
"params": mediaParams,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *TestAction) RunPost(params struct {
|
||||
InstanceId int64
|
||||
Subject string
|
||||
Body string
|
||||
User string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("instanceId", params.InstanceId).
|
||||
Gt(0, "请选择正确的媒介")
|
||||
|
||||
resp, err := this.RPC().MessageTaskRPC().CreateMessageTask(this.AdminContext(), &pb.CreateMessageTaskRequest{
|
||||
RecipientId: 0,
|
||||
InstanceId: params.InstanceId,
|
||||
User: params.User,
|
||||
Subject: params.Subject,
|
||||
Body: params.Body,
|
||||
IsPrimary: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["taskId"] = resp.MessageTaskId
|
||||
|
||||
defer this.CreateLogInfo("创建媒介测试任务 %d", resp.MessageTaskId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
package instances
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "update")
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunGet(params struct {
|
||||
InstanceId int64
|
||||
}) {
|
||||
instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: params.InstanceId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
instance := instanceResp.MessageMediaInstance
|
||||
if instance == nil || instance.MessageMedia == nil {
|
||||
this.NotFound("messageMediaInstance", params.InstanceId)
|
||||
return
|
||||
}
|
||||
|
||||
mediaParams := maps.Map{}
|
||||
if len(instance.ParamsJSON) > 0 {
|
||||
err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var rateConfig = &monitorconfigs.RateConfig{}
|
||||
if len(instance.RateJSON) > 0 {
|
||||
err = json.Unmarshal(instance.RateJSON, rateConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["instance"] = maps.Map{
|
||||
"id": instance.Id,
|
||||
"name": instance.Name,
|
||||
"isOn": instance.IsOn,
|
||||
"media": maps.Map{
|
||||
"type": instance.MessageMedia.Type,
|
||||
"name": instance.MessageMedia.Name,
|
||||
},
|
||||
"description": instance.Description,
|
||||
"params": mediaParams,
|
||||
"rate": rateConfig,
|
||||
"hashLife": instance.HashLife,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
InstanceId int64
|
||||
Name string
|
||||
MediaType string
|
||||
|
||||
EmailSmtp string
|
||||
EmailUsername string
|
||||
EmailPassword string
|
||||
EmailFrom string
|
||||
|
||||
WebHookURL string
|
||||
WebHookMethod string
|
||||
WebHookHeaderNames []string
|
||||
WebHookHeaderValues []string
|
||||
WebHookContentType string
|
||||
WebHookParamNames []string
|
||||
WebHookParamValues []string
|
||||
WebHookBody string
|
||||
|
||||
ScriptType string
|
||||
ScriptPath string
|
||||
ScriptLang string
|
||||
ScriptCode string
|
||||
ScriptCwd string
|
||||
ScriptEnvNames []string
|
||||
ScriptEnvValues []string
|
||||
|
||||
DingTalkWebHookURL string
|
||||
|
||||
QyWeixinCorporateId string
|
||||
QyWeixinAgentId string
|
||||
QyWeixinAppSecret string
|
||||
QyWeixinTextFormat string
|
||||
|
||||
QyWeixinRobotWebHookURL string
|
||||
QyWeixinRobotTextFormat string
|
||||
|
||||
AliyunSmsSign string
|
||||
AliyunSmsTemplateCode string
|
||||
AliyunSmsTemplateVarNames []string
|
||||
AliyunSmsTemplateVarValues []string
|
||||
AliyunSmsAccessKeyId string
|
||||
AliyunSmsAccessKeySecret string
|
||||
|
||||
TelegramToken string
|
||||
|
||||
GroupIds string
|
||||
Description string
|
||||
IsOn bool
|
||||
|
||||
RateMinutes int32
|
||||
RateCount int32
|
||||
|
||||
HashLife int32
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改消息媒介 %d", params.InstanceId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入媒介名称").
|
||||
Field("mediaType", params.MediaType).
|
||||
Require("请选择媒介类型")
|
||||
|
||||
options := maps.Map{}
|
||||
|
||||
switch params.MediaType {
|
||||
case "email":
|
||||
params.Must.
|
||||
Field("emailSmtp", params.EmailSmtp).
|
||||
Require("请输入SMTP地址").
|
||||
Field("emailUsername", params.EmailUsername).
|
||||
Require("请输入邮箱账号").
|
||||
Field("emailPassword", params.EmailPassword).
|
||||
Require("请输入密码或授权码")
|
||||
|
||||
options["smtp"] = params.EmailSmtp
|
||||
options["username"] = params.EmailUsername
|
||||
options["password"] = params.EmailPassword
|
||||
options["from"] = params.EmailFrom
|
||||
case "webHook":
|
||||
params.Must.
|
||||
Field("webHookURL", params.WebHookURL).
|
||||
Require("请输入URL地址").
|
||||
Match("(?i)^(http|https)://", "URL地址必须以http或https开头").
|
||||
Field("webHookMethod", params.WebHookMethod).
|
||||
Require("请选择请求方法")
|
||||
|
||||
options["url"] = params.WebHookURL
|
||||
options["method"] = params.WebHookMethod
|
||||
options["contentType"] = params.WebHookContentType
|
||||
|
||||
headers := []maps.Map{}
|
||||
if len(params.WebHookHeaderNames) > 0 {
|
||||
for index, name := range params.WebHookHeaderNames {
|
||||
if index < len(params.WebHookHeaderValues) {
|
||||
headers = append(headers, maps.Map{
|
||||
"name": name,
|
||||
"value": params.WebHookHeaderValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
options["headers"] = headers
|
||||
|
||||
if params.WebHookContentType == "params" {
|
||||
webHookParams := []maps.Map{}
|
||||
for index, name := range params.WebHookParamNames {
|
||||
if index < len(params.WebHookParamValues) {
|
||||
webHookParams = append(webHookParams, maps.Map{
|
||||
"name": name,
|
||||
"value": params.WebHookParamValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
options["params"] = webHookParams
|
||||
} else if params.WebHookContentType == "body" {
|
||||
options["body"] = params.WebHookBody
|
||||
}
|
||||
case "script":
|
||||
if params.ScriptType == "path" {
|
||||
params.Must.
|
||||
Field("scriptPath", params.ScriptPath).
|
||||
Require("请输入脚本路径")
|
||||
} else if params.ScriptType == "code" {
|
||||
params.Must.
|
||||
Field("scriptCode", params.ScriptCode).
|
||||
Require("请输入脚本代码")
|
||||
} else {
|
||||
params.Must.
|
||||
Field("scriptPath", params.ScriptPath).
|
||||
Require("请输入脚本路径")
|
||||
}
|
||||
|
||||
options["scriptType"] = params.ScriptType
|
||||
options["path"] = params.ScriptPath
|
||||
options["scriptLang"] = params.ScriptLang
|
||||
options["script"] = params.ScriptCode
|
||||
options["cwd"] = params.ScriptCwd
|
||||
|
||||
env := []maps.Map{}
|
||||
for index, envName := range params.ScriptEnvNames {
|
||||
if index < len(params.ScriptEnvValues) {
|
||||
env = append(env, maps.Map{
|
||||
"name": envName,
|
||||
"value": params.ScriptEnvValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
options["env"] = env
|
||||
case "dingTalk":
|
||||
params.Must.
|
||||
Field("dingTalkWebHookURL", params.DingTalkWebHookURL).
|
||||
Require("请输入Hook地址").
|
||||
Match("^https:", "Hook地址必须以https://开头")
|
||||
|
||||
options["webHookURL"] = params.DingTalkWebHookURL
|
||||
case "qyWeixin":
|
||||
params.Must.
|
||||
Field("qyWeixinCorporateId", params.QyWeixinCorporateId).
|
||||
Require("请输入企业ID").
|
||||
Field("qyWeixinAgentId", params.QyWeixinAgentId).
|
||||
Require("请输入应用AgentId").
|
||||
Field("qyWeixinSecret", params.QyWeixinAppSecret).
|
||||
Require("请输入应用Secret")
|
||||
|
||||
options["corporateId"] = params.QyWeixinCorporateId
|
||||
options["agentId"] = params.QyWeixinAgentId
|
||||
options["appSecret"] = params.QyWeixinAppSecret
|
||||
options["textFormat"] = params.QyWeixinTextFormat
|
||||
case "qyWeixinRobot":
|
||||
params.Must.
|
||||
Field("qyWeixinRobotWebHookURL", params.QyWeixinRobotWebHookURL).
|
||||
Require("请输入WebHook地址").
|
||||
Match("^https:", "WebHook地址必须以https://开头")
|
||||
|
||||
options["webHookURL"] = params.QyWeixinRobotWebHookURL
|
||||
options["textFormat"] = params.QyWeixinRobotTextFormat
|
||||
case "aliyunSms":
|
||||
params.Must.
|
||||
Field("aliyunSmsSign", params.AliyunSmsSign).
|
||||
Require("请输入签名名称").
|
||||
Field("aliyunSmsTemplateCode", params.AliyunSmsTemplateCode).
|
||||
Require("请输入模板CODE").
|
||||
Field("aliyunSmsAccessKeyId", params.AliyunSmsAccessKeyId).
|
||||
Require("请输入AccessKey ID").
|
||||
Field("aliyunSmsAccessKeySecret", params.AliyunSmsAccessKeySecret).
|
||||
Require("请输入AccessKey Secret")
|
||||
|
||||
options["sign"] = params.AliyunSmsSign
|
||||
options["templateCode"] = params.AliyunSmsTemplateCode
|
||||
options["accessKeyId"] = params.AliyunSmsAccessKeyId
|
||||
options["accessKeySecret"] = params.AliyunSmsAccessKeySecret
|
||||
|
||||
variables := []maps.Map{}
|
||||
for index, name := range params.AliyunSmsTemplateVarNames {
|
||||
if index < len(params.AliyunSmsTemplateVarValues) {
|
||||
variables = append(variables, maps.Map{
|
||||
"name": name,
|
||||
"value": params.AliyunSmsTemplateVarValues[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
options["variables"] = variables
|
||||
case "telegram":
|
||||
params.Must.
|
||||
Field("telegramToken", params.TelegramToken).
|
||||
Require("请输入机器人Token")
|
||||
options["token"] = params.TelegramToken
|
||||
default:
|
||||
this.Fail("错误的媒介类型")
|
||||
}
|
||||
|
||||
optionsJSON, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var rateConfig = &monitorconfigs.RateConfig{
|
||||
Minutes: params.RateMinutes,
|
||||
Count: params.RateCount,
|
||||
}
|
||||
rateJSON, err := json.Marshal(rateConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().MessageMediaInstanceRPC().UpdateMessageMediaInstance(this.AdminContext(), &pb.UpdateMessageMediaInstanceRequest{
|
||||
MessageMediaInstanceId: params.InstanceId,
|
||||
Name: params.Name,
|
||||
MediaType: params.MediaType,
|
||||
ParamsJSON: optionsJSON,
|
||||
Description: params.Description,
|
||||
RateJSON: rateJSON,
|
||||
HashLife: params.HashLife,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "log")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().MessageTaskLogRPC().CountMessageTaskLogs(this.AdminContext(), &pb.CountMessageTaskLogsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
page := this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
logsResp, err := this.RPC().MessageTaskLogRPC().ListMessageTaskLogs(this.AdminContext(), &pb.ListMessageTaskLogsRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
logMaps := []maps.Map{}
|
||||
for _, log := range logsResp.MessageTaskLogs {
|
||||
if log.MessageTask.MessageRecipient != nil {
|
||||
log.MessageTask.User = log.MessageTask.MessageRecipient.User
|
||||
}
|
||||
logMaps = append(logMaps, maps.Map{
|
||||
"task": maps.Map{
|
||||
"id": log.MessageTask.Id,
|
||||
"user": log.MessageTask.User,
|
||||
"subject": log.MessageTask.Subject,
|
||||
"body": log.MessageTask.Body,
|
||||
"instance": maps.Map{
|
||||
"id": log.MessageTask.MessageMediaInstance.Id,
|
||||
"name": log.MessageTask.MessageMediaInstance.Name,
|
||||
},
|
||||
},
|
||||
"isOk": log.IsOk,
|
||||
"error": log.Error,
|
||||
"response": log.Response,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
|
||||
})
|
||||
}
|
||||
this.Data["logs"] = logMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeAdmin)).
|
||||
Data("teaMenu", "admins").
|
||||
Data("teaSubMenu", "recipients").
|
||||
Prefix("/admins/recipients/logs").
|
||||
Get("", new(IndexAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// 媒介类型选项
|
||||
type MediaOptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *MediaOptionsAction) RunPost(params struct{}) {
|
||||
resp, err := this.RPC().MessageMediaRPC().FindAllMessageMedias(this.AdminContext(), &pb.FindAllMessageMediasRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
mediaMaps := []maps.Map{}
|
||||
for _, media := range resp.MessageMedias {
|
||||
mediaMaps = append(mediaMaps, maps.Map{
|
||||
"id": media.Id,
|
||||
"type": media.Type,
|
||||
"name": media.Name,
|
||||
"description": media.Description,
|
||||
})
|
||||
}
|
||||
this.Data["medias"] = mediaMaps
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type RecipientAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *RecipientAction) Init() {
|
||||
this.Nav("", "", "recipient")
|
||||
}
|
||||
|
||||
func (this *RecipientAction) RunGet(params struct {
|
||||
RecipientId int64
|
||||
}) {
|
||||
recipientResp, err := this.RPC().MessageRecipientRPC().FindEnabledMessageRecipient(this.AdminContext(), &pb.FindEnabledMessageRecipientRequest{MessageRecipientId: params.RecipientId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
recipient := recipientResp.MessageRecipient
|
||||
if recipient == nil || recipient.Admin == nil || recipient.MessageMediaInstance == nil {
|
||||
this.NotFound("messageRecipient", params.RecipientId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["recipient"] = maps.Map{
|
||||
"id": recipient.Id,
|
||||
"admin": maps.Map{
|
||||
"id": recipient.Admin.Id,
|
||||
"fullname": recipient.Admin.Fullname,
|
||||
"username": recipient.Admin.Username,
|
||||
},
|
||||
"groups": recipient.MessageRecipientGroups,
|
||||
"isOn": recipient.IsOn,
|
||||
"instance": maps.Map{
|
||||
"id": recipient.MessageMediaInstance.Id,
|
||||
"name": recipient.MessageMediaInstance.Name,
|
||||
"description": recipient.MessageMediaInstance.Description,
|
||||
},
|
||||
"user": recipient.User,
|
||||
"description": recipient.Description,
|
||||
"timeFrom": recipient.TimeFrom,
|
||||
"timeTo": recipient.TimeTo,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
TaskId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除消息发送任务 %d", params.TaskId)
|
||||
|
||||
_, err := this.RPC().MessageTaskRPC().DeleteMessageTask(this.AdminContext(), &pb.DeleteMessageTaskRequest{MessageTaskId: params.TaskId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "task")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Status int32
|
||||
}) {
|
||||
this.Data["status"] = params.Status
|
||||
if params.Status > 3 {
|
||||
params.Status = 0
|
||||
}
|
||||
|
||||
countWaitingResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusNone})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var countWaiting = countWaitingResp.Count
|
||||
this.Data["countWaiting"] = countWaiting
|
||||
|
||||
countFailedResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusFailed})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var countFailed = countFailedResp.Count
|
||||
this.Data["countFailed"] = countFailed
|
||||
|
||||
// 列表
|
||||
var total = int64(0)
|
||||
switch params.Status {
|
||||
case 0:
|
||||
total = countWaiting
|
||||
case 3:
|
||||
total = countFailed
|
||||
}
|
||||
page := this.NewPage(total)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
var taskMaps = []maps.Map{}
|
||||
tasksResp, err := this.RPC().MessageTaskRPC().ListMessageTasksWithStatus(this.AdminContext(), &pb.ListMessageTasksWithStatusRequest{
|
||||
Status: pb.ListMessageTasksWithStatusRequest_Status(params.Status),
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, task := range tasksResp.MessageTasks {
|
||||
var resultMap = maps.Map{}
|
||||
var result = task.Result
|
||||
if result != nil {
|
||||
resultMap = maps.Map{
|
||||
"isOk": result.IsOk,
|
||||
"error": result.Error,
|
||||
"response": result.Response,
|
||||
}
|
||||
}
|
||||
|
||||
//var recipients = []string{}
|
||||
var user = ""
|
||||
var instanceMap maps.Map
|
||||
if task.MessageRecipient != nil {
|
||||
user = task.MessageRecipient.User
|
||||
if task.MessageRecipient.MessageMediaInstance != nil {
|
||||
instanceMap = maps.Map{
|
||||
"id": task.MessageRecipient.MessageMediaInstance.Id,
|
||||
"name": task.MessageRecipient.MessageMediaInstance.Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
taskMaps = append(taskMaps, maps.Map{
|
||||
"id": task.Id,
|
||||
"subject": task.Subject,
|
||||
"body": task.Body,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
|
||||
"result": resultMap,
|
||||
"status": task.Status,
|
||||
"user": user,
|
||||
"instance": instanceMap,
|
||||
})
|
||||
}
|
||||
this.Data["tasks"] = taskMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeAdmin)).
|
||||
Data("teaMenu", "admins").
|
||||
Data("teaSubMenu", "recipients").
|
||||
Prefix("/admins/recipients/tasks").
|
||||
Get("", new(IndexAction)).
|
||||
Post("/taskInfo", new(TaskInfoAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type TaskInfoAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TaskInfoAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *TaskInfoAction) RunPost(params struct {
|
||||
TaskId int64
|
||||
}) {
|
||||
resp, err := this.RPC().MessageTaskRPC().FindEnabledMessageTask(this.AdminContext(), &pb.FindEnabledMessageTaskRequest{MessageTaskId: params.TaskId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.MessageTask == nil {
|
||||
this.NotFound("messageTask", params.TaskId)
|
||||
return
|
||||
}
|
||||
|
||||
result := resp.MessageTask.Result
|
||||
this.Data["status"] = resp.MessageTask.Status
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": result.IsOk,
|
||||
"response": result.Response,
|
||||
"error": result.Error,
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type TestAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestAction) Init() {
|
||||
this.Nav("", "", "test")
|
||||
}
|
||||
|
||||
func (this *TestAction) RunGet(params struct {
|
||||
RecipientId int64
|
||||
}) {
|
||||
recipientResp, err := this.RPC().MessageRecipientRPC().FindEnabledMessageRecipient(this.AdminContext(), &pb.FindEnabledMessageRecipientRequest{MessageRecipientId: params.RecipientId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
recipient := recipientResp.MessageRecipient
|
||||
if recipient == nil || recipient.Admin == nil || recipient.MessageMediaInstance == nil {
|
||||
this.NotFound("messageRecipient", params.RecipientId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["recipient"] = maps.Map{
|
||||
"id": recipient.Id,
|
||||
"admin": maps.Map{
|
||||
"id": recipient.Admin.Id,
|
||||
"fullname": recipient.Admin.Fullname,
|
||||
"username": recipient.Admin.Username,
|
||||
},
|
||||
"instance": maps.Map{
|
||||
"id": recipient.MessageMediaInstance.Id,
|
||||
"name": recipient.MessageMediaInstance.Name,
|
||||
"description": recipient.MessageMediaInstance.Description,
|
||||
},
|
||||
"user": recipient.User,
|
||||
}
|
||||
|
||||
instanceResp, err := this.RPC().MessageMediaInstanceRPC().FindEnabledMessageMediaInstance(this.AdminContext(), &pb.FindEnabledMessageMediaInstanceRequest{MessageMediaInstanceId: recipient.MessageMediaInstance.Id})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
instance := instanceResp.MessageMediaInstance
|
||||
if instance == nil || instance.MessageMedia == nil {
|
||||
this.NotFound("messageMediaInstance", recipient.MessageMediaInstance.Id)
|
||||
return
|
||||
}
|
||||
|
||||
mediaParams := maps.Map{}
|
||||
if len(instance.ParamsJSON) > 0 {
|
||||
err = json.Unmarshal(instance.ParamsJSON, &mediaParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["instance"] = maps.Map{
|
||||
"id": instance.Id,
|
||||
"isOn": instance.IsOn,
|
||||
"media": maps.Map{
|
||||
"type": instance.MessageMedia.Type,
|
||||
"name": instance.MessageMedia.Name,
|
||||
"userDescription": instance.MessageMedia.UserDescription,
|
||||
},
|
||||
"description": instance.Description,
|
||||
"params": mediaParams,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *TestAction) RunPost(params struct {
|
||||
InstanceId int64
|
||||
Subject string
|
||||
Body string
|
||||
User string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("instanceId", params.InstanceId).
|
||||
Gt(0, "请选择正确的媒介")
|
||||
|
||||
resp, err := this.RPC().MessageTaskRPC().CreateMessageTask(this.AdminContext(), &pb.CreateMessageTaskRequest{
|
||||
RecipientId: 0,
|
||||
InstanceId: params.InstanceId,
|
||||
User: params.User,
|
||||
Subject: params.Subject,
|
||||
Body: params.Body,
|
||||
IsPrimary: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["taskId"] = resp.MessageTaskId
|
||||
|
||||
defer this.CreateLogInfo("创建媒介测试任务 %d", resp.MessageTaskId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package recipients
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "update")
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunGet(params struct {
|
||||
RecipientId int64
|
||||
}) {
|
||||
recipientResp, err := this.RPC().MessageRecipientRPC().FindEnabledMessageRecipient(this.AdminContext(), &pb.FindEnabledMessageRecipientRequest{MessageRecipientId: params.RecipientId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
recipient := recipientResp.MessageRecipient
|
||||
if recipient == nil || recipient.Admin == nil || recipient.MessageMediaInstance == nil {
|
||||
this.NotFound("messageRecipient", params.RecipientId)
|
||||
return
|
||||
}
|
||||
|
||||
var timeFromHour = ""
|
||||
var timeFromMinute = ""
|
||||
var timeFromSecond = ""
|
||||
|
||||
if len(recipient.TimeFrom) > 0 {
|
||||
var pieces = strings.Split(recipient.TimeFrom, ":")
|
||||
timeFromHour = pieces[0]
|
||||
timeFromMinute = pieces[1]
|
||||
timeFromSecond = pieces[2]
|
||||
}
|
||||
|
||||
var timeToHour = ""
|
||||
var timeToMinute = ""
|
||||
var timeToSecond = ""
|
||||
if len(recipient.TimeTo) > 0 {
|
||||
var pieces = strings.Split(recipient.TimeTo, ":")
|
||||
timeToHour = pieces[0]
|
||||
timeToMinute = pieces[1]
|
||||
timeToSecond = pieces[2]
|
||||
}
|
||||
|
||||
this.Data["recipient"] = maps.Map{
|
||||
"id": recipient.Id,
|
||||
"admin": maps.Map{
|
||||
"id": recipient.Admin.Id,
|
||||
"fullname": recipient.Admin.Fullname,
|
||||
"username": recipient.Admin.Username,
|
||||
},
|
||||
"groups": recipient.MessageRecipientGroups,
|
||||
"isOn": recipient.IsOn,
|
||||
"instance": maps.Map{
|
||||
"id": recipient.MessageMediaInstance.Id,
|
||||
"name": recipient.MessageMediaInstance.Name,
|
||||
},
|
||||
"user": recipient.User,
|
||||
"description": recipient.Description,
|
||||
"timeFromHour": timeFromHour,
|
||||
"timeFromMinute": timeFromMinute,
|
||||
"timeFromSecond": timeFromSecond,
|
||||
"timeToHour": timeToHour,
|
||||
"timeToMinute": timeToMinute,
|
||||
"timeToSecond": timeToSecond,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
RecipientId int64
|
||||
|
||||
AdminId int64
|
||||
User string
|
||||
InstanceId int64
|
||||
|
||||
GroupIds string
|
||||
Description string
|
||||
IsOn bool
|
||||
|
||||
TimeFromHour string
|
||||
TimeFromMinute string
|
||||
TimeFromSecond string
|
||||
|
||||
TimeToHour string
|
||||
TimeToMinute string
|
||||
TimeToSecond string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改媒介接收人 %d", params.RecipientId)
|
||||
|
||||
params.Must.
|
||||
Field("adminId", params.AdminId).
|
||||
Gt(0, "请选择系统用户").
|
||||
Field("instanceId", params.InstanceId).
|
||||
Gt(0, "请选择媒介")
|
||||
|
||||
groupIds := utils.SplitNumbers(params.GroupIds)
|
||||
|
||||
var digitReg = regexp.MustCompile(`^\d+$`)
|
||||
|
||||
var timeFrom = ""
|
||||
if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
|
||||
timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
|
||||
}
|
||||
|
||||
var timeTo = ""
|
||||
if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
|
||||
timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
|
||||
}
|
||||
|
||||
_, err := this.RPC().MessageRecipientRPC().UpdateMessageRecipient(this.AdminContext(), &pb.UpdateMessageRecipientRequest{
|
||||
MessageRecipientId: params.RecipientId,
|
||||
AdminId: params.AdminId,
|
||||
MessageMediaInstanceId: params.InstanceId,
|
||||
User: params.User,
|
||||
MessageRecipientGroupIds: groupIds,
|
||||
Description: params.Description,
|
||||
IsOn: params.IsOn,
|
||||
TimeFrom: timeFrom,
|
||||
TimeTo: timeTo,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -59,7 +60,7 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
// 权限
|
||||
var moduleMaps = configloaders.AllModuleMaps()
|
||||
var moduleMaps = configloaders.AllModuleMaps(this.LangCode())
|
||||
for _, m := range moduleMaps {
|
||||
code := m.GetString("code")
|
||||
isChecked := false
|
||||
@@ -94,7 +95,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改系统用户 %d", params.AdminId)
|
||||
defer this.CreateLogInfo(codes.Admin_LogUpdateAdmin, params.AdminId)
|
||||
|
||||
params.Must.
|
||||
Field("fullname", params.Fullname).
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -27,18 +27,29 @@ func (this *CreateBatchAction) RunGet(params struct {
|
||||
}) {
|
||||
leftMenuItems := []maps.Map{
|
||||
{
|
||||
"name": "单个创建",
|
||||
"name": this.Lang(codes.NodeMenu_CreateSingleNode),
|
||||
"url": "/clusters/cluster/createNode?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||
"isActive": false,
|
||||
},
|
||||
{
|
||||
"name": "批量创建",
|
||||
"name": this.Lang(codes.NodeMenu_CreateMultipleNodes),
|
||||
"url": "/clusters/cluster/createBatch?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||
"isActive": true,
|
||||
},
|
||||
}
|
||||
this.Data["leftMenuItems"] = leftMenuItems
|
||||
|
||||
// 限额
|
||||
maxNodes, leftNodes, err := this.findNodesQuota()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["quota"] = maps.Map{
|
||||
"maxNodes": maxNodes,
|
||||
"leftNodes": leftNodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -104,7 +115,7 @@ func (this *CreateBatchAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "批量创建节点")
|
||||
defer this.CreateLogInfo(codes.Node_LogCreateNodeBatch)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package cluster
|
||||
|
||||
func (this *CreateBatchAction) findNodesQuota() (maxNodes int32, leftNodes int32, err error) {
|
||||
return
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package cluster
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -30,14 +30,19 @@ func (this *CreateNodeAction) Init() {
|
||||
func (this *CreateNodeAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
}) {
|
||||
if params.ClusterId <= 0 {
|
||||
this.RedirectURL("/clusters")
|
||||
return
|
||||
}
|
||||
|
||||
var leftMenuItems = []maps.Map{
|
||||
{
|
||||
"name": "单个创建",
|
||||
"name": this.Lang(codes.NodeMenu_CreateSingleNode),
|
||||
"url": "/clusters/cluster/createNode?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||
"isActive": true,
|
||||
},
|
||||
{
|
||||
"name": "批量创建",
|
||||
"name": this.Lang(codes.NodeMenu_CreateMultipleNodes),
|
||||
"url": "/clusters/cluster/createBatch?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||
"isActive": false,
|
||||
},
|
||||
@@ -92,6 +97,17 @@ func (this *CreateNodeAction) RunGet(params struct {
|
||||
// 安装文件下载
|
||||
this.Data["installerFiles"] = clusterutils.ListInstallerFiles()
|
||||
|
||||
// 限额
|
||||
maxNodes, leftNodes, err := this.findNodesQuota()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["quota"] = maps.Map{
|
||||
"maxNodes": maxNodes,
|
||||
"leftNodes": leftNodes,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -277,7 +293,7 @@ func (this *CreateNodeAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建节点 %d", nodeId)
|
||||
defer this.CreateLogInfo(codes.Node_LogCreateNode, nodeId)
|
||||
|
||||
// 响应数据
|
||||
this.Data["nodeId"] = nodeId
|
||||
@@ -300,7 +316,7 @@ func (this *CreateNodeAction) RunPost(params struct {
|
||||
"id": grantResp.NodeGrant.Id,
|
||||
"name": grantResp.NodeGrant.Name,
|
||||
"method": grantResp.NodeGrant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method, this.LangCode()),
|
||||
"username": grantResp.NodeGrant.Username,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package cluster
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -21,7 +22,7 @@ func (this *CreateNodeInstallAction) RunPost(params struct {
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
defer this.CreateLogInfo("安装节点 %d", params.NodeId)
|
||||
defer this.CreateLogInfo(codes.NodeSSH_LogUpdateNodeSSH, params.NodeId)
|
||||
|
||||
params.Must.
|
||||
Field("sshHost2", params.SshHost).
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package cluster
|
||||
|
||||
func (this *CreateNodeAction) findNodesQuota() (maxNodes int32, leftNodes int32, err error) {
|
||||
return
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ func (this *DeleteAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "删除集群 %d", params.ClusterId)
|
||||
defer this.CreateLogInfo(codes.NodeCluster_LogDeleteCluster, params.ClusterId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -48,7 +48,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建节点分组 %d", createResp.NodeGroupId)
|
||||
defer this.CreateLogInfo(codes.NodeGroup_LogCreateNodeGroup, createResp.NodeGroupId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ func (this *DeleteAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "删除集群分组 %d", params.GroupId)
|
||||
defer this.CreateLogInfo(codes.NodeGroup_LogDeleteNodeGroup, params.GroupId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ func (this *SortAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改集群分组排序")
|
||||
defer this.CreateLogInfo(codes.NodeGroup_LogSortNodeGroups)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -57,7 +57,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改集群分组 %d", params.GroupId)
|
||||
defer this.CreateLogInfo(codes.NodeGroup_LogUpdateNodeGroup, params.GroupId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
||||
Helper(clusters.NewClusterHelper()).
|
||||
Data("teaMenu", "clusters").
|
||||
Data("teaSubMenu", "cluster").
|
||||
Prefix("/clusters/cluster").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/nodes", new(NodesAction)).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user