Compare commits
291 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2e14cf0a6 | ||
|
|
bc9b6d1595 | ||
|
|
fd11b62390 | ||
|
|
8db00a5ab5 | ||
|
|
a6757e9374 | ||
|
|
e0fe385404 | ||
|
|
f9f4258ec1 | ||
|
|
f939d563ad | ||
|
|
fad03add54 | ||
|
|
9ab6dab081 | ||
|
|
5a55268830 | ||
|
|
fc3769239d | ||
|
|
c9fb3153eb | ||
|
|
a31f9ed9c5 | ||
|
|
2f75828ba4 | ||
|
|
89679ec358 | ||
|
|
98f77f52df | ||
|
|
ca647d44bb | ||
|
|
838b7dab5b | ||
|
|
4f7fe247b4 | ||
|
|
cee20bd7d2 | ||
|
|
f99ed0c4d9 | ||
|
|
4ae6523164 | ||
|
|
2ece764dcb | ||
|
|
f3bdd98af5 | ||
|
|
3c3c511c5b | ||
|
|
45167b87e5 | ||
|
|
f7d5755744 | ||
|
|
e2adafd16b | ||
|
|
9d178b238d | ||
|
|
431054a1be | ||
|
|
5bf6428253 | ||
|
|
6a4a03267b | ||
|
|
6598e16974 | ||
|
|
b1943a4cec | ||
|
|
5f70d5afd3 | ||
|
|
7dead36212 | ||
|
|
1f68a7830b | ||
|
|
42c5b7a181 | ||
|
|
6596b47e54 | ||
|
|
02e4a3e244 | ||
|
|
d9af90c76b | ||
|
|
a54405f24f | ||
|
|
2b39c5d517 | ||
|
|
a09d295948 | ||
|
|
2c0b0be8c4 | ||
|
|
b289877273 | ||
|
|
869d54b9a8 | ||
|
|
df48ae8316 | ||
|
|
e42cf2a420 | ||
|
|
bd899b649d | ||
|
|
2bc43ee2a5 | ||
|
|
48e2907426 | ||
|
|
4e33cce128 | ||
|
|
6658028f90 | ||
|
|
3fe67bb179 | ||
|
|
ce9a2d0cc3 | ||
|
|
45b7c7af15 | ||
|
|
3116aaeccb | ||
|
|
bfd5517c6c | ||
|
|
c83598a4f9 | ||
|
|
3dc983871f | ||
|
|
ac6fcefffd | ||
|
|
094080cc9f | ||
|
|
d2dff968fb | ||
|
|
496f82a01f | ||
|
|
9293c3e861 | ||
|
|
da7d42edd5 | ||
|
|
87b96d6526 | ||
|
|
a371821ff8 | ||
|
|
748cb6eb8f | ||
|
|
05bee642e9 | ||
|
|
081be04592 | ||
|
|
f77518d086 | ||
|
|
35e6202b3c | ||
|
|
1d84ea6ab9 | ||
|
|
df461d81d8 | ||
|
|
821d5aa595 | ||
|
|
c25bd71592 | ||
|
|
7e9224680e | ||
|
|
bc89116d3d | ||
|
|
aabe75ee13 | ||
|
|
8903adc523 | ||
|
|
61d8c8cd39 | ||
|
|
2e1d991e0c | ||
|
|
5a57167832 | ||
|
|
c3df181dcc | ||
|
|
699ea47ac5 | ||
|
|
fba69f3109 | ||
|
|
063583dda1 | ||
|
|
a31ca5cfb6 | ||
|
|
c0a13305ad | ||
|
|
9d8cbf87dc | ||
|
|
009f7da26b | ||
|
|
f1d0359dc4 | ||
|
|
473baa8c5b | ||
|
|
1a5dda6c72 | ||
|
|
8a78dcb06e | ||
|
|
185768a80f | ||
|
|
619a2817ce | ||
|
|
12a33ee9fc | ||
|
|
26302ca930 | ||
|
|
7e85555ba7 | ||
|
|
2546676f6a | ||
|
|
93b7edf5c4 | ||
|
|
571e432263 | ||
|
|
580b158567 | ||
|
|
395aa665d7 | ||
|
|
a80e54e0b3 | ||
|
|
8ccd41b551 | ||
|
|
9d6a3a8a0d | ||
|
|
a9d1b4b863 | ||
|
|
459d664a60 | ||
|
|
5f10b0156c | ||
|
|
348c07f847 | ||
|
|
934b1894c4 | ||
|
|
a660f4af93 | ||
|
|
12c0d39b13 | ||
|
|
bc6de68006 | ||
|
|
52bb753594 | ||
|
|
ed6b763d06 | ||
|
|
07e421afea | ||
|
|
71352841bf | ||
|
|
91e8fcbb24 | ||
|
|
5b67a85624 | ||
|
|
b49efa0d5a | ||
|
|
abeb585a0d | ||
|
|
cf621f1cc9 | ||
|
|
389a494e00 | ||
|
|
a9c55dc23b | ||
|
|
3d35b7e71b | ||
|
|
6f78146711 | ||
|
|
8afa47c351 | ||
|
|
20838cfc3e | ||
|
|
d00acd6d2f | ||
|
|
0b58a36779 | ||
|
|
54bc98e9c1 | ||
|
|
836daf2ad9 | ||
|
|
9a8cd9bd87 | ||
|
|
c555d91503 | ||
|
|
e0e7c1bcc4 | ||
|
|
4bf733beec | ||
|
|
e93f23c943 | ||
|
|
5384f4d9f2 | ||
|
|
3c0a97c3cc | ||
|
|
129db6cf4e | ||
|
|
c6bfa5652f | ||
|
|
780472d83e | ||
|
|
0d02e3f15a | ||
|
|
bf82f22d0f | ||
|
|
e18f182ce6 | ||
|
|
761c26b587 | ||
|
|
5e62769dcf | ||
|
|
86b8a718a0 | ||
|
|
a729cfc31d | ||
|
|
96cfda852a | ||
|
|
0423d9246c | ||
|
|
985798757f | ||
|
|
72876f6749 | ||
|
|
03d6e223d8 | ||
|
|
62d9f2ed97 | ||
|
|
a550a44a52 | ||
|
|
b19d586949 | ||
|
|
bbfa3ee57f | ||
|
|
af409dd3b8 | ||
|
|
3db79ca149 | ||
|
|
e880420494 | ||
|
|
28ec17b8fe | ||
|
|
8026a40807 | ||
|
|
068c6d406a | ||
|
|
57470e4ef0 | ||
|
|
ca8e1537f5 | ||
|
|
d67b818398 | ||
|
|
f5f46424bb | ||
|
|
1e259717ce | ||
|
|
91ece99a9c | ||
|
|
d30ebdb369 | ||
|
|
ade8522b69 | ||
|
|
159b308f31 | ||
|
|
837bf25f7b | ||
|
|
8301d3669b | ||
|
|
cc752e8d80 | ||
|
|
d20e6bd42f | ||
|
|
bfee9fe233 | ||
|
|
9c962b09f1 | ||
|
|
46edefead7 | ||
|
|
725cfc8a2b | ||
|
|
1ce48b9ef4 | ||
|
|
37cc28f225 | ||
|
|
5ebe3bb8e0 | ||
|
|
aa01512f89 | ||
|
|
37ff2b886a | ||
|
|
ce18212756 | ||
|
|
08f50a274a | ||
|
|
892ee0013a | ||
|
|
e9a47041fd | ||
|
|
d419fa06e8 | ||
|
|
8b961a890c | ||
|
|
db32915114 | ||
|
|
2ffdb10cce | ||
|
|
507fd7e5d4 | ||
|
|
7df599b5a9 | ||
|
|
9987334f55 | ||
|
|
d8c3365384 | ||
|
|
2e284b5af9 | ||
|
|
89ddd4e6a3 | ||
|
|
36524ea481 | ||
|
|
35cf693610 | ||
|
|
42148a66bd | ||
|
|
96878715bf | ||
|
|
1a5f3342e7 | ||
|
|
3613d13a2b | ||
|
|
7786140d85 | ||
|
|
3a23b57f1b | ||
|
|
5a6e6fba69 | ||
|
|
910b3a6162 | ||
|
|
0dc19bed45 | ||
|
|
a7bdb64301 | ||
|
|
4739072a85 | ||
|
|
07bdae2488 | ||
|
|
b84035d821 | ||
|
|
dc0a7b9dae | ||
|
|
2937bd8de0 | ||
|
|
37315ef4d9 | ||
|
|
1986fece07 | ||
|
|
16b1657f35 | ||
|
|
c115c62cd9 | ||
|
|
d2df7f8d5b | ||
|
|
6cb79864e6 | ||
|
|
982d28c7b4 | ||
|
|
0f57516fdc | ||
|
|
75a89defcb | ||
|
|
22a6c52060 | ||
|
|
37607e4a41 | ||
|
|
5936155998 | ||
|
|
8d76de935f | ||
|
|
9baa530064 | ||
|
|
103414b338 | ||
|
|
72fe68ebfe | ||
|
|
cfed31958b | ||
|
|
3d5fca2d36 | ||
|
|
a5710286ec | ||
|
|
d0ce0c6c58 | ||
|
|
779e2cf0f2 | ||
|
|
2108474777 | ||
|
|
e25e0f1747 | ||
|
|
e8e74b639c | ||
|
|
a14fcd1e50 | ||
|
|
485c0e0891 | ||
|
|
00a19e9d43 | ||
|
|
67d0dc0783 | ||
|
|
3718c35842 | ||
|
|
567ffc80b6 | ||
|
|
5d15a08ac8 | ||
|
|
2b84037346 | ||
|
|
536f11e617 | ||
|
|
5d367a384e | ||
|
|
04933e6bf0 | ||
|
|
12abe9aa69 | ||
|
|
cba642a4bc | ||
|
|
9fce0ac0aa | ||
|
|
681812b619 | ||
|
|
6d0be57698 | ||
|
|
1d521602e1 | ||
|
|
2f67e7937a | ||
|
|
e0078a42dc | ||
|
|
3ec875d49d | ||
|
|
35028d1310 | ||
|
|
7ba3d7c4bb | ||
|
|
c05e64098c | ||
|
|
e82ee56a2c | ||
|
|
5681b61aea | ||
|
|
d6ce7eab25 | ||
|
|
bf597fe41c | ||
|
|
6a920f964f | ||
|
|
977b66ba4e | ||
|
|
4659c29358 | ||
|
|
e3bc95b275 | ||
|
|
bf51255e13 | ||
|
|
915fe6837b | ||
|
|
d1237215c0 | ||
|
|
8ba5cfdfa6 | ||
|
|
0d1097425d | ||
|
|
0789a9ecc8 | ||
|
|
400f764b74 | ||
|
|
46e036e3a4 | ||
|
|
17e6264af8 | ||
|
|
468b6ae125 | ||
|
|
e5f5ee4f6a | ||
|
|
3695082ec2 | ||
|
|
f384d86014 |
@@ -56,5 +56,8 @@
|
||||
## 联系我们
|
||||
有什么问题和建议都可以加入QQ群 `659832182` 或者 [Telegram群](https://t.me/+5kVCMGxQhZxiODY9) 。
|
||||
|
||||
## 企业版
|
||||
* [GoEdge企业版](https://goedge.cn/commercial) - 功能更强大的CDN系统
|
||||
|
||||
## 感谢
|
||||
* 感谢 [Gitee](https://gitee.com/) 提供国内源代码托管平台
|
||||
@@ -105,8 +105,8 @@ function build() {
|
||||
find "$DIST" -name ".DS_Store" -delete
|
||||
find "$DIST" -name ".gitignore" -delete
|
||||
find "$DIST" -name "*.less" -delete
|
||||
find "$DIST" -name "*.css.map" -delete
|
||||
find "$DIST" -name "*.js.map" -delete
|
||||
#find "$DIST" -name "*.css.map" -delete
|
||||
#find "$DIST" -name "*.js.map" -delete
|
||||
|
||||
# zip
|
||||
echo "zip files ..."
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
@@ -9,10 +11,13 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -20,7 +25,7 @@ func main() {
|
||||
var app = apps.NewAppCmd().
|
||||
Version(teaconst.Version).
|
||||
Product(teaconst.ProductName).
|
||||
Usage(teaconst.ProcessName+" [-v|start|stop|restart|service|daemon|reset|recover|demo|upgrade]").
|
||||
Usage(teaconst.ProcessName+" [-h|-v|start|stop|restart|service|daemon|reset|recover|demo|upgrade]").
|
||||
Usage(teaconst.ProcessName+" [dev|prod]").
|
||||
Option("-h", "show this help").
|
||||
Option("-v", "show version").
|
||||
@@ -34,7 +39,7 @@ func main() {
|
||||
Option("demo", "switch to demo mode").
|
||||
Option("dev", "switch to 'dev' mode").
|
||||
Option("prod", "switch to 'prod' mode").
|
||||
Option("upgrade", "upgrade from official site")
|
||||
Option("upgrade [--url=URL]", "upgrade from official site or an url")
|
||||
|
||||
app.On("daemon", func() {
|
||||
nodes.NewAdminNode().Daemon()
|
||||
@@ -53,6 +58,20 @@ func main() {
|
||||
fmt.Println("[ERROR]reset failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// reset local api
|
||||
var apiNodeExe = Tea.Root + "/edge-api/bin/edge-api"
|
||||
_, err = os.Stat(apiNodeExe)
|
||||
if err == nil {
|
||||
var cmd = exec.Command(apiNodeExe, "reset")
|
||||
var stderr = &bytes.Buffer{}
|
||||
cmd.Stderr = stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println("reset api node failed: " + stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("done")
|
||||
})
|
||||
app.On("recover", func() {
|
||||
@@ -120,7 +139,12 @@ func main() {
|
||||
}
|
||||
})
|
||||
app.On("upgrade", func() {
|
||||
var manager = utils.NewUpgradeManager("admin")
|
||||
var downloadURL = ""
|
||||
var flagSet = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
flagSet.StringVar(&downloadURL, "url", "", "new version download url")
|
||||
_ = flagSet.Parse(os.Args[2:])
|
||||
|
||||
var manager = utils.NewUpgradeManager("admin", downloadURL)
|
||||
log.Println("checking latest version ...")
|
||||
var ticker = time.NewTicker(1 * time.Second)
|
||||
go func() {
|
||||
|
||||
1
docker/.gitignore
vendored
Normal file
1
docker/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.zip
|
||||
37
docker/Dockerfile
Normal file
37
docker/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
FROM alpine:latest
|
||||
LABEL maintainer="iwind.liu@gmail.com"
|
||||
ENV TZ "Asia/Shanghai"
|
||||
ENV VERSION 1.0.1
|
||||
ENV ROOT_DIR /usr/local/goedge
|
||||
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
|
||||
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
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
|
||||
RUN apk add wget
|
||||
RUN mkdir ${ROOT_DIR}; \
|
||||
cd ${ROOT_DIR}; \
|
||||
wget ${TAR_URL} -O ${TAR_FILE}; \
|
||||
apk add unzip; \
|
||||
unzip ${TAR_FILE}; \
|
||||
rm -f ${TAR_FILE}
|
||||
|
||||
RUN apk add mysql mysql-client; \
|
||||
sed -e "s/\[mysqld\]/\[mysqld\]\n\ndatadir=\/var\/lib\/mysql\nport=3306\ninnodb_flush_log_at_trx_commit=2\nmax_connections=256\nmax_prepared_stmt_count=65535\nbinlog_cache_size=1M\nbinlog_stmt_cache_size=1M\nthread_cache_size=32\nbinlog_expire_logs_seconds=1209600\n\n/" /etc/my.cnf > /tmp/my.cnf; \
|
||||
cp /tmp/my.cnf /etc/my.cnf; \
|
||||
sed -e "s/skip-networking/#skip-networking/" /etc/my.cnf.d/mariadb-server.cnf > /tmp/mariadb-server.cnf; \
|
||||
cp /tmp/mariadb-server.cnf /etc/my.cnf.d/mariadb-server.cnf; \
|
||||
mysql_install_db --user=mysql
|
||||
RUN mysqld_safe --user=mysql & \
|
||||
sleep 5; \
|
||||
mysql -uroot -hlocalhost --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';"
|
||||
|
||||
RUN echo -e "#!/usr/bin/env sh\n\nmysqld_safe --user=mysql &\n/usr/local/goedge/edge-admin/bin/edge-admin\n" > ${ROOT_DIR}/run.sh; \
|
||||
chmod u+x ${ROOT_DIR}/run.sh
|
||||
|
||||
EXPOSE 7788
|
||||
EXPOSE 8001
|
||||
EXPOSE 3306
|
||||
|
||||
ENTRYPOINT [ "/usr/local/goedge/run.sh" ]
|
||||
5
docker/build.sh
Executable file
5
docker/build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION=latest
|
||||
|
||||
docker build --no-cache -t goedge/edge-admin:${VERSION} .
|
||||
5
docker/run.sh
Executable file
5
docker/run.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION=latest
|
||||
|
||||
docker run -d -p 7788:7788 -p 8001:8001 -p 3306:3306 --name edge-admin goedge/edge-admin:${VERSION}
|
||||
14
go.mod
14
go.mod
@@ -8,16 +8,17 @@ 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-20220304043459-0dd944a5b475
|
||||
github.com/iwind/TeaGo v0.0.0-20230304012706-c1f4a4e27470
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||
github.com/miekg/dns v1.1.43
|
||||
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/sys v0.0.0-20220412211240-33da011f77ad
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/sys v0.6.0
|
||||
google.golang.org/grpc v1.45.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -26,18 +27,15 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/rogpeppe/fastuuid v1.2.0 // indirect
|
||||
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
||||
37
go.sum
37
go.sum
@@ -21,7 +21,6 @@ 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -74,13 +73,12 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
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-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
|
||||
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/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=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -92,11 +90,7 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2
|
||||
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=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -111,7 +105,6 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
|
||||
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=
|
||||
@@ -131,7 +124,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
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/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
|
||||
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
|
||||
@@ -146,6 +138,8 @@ 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/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=
|
||||
@@ -170,8 +164,8 @@ 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.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
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=
|
||||
@@ -198,20 +192,17 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
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.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
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/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=
|
||||
@@ -266,7 +257,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -209,7 +209,7 @@ func (this *AppCmd) runStop() {
|
||||
fmt.Println(this.product+" stopped ok, pid:", types.String(pid))
|
||||
}
|
||||
|
||||
// 重启
|
||||
// RunRestart 重启
|
||||
func (this *AppCmd) RunRestart() {
|
||||
this.runStop()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
@@ -167,7 +167,7 @@ func AllModuleMaps() []maps.Map {
|
||||
"url": "/dashboard",
|
||||
},
|
||||
{
|
||||
"name": "网站服务",
|
||||
"name": "网站列表",
|
||||
"code": AdminModuleCodeServer,
|
||||
"url": "/servers",
|
||||
},
|
||||
|
||||
@@ -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,96 +0,0 @@
|
||||
package configloaders
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var sharedUserUIConfig *systemconfigs.UserUIConfig = nil
|
||||
|
||||
const (
|
||||
UserUISettingName = "userUIConfig"
|
||||
)
|
||||
|
||||
func LoadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
config, err := loadUserUIConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.UserUIConfig)
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func UpdateUserUIConfig(uiConfig *systemconfigs.UserUIConfig) error {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
|
||||
var rpcClient, err = rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valueJSON, err := json.Marshal(uiConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
|
||||
Code: UserUISettingName,
|
||||
ValueJSON: valueJSON,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sharedUserUIConfig = uiConfig
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
|
||||
if sharedUserUIConfig != nil {
|
||||
return sharedUserUIConfig, nil
|
||||
}
|
||||
var rpcClient, err = rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
|
||||
Code: UserUISettingName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp.ValueJSON) == 0 {
|
||||
sharedUserUIConfig = defaultUserUIConfig()
|
||||
return sharedUserUIConfig, nil
|
||||
}
|
||||
|
||||
config := &systemconfigs.UserUIConfig{}
|
||||
err = json.Unmarshal(resp.ValueJSON, config)
|
||||
if err != nil {
|
||||
logs.Println("[UI_MANAGER]" + err.Error())
|
||||
sharedUserUIConfig = defaultUserUIConfig()
|
||||
return sharedUserUIConfig, nil
|
||||
}
|
||||
sharedUserUIConfig = config
|
||||
return sharedUserUIConfig, nil
|
||||
}
|
||||
|
||||
func defaultUserUIConfig() *systemconfigs.UserUIConfig {
|
||||
return &systemconfigs.UserUIConfig{
|
||||
ProductName: "GoEdge",
|
||||
UserSystemName: "GoEdge用户系统",
|
||||
ShowOpenSourceInfo: true,
|
||||
ShowVersion: true,
|
||||
ShowFinance: true,
|
||||
BandwidthUnit: systemconfigs.BandwidthUnitBit,
|
||||
ShowBandwidthCharts: true,
|
||||
ShowTrafficCharts: true,
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,9 @@ type APIConfig struct {
|
||||
// LoadAPIConfig 加载API配置
|
||||
func LoadAPIConfig() (*APIConfig, error) {
|
||||
// 候选文件
|
||||
localFile := Tea.ConfigFile("api.yaml")
|
||||
isFromLocal := false
|
||||
paths := []string{localFile}
|
||||
var localFile = Tea.ConfigFile("api.yaml")
|
||||
var isFromLocal = false
|
||||
var paths = []string{localFile}
|
||||
homeDir, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
paths = append(paths, homeDir+"/."+teaconst.ProcessName+"/api.yaml")
|
||||
@@ -45,7 +45,7 @@ func LoadAPIConfig() (*APIConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := &APIConfig{}
|
||||
var config = &APIConfig{}
|
||||
err = yaml.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -61,10 +61,11 @@ func LoadAPIConfig() (*APIConfig, error) {
|
||||
|
||||
// ResetAPIConfig 重置配置
|
||||
func ResetAPIConfig() error {
|
||||
filename := "api.yaml"
|
||||
var filename = "api.yaml"
|
||||
|
||||
// 重置 configs/api.yaml
|
||||
{
|
||||
configFile := Tea.ConfigFile(filename)
|
||||
var configFile = Tea.ConfigFile(filename)
|
||||
stat, err := os.Stat(configFile)
|
||||
if err == nil && !stat.IsDir() {
|
||||
err = os.Remove(configFile)
|
||||
@@ -77,7 +78,7 @@ func ResetAPIConfig() error {
|
||||
// 重置 ~/.edge-admin/api.yaml
|
||||
homeDir, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
configFile := homeDir + "/." + teaconst.ProcessName + "/" + filename
|
||||
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
|
||||
stat, err := os.Stat(configFile)
|
||||
if err == nil && !stat.IsDir() {
|
||||
err = os.Remove(configFile)
|
||||
@@ -89,7 +90,7 @@ func ResetAPIConfig() error {
|
||||
|
||||
// 重置 /etc/edge-admin/api.yaml
|
||||
{
|
||||
configFile := "/etc/" + teaconst.ProcessName + "/" + filename
|
||||
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
|
||||
stat, err := os.Stat(configFile)
|
||||
if err == nil && !stat.IsDir() {
|
||||
err = os.Remove(configFile)
|
||||
@@ -154,3 +155,15 @@ func (this *APIConfig) WriteFile(path string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone 克隆当前配置
|
||||
func (this *APIConfig) Clone() *APIConfig {
|
||||
return &APIConfig{
|
||||
RPC: struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
DisableUpdate bool `yaml:"disableUpdate"`
|
||||
}{},
|
||||
NodeId: this.NodeId,
|
||||
Secret: this.Secret,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package configs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"os"
|
||||
)
|
||||
|
||||
var plusConfigFile = "plus.cache.json"
|
||||
|
||||
type PlusConfig struct {
|
||||
IsPlus bool `json:"isPlus"`
|
||||
Components []string `json:"components"`
|
||||
DayTo string `json:"dayTo"`
|
||||
}
|
||||
|
||||
func ReadPlusConfig() *PlusConfig {
|
||||
data, err := os.ReadFile(Tea.ConfigFile(plusConfigFile))
|
||||
if err != nil {
|
||||
return &PlusConfig{IsPlus: false}
|
||||
}
|
||||
var config = &PlusConfig{IsPlus: false}
|
||||
err = json.Unmarshal(data, config)
|
||||
if err != nil {
|
||||
return config
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func WritePlusConfig(config *PlusConfig) error {
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(Tea.ConfigFile(plusConfigFile), configJSON, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.5.5"
|
||||
Version = "1.0.4"
|
||||
|
||||
APINodeVersion = "0.5.5"
|
||||
APINodeVersion = "1.0.4"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
@@ -18,5 +18,5 @@ const (
|
||||
CookieSID = "edgesid"
|
||||
|
||||
SystemdServiceName = "edge-admin"
|
||||
UpdatesURL = "https://goedge.cn/api/boot/versions?os=${os}&arch=${arch}"
|
||||
UpdatesURL = "https://goedge.cn/api/boot/versions?os=${os}&arch=${arch}&version=${version}"
|
||||
)
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
package teaconst
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
IsRecoverMode = false
|
||||
|
||||
@@ -10,4 +15,18 @@ var (
|
||||
|
||||
NewVersionCode = "" // 有新的版本
|
||||
NewVersionDownloadURL = "" // 新版本下载地址
|
||||
|
||||
IsMain = checkMain()
|
||||
)
|
||||
|
||||
// 检查是否为主程序
|
||||
func checkMain() bool {
|
||||
if len(os.Args) == 1 ||
|
||||
(len(os.Args) >= 2 && os.Args[1] == "pprof") {
|
||||
return true
|
||||
}
|
||||
exe, _ := os.Executable()
|
||||
return strings.HasSuffix(exe, ".test") ||
|
||||
strings.HasSuffix(exe, ".test.exe") ||
|
||||
strings.Contains(exe, "___")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"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"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
@@ -115,6 +116,16 @@ func generateComponentsJSFile() error {
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
}
|
||||
|
||||
// WAF操作符
|
||||
wafOperatorsJSON, err := json.Marshal(firewallconfigs.AllRuleOperators)
|
||||
if err != nil {
|
||||
logs.Println("ComponentsAction marshal waf rule operators failed: " + err.Error())
|
||||
} else {
|
||||
buffer.WriteString("window.WAF_RULE_OPERATORS = ")
|
||||
buffer.Write(wafOperatorsJSON)
|
||||
buffer.Write([]byte{'\n', '\n'})
|
||||
}
|
||||
|
||||
fp, err := os.OpenFile(filepath.Clean(Tea.PublicFile("/js/components.src.js")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/sessions"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/iwind/gosock/pkg/gosock"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -21,6 +20,8 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
@@ -84,11 +85,19 @@ func (this *AdminNode) Run() {
|
||||
// 启动API节点
|
||||
this.startAPINode()
|
||||
|
||||
// 启动IP库
|
||||
this.startIPLibrary()
|
||||
|
||||
// 启动Web服务
|
||||
sessionManager, err := NewSessionManager()
|
||||
if err != nil {
|
||||
log.Fatal("start session failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
TeaGo.NewServer(false).
|
||||
AccessLog(false).
|
||||
EndAll().
|
||||
Session(sessions.NewFileSessionManager(86400, secret), teaconst.CookieSID).
|
||||
Session(sessionManager, teaconst.CookieSID).
|
||||
ReadHeaderTimeout(3 * time.Second).
|
||||
ReadTimeout(1200 * time.Second).
|
||||
Start()
|
||||
@@ -360,6 +369,16 @@ func (this *AdminNode) listenSock() error {
|
||||
}
|
||||
}
|
||||
|
||||
// 停止当前目录下的API节点
|
||||
var apiSock = gosock.NewTmpSock("edge-api")
|
||||
apiReply, err := apiSock.Send(&gosock.Command{Code: "info"})
|
||||
if err == nil {
|
||||
adminExe, _ := os.Executable()
|
||||
if len(adminExe) > 0 && apiReply != nil && strings.HasPrefix(maps.NewMap(apiReply.Params).GetString("path"), filepath.Dir(filepath.Dir(adminExe))) {
|
||||
_, _ = apiSock.Send(&gosock.Command{Code: "stop"})
|
||||
}
|
||||
}
|
||||
|
||||
// 退出主进程
|
||||
events.Notify(events.EventQuit)
|
||||
os.Exit(0)
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
98
internal/nodes/session_manager.go
Normal file
98
internal/nodes/session_manager.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SessionManager SESSION管理
|
||||
type SessionManager struct {
|
||||
life uint
|
||||
}
|
||||
|
||||
func NewSessionManager() (*SessionManager, error) {
|
||||
return &SessionManager{}, nil
|
||||
}
|
||||
|
||||
func (this *SessionManager) Init(config *actions.SessionConfig) {
|
||||
this.life = config.Life
|
||||
}
|
||||
|
||||
func (this *SessionManager) Read(sid string) map[string]string {
|
||||
// 忽略OTP
|
||||
if strings.HasSuffix(sid, "_otp") {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
var result = map[string]string{}
|
||||
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return 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
|
||||
}
|
||||
|
||||
var session = resp.LoginSession
|
||||
if session == nil || len(session.ValuesJSON) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
err = json.Unmarshal(session.ValuesJSON, &result)
|
||||
if err != nil {
|
||||
logs.Println("SESSION", "decode '"+sid+"' values failed: "+err.Error())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *SessionManager) WriteItem(sid string, key string, value string) bool {
|
||||
// 忽略OTP
|
||||
if strings.HasSuffix(sid, "_otp") {
|
||||
return false
|
||||
}
|
||||
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err = rpcClient.LoginSessionRPC().WriteLoginSessionValue(rpcClient.Context(0), &pb.WriteLoginSessionValueRequest{
|
||||
Sid: sid,
|
||||
Key: key,
|
||||
Value: value,
|
||||
})
|
||||
if err != nil {
|
||||
logs.Println("SESSION", "write sid:'"+sid+"' key:'"+key+"' failed: "+err.Error())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *SessionManager) Delete(sid string) bool {
|
||||
// 忽略OTP
|
||||
if strings.HasSuffix(sid, "_otp") {
|
||||
return false
|
||||
}
|
||||
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = rpcClient.LoginSessionRPC().DeleteLoginSession(rpcClient.Context(0), &pb.DeleteLoginSessionRequest{Sid: sid})
|
||||
if err != nil {
|
||||
logs.Println("SESSION", "delete '"+sid+"' failed: "+err.Error())
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -98,10 +98,6 @@ func (this *RPCClient) NodeRegionRPC() pb.NodeRegionServiceClient {
|
||||
return pb.NewNodeRegionServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) NodePriceItemRPC() pb.NodePriceItemServiceClient {
|
||||
return pb.NewNodePriceItemServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
|
||||
return pb.NewNodeIPAddressServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -411,6 +407,10 @@ func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
|
||||
return pb.NewLoginServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) LoginSessionRPC() pb.LoginSessionServiceClient {
|
||||
return pb.NewLoginSessionServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) NodeTaskRPC() pb.NodeTaskServiceClient {
|
||||
return pb.NewNodeTaskServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -455,14 +455,6 @@ func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceC
|
||||
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) PlanRPC() pb.PlanServiceClient {
|
||||
return pb.NewPlanServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) UserPlanRPC() pb.UserPlanServiceClient {
|
||||
return pb.NewUserPlanServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
|
||||
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
|
||||
}
|
||||
@@ -552,8 +544,11 @@ func (this *RPCClient) init() error {
|
||||
}
|
||||
|
||||
var conn *grpc.ClientConn
|
||||
var callOptions = grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024),
|
||||
grpc.UseCompressor(gzip.Name))
|
||||
var callOptions = grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(128<<20),
|
||||
grpc.MaxCallSendMsgSize(128<<20),
|
||||
grpc.UseCompressor(gzip.Name),
|
||||
)
|
||||
if u.Scheme == "http" {
|
||||
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions)
|
||||
} else if u.Scheme == "https" {
|
||||
@@ -583,41 +578,30 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
|
||||
defer this.locker.Unlock()
|
||||
|
||||
// 检查连接状态
|
||||
if len(this.conns) > 0 {
|
||||
var availableConns = []*grpc.ClientConn{}
|
||||
for _, state := range []connectivity.State{connectivity.Ready, connectivity.Idle, connectivity.Connecting} {
|
||||
var countConns = len(this.conns)
|
||||
if countConns > 0 {
|
||||
if countConns == 1 {
|
||||
return this.conns[0]
|
||||
}
|
||||
for _, state := range []connectivity.State{
|
||||
connectivity.Ready,
|
||||
connectivity.Idle,
|
||||
connectivity.Connecting,
|
||||
connectivity.TransientFailure,
|
||||
} {
|
||||
var availableConns = []*grpc.ClientConn{}
|
||||
for _, conn := range this.conns {
|
||||
if conn.GetState() == state {
|
||||
availableConns = append(availableConns, conn)
|
||||
}
|
||||
}
|
||||
if len(availableConns) > 0 {
|
||||
break
|
||||
return this.randConn(availableConns)
|
||||
}
|
||||
}
|
||||
|
||||
if len(availableConns) > 0 {
|
||||
return availableConns[rands.Int(0, len(availableConns)-1)]
|
||||
}
|
||||
|
||||
// 关闭
|
||||
for _, conn := range this.conns {
|
||||
_ = conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// 重新初始化
|
||||
err := this.init()
|
||||
if err != nil {
|
||||
// 错误提示已经在构造对象时打印过,所以这里不再重复打印
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(this.conns) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return this.conns[rands.Int(0, len(this.conns)-1)]
|
||||
return this.randConn(this.conns)
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
@@ -651,3 +635,14 @@ func (this *RPCClient) localIPAddrs() []string {
|
||||
}
|
||||
return localIPAddrs
|
||||
}
|
||||
|
||||
func (this *RPCClient) randConn(conns []*grpc.ClientConn) *grpc.ClientConn {
|
||||
var l = len(conns)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
if l == 1 {
|
||||
return conns[0]
|
||||
}
|
||||
return conns[rands.Int(0, l-1)]
|
||||
}
|
||||
|
||||
@@ -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,23 @@ 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
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ func (this *CheckUpdatesTask) Loop() error {
|
||||
var apiURL = teaconst.UpdatesURL
|
||||
apiURL = strings.ReplaceAll(apiURL, "${os}", runtime.GOOS)
|
||||
apiURL = strings.ReplaceAll(apiURL, "${arch}", runtime.GOARCH)
|
||||
apiURL = strings.ReplaceAll(apiURL, "${version}", teaconst.Version)
|
||||
resp, err := http.Get(apiURL)
|
||||
if err != nil {
|
||||
return errors.New("read api failed: " + err.Error())
|
||||
|
||||
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
|
||||
}
|
||||
30
internal/utils/apinodeutils/manager.go
Normal file
30
internal/utils/apinodeutils/manager.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils
|
||||
|
||||
var SharedManager = NewManager()
|
||||
|
||||
type Manager struct {
|
||||
upgraderMap map[int64]*Upgrader
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{
|
||||
upgraderMap: map[int64]*Upgrader{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Manager) AddUpgrader(upgrader *Upgrader) {
|
||||
this.upgraderMap[upgrader.apiNodeId] = upgrader
|
||||
}
|
||||
|
||||
func (this *Manager) FindUpgrader(apiNodeId int64) *Upgrader {
|
||||
return this.upgraderMap[apiNodeId]
|
||||
}
|
||||
|
||||
func (this *Manager) RemoveUpgrader(upgrader *Upgrader) {
|
||||
if upgrader == nil {
|
||||
return
|
||||
}
|
||||
delete(this.upgraderMap, upgrader.apiNodeId)
|
||||
}
|
||||
349
internal/utils/apinodeutils/upgrader.go
Normal file
349
internal/utils/apinodeutils/upgrader.go
Normal file
@@ -0,0 +1,349 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Progress struct {
|
||||
Percent float64
|
||||
}
|
||||
|
||||
type Upgrader struct {
|
||||
progress *Progress
|
||||
apiExe string
|
||||
apiNodeId int64
|
||||
}
|
||||
|
||||
func NewUpgrader(apiNodeId int64) *Upgrader {
|
||||
return &Upgrader{
|
||||
apiExe: apiExe(),
|
||||
progress: &Progress{Percent: 0},
|
||||
apiNodeId: apiNodeId,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Upgrader) Upgrade() error {
|
||||
sharedClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiNodeResp, err := sharedClient.APINodeRPC().FindEnabledAPINode(sharedClient.Context(0), &pb.FindEnabledAPINodeRequest{ApiNodeId: this.apiNodeId})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var apiNode = apiNodeResp.ApiNode
|
||||
if apiNode == nil {
|
||||
return errors.New("could not find api node with id '" + types.String(this.apiNodeId) + "'")
|
||||
}
|
||||
|
||||
apiConfig, err := configs.LoadAPIConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var newAPIConfig = apiConfig.Clone()
|
||||
newAPIConfig.RPC.Endpoints = apiNode.AccessAddrs
|
||||
|
||||
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级边缘节点
|
||||
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 errors.New("upgrade api node failed: " + err.Error())
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if !Tea.IsTesting() /** 开发环境下允许突破此限制方便测试 **/ &&
|
||||
(stringutil.VersionCompare(versionResp.Version, "0.6.4" /** 从0.6.4开始支持 **/) < 0 || versionResp.Os != runtime.GOOS || versionResp.Arch != runtime.GOARCH) {
|
||||
return errors.New("could not upgrade api node v" + versionResp.Version + "/" + versionResp.Os + "/" + versionResp.Arch)
|
||||
}
|
||||
|
||||
// 检查本地文件版本
|
||||
canUpgrade, reason := CanUpgrade(versionResp.Version, versionResp.Os, versionResp.Arch)
|
||||
if !canUpgrade {
|
||||
return errors.New(reason)
|
||||
}
|
||||
|
||||
localVersion, err := lookupLocalVersion()
|
||||
if err != nil {
|
||||
return errors.New("lookup version failed: " + err.Error())
|
||||
}
|
||||
|
||||
// 检查要升级的文件
|
||||
var gzFile = this.apiExe + "." + localVersion + ".gz"
|
||||
|
||||
gzReader, err := os.Open(gzFile)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
err = func() error {
|
||||
// 压缩文件
|
||||
exeReader, err := os.Open(this.apiExe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = exeReader.Close()
|
||||
}()
|
||||
var tmpGzFile = gzFile + ".tmp"
|
||||
gzFileWriter, err := os.OpenFile(tmpGzFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var gzWriter = gzip.NewWriter(gzFileWriter)
|
||||
defer func() {
|
||||
_ = gzWriter.Close()
|
||||
_ = gzFileWriter.Close()
|
||||
|
||||
_ = os.Rename(tmpGzFile, gzFile)
|
||||
}()
|
||||
_, err = io.Copy(gzWriter, exeReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gzReader, err = os.Open(gzFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = gzReader.Close()
|
||||
}()
|
||||
|
||||
// 开始上传
|
||||
var hash = md5.New()
|
||||
var buf = make([]byte, 128*4096)
|
||||
var isFirst = true
|
||||
stat, err := gzReader.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var totalSize = stat.Size()
|
||||
if totalSize == 0 {
|
||||
_ = gzReader.Close()
|
||||
_ = os.Remove(gzFile)
|
||||
return errors.New("invalid gz file")
|
||||
}
|
||||
|
||||
var uploadedSize int64 = 0
|
||||
for {
|
||||
n, err := gzReader.Read(buf)
|
||||
if n > 0 {
|
||||
// 计算Hash
|
||||
hash.Write(buf[:n])
|
||||
|
||||
// 上传
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadAPINodeFile(rpcClient.Context(0), &pb.UploadAPINodeFileRequest{
|
||||
Filename: filepath.Base(this.apiExe),
|
||||
Sum: "",
|
||||
ChunkData: buf[:n],
|
||||
IsFirstChunk: isFirst,
|
||||
IsLastChunk: false,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
// 进度
|
||||
uploadedSize += int64(n)
|
||||
this.progress = &Progress{Percent: float64(uploadedSize*100) / float64(totalSize)}
|
||||
}
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
if err == io.EOF {
|
||||
_, uploadErr := rpcClient.APINodeRPC().UploadAPINodeFile(rpcClient.Context(0), &pb.UploadAPINodeFileRequest{
|
||||
Filename: filepath.Base(this.apiExe),
|
||||
Sum: fmt.Sprintf("%x", hash.Sum(nil)),
|
||||
ChunkData: buf[:n],
|
||||
IsFirstChunk: isFirst,
|
||||
IsLastChunk: true,
|
||||
})
|
||||
if uploadErr != nil {
|
||||
return uploadErr
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 升级边缘节点
|
||||
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 errors.New("upload deploy file '" + filepath.Base(deployFile.Path) + "' failed: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 errors.New("upload deploy file '" + filepath.Base(deployFile.Path) + "' failed: " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
22
internal/utils/apinodeutils/upgrader_test.go
Normal file
22
internal/utils/apinodeutils/upgrader_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpgrader_CanUpgrade(t *testing.T) {
|
||||
t.Log(apinodeutils.CanUpgrade("0.6.3", runtime.GOOS, runtime.GOARCH))
|
||||
}
|
||||
|
||||
func TestUpgrader_Upgrade(t *testing.T) {
|
||||
var upgrader = apinodeutils.NewUpgrader(1)
|
||||
err := upgrader.Upgrade()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
77
internal/utils/apinodeutils/utils.go
Normal file
77
internal/utils/apinodeutils/utils.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool, reason string) {
|
||||
if len(apiVersion) == 0 {
|
||||
return false, "current api version should not be empty"
|
||||
}
|
||||
|
||||
if stringutil.VersionCompare(apiVersion, "0.6.4") < 0 {
|
||||
return false, "api node version must greater than or equal to 0.6.4"
|
||||
}
|
||||
|
||||
if osName != runtime.GOOS {
|
||||
return false, "os not match: " + osName
|
||||
}
|
||||
if arch != runtime.GOARCH {
|
||||
return false, "arch not match: " + arch
|
||||
}
|
||||
|
||||
stat, err := os.Stat(apiExe())
|
||||
if err != nil {
|
||||
return false, "stat error: " + err.Error()
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return false, "is directory"
|
||||
}
|
||||
|
||||
localVersion, err := lookupLocalVersion()
|
||||
if err != nil {
|
||||
return false, "lookup version failed: " + err.Error()
|
||||
}
|
||||
if localVersion != teaconst.APINodeVersion {
|
||||
return false, "not newest api node"
|
||||
}
|
||||
if stringutil.VersionCompare(localVersion, apiVersion) <= 0 {
|
||||
return false, "need not upgrade, local '" + localVersion + "' vs remote '" + apiVersion + "'"
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func lookupLocalVersion() (string, error) {
|
||||
var cmd = exec.Command(apiExe(), "-V")
|
||||
var output = &bytes.Buffer{}
|
||||
cmd.Stdout = output
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var localVersion = strings.TrimSpace(output.String())
|
||||
|
||||
// 检查版本号
|
||||
var reg = regexp.MustCompile(`^[\d.]+$`)
|
||||
if !reg.MatchString(localVersion) {
|
||||
return "", errors.New("lookup version failed: " + localVersion)
|
||||
}
|
||||
|
||||
return localVersion, nil
|
||||
}
|
||||
|
||||
func apiExe() string {
|
||||
return Tea.Root + "/edge-api/bin/edge-api"
|
||||
}
|
||||
12
internal/utils/dateutils/utils.go
Normal file
12
internal/utils/dateutils/utils.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package dateutils
|
||||
|
||||
// SplitYmd 分隔Ymd格式的日期
|
||||
// Ymd => Y-m-d
|
||||
func SplitYmd(day string) string {
|
||||
if len(day) != 8 {
|
||||
return day
|
||||
}
|
||||
return day[:4] + "-" + day[4:6] + "-" + day[6:]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -1,26 +1,40 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// LookupCNAME 获取CNAME
|
||||
func LookupCNAME(host string) (string, error) {
|
||||
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
return "", err
|
||||
var sharedDNSClient *dns.Client
|
||||
var sharedDNSConfig *dns.ClientConfig
|
||||
|
||||
func init() {
|
||||
if !teaconst.IsMain {
|
||||
return
|
||||
}
|
||||
|
||||
c := new(dns.Client)
|
||||
m := new(dns.Msg)
|
||||
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
logs.Println("ERROR: configure dns client failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
sharedDNSConfig = config
|
||||
sharedDNSClient = &dns.Client{}
|
||||
}
|
||||
|
||||
// LookupCNAME 获取CNAME
|
||||
func LookupCNAME(host string) (string, error) {
|
||||
var m = new(dns.Msg)
|
||||
|
||||
m.SetQuestion(host+".", dns.TypeCNAME)
|
||||
m.RecursionDesired = true
|
||||
|
||||
var lastErr error
|
||||
for _, serverAddr := range config.Servers {
|
||||
r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
|
||||
for _, serverAddr := range sharedDNSConfig.Servers {
|
||||
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
|
||||
12
internal/utils/lookup_test.go
Normal file
12
internal/utils/lookup_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLookupCNAME(t *testing.T) {
|
||||
t.Log(utils.LookupCNAME("www.yun4s.cn"))
|
||||
}
|
||||
@@ -3,7 +3,9 @@ package numberutils
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func FormatInt64(value int64) string {
|
||||
@@ -28,17 +30,35 @@ func FormatBytes(bytes int64) string {
|
||||
if bytes < Pow1024(1) {
|
||||
return FormatInt64(bytes) + "B"
|
||||
} else if bytes < Pow1024(2) {
|
||||
return fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1)))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1))))
|
||||
} else if bytes < Pow1024(3) {
|
||||
return fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2)))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2))))
|
||||
} else if bytes < Pow1024(4) {
|
||||
return fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3)))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3))))
|
||||
} else if bytes < Pow1024(5) {
|
||||
return fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4)))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4))))
|
||||
} else if bytes < Pow1024(6) {
|
||||
return fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5)))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5))))
|
||||
} else {
|
||||
return fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6)))
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6))))
|
||||
}
|
||||
}
|
||||
|
||||
func FormatBits(bits int64) string {
|
||||
if bits < Pow1024(1) {
|
||||
return FormatInt64(bits) + "bps"
|
||||
} else if bits < Pow1024(2) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.4fKbps", float64(bits)/float64(Pow1024(1))))
|
||||
} else if bits < Pow1024(3) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.4fMbps", float64(bits)/float64(Pow1024(2))))
|
||||
} else if bits < Pow1024(4) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.4fGbps", float64(bits)/float64(Pow1024(3))))
|
||||
} else if bits < Pow1024(5) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.4fTbps", float64(bits)/float64(Pow1024(4))))
|
||||
} else if bits < Pow1024(6) {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.4fPbps", float64(bits)/float64(Pow1024(5))))
|
||||
} else {
|
||||
return TrimZeroSuffix(fmt.Sprintf("%.4fEbps", float64(bits)/float64(Pow1024(6))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,22 +75,92 @@ 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 ""
|
||||
}
|
||||
switch x := f.(type) {
|
||||
case float32, float64:
|
||||
var s = fmt.Sprintf("%."+types.String(decimal)+"f", x)
|
||||
|
||||
// 分隔
|
||||
var dotIndex = strings.Index(s, ".")
|
||||
if dotIndex > 0 {
|
||||
var d = s[:dotIndex]
|
||||
var f2 = s[dotIndex:]
|
||||
f2 = strings.TrimRight(strings.TrimRight(f2, "0"), ".")
|
||||
return formatDigit(d) + f2
|
||||
}
|
||||
|
||||
return s
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return types.String(x)
|
||||
return formatDigit(types.String(x))
|
||||
case string:
|
||||
return x
|
||||
}
|
||||
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
|
||||
func TrimZeroSuffix(s string) string {
|
||||
var matches = decimalReg.FindStringSubmatch(s)
|
||||
if len(matches) < 3 {
|
||||
return s
|
||||
}
|
||||
return strings.TrimRight(strings.TrimRight(matches[1], "0"), ".") + matches[2]
|
||||
}
|
||||
|
||||
func formatDigit(d string) string {
|
||||
if len(d) == 0 {
|
||||
return d
|
||||
}
|
||||
|
||||
var prefix = ""
|
||||
if d[0] < '0' || d[0] > '9' {
|
||||
prefix = d[:1]
|
||||
d = d[1:]
|
||||
}
|
||||
|
||||
var l = len(d)
|
||||
if l > 3 {
|
||||
var pieces = l / 3
|
||||
var commIndex = l - pieces*3
|
||||
var d2 = ""
|
||||
if commIndex > 0 {
|
||||
d2 = d[:commIndex] + ", "
|
||||
}
|
||||
for i := 0; i < pieces; i++ {
|
||||
d2 += d[commIndex+i*3 : commIndex+i*3+3]
|
||||
if i != pieces-1 {
|
||||
d2 += ", "
|
||||
}
|
||||
}
|
||||
return prefix + d2
|
||||
}
|
||||
return prefix + d
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package numberutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -33,4 +34,53 @@ func TestFormatFloat(t *testing.T) {
|
||||
t.Log(numberutils.FormatFloat(100.23456, 2))
|
||||
t.Log(numberutils.FormatFloat(100.000023, 2))
|
||||
t.Log(numberutils.FormatFloat(100.012, 2))
|
||||
t.Log(numberutils.FormatFloat(123.012, 2))
|
||||
t.Log(numberutils.FormatFloat(1234.012, 2))
|
||||
t.Log(numberutils.FormatFloat(12345.012, 2))
|
||||
t.Log(numberutils.FormatFloat(123456.012, 2))
|
||||
t.Log(numberutils.FormatFloat(1234567.012, 2))
|
||||
t.Log(numberutils.FormatFloat(12345678.012, 2))
|
||||
t.Log(numberutils.FormatFloat(123456789.012, 2))
|
||||
t.Log(numberutils.FormatFloat(1234567890.012, 2))
|
||||
t.Log(numberutils.FormatFloat(123, 2))
|
||||
t.Log(numberutils.FormatFloat(1234, 2))
|
||||
t.Log(numberutils.FormatFloat(1234.00001, 4))
|
||||
t.Log(numberutils.FormatFloat(1234.56700, 4))
|
||||
t.Log(numberutils.FormatFloat(-1234.56700, 2))
|
||||
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",
|
||||
"1.0000",
|
||||
"1.10",
|
||||
"100",
|
||||
"100.0000",
|
||||
"100.0",
|
||||
"100.0123",
|
||||
"100.0010",
|
||||
"100.000KB",
|
||||
"100.010MB",
|
||||
} {
|
||||
t.Log(s, "=>", numberutils.TrimZeroSuffix(s))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ After=network-online.target
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
RestartSec=5s
|
||||
ExecStart=` + exePath + ` daemon
|
||||
ExecStop=` + exePath + ` stop
|
||||
ExecReload=` + exePath + ` reload
|
||||
|
||||
55
internal/utils/time.go
Normal file
55
internal/utils/time.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// RangeTimes 计算时间点
|
||||
func RangeTimes(timeFrom string, timeTo string, everyMinutes int32) (result []string, err error) {
|
||||
if everyMinutes <= 0 {
|
||||
return nil, errors.New("invalid 'everyMinutes'")
|
||||
}
|
||||
|
||||
var reg = regexp.MustCompile(`^\d{4}$`)
|
||||
if !reg.MatchString(timeFrom) {
|
||||
return nil, errors.New("invalid timeFrom '" + timeFrom + "'")
|
||||
}
|
||||
if !reg.MatchString(timeTo) {
|
||||
return nil, errors.New("invalid timeTo '" + timeTo + "'")
|
||||
}
|
||||
|
||||
if timeFrom > timeTo {
|
||||
// swap
|
||||
timeFrom, timeTo = timeTo, timeFrom
|
||||
}
|
||||
|
||||
var everyMinutesInt = int(everyMinutes)
|
||||
|
||||
var fromHour = types.Int(timeFrom[:2])
|
||||
var fromMinute = types.Int(timeFrom[2:])
|
||||
var toHour = types.Int(timeTo[:2])
|
||||
var toMinute = types.Int(timeTo[2:])
|
||||
|
||||
if fromMinute%everyMinutesInt == 0 {
|
||||
result = append(result, timeFrom)
|
||||
}
|
||||
|
||||
for {
|
||||
fromMinute += everyMinutesInt
|
||||
if fromMinute > 59 {
|
||||
fromHour += fromMinute / 60
|
||||
fromMinute = fromMinute % 60
|
||||
}
|
||||
if fromHour > toHour || (fromHour == toHour && fromMinute > toMinute) {
|
||||
break
|
||||
}
|
||||
result = append(result, fmt.Sprintf("%02d%02d", fromHour, fromMinute))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -52,11 +52,14 @@ type UpgradeManager struct {
|
||||
writer *UpgradeFileWriter
|
||||
body io.ReadCloser
|
||||
isCancelled bool
|
||||
|
||||
downloadURL string
|
||||
}
|
||||
|
||||
func NewUpgradeManager(component string) *UpgradeManager {
|
||||
func NewUpgradeManager(component string, downloadURL string) *UpgradeManager {
|
||||
return &UpgradeManager{
|
||||
component: component,
|
||||
component: component,
|
||||
downloadURL: downloadURL,
|
||||
client: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
@@ -96,8 +99,8 @@ func (this *UpgradeManager) Start() error {
|
||||
}
|
||||
|
||||
// 检查新版本
|
||||
var downloadURL = ""
|
||||
{
|
||||
var downloadURL = this.downloadURL
|
||||
if len(downloadURL) == 0 {
|
||||
var url = teaconst.UpdatesURL
|
||||
var osName = runtime.GOOS
|
||||
if Tea.IsTesting() && osName == "darwin" {
|
||||
@@ -105,10 +108,12 @@ func (this *UpgradeManager) Start() error {
|
||||
}
|
||||
url = strings.ReplaceAll(url, "${os}", osName)
|
||||
url = strings.ReplaceAll(url, "${arch}", runtime.GOARCH)
|
||||
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())
|
||||
}
|
||||
req.Header.Set("User-Agent", "Edge-Admin/"+teaconst.Version)
|
||||
|
||||
resp, err := this.client.Do(req)
|
||||
if err != nil {
|
||||
@@ -169,6 +174,7 @@ func (this *UpgradeManager) Start() error {
|
||||
if err != nil {
|
||||
return errors.New("create download request failed: " + err.Error())
|
||||
}
|
||||
req.Header.Set("User-Agent", "Edge-Admin/"+teaconst.Version)
|
||||
|
||||
resp, err := this.client.Do(req)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewUpgradeManager(t *testing.T) {
|
||||
var manager = utils.NewUpgradeManager("admin")
|
||||
var manager = utils.NewUpgradeManager("admin", "")
|
||||
|
||||
var ticker = time.NewTicker(2 * time.Second)
|
||||
go func() {
|
||||
|
||||
@@ -49,7 +49,11 @@ func (this *ParentAction) ErrorText(err string) {
|
||||
}
|
||||
|
||||
func (this *ParentAction) NotFound(name string, itemId int64) {
|
||||
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
|
||||
if itemId > 0 {
|
||||
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
|
||||
} else {
|
||||
this.ErrorPage(errors.New(name + " is not found"))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
|
||||
@@ -126,11 +130,8 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
|
||||
}
|
||||
|
||||
// AdminContext 获取Context
|
||||
// 每个请求的context都必须是一个新的实例
|
||||
func (this *ParentAction) AdminContext() context.Context {
|
||||
if this.ctx != nil {
|
||||
return this.ctx
|
||||
}
|
||||
|
||||
if this.rpcClient == nil {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
|
||||
@@ -102,7 +102,7 @@ func FailPage(action actions.ActionWrapper, err error) {
|
||||
var html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>有系统错误需要处理</title>
|
||||
<title>正在处理...</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<style type="text/css">
|
||||
hr { border-top: 1px #ccc solid; }
|
||||
@@ -116,7 +116,7 @@ func FailPage(action actions.ActionWrapper, err error) {
|
||||
html += "<div class=\"red\">API节点正在启动,请耐心等待完成"
|
||||
|
||||
if len(apiNodeProgress) > 0 {
|
||||
html += ":" + apiNodeProgress
|
||||
html += ":" + apiNodeProgress + "(刷新当前页面查看最新状态)"
|
||||
}
|
||||
|
||||
html += "</div>"
|
||||
|
||||
@@ -45,6 +45,7 @@ func (this *AdminAction) RunGet(params struct {
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
"canLogin": admin.CanLogin,
|
||||
"hasWeakPassword": admin.HasWeakPassword,
|
||||
"countAccessKeys": countAccessKeys,
|
||||
}
|
||||
|
||||
|
||||
@@ -15,34 +15,46 @@ func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
page := this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Keyword string
|
||||
HasWeakPassword bool
|
||||
}) {
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["hasWeakPassword"] = params.HasWeakPassword
|
||||
|
||||
adminsResp, err := this.RPC().AdminRPC().ListEnabledAdmins(this.AdminContext(), &pb.ListEnabledAdminsRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
countResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{
|
||||
Keyword: params.Keyword,
|
||||
HasWeakPassword: params.HasWeakPassword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
adminMaps := []maps.Map{}
|
||||
var page = this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
adminsResp, err := this.RPC().AdminRPC().ListEnabledAdmins(this.AdminContext(), &pb.ListEnabledAdminsRequest{
|
||||
Keyword: params.Keyword,
|
||||
HasWeakPassword: params.HasWeakPassword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var adminMaps = []maps.Map{}
|
||||
for _, admin := range adminsResp.Admins {
|
||||
adminMaps = append(adminMaps, maps.Map{
|
||||
"id": admin.Id,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
"username": admin.Username,
|
||||
"fullname": admin.Fullname,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", admin.CreatedAt),
|
||||
"otpLoginIsOn": admin.OtpLogin != nil && admin.OtpLogin.IsOn,
|
||||
"canLogin": admin.CanLogin,
|
||||
"id": admin.Id,
|
||||
"isOn": admin.IsOn,
|
||||
"isSuper": admin.IsSuper,
|
||||
"username": admin.Username,
|
||||
"fullname": admin.Fullname,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", admin.CreatedAt),
|
||||
"otpLoginIsOn": admin.OtpLogin != nil && admin.OtpLogin.IsOn,
|
||||
"canLogin": admin.CanLogin,
|
||||
"hasWeakPassword": admin.HasWeakPassword,
|
||||
})
|
||||
}
|
||||
this.Data["admins"] = adminMaps
|
||||
|
||||
@@ -22,7 +22,7 @@ func init() {
|
||||
Post("/options", new(OptionsAction)).
|
||||
|
||||
// AccessKeys
|
||||
Prefix("/admins/accessKeys").
|
||||
Prefix("/admins/accesskeys").
|
||||
Get("", new(accesskeys.IndexAction)).
|
||||
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
|
||||
Post("/delete", new(accesskeys.DeleteAction)).
|
||||
|
||||
@@ -3,12 +3,15 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
@@ -44,7 +47,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
|
||||
for _, node := range nodesResp.ApiNodes {
|
||||
// 状态
|
||||
status := &nodeconfigs.NodeStatus{}
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
@@ -55,7 +58,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
}
|
||||
|
||||
// Rest地址
|
||||
restAccessAddrs := []string{}
|
||||
var restAccessAddrs = []string{}
|
||||
if node.RestIsOn {
|
||||
if len(node.RestHTTPJSON) > 0 {
|
||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
@@ -79,13 +82,16 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_ = httpsConfig.Init()
|
||||
_ = httpsConfig.Init(nil)
|
||||
if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 {
|
||||
restAccessAddrs = append(restAccessAddrs, httpsConfig.FullAddresses()...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var shouldUpgrade = status.IsActive && len(status.BuildVersion) > 0 && stringutil.VersionCompare(teaconst.APINodeVersion, status.BuildVersion) > 0
|
||||
canUpgrade, _ := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"isOn": node.IsOn,
|
||||
@@ -94,14 +100,17 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
"restAccessAddrs": restAccessAddrs,
|
||||
"isPrimary": node.IsPrimary,
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"buildVersion": status.BuildVersion,
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"buildVersion": status.BuildVersion,
|
||||
"latestVersion": teaconst.APINodeVersion,
|
||||
"shouldUpgrade": shouldUpgrade,
|
||||
"canUpgrade": shouldUpgrade && canUpgrade,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
@@ -40,11 +42,11 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
Field("name", params.Name).
|
||||
Require("请输入API节点名称")
|
||||
|
||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
|
||||
// 监听地址
|
||||
listens := []*serverconfigs.NetworkAddressConfig{}
|
||||
var listens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err := json.Unmarshal(params.ListensJSON, &listens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -64,15 +66,19 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// Rest监听地址
|
||||
restHTTPConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
restHTTPSConfig := &serverconfigs.HTTPSProtocolConfig{}
|
||||
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if params.RestIsOn {
|
||||
restListens := []*serverconfigs.NetworkAddressConfig{}
|
||||
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(restListens) == 0 {
|
||||
this.Fail("请至少添加一个HTTP API监听端口")
|
||||
return
|
||||
}
|
||||
for _, addr := range restListens {
|
||||
if addr.Protocol.IsHTTPFamily() {
|
||||
restHTTPConfig.IsOn = true
|
||||
@@ -82,10 +88,35 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// 是否有端口冲突
|
||||
var rpcAddresses = []string{}
|
||||
for _, listen := range listens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
|
||||
}
|
||||
|
||||
for _, listen := range restListens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
for _, address := range listen.Addresses() {
|
||||
if lists.ContainsString(rpcAddresses, address) {
|
||||
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突,请修改后提交")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 证书
|
||||
certIds := []int64{}
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
@@ -97,7 +128,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
this.Fail("请添加至少一个证书")
|
||||
}
|
||||
|
||||
certRefs := []*sslconfigs.SSLCertRef{}
|
||||
var certRefs = []*sslconfigs.SSLCertRef{}
|
||||
for _, certId := range certIds {
|
||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
@@ -131,7 +162,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 访问地址
|
||||
accessAddrs := []*serverconfigs.NetworkAddressConfig{}
|
||||
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -65,7 +65,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
// 证书信息
|
||||
var certs = []*sslconfigs.SSLCertConfig{}
|
||||
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId})
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
|
||||
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
|
||||
IgnoreData: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -24,7 +24,10 @@ func init() {
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
Get("/install", new(InstallAction)).
|
||||
Get("/logs", new(LogsAction)).
|
||||
GetPost("/upgradePopup", new(UpgradePopupAction)).
|
||||
Post("/upgradeCheck", new(UpgradeCheckAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
@@ -83,7 +85,10 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
var certs = []*sslconfigs.SSLCertConfig{}
|
||||
var sslPolicyId = int64(0)
|
||||
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId})
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
|
||||
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
|
||||
IgnoreData: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
@@ -175,12 +180,16 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
if params.RestIsOn {
|
||||
restListens := []*serverconfigs.NetworkAddressConfig{}
|
||||
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(restListens) == 0 {
|
||||
this.Fail("请至少添加一个HTTP API监听端口")
|
||||
return
|
||||
}
|
||||
for _, addr := range restListens {
|
||||
if addr.Protocol.IsHTTPFamily() {
|
||||
restHTTPConfig.IsOn = true
|
||||
@@ -190,6 +199,31 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// 是否有端口冲突
|
||||
var rpcAddresses = []string{}
|
||||
for _, listen := range listens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
|
||||
}
|
||||
|
||||
for _, listen := range restListens {
|
||||
err := listen.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
for _, address := range listen.Addresses() {
|
||||
if lists.ContainsString(rpcAddresses, address) {
|
||||
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突,请修改后提交")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 证书
|
||||
|
||||
67
internal/web/actions/default/api/node/upgradeCheck.go
Normal file
67
internal/web/actions/default/api/node/upgradeCheck.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// UpgradeCheckAction 检查升级结果
|
||||
type UpgradeCheckAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradeCheckAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpgradeCheckAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["isOk"] = false
|
||||
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil || len(node.AccessAddrs) == 0 {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
apiConfig, err := configs.LoadAPIConfig()
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var newAPIConfig = apiConfig.Clone()
|
||||
newAPIConfig.RPC.Endpoints = node.AccessAddrs
|
||||
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(rpcClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
if versionResp.Version != teaconst.Version {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["isOk"] = true
|
||||
|
||||
this.Success()
|
||||
}
|
||||
124
internal/web/actions/default/api/node/upgradePopup.go
Normal file
124
internal/web/actions/default/api/node/upgradePopup.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpgradePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
this.Data["nodeName"] = ""
|
||||
this.Data["currentVersion"] = ""
|
||||
this.Data["latestVersion"] = ""
|
||||
this.Data["result"] = ""
|
||||
this.Data["resultIsOk"] = true
|
||||
this.Data["canUpgrade"] = false
|
||||
this.Data["isUpgrading"] = false
|
||||
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil {
|
||||
this.Data["result"] = "要升级的节点不存在"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["nodeName"] = node.Name + " / [" + strings.Join(node.AccessAddrs, ", ") + "]"
|
||||
|
||||
// 节点状态
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
this.ErrorPage(errors.New("decode status failed: " + err.Error()))
|
||||
return
|
||||
}
|
||||
this.Data["currentVersion"] = status.BuildVersion
|
||||
} else {
|
||||
this.Data["result"] = "无法检测到节点当前版本"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["latestVersion"] = teaconst.APINodeVersion
|
||||
|
||||
if status.IsActive && len(status.BuildVersion) > 0 {
|
||||
canUpgrade, reason := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
|
||||
if !canUpgrade {
|
||||
this.Data["result"] = reason
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["canUpgrade"] = true
|
||||
this.Data["result"] = "等待升级"
|
||||
this.Data["resultIsOk"] = true
|
||||
} else {
|
||||
this.Data["result"] = "当前节点非连接状态无法远程升级"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
// 是否正在升级
|
||||
var oldUpgrader = apinodeutils.SharedManager.FindUpgrader(params.NodeId)
|
||||
if oldUpgrader != nil {
|
||||
this.Data["result"] = "正在升级中..."
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Data["isUpgrading"] = true
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
var manager = apinodeutils.SharedManager
|
||||
|
||||
var oldUpgrader = manager.FindUpgrader(params.NodeId)
|
||||
if oldUpgrader != nil {
|
||||
this.Fail("正在升级中,无需重复提交 ...")
|
||||
return
|
||||
}
|
||||
|
||||
var upgrader = apinodeutils.NewUpgrader(params.NodeId)
|
||||
manager.AddUpgrader(upgrader)
|
||||
defer func() {
|
||||
manager.RemoveUpgrader(upgrader)
|
||||
}()
|
||||
|
||||
err := upgrader.Upgrade()
|
||||
if err != nil {
|
||||
this.Fail("升级失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
@@ -88,7 +89,7 @@ func (this *DetailAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
var ipAddresses = ipAddressesResp.NodeIPAddresses
|
||||
ipAddressMaps := []maps.Map{}
|
||||
var ipAddressMaps = []maps.Map{}
|
||||
for _, addr := range ipAddressesResp.NodeIPAddresses {
|
||||
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
|
||||
if err != nil {
|
||||
@@ -102,6 +103,15 @@ func (this *DetailAction) RunGet(params struct {
|
||||
addr.Ip = addr.BackupIP
|
||||
}
|
||||
|
||||
// 专属集群
|
||||
var addrClusterMaps = []maps.Map{}
|
||||
for _, addrCluster := range addr.NodeClusters {
|
||||
addrClusterMaps = append(addrClusterMaps, maps.Map{
|
||||
"id": addrCluster.Id,
|
||||
"name": addrCluster.Name,
|
||||
})
|
||||
}
|
||||
|
||||
ipAddressMaps = append(ipAddressMaps, maps.Map{
|
||||
"id": addr.Id,
|
||||
"name": addr.Name,
|
||||
@@ -110,6 +120,7 @@ func (this *DetailAction) RunGet(params struct {
|
||||
"canAccess": addr.CanAccess,
|
||||
"isOn": addr.IsOn,
|
||||
"isUp": addr.IsUp,
|
||||
"clusters": addrClusterMaps,
|
||||
"thresholds": thresholds,
|
||||
})
|
||||
}
|
||||
@@ -151,16 +162,31 @@ func (this *DetailAction) RunGet(params struct {
|
||||
if !addr.CanAccess || !addr.IsUp || !addr.IsOn {
|
||||
continue
|
||||
}
|
||||
|
||||
// 过滤集群
|
||||
if len(addr.NodeClusters) > 0 {
|
||||
var inCluster = false
|
||||
for _, addrCluster := range addr.NodeClusters {
|
||||
if addrCluster.Id == cluster.Id {
|
||||
inCluster = true
|
||||
}
|
||||
}
|
||||
if !inCluster {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for _, route := range dnsInfo.Routes {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(addr.Ip) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
recordMaps = append(recordMaps, maps.Map{
|
||||
"name": dnsInfo.NodeClusterDNSName + "." + domainName,
|
||||
"type": recordType,
|
||||
"route": route.Name,
|
||||
"value": addr.Ip,
|
||||
"name": dnsInfo.NodeClusterDNSName + "." + domainName,
|
||||
"type": recordType,
|
||||
"route": route.Name,
|
||||
"value": addr.Ip,
|
||||
"clusterName": cluster.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -178,8 +204,8 @@ func (this *DetailAction) RunGet(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
grantMap := maps.Map{}
|
||||
grantId := loginParams.GetInt64("grantId")
|
||||
var grantMap = maps.Map{}
|
||||
var grantId = loginParams.GetInt64("grantId")
|
||||
if grantId > 0 {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
|
||||
if err != nil {
|
||||
@@ -300,6 +326,22 @@ func (this *DetailAction) RunGet(params struct {
|
||||
lnAddrs = []string{}
|
||||
}
|
||||
|
||||
// API节点地址
|
||||
var apiNodeAddrStrings = []string{}
|
||||
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
if len(node.ApiNodeAddrsJSON) > 0 {
|
||||
err = json.Unmarshal(node.ApiNodeAddrsJSON, &apiNodeAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, addr := range apiNodeAddrs {
|
||||
if addr.Init() == nil {
|
||||
apiNodeAddrStrings = append(apiNodeAddrStrings, addr.FullAddresses()...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
@@ -318,6 +360,8 @@ func (this *DetailAction) RunGet(params struct {
|
||||
"level": node.Level,
|
||||
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
|
||||
"lnAddrs": lnAddrs,
|
||||
"enableIPLists": node.EnableIPLists,
|
||||
"apiNodeAddrs": apiNodeAddrStrings,
|
||||
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
@@ -337,6 +381,8 @@ func (this *DetailAction) RunGet(params struct {
|
||||
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
|
||||
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
|
||||
"exePath": status.ExePath,
|
||||
"apiSuccessPercent": status.APISuccessPercent,
|
||||
"apiAvgCostSeconds": status.APIAvgCostSeconds,
|
||||
},
|
||||
|
||||
"group": groupMap,
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -57,9 +58,19 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
var diskSubDirs = []*serverconfigs.CacheDir{}
|
||||
if len(node.CacheDiskSubDirsJSON) > 0 {
|
||||
err = json.Unmarshal(node.CacheDiskSubDirsJSON, &diskSubDirs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var nodeMap = this.Data["node"].(maps.Map)
|
||||
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
|
||||
nodeMap["cacheDiskDir"] = node.CacheDiskDir
|
||||
nodeMap["cacheDiskSubDirs"] = diskSubDirs
|
||||
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
|
||||
|
||||
this.Show()
|
||||
@@ -69,6 +80,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
MaxCacheDiskCapacityJSON []byte
|
||||
CacheDiskDir string
|
||||
CacheDiskSubDirsJSON []byte
|
||||
MaxCacheMemoryCapacityJSON []byte
|
||||
|
||||
Must *actions.Must
|
||||
@@ -105,10 +117,20 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
if len(params.CacheDiskSubDirsJSON) > 0 {
|
||||
var cacheSubDirs = []*serverconfigs.CacheDir{}
|
||||
err := json.Unmarshal(params.CacheDiskSubDirsJSON, &cacheSubDirs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
|
||||
NodeId: params.NodeId,
|
||||
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
|
||||
CacheDiskDir: params.CacheDiskDir,
|
||||
CacheDiskSubDirsJSON: params.CacheDiskSubDirsJSON,
|
||||
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
@@ -50,6 +51,22 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["dnsResolverConfig"] = dnsResolverConfig
|
||||
|
||||
// API相关
|
||||
apiConfigResp, err := this.RPC().NodeRPC().FindNodeAPIConfig(this.AdminContext(), &pb.FindNodeAPIConfigRequest{NodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
if len(apiConfigResp.ApiNodeAddrsJSON) > 0 {
|
||||
err = json.Unmarshal(apiConfigResp.ApiNodeAddrsJSON, &apiNodeAddrs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.Data["apiNodeAddrs"] = apiNodeAddrs
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -59,6 +76,8 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
DnsResolverJSON []byte
|
||||
|
||||
ApiNodeAddrsJSON []byte
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
@@ -68,6 +87,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.Fail("CPU线程数不能小于0")
|
||||
}
|
||||
|
||||
// 系统设置
|
||||
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
|
||||
NodeId: params.NodeId,
|
||||
MaxCPU: params.MaxCPU,
|
||||
@@ -77,6 +97,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// DNS解析设置
|
||||
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
|
||||
err = json.Unmarshal(params.DnsResolverJSON, dnsResolverConfig)
|
||||
if err != nil {
|
||||
@@ -98,5 +119,28 @@ func (this *IndexAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// API节点设置
|
||||
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||
if len(params.ApiNodeAddrsJSON) > 0 {
|
||||
err = json.Unmarshal(params.ApiNodeAddrsJSON, &apiNodeAddrs)
|
||||
if err != nil {
|
||||
this.Fail("API节点地址校验错误:" + err.Error())
|
||||
}
|
||||
for _, addr := range apiNodeAddrs {
|
||||
err = addr.Init()
|
||||
if err != nil {
|
||||
this.Fail("API节点地址校验错误:" + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err = this.RPC().NodeRPC().UpdateNodeAPIConfig(this.AdminContext(), &pb.UpdateNodeAPIConfigRequest{
|
||||
NodeId: params.NodeId,
|
||||
ApiNodeAddrsJSON: params.ApiNodeAddrsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -64,12 +64,22 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}
|
||||
var ipAddressMaps = []maps.Map{}
|
||||
for _, addr := range ipAddressesResp.NodeIPAddresses {
|
||||
// 阈值
|
||||
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 专属集群
|
||||
var clusterMaps = []maps.Map{}
|
||||
for _, addrCluster := range addr.NodeClusters {
|
||||
clusterMaps = append(clusterMaps, maps.Map{
|
||||
"id": addrCluster.Id,
|
||||
"name": addrCluster.Name,
|
||||
})
|
||||
}
|
||||
|
||||
ipAddressMaps = append(ipAddressMaps, maps.Map{
|
||||
"id": addr.Id,
|
||||
"name": addr.Name,
|
||||
@@ -78,6 +88,7 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
"isOn": addr.IsOn,
|
||||
"isUp": addr.IsUp,
|
||||
"thresholds": thresholds,
|
||||
"clusters": clusterMaps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,14 +111,15 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
var nodeMap = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddresses": ipAddressMaps,
|
||||
"cluster": clusterMap,
|
||||
"isOn": node.IsOn,
|
||||
"group": groupMap,
|
||||
"region": regionMap,
|
||||
"level": node.Level,
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddresses": ipAddressMaps,
|
||||
"cluster": clusterMap,
|
||||
"isOn": node.IsOn,
|
||||
"group": groupMap,
|
||||
"region": regionMap,
|
||||
"level": node.Level,
|
||||
"enableIPLists": node.EnableIPLists,
|
||||
}
|
||||
|
||||
if node.LnAddrs == nil {
|
||||
@@ -157,6 +169,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
IsOn bool
|
||||
Level int32
|
||||
LnAddrs []string
|
||||
EnableIPLists bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
@@ -234,6 +247,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
IsOn: params.IsOn,
|
||||
Level: params.Level,
|
||||
LnAddrs: lnAddrs,
|
||||
EnableIPLists: params.EnableIPLists,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -32,11 +32,12 @@ func (this *NodesAction) RunGet(params struct {
|
||||
Keyword string
|
||||
Level int32
|
||||
|
||||
CpuOrder string
|
||||
MemoryOrder string
|
||||
TrafficInOrder string
|
||||
TrafficOutOrder string
|
||||
LoadOrder string
|
||||
CpuOrder string
|
||||
MemoryOrder string
|
||||
TrafficInOrder string
|
||||
TrafficOutOrder string
|
||||
LoadOrder string
|
||||
ConnectionsOrder string
|
||||
}) {
|
||||
this.Data["groupId"] = params.GroupId
|
||||
this.Data["regionId"] = params.RegionId
|
||||
@@ -44,7 +45,7 @@ func (this *NodesAction) RunGet(params struct {
|
||||
this.Data["activeState"] = params.ActiveState
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["level"] = params.Level
|
||||
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
|
||||
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 || len(params.ConnectionsOrder) > 0
|
||||
|
||||
// 集群是否已经设置了线路
|
||||
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
|
||||
@@ -112,6 +113,10 @@ func (this *NodesAction) RunGet(params struct {
|
||||
req.LoadAsc = true
|
||||
} else if params.LoadOrder == "desc" {
|
||||
req.LoadDesc = true
|
||||
} else if params.ConnectionsOrder == "asc" {
|
||||
req.ConnectionsAsc = true
|
||||
} else if params.ConnectionsOrder == "desc" {
|
||||
req.ConnectionsDesc = true
|
||||
}
|
||||
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
|
||||
if err != nil {
|
||||
@@ -121,8 +126,8 @@ func (this *NodesAction) RunGet(params struct {
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range nodesResp.Nodes {
|
||||
// 状态
|
||||
isSynced := false
|
||||
status := &nodeconfigs.NodeStatus{}
|
||||
var isSynced = false
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
@@ -142,8 +147,17 @@ func (this *NodesAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
ipAddresses := []maps.Map{}
|
||||
var ipAddresses = []maps.Map{}
|
||||
for _, addr := range ipAddressesResp.NodeIPAddresses {
|
||||
// 专属集群
|
||||
var addrClusterMaps = []maps.Map{}
|
||||
for _, addrCluster := range addr.NodeClusters {
|
||||
addrClusterMaps = append(addrClusterMaps, maps.Map{
|
||||
"id": addrCluster.Id,
|
||||
"name": addrCluster.Name,
|
||||
})
|
||||
}
|
||||
|
||||
ipAddresses = append(ipAddresses, maps.Map{
|
||||
"id": addr.Id,
|
||||
"name": addr.Name,
|
||||
@@ -151,6 +165,7 @@ func (this *NodesAction) RunGet(params struct {
|
||||
"canAccess": addr.CanAccess,
|
||||
"isUp": addr.IsUp,
|
||||
"isOn": addr.IsOn,
|
||||
"clusters": addrClusterMaps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -201,16 +216,17 @@ func (this *NodesAction) RunGet(params struct {
|
||||
"error": node.InstallStatus.Error,
|
||||
},
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"trafficInBytes": status.TrafficInBytes,
|
||||
"trafficOutBytes": status.TrafficOutBytes,
|
||||
"load1m": numberutils.FormatFloat2(status.Load1m),
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"trafficInBytes": status.TrafficInBytes,
|
||||
"trafficOutBytes": status.TrafficOutBytes,
|
||||
"load1m": numberutils.PadFloatZero(numberutils.FormatFloat2(status.Load1m), 2),
|
||||
"countConnections": status.ConnectionCount,
|
||||
},
|
||||
"cluster": maps.Map{
|
||||
"id": node.NodeCluster.Id,
|
||||
|
||||
@@ -78,8 +78,20 @@ func (this *IndexAction) RunPost(params struct {
|
||||
HttpAllAllowNodeIP bool
|
||||
HttpAllDefaultDomain string
|
||||
|
||||
HttpAllSupportsLowVersionHTTP bool
|
||||
|
||||
HttpAccessLogEnableRequestHeaders bool
|
||||
HttpAccessLogEnableResponseHeaders bool
|
||||
HttpAccessLogCommonRequestHeadersOnly bool
|
||||
HttpAccessLogEnableCookies bool
|
||||
HttpAccessLogEnableServerNotFound bool
|
||||
|
||||
LogRecordServerError bool
|
||||
|
||||
PerformanceAutoReadTimeout bool
|
||||
PerformanceAutoWriteTimeout bool
|
||||
PerformanceDebug bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
@@ -119,12 +131,29 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 域名
|
||||
config.HTTPAll.AllowMismatchDomains = allowMismatchDomains
|
||||
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
|
||||
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
|
||||
|
||||
// HTTP All
|
||||
config.HTTPAll.SupportsLowVersionHTTP = params.HttpAllSupportsLowVersionHTTP
|
||||
|
||||
// 访问日志
|
||||
config.HTTPAccessLog.EnableRequestHeaders = params.HttpAccessLogEnableRequestHeaders
|
||||
config.HTTPAccessLog.EnableResponseHeaders = params.HttpAccessLogEnableResponseHeaders
|
||||
config.HTTPAccessLog.CommonRequestHeadersOnly = params.HttpAccessLogCommonRequestHeadersOnly
|
||||
config.HTTPAccessLog.EnableCookies = params.HttpAccessLogEnableCookies
|
||||
config.HTTPAccessLog.EnableServerNotFound = params.HttpAccessLogEnableServerNotFound
|
||||
|
||||
// 日志
|
||||
config.Log.RecordServerError = params.LogRecordServerError
|
||||
|
||||
// 性能
|
||||
config.Performance.AutoReadTimeout = params.PerformanceAutoReadTimeout
|
||||
config.Performance.AutoWriteTimeout = params.PerformanceAutoWriteTimeout
|
||||
config.Performance.Debug = params.PerformanceDebug
|
||||
|
||||
err = config.Init()
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
|
||||
@@ -45,7 +45,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
grant := grantResp.NodeGrant
|
||||
var grant = grantResp.NodeGrant
|
||||
if grant != nil {
|
||||
grantMap = maps.Map{
|
||||
"id": grant.Id,
|
||||
@@ -79,6 +79,29 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// SSH参数
|
||||
var sshParams = nodeconfigs.DefaultSSHParams()
|
||||
if len(cluster.SshParamsJSON) > 0 {
|
||||
err = json.Unmarshal(cluster.SshParamsJSON, sshParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DNS信息
|
||||
var fullDomainName = ""
|
||||
if len(cluster.DnsName) > 0 && cluster.DnsDomainId > 0 {
|
||||
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{DnsDomainId: cluster.DnsDomainId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if domainResp.DnsDomain != nil {
|
||||
fullDomainName = cluster.DnsName + "." + domainResp.DnsDomain.Name
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["cluster"] = maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
@@ -89,6 +112,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
"clock": clockConfig,
|
||||
"autoRemoteStart": cluster.AutoRemoteStart,
|
||||
"autoInstallNftables": cluster.AutoInstallNftables,
|
||||
"sshParams": sshParams,
|
||||
"domainName": fullDomainName,
|
||||
}
|
||||
|
||||
// 默认值
|
||||
@@ -104,12 +129,14 @@ func (this *IndexAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
Name string
|
||||
GrantId int64
|
||||
SshParamsPort int
|
||||
InstallDir string
|
||||
TimeZone string
|
||||
NodeMaxThreads int32
|
||||
AutoOpenPorts bool
|
||||
ClockAutoSync bool
|
||||
ClockServer string
|
||||
ClockCheckChrony bool
|
||||
AutoRemoteStart bool
|
||||
AutoInstallNftables bool
|
||||
|
||||
@@ -129,9 +156,20 @@ func (this *IndexAction) RunPost(params struct {
|
||||
Lte(int64(nodeconfigs.DefaultMaxThreadsMax), "单节点最大线程数最大值不能大于"+types.String(nodeconfigs.DefaultMaxThreadsMax))
|
||||
}
|
||||
|
||||
// ssh
|
||||
var sshParams = nodeconfigs.DefaultSSHParams()
|
||||
sshParams.Port = params.SshParamsPort
|
||||
sshParamsJSON, err := json.Marshal(sshParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// clock
|
||||
var clockConfig = nodeconfigs.DefaultClockConfig()
|
||||
clockConfig.AutoSync = params.ClockAutoSync
|
||||
clockConfig.Server = params.ClockServer
|
||||
clockConfig.CheckChrony = params.ClockCheckChrony
|
||||
clockConfigJSON, err := json.Marshal(clockConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -155,6 +193,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
ClockJSON: clockConfigJSON,
|
||||
AutoRemoteStart: params.AutoRemoteStart,
|
||||
AutoInstallNftables: params.AutoInstallNftables,
|
||||
SshParamsJSON: sshParamsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -60,7 +60,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
||||
var tabbar = actionutils.NewTabbar()
|
||||
tabbar.Add("集群列表", "", "/clusters", "", false)
|
||||
if teaconst.IsPlus {
|
||||
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board")
|
||||
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
|
||||
}
|
||||
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
|
||||
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
|
||||
@@ -68,7 +68,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
||||
actionutils.SetTabbar(action, tabbar)
|
||||
|
||||
// 左侧菜单
|
||||
secondMenuItem := action.Data.GetString("secondMenuItem")
|
||||
var secondMenuItem = action.Data.GetString("secondMenuItem")
|
||||
switch selectedTabbar {
|
||||
case "setting":
|
||||
var menuItems = this.createSettingMenu(cluster, clusterInfo, secondMenuItem)
|
||||
@@ -157,6 +157,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
|
||||
"name": "服务设置",
|
||||
"url": "/clusters/cluster/settings/global-server-config?clusterId=" + clusterId,
|
||||
"isActive": selectedItem == "globalServerConfig",
|
||||
"isOn": true,
|
||||
})
|
||||
|
||||
items = append(items, maps.Map{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type CreateAction struct {
|
||||
@@ -50,6 +51,18 @@ func (this *CreateAction) RunPost(params struct {
|
||||
if len(params.PrivateKey) == 0 {
|
||||
this.FailField("privateKey", "请输入RSA私钥")
|
||||
}
|
||||
|
||||
// 验证私钥
|
||||
var err error
|
||||
if len(params.Passphrase) > 0 {
|
||||
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
|
||||
} else {
|
||||
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
|
||||
}
|
||||
if err != nil {
|
||||
this.Fail("私钥验证失败,请检查格式:" + err.Error())
|
||||
return
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的认证方式")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
@@ -51,6 +52,18 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
if len(params.PrivateKey) == 0 {
|
||||
this.FailField("privateKey", "请输入RSA私钥")
|
||||
}
|
||||
|
||||
// 验证私钥
|
||||
var err error
|
||||
if len(params.Passphrase) > 0 {
|
||||
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
|
||||
} else {
|
||||
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
|
||||
}
|
||||
if err != nil {
|
||||
this.Fail("私钥验证失败,请检查格式:" + err.Error())
|
||||
return
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的认证方式")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
@@ -83,6 +84,18 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
if len(params.PrivateKey) == 0 {
|
||||
this.FailField("privateKey", "请输入RSA私钥")
|
||||
}
|
||||
|
||||
// 验证私钥
|
||||
var err error
|
||||
if len(params.Passphrase) > 0 {
|
||||
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
|
||||
} else {
|
||||
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
|
||||
}
|
||||
if err != nil {
|
||||
this.Fail("私钥验证失败,请检查格式:" + err.Error())
|
||||
return
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的认证方式")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
@@ -83,6 +84,18 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
if len(params.PrivateKey) == 0 {
|
||||
this.FailField("privateKey", "请输入RSA私钥")
|
||||
}
|
||||
|
||||
// 验证私钥
|
||||
var err error
|
||||
if len(params.Passphrase) > 0 {
|
||||
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
|
||||
} else {
|
||||
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
|
||||
}
|
||||
if err != nil {
|
||||
this.Fail("私钥验证失败,请检查格式:" + err.Error())
|
||||
return
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的认证方式")
|
||||
}
|
||||
|
||||
60
internal/web/actions/default/clusters/logs/deleteAll.go
Normal file
60
internal/web/actions/default/clusters/logs/deleteAll.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAllAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAllAction) RunPost(params struct {
|
||||
DayFrom string
|
||||
DayTo string
|
||||
Keyword string
|
||||
Level string
|
||||
Type string // unread, needFix
|
||||
Tag string
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("批量删除节点运行日志")
|
||||
|
||||
// 目前仅允许通过关键词删除,防止误删
|
||||
if len(params.Keyword) == 0 {
|
||||
this.Fail("目前仅允许通过关键词删除")
|
||||
return
|
||||
}
|
||||
|
||||
var fixedState configutils.BoolState = 0
|
||||
var allServers = false
|
||||
if params.Type == "needFix" {
|
||||
fixedState = configutils.BoolStateNo
|
||||
allServers = true
|
||||
}
|
||||
|
||||
_, err := this.RPC().NodeLogRPC().DeleteNodeLogs(this.AdminContext(), &pb.DeleteNodeLogsRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeId: params.NodeId,
|
||||
Role: nodeconfigs.NodeRoleNode,
|
||||
DayFrom: params.DayFrom,
|
||||
DayTo: params.DayTo,
|
||||
Keyword: params.Keyword,
|
||||
Level: params.Level,
|
||||
IsUnread: params.Type == "unread",
|
||||
Tag: params.Tag,
|
||||
FixedState: int32(fixedState),
|
||||
AllServers: allServers,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -39,6 +39,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["dayFrom"] = params.DayFrom
|
||||
this.Data["dayTo"] = params.DayTo
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["searchedKeyword"] = params.Keyword
|
||||
this.Data["level"] = params.Level
|
||||
this.Data["type"] = params.Type
|
||||
this.Data["tag"] = params.Tag
|
||||
@@ -97,6 +98,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
var count = countResp.Count
|
||||
this.Data["countLogs"] = count
|
||||
|
||||
var page = this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ func init() {
|
||||
Post("/readAllLogs", new(ReadAllLogsAction)).
|
||||
Post("/fix", new(FixAction)).
|
||||
Post("/fixAll", new(FixAllAction)).
|
||||
Post("/deleteAll", new(DeleteAllAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,11 +33,12 @@ func (this *NodesAction) RunGet(params struct {
|
||||
Keyword string
|
||||
Level int32
|
||||
|
||||
CpuOrder string
|
||||
MemoryOrder string
|
||||
TrafficInOrder string
|
||||
TrafficOutOrder string
|
||||
LoadOrder string
|
||||
CpuOrder string
|
||||
MemoryOrder string
|
||||
TrafficInOrder string
|
||||
TrafficOutOrder string
|
||||
LoadOrder string
|
||||
ConnectionsOrder string
|
||||
}) {
|
||||
this.Data["groupId"] = params.GroupId
|
||||
this.Data["regionId"] = params.RegionId
|
||||
@@ -46,7 +47,7 @@ func (this *NodesAction) RunGet(params struct {
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["level"] = params.Level
|
||||
this.Data["clusterId"] = params.ClusterId
|
||||
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0
|
||||
this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 || len(params.ConnectionsOrder) > 0
|
||||
|
||||
// 集群是否已经设置了线路
|
||||
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
|
||||
@@ -114,6 +115,10 @@ func (this *NodesAction) RunGet(params struct {
|
||||
req.LoadAsc = true
|
||||
} else if params.LoadOrder == "desc" {
|
||||
req.LoadDesc = true
|
||||
} else if params.ConnectionsOrder == "asc" {
|
||||
req.ConnectionsAsc = true
|
||||
} else if params.ConnectionsOrder == "desc" {
|
||||
req.ConnectionsDesc = true
|
||||
}
|
||||
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
|
||||
if err != nil {
|
||||
@@ -123,8 +128,8 @@ func (this *NodesAction) RunGet(params struct {
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range nodesResp.Nodes {
|
||||
// 状态
|
||||
isSynced := false
|
||||
status := &nodeconfigs.NodeStatus{}
|
||||
var isSynced = false
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
@@ -144,8 +149,17 @@ func (this *NodesAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
ipAddresses := []maps.Map{}
|
||||
var ipAddresses = []maps.Map{}
|
||||
for _, addr := range ipAddressesResp.NodeIPAddresses {
|
||||
// 专属集群
|
||||
var addrClusterMaps = []maps.Map{}
|
||||
for _, addrCluster := range addr.NodeClusters {
|
||||
addrClusterMaps = append(addrClusterMaps, maps.Map{
|
||||
"id": addrCluster.Id,
|
||||
"name": addrCluster.Name,
|
||||
})
|
||||
}
|
||||
|
||||
ipAddresses = append(ipAddresses, maps.Map{
|
||||
"id": addr.Id,
|
||||
"name": addr.Name,
|
||||
@@ -153,6 +167,7 @@ func (this *NodesAction) RunGet(params struct {
|
||||
"canAccess": addr.CanAccess,
|
||||
"isUp": addr.IsUp,
|
||||
"isOn": addr.IsOn,
|
||||
"clusters": addrClusterMaps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -175,7 +190,7 @@ func (this *NodesAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
// DNS
|
||||
dnsRouteNames := []string{}
|
||||
var dnsRouteNames = []string{}
|
||||
for _, route := range node.DnsRoutes {
|
||||
dnsRouteNames = append(dnsRouteNames, route.Name)
|
||||
}
|
||||
@@ -203,16 +218,17 @@ func (this *NodesAction) RunGet(params struct {
|
||||
"error": node.InstallStatus.Error,
|
||||
},
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100),
|
||||
"trafficInBytes": status.TrafficInBytes,
|
||||
"trafficOutBytes": status.TrafficOutBytes,
|
||||
"load1m": numberutils.FormatFloat2(status.Load1m),
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": numberutils.FormatFloat2(status.CPUUsage * 100) + "%",
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": numberutils.FormatFloat2(status.MemoryUsage * 100) + "%",
|
||||
"trafficInBytes": status.TrafficInBytes,
|
||||
"trafficOutBytes": status.TrafficOutBytes,
|
||||
"load1m": numberutils.PadFloatZero(numberutils.FormatFloat2(status.Load1m), 2),
|
||||
"countConnections": status.ConnectionCount,
|
||||
},
|
||||
"cluster": maps.Map{
|
||||
"id": node.NodeCluster.Id,
|
||||
|
||||
@@ -11,7 +11,7 @@ type IndexAction struct {
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "region")
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
@@ -20,7 +20,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
regionMaps := []maps.Map{}
|
||||
var regionMaps = []maps.Map{}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeRegionId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeRegionIdRequest{NodeRegionId: region.Id})
|
||||
if err != nil {
|
||||
|
||||
@@ -18,9 +18,13 @@ func init() {
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Post("/sort", new(SortAction)).
|
||||
Get("/nodes", new(NodesAction)).
|
||||
GetPost("/updateNodeRegionPopup", new(UpdateNodeRegionPopupAction)).
|
||||
|
||||
//
|
||||
GetPost("/selectPopup", new(SelectPopupAction)).
|
||||
GetPost("/prices", new(PricesAction)).
|
||||
GetPost("/updatePricePopup", new(UpdatePricePopupAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package items
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
|
||||
"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
|
||||
BitsFrom int64
|
||||
BitsTo int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入名称").
|
||||
Field("bitsFrom", params.BitsFrom).
|
||||
Gte(0, "请输入不小于0的整数").
|
||||
Field("bitsTo", params.BitsTo).
|
||||
Gte(0, "请输入不小于0的整数")
|
||||
|
||||
createResp, err := this.RPC().NodePriceItemRPC().CreateNodePriceItem(this.AdminContext(), &pb.CreateNodePriceItemRequest{
|
||||
Name: params.Name,
|
||||
Type: regionutils.PriceTypeTraffic,
|
||||
BitsFrom: params.BitsFrom * 1000 * 1000,
|
||||
BitsTo: params.BitsTo * 1000 * 1000,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
defer this.CreateLogInfo("创建流量价格项目", createResp.NodePriceItemId)
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package items
|
||||
|
||||
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 {
|
||||
ItemId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo("删除流量价格项目 %d", params.ItemId)
|
||||
|
||||
_, err := this.RPC().NodePriceItemRPC().DeleteNodePriceItem(this.AdminContext(), &pb.DeleteNodePriceItemRequest{NodePriceItemId: params.ItemId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package items
|
||||
|
||||
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.AdminModuleCodeNode)).
|
||||
Data("teaMenu", "clusters").
|
||||
Data("teaSubMenu", "region").
|
||||
Prefix("/clusters/regions/items").
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package items
|
||||
|
||||
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 {
|
||||
ItemId int64
|
||||
}) {
|
||||
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
item := itemResp.NodePriceItem
|
||||
if item == nil {
|
||||
this.NotFound("nodePriceItem", params.ItemId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["item"] = maps.Map{
|
||||
"id": item.Id,
|
||||
"name": item.Name,
|
||||
"bitsFrom": item.BitsFrom,
|
||||
"bitsTo": item.BitsTo,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
ItemId int64
|
||||
Name string
|
||||
BitsFrom int64
|
||||
BitsTo int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改流量价格项目 %d", params.ItemId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入名称").
|
||||
Field("bitsFrom", params.BitsFrom).
|
||||
Gte(0, "请输入不小于0的整数").
|
||||
Field("bitsTo", params.BitsTo).
|
||||
Gte(0, "请输入不小于0的整数")
|
||||
|
||||
_, err := this.RPC().NodePriceItemRPC().UpdateNodePriceItem(this.AdminContext(), &pb.UpdateNodePriceItemRequest{
|
||||
NodePriceItemId: params.ItemId,
|
||||
Name: params.Name,
|
||||
BitsFrom: params.BitsFrom * 1000 * 1000,
|
||||
BitsTo: params.BitsTo * 1000 * 1000,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
92
internal/web/actions/default/clusters/regions/nodes.go
Normal file
92
internal/web/actions/default/clusters/regions/nodes.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package regions
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type NodesAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *NodesAction) Init() {
|
||||
this.Nav("", "", "node")
|
||||
}
|
||||
|
||||
func (this *NodesAction) RunGet(params struct {
|
||||
RegionId int64
|
||||
}) {
|
||||
this.Data["regionId"] = params.RegionId
|
||||
|
||||
// 所有区域
|
||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var regionMaps = []maps.Map{}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
regionMaps = append(regionMaps, maps.Map{
|
||||
"id": region.Id,
|
||||
"name": region.Name,
|
||||
})
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
|
||||
// 节点数量
|
||||
countResp, err := this.RPC().NodeRPC().CountAllNodeRegionInfo(this.AdminContext(), &pb.CountAllNodeRegionInfoRequest{NodeRegionId: params.RegionId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var page = this.NewPage(countResp.Count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
// 节点列表
|
||||
var hasNodesWithoutRegion = false
|
||||
nodesResp, err := this.RPC().NodeRPC().ListNodeRegionInfo(this.AdminContext(), &pb.ListNodeRegionInfoRequest{
|
||||
NodeRegionId: params.RegionId,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range nodesResp.InfoList {
|
||||
// region
|
||||
var regionMap maps.Map
|
||||
if node.NodeRegion != nil {
|
||||
regionMap = maps.Map{
|
||||
"id": node.NodeRegion.Id,
|
||||
"name": node.NodeRegion.Name,
|
||||
}
|
||||
} else {
|
||||
hasNodesWithoutRegion = true
|
||||
}
|
||||
|
||||
// cluster
|
||||
var clusterMap maps.Map
|
||||
if node.NodeCluster != nil {
|
||||
clusterMap = maps.Map{
|
||||
"id": node.NodeCluster.Id,
|
||||
"name": node.NodeCluster.Name,
|
||||
}
|
||||
}
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"region": regionMap,
|
||||
"cluster": clusterMap,
|
||||
})
|
||||
}
|
||||
this.Data["nodes"] = nodeMaps
|
||||
this.Data["hasNodesWithoutRegion"] = hasNodesWithoutRegion
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package regions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type PricesAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *PricesAction) Init() {
|
||||
this.Nav("", "", "price")
|
||||
}
|
||||
|
||||
func (this *PricesAction) RunGet(params struct{}) {
|
||||
// 所有价格项目
|
||||
itemsResp, err := this.RPC().NodePriceItemRPC().FindAllAvailableNodePriceItems(this.AdminContext(), &pb.FindAllAvailableNodePriceItemsRequest{Type: regionutils.PriceTypeTraffic})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
itemMaps := []maps.Map{}
|
||||
for _, item := range itemsResp.NodePriceItems {
|
||||
|
||||
itemMaps = append(itemMaps, maps.Map{
|
||||
"id": item.Id,
|
||||
"name": item.Name,
|
||||
"bitsFromString": this.formatBits(item.BitsFrom),
|
||||
"bitsToString": this.formatBits(item.BitsTo),
|
||||
})
|
||||
}
|
||||
this.Data["items"] = itemMaps
|
||||
|
||||
// 所有区域
|
||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledNodeRegions(this.AdminContext(), &pb.FindAllEnabledNodeRegionsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
regionMaps := []maps.Map{}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
pricesMap := map[string]float32{}
|
||||
if len(region.PricesJSON) > 0 {
|
||||
err = json.Unmarshal(region.PricesJSON, &pricesMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
regionMaps = append(regionMaps, maps.Map{
|
||||
"id": region.Id,
|
||||
"isOn": region.IsOn,
|
||||
"name": region.Name,
|
||||
"prices": pricesMap,
|
||||
})
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *PricesAction) formatBits(bits int64) string {
|
||||
sizeHuman := ""
|
||||
if bits < 1000 {
|
||||
sizeHuman = numberutils.FormatInt64(bits) + "BPS"
|
||||
} else if bits < 1_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fKBPS", float64(bits)/1000)
|
||||
} else if bits < 1_000_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fMBPS", float64(bits)/1000/1000)
|
||||
} else if bits < 1_000_000_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fGBPS", float64(bits)/1000/1000/1000)
|
||||
} else if bits < 1_000_000_000_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fTBPS", float64(bits)/1000/1000/1000/1000)
|
||||
} else if bits < 1_000_000_000_000_000_000 {
|
||||
sizeHuman = fmt.Sprintf("%.2fPBPS", float64(bits)/1000/1000/1000/1000/1000)
|
||||
} else {
|
||||
sizeHuman = fmt.Sprintf("%.2fEBPS", float64(bits)/1000/1000/1000/1000/1000/1000)
|
||||
}
|
||||
return sizeHuman
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package regionutils
|
||||
|
||||
const (
|
||||
PriceTypeTraffic = "traffic"
|
||||
)
|
||||
@@ -40,12 +40,18 @@ func (this *SelectPopupAction) RunPost(params struct {
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
if params.RegionId <= 0 {
|
||||
this.Data["region"] = nil
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
region := regionResp.NodeRegion
|
||||
var region = regionResp.NodeRegion
|
||||
if region == nil {
|
||||
this.NotFound("nodeRegion", params.RegionId)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package regions
|
||||
|
||||
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 UpdateNodeRegionPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateNodeRegionPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdateNodeRegionPopupAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
RegionId int64
|
||||
}) {
|
||||
// node
|
||||
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.Node
|
||||
if node == nil {
|
||||
this.NotFound("node", params.NodeId)
|
||||
return
|
||||
}
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
}
|
||||
|
||||
// region
|
||||
this.Data["region"] = maps.Map{
|
||||
"id": 0,
|
||||
"name": "",
|
||||
}
|
||||
if params.RegionId > 0 {
|
||||
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var region = regionResp.NodeRegion
|
||||
if region != nil {
|
||||
this.Data["region"] = maps.Map{
|
||||
"id": region.Id,
|
||||
"name": region.Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all regions
|
||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var regionMaps = []maps.Map{}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
regionMaps = append(regionMaps, maps.Map{
|
||||
"id": region.Id,
|
||||
"name": region.Name,
|
||||
})
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateNodeRegionPopupAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
RegionId int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改节点 %d 区域到 %d", params.RegionId)
|
||||
|
||||
_, err := this.RPC().NodeRPC().UpdateNodeRegionInfo(this.AdminContext(), &pb.UpdateNodeRegionInfoRequest{
|
||||
NodeId: params.NodeId,
|
||||
NodeRegionId: params.RegionId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package regions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"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 UpdatePricePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePricePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePricePopupAction) RunGet(params struct {
|
||||
RegionId int64
|
||||
ItemId int64
|
||||
}) {
|
||||
// 区域
|
||||
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
region := regionResp.NodeRegion
|
||||
if region == nil {
|
||||
this.NotFound("nodeRegion", params.RegionId)
|
||||
return
|
||||
}
|
||||
this.Data["region"] = maps.Map{
|
||||
"id": region.Id,
|
||||
"isOn": region.IsOn,
|
||||
"name": region.Name,
|
||||
}
|
||||
|
||||
// 当前价格
|
||||
pricesMap := map[string]float32{}
|
||||
if len(region.PricesJSON) > 0 {
|
||||
err = json.Unmarshal(region.PricesJSON, &pricesMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.Data["price"] = pricesMap[numberutils.FormatInt64(params.ItemId)]
|
||||
|
||||
// 价格项
|
||||
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
item := itemResp.NodePriceItem
|
||||
if item == nil {
|
||||
this.NotFound("nodePriceItem", params.ItemId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["item"] = maps.Map{
|
||||
"id": item.Id,
|
||||
"name": item.Name,
|
||||
"bitsFrom": item.BitsFrom,
|
||||
"bitsTo": item.BitsTo,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePricePopupAction) RunPost(params struct {
|
||||
RegionId int64
|
||||
ItemId int64
|
||||
Price float32
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改区域 %d-价格项 %d 的价格", params.RegionId, params.ItemId)
|
||||
|
||||
_, err := this.RPC().NodeRegionRPC().UpdateNodeRegionPrice(this.AdminContext(), &pb.UpdateNodeRegionPriceRequest{
|
||||
NodeRegionId: params.RegionId,
|
||||
NodeItemId: params.ItemId,
|
||||
Price: params.Price,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -3,23 +3,41 @@ package tasks
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CheckAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CheckAction) RunPost(params struct{}) {
|
||||
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{
|
||||
ExcludeTypes: []string{"ipItemChanged"},
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
func (this *CheckAction) RunPost(params struct {
|
||||
IsDoing bool
|
||||
HasError bool
|
||||
IsUpdated bool
|
||||
}) {
|
||||
var isStream = this.Request.ProtoMajor >= 2
|
||||
this.Data["isStream"] = isStream
|
||||
|
||||
this.Data["isDoing"] = resp.ExistTasks
|
||||
this.Data["hasError"] = resp.ExistError
|
||||
var maxTries = 10
|
||||
for i := 0; i < maxTries; i++ {
|
||||
resp, err := this.RPC().NodeTaskRPC().ExistsNodeTasks(this.AdminContext(), &pb.ExistsNodeTasksRequest{
|
||||
ExcludeTypes: []string{"ipItemChanged"},
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有数据变化,继续查询
|
||||
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError && isStream {
|
||||
time.Sleep(3 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
this.Data["isDoing"] = resp.ExistTasks
|
||||
this.Data["hasError"] = resp.ExistError
|
||||
break
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
helpers.NotifyIPItemsCountChanges()
|
||||
helpers.NotifyNodeLogsCountChange()
|
||||
|
||||
if teaconst.IsPlus {
|
||||
if this.checkPlus() {
|
||||
this.RedirectURL("/dashboard/boards")
|
||||
return
|
||||
}
|
||||
@@ -76,16 +76,14 @@ func (this *IndexAction) RunPost(params struct{}) {
|
||||
this.Data["dashboard"] = maps.Map{
|
||||
"defaultClusterId": resp.DefaultNodeClusterId,
|
||||
|
||||
"countServers": resp.CountServers,
|
||||
"countNodeClusters": resp.CountNodeClusters,
|
||||
"countNodes": resp.CountNodes,
|
||||
"countOfflineNodes": resp.CountOfflineNodes,
|
||||
"countUsers": resp.CountUsers,
|
||||
"countAPINodes": resp.CountAPINodes,
|
||||
"countOfflineAPINodes": resp.CountOfflineAPINodes,
|
||||
"countDBNodes": resp.CountDBNodes,
|
||||
"countUserNodes": resp.CountUserNodes,
|
||||
"countOfflineUserNodes": resp.CountOfflineUserNodes,
|
||||
"countServers": resp.CountServers,
|
||||
"countNodeClusters": resp.CountNodeClusters,
|
||||
"countNodes": resp.CountNodes,
|
||||
"countOfflineNodes": resp.CountOfflineNodes,
|
||||
"countUsers": resp.CountUsers,
|
||||
"countAPINodes": resp.CountAPINodes,
|
||||
"countOfflineAPINodes": resp.CountOfflineAPINodes,
|
||||
"countDBNodes": resp.CountDBNodes,
|
||||
|
||||
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
|
||||
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
|
||||
@@ -278,5 +276,13 @@ func (this *IndexAction) RunPost(params struct{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// 弱密码提示
|
||||
countWeakAdminsResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{HasWeakPassword: true})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
8
internal/web/actions/default/dashboard/index_ext.go
Normal file
8
internal/web/actions/default/dashboard/index_ext.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package dashboard
|
||||
|
||||
func (this *IndexAction) checkPlus() bool {
|
||||
return false
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db/dbnodeutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type NodeAction struct {
|
||||
@@ -33,7 +34,7 @@ func (this *NodeAction) RunGet(params struct {
|
||||
"host": node.Host,
|
||||
"port": node.Port,
|
||||
"username": node.Username,
|
||||
"password": node.Password,
|
||||
"password": strings.Repeat("*", len(node.Password)),
|
||||
"description": node.Description,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
cluster := clusterResp.NodeCluster
|
||||
var cluster = clusterResp.NodeCluster
|
||||
if cluster == nil {
|
||||
this.NotFound("nodeCluster", params.ClusterId)
|
||||
return
|
||||
@@ -42,8 +42,8 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
var defaultRoute = dnsResp.DefaultRoute
|
||||
domainName := ""
|
||||
dnsMap := maps.Map{
|
||||
var domainName = ""
|
||||
var dnsMap = maps.Map{
|
||||
"dnsName": dnsResp.Name,
|
||||
"domainId": 0,
|
||||
"domainName": "",
|
||||
@@ -70,20 +70,43 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
|
||||
this.Data["dnsInfo"] = dnsMap
|
||||
|
||||
// 节点DNS解析记录
|
||||
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{NodeClusterId: params.ClusterId})
|
||||
// 未安装的节点
|
||||
notInstalledNodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
IsInstalled: false,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
nodeMaps := []maps.Map{}
|
||||
var allNodes = notInstalledNodesResp.Nodes
|
||||
|
||||
// 节点DNS解析记录
|
||||
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: params.ClusterId,
|
||||
IsInstalled: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var installedNodeIdsMap = map[int64]bool{}
|
||||
for _, node := range nodesResp.Nodes {
|
||||
installedNodeIdsMap[node.Id] = true
|
||||
}
|
||||
|
||||
allNodes = append(allNodes, nodesResp.Nodes...)
|
||||
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range allNodes {
|
||||
var isInstalled = installedNodeIdsMap[node.Id]
|
||||
|
||||
if len(node.Routes) > 0 {
|
||||
for _, route := range node.Routes {
|
||||
// 检查是否已解析
|
||||
isResolved := false
|
||||
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
recordType := "A"
|
||||
var isResolved = false
|
||||
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
@@ -102,22 +125,24 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"ipAddrId": node.NodeIPAddressId,
|
||||
"route": maps.Map{
|
||||
"name": route.Name,
|
||||
"code": route.Code,
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 默认线路
|
||||
var isResolved = false
|
||||
if len(defaultRoute) > 0 {
|
||||
recordType := "A"
|
||||
if isInstalled && len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
@@ -135,15 +160,17 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddr": node.IpAddr,
|
||||
"ipAddrId": node.NodeIPAddressId,
|
||||
"route": maps.Map{
|
||||
"name": "",
|
||||
"code": "",
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -155,7 +182,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
serverMaps := []maps.Map{}
|
||||
var serverMaps = []maps.Map{}
|
||||
for _, server := range serversResp.Servers {
|
||||
// 检查是否已解析
|
||||
isResolved := false
|
||||
@@ -198,7 +225,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
issueMaps := []maps.Map{}
|
||||
var issueMaps = []maps.Map{}
|
||||
for _, issue := range issuesResp.Issues {
|
||||
issueMaps = append(issueMaps, maps.Map{
|
||||
"target": issue.Target,
|
||||
@@ -218,7 +245,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
taskMaps := []maps.Map{}
|
||||
var taskMaps = []maps.Map{}
|
||||
for _, task := range resp.DnsTasks {
|
||||
var clusterMap maps.Map = nil
|
||||
var nodeMap maps.Map = nil
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// DomainOptionsAction 域名列表选项
|
||||
@@ -21,10 +22,18 @@ func (this *DomainOptionsAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
domainMaps := []maps.Map{}
|
||||
|
||||
// 排序
|
||||
if len(domainsResp.DnsDomains) > 0 {
|
||||
sort.Slice(domainsResp.DnsDomains, func(i, j int) bool {
|
||||
return domainsResp.DnsDomains[i].Name < domainsResp.DnsDomains[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
var domainMaps = []maps.Map{}
|
||||
for _, domain := range domainsResp.DnsDomains {
|
||||
// 未开启或者已删除的先跳过
|
||||
if !domain.IsOn || domain.IsDeleted {
|
||||
if !domain.IsOn || domain.IsDeleted || !domain.IsUp {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,10 @@ func ValidateRecordName(name string) bool {
|
||||
}
|
||||
|
||||
pieces := strings.Split(name, ".")
|
||||
for _, piece := range pieces {
|
||||
for index, piece := range pieces {
|
||||
if index == 0 && piece == "*" {
|
||||
continue
|
||||
}
|
||||
if piece == "-" ||
|
||||
strings.HasPrefix(piece, "-") ||
|
||||
strings.HasSuffix(piece, "-") ||
|
||||
@@ -130,6 +133,11 @@ func ValidateRecordValue(recordType dnsconfigs.RecordType, value string) (messag
|
||||
message = "请输入正确的邮件服务器域名"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeSRV:
|
||||
if len(value) == 0 {
|
||||
message = "请输入主机名"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeTXT:
|
||||
if len(value) > 512 {
|
||||
message = "文本长度不能超出512字节"
|
||||
|
||||
@@ -18,6 +18,8 @@ func (this *NodesPopupAction) Init() {
|
||||
func (this *NodesPopupAction) RunGet(params struct {
|
||||
DomainId int64
|
||||
}) {
|
||||
this.Data["domainId"] = params.DomainId
|
||||
|
||||
// 域名信息
|
||||
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
|
||||
DnsDomainId: params.DomainId,
|
||||
@@ -26,7 +28,7 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
domain := domainResp.DnsDomain
|
||||
var domain = domainResp.DnsDomain
|
||||
if domain == nil {
|
||||
this.NotFound("dnsDomain", params.DomainId)
|
||||
return
|
||||
@@ -35,7 +37,7 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
this.Data["domain"] = domain.Name
|
||||
|
||||
// 集群
|
||||
clusterMaps := []maps.Map{}
|
||||
var clusterMaps = []maps.Map{}
|
||||
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithDNSDomainId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithDNSDomainIdRequest{DnsDomainId: params.DomainId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -43,18 +45,24 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
for _, cluster := range clustersResp.NodeClusters {
|
||||
// 默认值
|
||||
var defaultRoute = cluster.DnsDefaultRoute
|
||||
|
||||
// 节点DNS解析记录
|
||||
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{NodeClusterId: cluster.Id})
|
||||
nodesResp, err := this.RPC().NodeRPC().FindAllEnabledNodesDNSWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodesDNSWithNodeClusterIdRequest{
|
||||
NodeClusterId: cluster.Id,
|
||||
IsInstalled: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
nodeMaps := []maps.Map{}
|
||||
var nodeMaps = []maps.Map{}
|
||||
for _, node := range nodesResp.Nodes {
|
||||
if len(node.Routes) > 0 {
|
||||
for _, route := range node.Routes {
|
||||
// 检查是否有域名解析记录
|
||||
isOk := false
|
||||
var isResolved = false
|
||||
if len(route.Name) > 0 && len(node.IpAddr) > 0 && len(cluster.DnsName) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
@@ -71,7 +79,7 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isOk = checkResp.IsOk
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
@@ -83,10 +91,30 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
"code": route.Code,
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isOk": isOk,
|
||||
"isOk": isResolved,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 默认线路
|
||||
var isResolved = false
|
||||
if len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
}
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: cluster.DnsDomainId,
|
||||
Name: cluster.DnsName,
|
||||
Type: recordType,
|
||||
Route: defaultRoute,
|
||||
Value: node.IpAddr,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
isResolved = checkResp.IsOk
|
||||
}
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
@@ -96,7 +124,7 @@ func (this *NodesPopupAction) RunGet(params struct {
|
||||
"code": "",
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isOk": false,
|
||||
"isOk": isResolved,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ func (this *ServersPopupAction) Init() {
|
||||
func (this *ServersPopupAction) RunGet(params struct {
|
||||
DomainId int64
|
||||
}) {
|
||||
this.Data["domainId"] = params.DomainId
|
||||
|
||||
// 域名信息
|
||||
domainResp, err := this.RPC().DNSDomainRPC().FindBasicDNSDomain(this.AdminContext(), &pb.FindBasicDNSDomainRequest{
|
||||
DnsDomainId: params.DomainId,
|
||||
@@ -25,7 +27,7 @@ func (this *ServersPopupAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
domain := domainResp.DnsDomain
|
||||
var domain = domainResp.DnsDomain
|
||||
if domain == nil {
|
||||
this.NotFound("dnsDomain", params.DomainId)
|
||||
return
|
||||
@@ -34,7 +36,7 @@ func (this *ServersPopupAction) RunGet(params struct {
|
||||
this.Data["domain"] = domain.Name
|
||||
|
||||
// 服务信息
|
||||
clusterMaps := []maps.Map{}
|
||||
var clusterMaps = []maps.Map{}
|
||||
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithDNSDomainId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithDNSDomainIdRequest{DnsDomainId: params.DomainId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -46,9 +48,9 @@ func (this *ServersPopupAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
serverMaps := []maps.Map{}
|
||||
var serverMaps = []maps.Map{}
|
||||
for _, server := range serversResp.Servers {
|
||||
isOk := false
|
||||
var isOk = false
|
||||
if len(cluster.DnsName) > 0 && len(server.DnsName) > 0 {
|
||||
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
|
||||
DnsDomainId: params.DomainId,
|
||||
|
||||
@@ -22,12 +22,14 @@ func (this *UpdateNodePopupAction) Init() {
|
||||
func (this *UpdateNodePopupAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
IpAddrId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
|
||||
NodeId: params.NodeId,
|
||||
NodeClusterId: params.ClusterId,
|
||||
NodeIPAddrId: params.IpAddrId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -39,12 +41,13 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
this.Data["ipAddr"] = dnsInfo.IpAddr
|
||||
this.Data["ipAddrId"] = dnsInfo.NodeIPAddressId
|
||||
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
|
||||
this.Data["domainId"] = dnsInfo.DnsDomainId
|
||||
this.Data["domainName"] = dnsInfo.DnsDomainName
|
||||
|
||||
// 读取所有线路
|
||||
allRouteMaps := []maps.Map{}
|
||||
var allRouteMaps = []maps.Map{}
|
||||
if dnsInfo.DnsDomainId > 0 {
|
||||
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
|
||||
if err != nil {
|
||||
@@ -75,6 +78,7 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
|
||||
func (this *UpdateNodePopupAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
IpAddr string
|
||||
IpAddrId int64
|
||||
DomainId int64
|
||||
DnsRoutesJSON []byte
|
||||
|
||||
@@ -84,7 +88,7 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
|
||||
// 操作日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
|
||||
|
||||
routes := []string{}
|
||||
var routes = []string{}
|
||||
if len(params.DnsRoutesJSON) > 0 {
|
||||
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
|
||||
if err != nil {
|
||||
@@ -103,10 +107,11 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
|
||||
|
||||
// 执行修改
|
||||
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
|
||||
NodeId: params.NodeId,
|
||||
IpAddr: params.IpAddr,
|
||||
DnsDomainId: params.DomainId,
|
||||
Routes: routes,
|
||||
NodeId: params.NodeId,
|
||||
IpAddr: params.IpAddr,
|
||||
NodeIPAddressId: params.IpAddrId,
|
||||
DnsDomainId: params.DomainId,
|
||||
Routes: routes,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -64,6 +64,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
// HuaweiDNS
|
||||
ParamHuaweiAccessKeyId string
|
||||
ParamHuaweiAccessKeySecret string
|
||||
ParamHuaweiEndpoint string
|
||||
|
||||
// CloudFlare
|
||||
ParamCloudFlareAPIKey string
|
||||
@@ -119,6 +120,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
|
||||
apiParams["accessKeyId"] = params.ParamHuaweiAccessKeyId
|
||||
apiParams["accessKeySecret"] = params.ParamHuaweiAccessKeySecret
|
||||
apiParams["endpoint"] = params.ParamHuaweiEndpoint
|
||||
case "cloudFlare":
|
||||
params.Must.
|
||||
Field("paramCloudFlareAPIKey", params.ParamCloudFlareAPIKey).
|
||||
|
||||
@@ -59,7 +59,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
var providerMaps = []maps.Map{}
|
||||
for _, provider := range providersResp.DnsProviders {
|
||||
dataUpdatedTime := ""
|
||||
var dataUpdatedTime = ""
|
||||
if provider.DataUpdatedAt > 0 {
|
||||
dataUpdatedTime = timeutil.FormatTime("Y-m-d H:i:s", provider.DataUpdatedAt)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countDomains := countDomainsResp.Count
|
||||
var countDomains = countDomainsResp.Count
|
||||
|
||||
providerMaps = append(providerMaps, maps.Map{
|
||||
"id": provider.Id,
|
||||
|
||||
@@ -91,6 +91,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
// HuaweiDNS
|
||||
ParamHuaweiAccessKeyId string
|
||||
ParamHuaweiAccessKeySecret string
|
||||
ParamHuaweiEndpoint string
|
||||
|
||||
// CloudFlare
|
||||
ParamCloudFlareAPIKey string
|
||||
@@ -148,6 +149,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
|
||||
apiParams["accessKeyId"] = params.ParamHuaweiAccessKeyId
|
||||
apiParams["accessKeySecret"] = params.ParamHuaweiAccessKeySecret
|
||||
apiParams["endpoint"] = params.ParamHuaweiEndpoint
|
||||
case "cloudFlare":
|
||||
params.Must.
|
||||
Field("paramCloudFlareAPIKey", params.ParamCloudFlareAPIKey).
|
||||
|
||||
@@ -3,21 +3,39 @@ package tasks
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CheckAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CheckAction) RunPost(params struct{}) {
|
||||
resp, err := this.RPC().DNSTaskRPC().ExistsDNSTasks(this.AdminContext(), &pb.ExistsDNSTasksRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
func (this *CheckAction) RunPost(params struct {
|
||||
IsDoing bool
|
||||
HasError bool
|
||||
IsUpdated bool
|
||||
}) {
|
||||
var isStream = this.Request.ProtoMajor >= 2
|
||||
this.Data["isStream"] = isStream
|
||||
|
||||
this.Data["isDoing"] = resp.ExistTasks
|
||||
this.Data["hasError"] = resp.ExistError
|
||||
var maxTries = 10
|
||||
for i := 0; i < maxTries; i++ {
|
||||
resp, err := this.RPC().DNSTaskRPC().ExistsDNSTasks(this.AdminContext(), &pb.ExistsDNSTasksRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有数据变化,继续查询
|
||||
if i < maxTries-1 && params.IsUpdated && resp.ExistTasks == params.IsDoing && resp.ExistError == params.HasError && isStream {
|
||||
time.Sleep(3 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
this.Data["isDoing"] = resp.ExistTasks
|
||||
this.Data["hasError"] = resp.ExistError
|
||||
break
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
// 检查是否需要OTP
|
||||
type CheckOTPAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CheckOTPAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CheckOTPAction) RunPost(params struct {
|
||||
Username string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
checkResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["requireOTP"] = checkResp.RequireOTP
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
@@ -10,14 +9,16 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/xlzd/gotp"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -27,13 +28,33 @@ type IndexAction struct {
|
||||
|
||||
// 首页(登录页)
|
||||
|
||||
var TokenSalt = stringutil.Rand(32)
|
||||
// TokenKey 加密用的密钥
|
||||
var TokenKey = stringutil.Rand(32)
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
From string
|
||||
|
||||
Auth *helpers.UserShouldAuth
|
||||
}) {
|
||||
// 是否自动从HTTP跳转到HTTPS
|
||||
if this.Request.TLS == nil {
|
||||
httpsPort, _ := adminserverutils.ReadServerHTTPS()
|
||||
if httpsPort > 0 {
|
||||
currentHost, _, err := net.SplitHostPort(this.Request.Host)
|
||||
if err != nil {
|
||||
currentHost = this.Request.Host
|
||||
}
|
||||
|
||||
var newHost = configutils.QuoteIP(currentHost)
|
||||
if httpsPort != 443 /** default https port **/ {
|
||||
newHost += ":" + types.String(httpsPort)
|
||||
}
|
||||
|
||||
this.RedirectURL("https://" + newHost + this.Request.RequestURI)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DEMO模式
|
||||
this.Data["isDemo"] = teaconst.IsDemoMode
|
||||
|
||||
@@ -59,7 +80,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["menu"] = "signIn"
|
||||
|
||||
var timestamp = fmt.Sprintf("%d", time.Now().Unix())
|
||||
this.Data["token"] = stringutil.Md5(TokenSalt+timestamp) + timestamp
|
||||
this.Data["token"] = stringutil.Md5(TokenKey+timestamp) + timestamp
|
||||
this.Data["from"] = params.From
|
||||
|
||||
uiConfig, err := configloaders.LoadAdminUIConfig()
|
||||
@@ -83,6 +104,9 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["rememberLogin"] = securityConfig.AllowRememberLogin
|
||||
}
|
||||
|
||||
// 删除Cookie
|
||||
loginutils.UnsetCookie(this.Object())
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -93,9 +117,10 @@ func (this *IndexAction) RunPost(params struct {
|
||||
Password string
|
||||
OtpCode string
|
||||
Remember bool
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
CSRF *actionutils.CSRF
|
||||
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("username", params.Username).
|
||||
@@ -112,7 +137,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.Fail("请通过登录页面登录")
|
||||
}
|
||||
var timestampString = params.Token[32:]
|
||||
if stringutil.Md5(TokenSalt+timestampString) != params.Token[:32] {
|
||||
if stringutil.Md5(TokenKey+timestampString) != params.Token[:32] {
|
||||
this.FailField("refresh", "登录页面已过期,请刷新后重试")
|
||||
}
|
||||
var timestamp = types.Int64(timestampString)
|
||||
@@ -123,6 +148,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
this.Fail("服务器出了点小问题:" + err.Error())
|
||||
return
|
||||
}
|
||||
resp, err := rpcClient.AdminRPC().LoginAdmin(rpcClient.Context(0), &pb.LoginAdminRequest{
|
||||
Username: params.Username,
|
||||
@@ -136,6 +162,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
actionutils.Fail(this, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.IsOk {
|
||||
@@ -145,31 +172,37 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
this.Fail("请输入正确的用户名密码")
|
||||
return
|
||||
}
|
||||
var adminId = resp.AdminId
|
||||
|
||||
// 检查OTP
|
||||
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: resp.AdminId,
|
||||
Type: "otp",
|
||||
})
|
||||
// 检查是否支持OTP
|
||||
checkOTPResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
|
||||
var loginParams = maps.Map{}
|
||||
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
|
||||
var requireOTP = checkOTPResp.RequireOTP
|
||||
this.Data["requireOTP"] = requireOTP
|
||||
if requireOTP {
|
||||
this.Data["remember"] = params.Remember
|
||||
|
||||
var sid = this.Session().Sid
|
||||
this.Data["sid"] = sid
|
||||
_, err = this.RPC().LoginSessionRPC().WriteLoginSessionValue(this.AdminContext(), &pb.WriteLoginSessionValueRequest{
|
||||
Sid: sid + "_otp",
|
||||
Key: "adminId",
|
||||
Value: types.String(adminId),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
secret := loginParams.GetString("secret")
|
||||
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
|
||||
this.Fail("请输入正确的OTP动态密码")
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var adminId = resp.AdminId
|
||||
// 写入SESSION
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
|
||||
// 记录日志
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Post("/checkOTP", new(CheckOTPAction)).
|
||||
Prefix("/").
|
||||
GetPost("", new(IndexAction)).
|
||||
Prefix("").
|
||||
GetPost("/", new(IndexAction)).
|
||||
GetPost("/index/otp", new(OtpAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
86
internal/web/actions/default/index/loginutils/utils.go
Normal file
86
internal/web/actions/default/index/loginutils/utils.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package loginutils
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CalculateClientFingerprint 计算客户端指纹
|
||||
func CalculateClientFingerprint(action *actions.ActionObject) string {
|
||||
return stringutil.Md5(RemoteIP(action) + "@" + action.Request.UserAgent())
|
||||
}
|
||||
|
||||
// RemoteIP 获取客户端IP
|
||||
// TODO 将来增加是否使用代理设置(即从X-Real-IP中获取IP)
|
||||
func RemoteIP(action *actions.ActionObject) string {
|
||||
ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
|
||||
return ip
|
||||
}
|
||||
|
||||
// LookupIPRegion 查找登录区域
|
||||
func LookupIPRegion(ip string) string {
|
||||
if len(ip) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result = iplibrary.LookupIP(ip)
|
||||
if result != nil && result.IsOk() {
|
||||
// 这里不需要网络运营商信息
|
||||
return result.CountryName() + "@" + result.ProvinceName() + "@" + result.CityName() + "@" + result.TownName()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// SetCookie 设置Cookie
|
||||
func SetCookie(action *actions.ActionObject, remember bool) {
|
||||
if remember {
|
||||
var cookie = &http.Cookie{
|
||||
Name: teaconst.CookieSID,
|
||||
Value: action.Session().Sid,
|
||||
Path: "/",
|
||||
MaxAge: 14 * 86400,
|
||||
HttpOnly: true,
|
||||
}
|
||||
if action.Request.TLS != nil {
|
||||
cookie.SameSite = http.SameSiteStrictMode
|
||||
cookie.Secure = true
|
||||
}
|
||||
action.AddCookie(cookie)
|
||||
} else {
|
||||
var cookie = &http.Cookie{
|
||||
Name: teaconst.CookieSID,
|
||||
Value: action.Session().Sid,
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
HttpOnly: true,
|
||||
}
|
||||
if action.Request.TLS != nil {
|
||||
cookie.SameSite = http.SameSiteStrictMode
|
||||
cookie.Secure = true
|
||||
}
|
||||
action.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// UnsetCookie 重置Cookie
|
||||
func UnsetCookie(action *actions.ActionObject) {
|
||||
cookie := &http.Cookie{
|
||||
Name: teaconst.CookieSID,
|
||||
Value: action.Session().Sid,
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
}
|
||||
if action.Request.TLS != nil {
|
||||
cookie.SameSite = http.SameSiteStrictMode
|
||||
cookie.Secure = true
|
||||
}
|
||||
action.AddCookie(cookie)
|
||||
}
|
||||
154
internal/web/actions/default/index/otp.go
Normal file
154
internal/web/actions/default/index/otp.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package index
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/setup"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/xlzd/gotp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OtpAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *OtpAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *OtpAction) RunGet(params struct {
|
||||
From string
|
||||
Sid string
|
||||
Remember bool
|
||||
}) {
|
||||
// 检查系统是否已经配置过
|
||||
if !setup.IsConfigured() {
|
||||
this.RedirectURL("/setup")
|
||||
return
|
||||
}
|
||||
|
||||
//// 是否新安装
|
||||
if setup.IsNewInstalled() {
|
||||
this.RedirectURL("/setup/confirm")
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["isUser"] = false
|
||||
this.Data["menu"] = "signIn"
|
||||
|
||||
var timestamp = fmt.Sprintf("%d", time.Now().Unix())
|
||||
this.Data["token"] = stringutil.Md5(TokenKey+timestamp) + timestamp
|
||||
this.Data["from"] = params.From
|
||||
this.Data["sid"] = params.Sid
|
||||
|
||||
uiConfig, err := configloaders.LoadAdminUIConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["systemName"] = uiConfig.AdminSystemName
|
||||
this.Data["showVersion"] = uiConfig.ShowVersion
|
||||
if len(uiConfig.Version) > 0 {
|
||||
this.Data["version"] = uiConfig.Version
|
||||
} else {
|
||||
this.Data["version"] = teaconst.Version
|
||||
}
|
||||
this.Data["faviconFileId"] = uiConfig.FaviconFileId
|
||||
this.Data["remember"] = params.Remember
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *OtpAction) RunPost(params struct {
|
||||
Sid string
|
||||
OtpCode string
|
||||
Remember bool
|
||||
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
}) {
|
||||
if len(params.OtpCode) == 0 {
|
||||
this.FailField("otpCode", "请输入正确的OTP动态密码")
|
||||
return
|
||||
}
|
||||
|
||||
var sid = params.Sid
|
||||
if len(sid) == 0 || len(sid) > 64 {
|
||||
this.Fail("参数错误,请重新登录(001)")
|
||||
return
|
||||
}
|
||||
sid += "_otp"
|
||||
|
||||
// 获取SESSION
|
||||
sessionResp, err := this.RPC().LoginSessionRPC().FindLoginSession(this.AdminContext(), &pb.FindLoginSessionRequest{Sid: sid})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var session = sessionResp.LoginSession
|
||||
if session == nil || session.AdminId <= 0 {
|
||||
this.Fail("参数错误,请重新登录(002)")
|
||||
return
|
||||
}
|
||||
var adminId = session.AdminId
|
||||
|
||||
// 检查OTP
|
||||
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: adminId,
|
||||
Type: "otp",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
|
||||
var loginParams = maps.Map{}
|
||||
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var secret = loginParams.GetString("secret")
|
||||
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
|
||||
this.FailField("otpCode", "请输入正确的OTP动态密码")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 写入SESSION
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
|
||||
// 删除OTP SESSION
|
||||
_, err = this.RPC().LoginSessionRPC().DeleteLoginSession(this.AdminContext(), &pb.DeleteLoginSessionRequest{Sid: sid})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, "成功通过OTP验证登录系统", this.RequestRemoteIP())
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -23,6 +23,7 @@ func (this *ExportExcelAction) RunGet(params struct {
|
||||
DayTo string
|
||||
Keyword string
|
||||
UserType string
|
||||
Level string
|
||||
}) {
|
||||
logsResp, err := this.RPC().LogRPC().ListLogs(this.AdminContext(), &pb.ListLogsRequest{
|
||||
Offset: 0,
|
||||
@@ -31,6 +32,7 @@ func (this *ExportExcelAction) RunGet(params struct {
|
||||
DayTo: params.DayTo,
|
||||
Keyword: params.Keyword,
|
||||
UserType: params.UserType,
|
||||
Level: params.Level,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -56,6 +58,7 @@ func (this *ExportExcelAction) RunGet(params struct {
|
||||
row.AddCell().SetString("区域")
|
||||
row.AddCell().SetString("运营商")
|
||||
row.AddCell().SetString("页面地址")
|
||||
row.AddCell().SetString("级别")
|
||||
}
|
||||
|
||||
// 数据
|
||||
@@ -95,6 +98,17 @@ func (this *ExportExcelAction) RunGet(params struct {
|
||||
row.AddCell().SetString(regionName)
|
||||
row.AddCell().SetString(ispName)
|
||||
row.AddCell().SetString(log.Action)
|
||||
|
||||
var levelName = ""
|
||||
switch log.Level {
|
||||
case "info":
|
||||
levelName = "信息"
|
||||
case "warn", "warning":
|
||||
levelName = "警告"
|
||||
case "error":
|
||||
levelName = "错误"
|
||||
}
|
||||
row.AddCell().SetString(levelName)
|
||||
}
|
||||
|
||||
this.AddHeader("Content-Type", "application/vnd.ms-excel")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user