Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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,7 +1,7 @@
|
||||
FROM alpine:latest
|
||||
LABEL maintainer="iwind.liu@gmail.com"
|
||||
ENV TZ "Asia/Shanghai"
|
||||
ENV VERSION 0.6.4.1
|
||||
ENV VERSION 1.0.0
|
||||
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"
|
||||
|
||||
7
go.mod
7
go.mod
@@ -15,7 +15,8 @@ require (
|
||||
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.5.0
|
||||
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.1
|
||||
)
|
||||
@@ -33,8 +34,8 @@ require (
|
||||
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.7.0 // indirect
|
||||
golang.org/x/text v0.7.0 // 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
|
||||
|
||||
19
go.sum
19
go.sum
@@ -74,10 +74,6 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||
github.com/iwind/TeaGo v0.0.0-20230207032553-d6dcde0cd518 h1:zuWjQ57zc67ZSTHpxNK95JYoa9Ph/JRSnapsTY/hlhQ=
|
||||
github.com/iwind/TeaGo v0.0.0-20230207032553-d6dcde0cd518/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||
github.com/iwind/TeaGo v0.0.0-20230303070415-9d0689db6456 h1:xv3AVaxuwjThkBDptAfsFSmuHQIrRrvt8BRaekWnsvs=
|
||||
github.com/iwind/TeaGo v0.0.0-20230303070415-9d0689db6456/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||
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=
|
||||
@@ -142,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=
|
||||
@@ -166,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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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=
|
||||
@@ -195,15 +193,16 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/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.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.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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=
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.6.4.2"
|
||||
Version = "1.0.1"
|
||||
|
||||
APINodeVersion = "0.6.4.2"
|
||||
APINodeVersion = "1.0.1"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
@@ -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, "___")
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SessionManager SESSION管理
|
||||
type SessionManager struct {
|
||||
life uint
|
||||
}
|
||||
|
||||
@@ -544,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" {
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -82,7 +82,7 @@ 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()...)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -85,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
|
||||
|
||||
@@ -78,6 +78,8 @@ func (this *IndexAction) RunPost(params struct {
|
||||
HttpAllAllowNodeIP bool
|
||||
HttpAllDefaultDomain string
|
||||
|
||||
HttpAllSupportsLowVersionHTTP bool
|
||||
|
||||
HttpAccessLogEnableRequestHeaders bool
|
||||
HttpAccessLogEnableResponseHeaders bool
|
||||
HttpAccessLogCommonRequestHeadersOnly bool
|
||||
@@ -134,6 +136,9 @@ func (this *IndexAction) RunPost(params struct {
|
||||
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
|
||||
|
||||
@@ -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("请选择正确的认证方式")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -276,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,7 +42,7 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
var defaultRoute = dnsResp.DefaultRoute
|
||||
domainName := ""
|
||||
var domainName = ""
|
||||
var dnsMap = maps.Map{
|
||||
"dnsName": dnsResp.Name,
|
||||
"domainId": 0,
|
||||
@@ -70,19 +70,42 @@ 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
|
||||
}
|
||||
var 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 {
|
||||
// 检查是否已解析
|
||||
var isResolved = false
|
||||
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
@@ -110,14 +133,15 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
"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 {
|
||||
if isInstalled && len(defaultRoute) > 0 {
|
||||
var recordType = "A"
|
||||
if utils.IsIPv6(node.IpAddr) {
|
||||
recordType = "AAAA"
|
||||
@@ -144,8 +168,9 @@ func (this *ClusterAction) RunGet(params struct {
|
||||
"name": "",
|
||||
"code": "",
|
||||
},
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"clusterId": node.NodeClusterId,
|
||||
"isResolved": isResolved,
|
||||
"isInstalled": isInstalled,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -9,12 +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/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -32,6 +36,25 @@ func (this *IndexAction) RunGet(params struct {
|
||||
|
||||
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
|
||||
|
||||
@@ -81,6 +104,9 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["rememberLogin"] = securityConfig.AllowRememberLogin
|
||||
}
|
||||
|
||||
// 删除Cookie
|
||||
loginutils.UnsetCookie(this.Object())
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
|
||||
60
internal/web/actions/default/index/loginutils/utils.go
Normal file
60
internal/web/actions/default/index/loginutils/utils.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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/iwind/TeaGo/actions"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CalculateClientFingerprint 计算客户端指纹
|
||||
func CalculateClientFingerprint(action *actions.ActionObject) string {
|
||||
return stringutil.Md5(action.RequestRemoteIP() + "@" + action.Request.UserAgent())
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -21,6 +21,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
DayTo string
|
||||
Keyword string
|
||||
UserType string
|
||||
Level string
|
||||
}) {
|
||||
// 读取配置
|
||||
config, err := configloaders.LoadLogConfig()
|
||||
@@ -35,18 +36,36 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["userType"] = params.UserType
|
||||
|
||||
// 级别
|
||||
this.Data["level"] = params.Level
|
||||
this.Data["levelOptions"] = []maps.Map{
|
||||
{
|
||||
"code": "info",
|
||||
"name": "信息",
|
||||
},
|
||||
{
|
||||
"code": "warn",
|
||||
"name": "警告",
|
||||
},
|
||||
{
|
||||
"code": "error",
|
||||
"name": "错误",
|
||||
},
|
||||
}
|
||||
|
||||
countResp, err := this.RPC().LogRPC().CountLogs(this.AdminContext(), &pb.CountLogRequest{
|
||||
DayFrom: params.DayFrom,
|
||||
DayTo: params.DayTo,
|
||||
Keyword: params.Keyword,
|
||||
UserType: params.UserType,
|
||||
Level: params.Level,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
logsResp, err := this.RPC().LogRPC().ListLogs(this.AdminContext(), &pb.ListLogsRequest{
|
||||
@@ -56,12 +75,13 @@ func (this *IndexAction) RunGet(params struct {
|
||||
DayTo: params.DayTo,
|
||||
Keyword: params.Keyword,
|
||||
UserType: params.UserType,
|
||||
Level: params.Level,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
logMaps := []maps.Map{}
|
||||
var logMaps = []maps.Map{}
|
||||
for _, log := range logsResp.Logs {
|
||||
regionName := ""
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: log.Ip})
|
||||
|
||||
@@ -21,6 +21,7 @@ func init() {
|
||||
Data("leftMenuItem", "cert").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/uploadPopup", new(UploadPopupAction)).
|
||||
GetPost("/uploadBatchPopup", new(UploadBatchPopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
Get("/certPopup", new(CertPopupAction)).
|
||||
|
||||
@@ -2,9 +2,11 @@ package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -23,15 +25,82 @@ func (this *SelectPopupAction) Init() {
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunGet(params struct {
|
||||
ServerId int64 // 搜索的服务
|
||||
UserId int64 // 搜索的用户名
|
||||
SearchingDomains string // 搜索的域名
|
||||
SearchingType string // 搜索类型:match|all
|
||||
|
||||
ViewSize string
|
||||
SelectedCertIds string
|
||||
Keyword string
|
||||
}) {
|
||||
// TODO 列出常用和最新的证书供用户选择
|
||||
// 服务相关
|
||||
if params.ServerId > 0 {
|
||||
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var server = serverResp.Server
|
||||
if server != nil {
|
||||
if server.UserId > 0 {
|
||||
params.UserId = server.UserId
|
||||
}
|
||||
|
||||
// 读取所有ServerNames
|
||||
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.AdminContext(), &pb.FindServerNamesRequest{ServerId: params.ServerId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(serverNamesResp.ServerNamesJSON) > 0 {
|
||||
var serverNames = []*serverconfigs.ServerNameConfig{}
|
||||
err = json.Unmarshal(serverNamesResp.ServerNamesJSON, &serverNames)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
params.SearchingDomains = strings.Join(serverconfigs.PlainServerNames(serverNames), ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用户相关
|
||||
this.Data["userId"] = params.UserId
|
||||
|
||||
// 域名搜索相关
|
||||
var url = this.Request.URL.Path
|
||||
var query = this.Request.URL.Query()
|
||||
query.Del("searchingType")
|
||||
this.Data["baseURL"] = url + "?" + query.Encode()
|
||||
|
||||
var searchingDomains = []string{}
|
||||
if len(params.SearchingDomains) > 0 {
|
||||
searchingDomains = strings.Split(params.SearchingDomains, ",")
|
||||
}
|
||||
const maxDomains = 2_000 // 限制搜索的域名数量
|
||||
if len(searchingDomains) > maxDomains {
|
||||
searchingDomains = searchingDomains[:maxDomains]
|
||||
}
|
||||
this.Data["searchingDomains"] = searchingDomains
|
||||
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["selectedCertIds"] = params.SelectedCertIds
|
||||
|
||||
var searchingType = params.SearchingType
|
||||
if len(searchingType) == 0 {
|
||||
if len(params.SearchingDomains) == 0 {
|
||||
searchingType = "all"
|
||||
} else {
|
||||
searchingType = "match"
|
||||
}
|
||||
}
|
||||
if searchingType != "all" && searchingType != "match" {
|
||||
this.ErrorPage(errors.New("invalid searching type '" + searchingType + "'"))
|
||||
return
|
||||
}
|
||||
this.Data["searchingType"] = searchingType
|
||||
|
||||
// 已经选择的证书
|
||||
var selectedCertIds = []string{}
|
||||
if len(params.SelectedCertIds) > 0 {
|
||||
@@ -43,24 +112,68 @@ func (this *SelectPopupAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["viewSize"] = params.ViewSize
|
||||
|
||||
countResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
|
||||
// 全部证书数量
|
||||
countAllResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
|
||||
UserId: params.UserId,
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var totalAll = countAllResp.Count
|
||||
this.Data["totalAll"] = totalAll
|
||||
|
||||
page := this.NewPage(countResp.Count)
|
||||
// 已匹配证书数量
|
||||
var totalMatch int64 = 0
|
||||
if len(searchingDomains) > 0 {
|
||||
countMatchResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
|
||||
UserId: params.UserId,
|
||||
Keyword: params.Keyword,
|
||||
Domains: searchingDomains,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
totalMatch = countMatchResp.Count
|
||||
}
|
||||
this.Data["totalMatch"] = totalMatch
|
||||
|
||||
var totalCerts int64
|
||||
if searchingType == "all" {
|
||||
totalCerts = totalAll
|
||||
} else if searchingType == "match" {
|
||||
totalCerts = totalMatch
|
||||
}
|
||||
|
||||
var page = this.NewPage(totalCerts)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
listResp, err := this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
|
||||
Keyword: params.Keyword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
var listResp *pb.ListSSLCertsResponse
|
||||
if searchingType == "all" {
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
|
||||
UserId: params.UserId,
|
||||
Keyword: params.Keyword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
} else if searchingType == "match" {
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
|
||||
UserId: params.UserId,
|
||||
Keyword: params.Keyword,
|
||||
Domains: searchingDomains,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
}
|
||||
|
||||
certConfigs := []*sslconfigs.SSLCertConfig{}
|
||||
if listResp == nil {
|
||||
this.ErrorPage(errors.New("'listResp' should not be nil"))
|
||||
return
|
||||
}
|
||||
|
||||
var certConfigs = []*sslconfigs.SSLCertConfig{}
|
||||
err = json.Unmarshal(listResp.SslCertsJSON, &certConfigs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -68,8 +181,8 @@ func (this *SelectPopupAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["certs"] = certConfigs
|
||||
|
||||
certMaps := []maps.Map{}
|
||||
nowTime := time.Now().Unix()
|
||||
var certMaps = []maps.Map{}
|
||||
var nowTime = time.Now().Unix()
|
||||
for _, certConfig := range certConfigs {
|
||||
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: certConfig.Id})
|
||||
if err != nil {
|
||||
|
||||
@@ -26,18 +26,20 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
certConfigJSON := certConfigResp.SslCertJSON
|
||||
var certConfigJSON = certConfigResp.SslCertJSON
|
||||
if len(certConfigJSON) == 0 {
|
||||
this.NotFound("cert", params.CertId)
|
||||
return
|
||||
}
|
||||
|
||||
certConfig := &sslconfigs.SSLCertConfig{}
|
||||
var certConfig = &sslconfigs.SSLCertConfig{}
|
||||
err = json.Unmarshal(certConfigJSON, certConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
certConfig.CertData = nil // cert & key 不需要在界面上显示
|
||||
certConfig.KeyData = nil
|
||||
this.Data["certConfig"] = certConfig
|
||||
|
||||
this.Show()
|
||||
@@ -118,7 +120,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
|
||||
// 校验
|
||||
certConfig.IsCA = params.IsCA
|
||||
err = certConfig.Init()
|
||||
err = certConfig.Init(nil)
|
||||
if err != nil {
|
||||
if params.IsCA {
|
||||
this.Fail("证书校验错误:" + err.Error())
|
||||
|
||||
225
internal/web/actions/default/servers/certs/uploadBatchPopup.go
Normal file
225
internal/web/actions/default/servers/certs/uploadBatchPopup.go
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package certs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UploadBatchPopupAction 批量上传证书
|
||||
type UploadBatchPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UploadBatchPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UploadBatchPopupAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
UserId int64
|
||||
}) {
|
||||
// 读取服务用户
|
||||
if params.ServerId > 0 {
|
||||
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var server = serverResp.Server
|
||||
if server != nil {
|
||||
params.UserId = server.UserId
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["userId"] = params.UserId
|
||||
this.Data["maxFiles"] = this.maxFiles()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UploadBatchPopupAction) RunPost(params struct {
|
||||
UserId int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo("批量上传证书")
|
||||
|
||||
var files = this.Request.MultipartForm.File["certFiles"]
|
||||
if len(files) == 0 {
|
||||
this.Fail("请选择要上传的证书和私钥文件")
|
||||
return
|
||||
}
|
||||
|
||||
// 限制每次上传的文件数量
|
||||
var maxFiles = this.maxFiles()
|
||||
if len(files) > maxFiles {
|
||||
this.Fail("每次上传最多不能超过" + types.String(maxFiles) + "个文件")
|
||||
return
|
||||
}
|
||||
|
||||
type certInfo struct {
|
||||
filename string
|
||||
data []byte
|
||||
}
|
||||
|
||||
var certDataList = []*certInfo{}
|
||||
var keyDataList = [][]byte{}
|
||||
|
||||
var failMessages = []string{}
|
||||
for _, file := range files {
|
||||
func(file *multipart.FileHeader) {
|
||||
fp, err := file.Open()
|
||||
if err != nil {
|
||||
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
data, err := io.ReadAll(fp)
|
||||
if err != nil {
|
||||
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if bytes.Contains(data, []byte("CERTIFICATE-")) {
|
||||
certDataList = append(certDataList, &certInfo{
|
||||
filename: file.Filename,
|
||||
data: data,
|
||||
})
|
||||
} else if bytes.Contains(data, []byte("PRIVATE KEY-")) {
|
||||
keyDataList = append(keyDataList, data)
|
||||
} else {
|
||||
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:文件格式错误,无法识别是证书还是私钥")
|
||||
return
|
||||
}
|
||||
}(file)
|
||||
}
|
||||
|
||||
if len(failMessages) > 0 {
|
||||
this.Fail("发生了错误:" + strings.Join(failMessages, ";"))
|
||||
return
|
||||
}
|
||||
|
||||
// 对比证书和私钥数量是否一致
|
||||
if len(certDataList) != len(keyDataList) {
|
||||
this.Fail("证书文件数量(" + types.String(len(certDataList)) + ")和私钥文件数量(" + types.String(len(keyDataList)) + ")不一致")
|
||||
return
|
||||
}
|
||||
|
||||
// 自动匹配
|
||||
var pairs = [][2][]byte{} // [] { cert, key }
|
||||
var keyIndexMap = map[int]bool{} // 方便下面跳过已匹配的Key
|
||||
for _, cert := range certDataList {
|
||||
var found = false
|
||||
for keyIndex, keyData := range keyDataList {
|
||||
if keyIndexMap[keyIndex] {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := tls.X509KeyPair(cert.data, keyData)
|
||||
if err == nil {
|
||||
found = true
|
||||
pairs = append(pairs, [2][]byte{cert.data, keyData})
|
||||
keyIndexMap[keyIndex] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
this.Fail("找不到" + cert.filename + "对应的私钥")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 组织 CertConfig
|
||||
var pbCerts = []*pb.CreateSSLCertsRequestCert{}
|
||||
var certConfigs = []*sslconfigs.SSLCertConfig{}
|
||||
for _, pair := range pairs {
|
||||
certData, keyData := pair[0], pair[1]
|
||||
|
||||
var certConfig = &sslconfigs.SSLCertConfig{
|
||||
IsCA: false,
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
}
|
||||
err := certConfig.Init(nil)
|
||||
if err != nil {
|
||||
this.Fail("证书验证失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
certConfigs = append(certConfigs, certConfig)
|
||||
|
||||
var certName = ""
|
||||
if len(certConfig.DNSNames) > 0 {
|
||||
certName = certConfig.DNSNames[0]
|
||||
if len(certConfig.DNSNames) > 1 {
|
||||
certName += "等" + types.String(len(certConfig.DNSNames)) + "个域名"
|
||||
}
|
||||
}
|
||||
certConfig.Name = certName
|
||||
|
||||
pbCerts = append(pbCerts, &pb.CreateSSLCertsRequestCert{
|
||||
IsOn: true,
|
||||
Name: certName,
|
||||
Description: "",
|
||||
ServerName: "",
|
||||
IsCA: false,
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
TimeBeginAt: certConfig.TimeBeginAt,
|
||||
TimeEndAt: certConfig.TimeEndAt,
|
||||
DnsNames: certConfig.DNSNames,
|
||||
CommonNames: certConfig.CommonNames,
|
||||
})
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().SSLCertRPC().CreateSSLCerts(this.AdminContext(), &pb.CreateSSLCertsRequest{
|
||||
UserId: params.UserId,
|
||||
SSLCerts: pbCerts,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var certIds = createResp.SslCertIds
|
||||
if len(certIds) != len(certConfigs) {
|
||||
this.Fail("上传成功但API返回的证书ID数量错误,请反馈给开发者")
|
||||
return
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
this.Data["count"] = len(pbCerts)
|
||||
|
||||
var certRefs = []*sslconfigs.SSLCertRef{}
|
||||
for index, cert := range certConfigs {
|
||||
// ID
|
||||
cert.Id = certIds[index]
|
||||
|
||||
// 减少不必要的数据
|
||||
cert.CertData = nil
|
||||
cert.KeyData = nil
|
||||
|
||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: cert.Id,
|
||||
})
|
||||
}
|
||||
this.Data["certs"] = certConfigs
|
||||
this.Data["certRefs"] = certRefs
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package certs
|
||||
|
||||
func (this *UploadBatchPopupAction) maxFiles() int {
|
||||
return 20
|
||||
}
|
||||
@@ -18,11 +18,30 @@ func (this *UploadPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UploadPopupAction) RunGet(params struct{}) {
|
||||
func (this *UploadPopupAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
UserId int64
|
||||
}) {
|
||||
// 读取服务用户
|
||||
if params.ServerId > 0 {
|
||||
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var server = serverResp.Server
|
||||
if server != nil {
|
||||
params.UserId = server.UserId
|
||||
}
|
||||
}
|
||||
this.Data["userId"] = params.UserId
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UploadPopupAction) RunPost(params struct {
|
||||
UserId int64
|
||||
|
||||
TextMode bool
|
||||
Name string
|
||||
IsCA bool
|
||||
@@ -85,7 +104,7 @@ func (this *UploadPopupAction) RunPost(params struct {
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
}
|
||||
err := certConfig.Init()
|
||||
err := certConfig.Init(nil)
|
||||
if err != nil {
|
||||
if params.IsCA {
|
||||
this.Fail("证书校验错误:" + err.Error())
|
||||
@@ -107,6 +126,7 @@ func (this *UploadPopupAction) RunPost(params struct {
|
||||
// 保存
|
||||
createResp, err := this.RPC().SSLCertRPC().CreateSSLCert(this.AdminContext(), &pb.CreateSSLCertRequest{
|
||||
IsOn: params.IsOn,
|
||||
UserId: params.UserId,
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
ServerName: "",
|
||||
|
||||
@@ -36,24 +36,6 @@ func (this *CreateAction) RunGet(params struct{}) {
|
||||
}
|
||||
this.Data["countAuditing"] = countAuditingResp.Count
|
||||
|
||||
// 所有集群
|
||||
resp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClusters(this.AdminContext(), &pb.FindAllEnabledNodeClustersRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
}
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
clusterMaps := []maps.Map{}
|
||||
for _, cluster := range resp.NodeClusters {
|
||||
clusterMaps = append(clusterMaps, maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
})
|
||||
}
|
||||
this.Data["clusters"] = clusterMaps
|
||||
|
||||
// 服务类型
|
||||
this.Data["serverTypes"] = serverconfigs.AllServerTypes()
|
||||
|
||||
@@ -95,10 +77,6 @@ func (this *CreateAction) RunPost(params struct {
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入服务名称")
|
||||
|
||||
var clusterId = params.ClusterId
|
||||
|
||||
// 用户
|
||||
@@ -129,7 +107,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
|
||||
switch params.ServerType {
|
||||
case serverconfigs.ServerTypeHTTPProxy, serverconfigs.ServerTypeHTTPWeb:
|
||||
listen := []*serverconfigs.NetworkAddressConfig{}
|
||||
var listen = []*serverconfigs.NetworkAddressConfig{}
|
||||
err := json.Unmarshal([]byte(params.Addresses), &listen)
|
||||
if err != nil {
|
||||
this.Fail("端口地址解析失败:" + err.Error())
|
||||
@@ -166,7 +144,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
this.Fail("DEMO模式下不能创建TCP反向代理")
|
||||
}
|
||||
|
||||
listen := []*serverconfigs.NetworkAddressConfig{}
|
||||
var listen = []*serverconfigs.NetworkAddressConfig{}
|
||||
err := json.Unmarshal([]byte(params.Addresses), &listen)
|
||||
if err != nil {
|
||||
this.Fail("端口地址解析失败:" + err.Error())
|
||||
@@ -197,13 +175,17 @@ func (this *CreateAction) RunPost(params struct {
|
||||
tlsConfig.AddListen(addr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(params.Name) == 0 {
|
||||
params.Name = "TCP负载均衡"
|
||||
}
|
||||
case serverconfigs.ServerTypeUDPProxy:
|
||||
// 在DEMO模式下不能创建
|
||||
if teaconst.IsDemoMode {
|
||||
this.Fail("DEMO模式下不能创建UDP反向代理")
|
||||
}
|
||||
|
||||
listen := []*serverconfigs.NetworkAddressConfig{}
|
||||
var listen = []*serverconfigs.NetworkAddressConfig{}
|
||||
err := json.Unmarshal([]byte(params.Addresses), &listen)
|
||||
if err != nil {
|
||||
this.Fail("端口地址解析失败:" + err.Error())
|
||||
@@ -225,20 +207,24 @@ func (this *CreateAction) RunPost(params struct {
|
||||
udpConfig.AddListen(addr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(params.Name) == 0 {
|
||||
params.Name = "UDP负载均衡"
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的服务类型")
|
||||
}
|
||||
|
||||
// 证书
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
certIds := []int64{}
|
||||
var certIds = []int64{}
|
||||
err := json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(certIds) > 0 {
|
||||
certRefs := []*sslconfigs.SSLCertRef{}
|
||||
var certRefs = []*sslconfigs.SSLCertRef{}
|
||||
for _, certId := range certIds {
|
||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
@@ -265,7 +251,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
sslPolicyId := sslPolicyIdResp.SslPolicyId
|
||||
var sslPolicyId = sslPolicyIdResp.SslPolicyId
|
||||
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyId,
|
||||
@@ -282,8 +268,13 @@ func (this *CreateAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 检查域名是否已经存在
|
||||
allServerNames := serverconfigs.PlainServerNames(serverNames)
|
||||
var allServerNames = serverconfigs.PlainServerNames(serverNames)
|
||||
if len(allServerNames) > 0 {
|
||||
// 指定默认名称
|
||||
if len(params.Name) == 0 {
|
||||
params.Name = allServerNames[0]
|
||||
}
|
||||
|
||||
dupResp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.AdminContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
|
||||
ServerNames: allServerNames,
|
||||
NodeClusterId: clusterId,
|
||||
@@ -396,7 +387,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
AdminId: this.AdminId(),
|
||||
Type: params.ServerType,
|
||||
Name: params.Name,
|
||||
ServerNamesJON: params.ServerNames,
|
||||
ServerNamesJSON: params.ServerNames,
|
||||
Description: params.Description,
|
||||
NodeClusterId: clusterId,
|
||||
IncludeNodesJSON: includeNodesJSON,
|
||||
|
||||
@@ -65,14 +65,14 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = reverseProxyConfig.Init()
|
||||
err = reverseProxyConfig.Init(nil)
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -65,14 +65,14 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = reverseProxyConfig.Init()
|
||||
err = reverseProxyConfig.Init(nil)
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
err = reverseProxyConfig.Init()
|
||||
err = reverseProxyConfig.Init(nil)
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ func (this *UpdateRefsAction) RunPost(params struct {
|
||||
|
||||
// 校验配置
|
||||
var cacheConfig = webConfig.Cache
|
||||
if cacheConfig == nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var refs = []*serverconfigs.HTTPCacheRef{}
|
||||
err = json.Unmarshal(params.RefsJSON, &refs)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
@@ -30,7 +31,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
if !isOk {
|
||||
return
|
||||
}
|
||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
if len(server.HttpJSON) > 0 {
|
||||
err := json.Unmarshal(server.HttpJSON, httpConfig)
|
||||
if err != nil {
|
||||
@@ -40,6 +41,26 @@ func (this *IndexAction) RunGet(params struct {
|
||||
} else {
|
||||
httpConfig.IsOn = true
|
||||
}
|
||||
_ = httpConfig.Init()
|
||||
var httpPorts = httpConfig.AllPorts()
|
||||
|
||||
// 检查http和https端口冲突
|
||||
var conflictingPorts = []int{}
|
||||
if len(server.HttpsJSON) > 0 {
|
||||
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||
err := json.Unmarshal(server.HttpsJSON, httpsConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_ = httpsConfig.Init(nil)
|
||||
for _, port := range httpsConfig.AllPorts() {
|
||||
if lists.ContainsInt(httpPorts, port) {
|
||||
conflictingPorts = append(conflictingPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["conflictingPorts"] = conflictingPorts
|
||||
|
||||
this.Data["serverType"] = server.Type
|
||||
this.Data["httpConfig"] = maps.Map{
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"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"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
@@ -42,6 +43,27 @@ func (this *IndexAction) RunGet(params struct {
|
||||
httpsConfig.IsOn = true
|
||||
}
|
||||
|
||||
_ = httpsConfig.Init(nil)
|
||||
var httpsPorts = httpsConfig.AllPorts()
|
||||
|
||||
// 检查http和https端口冲突
|
||||
var conflictingPorts = []int{}
|
||||
if len(server.HttpJSON) > 0 {
|
||||
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||
err := json.Unmarshal(server.HttpJSON, httpConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_ = httpConfig.Init()
|
||||
for _, port := range httpConfig.AllPorts() {
|
||||
if lists.ContainsInt(httpsPorts, port) {
|
||||
conflictingPorts = append(conflictingPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["conflictingPorts"] = conflictingPorts
|
||||
|
||||
var sslPolicy *sslconfigs.SSLPolicy
|
||||
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
|
||||
@@ -199,6 +221,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
httpsConfig.SSLPolicy = nil
|
||||
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyId,
|
||||
|
||||
@@ -22,7 +22,7 @@ func FindLocationConfig(parentAction *actionutils.ParentAction, locationId int64
|
||||
return
|
||||
}
|
||||
|
||||
err = locationConfig.Init()
|
||||
err = locationConfig.Init(nil)
|
||||
if err != nil {
|
||||
parentAction.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -56,14 +56,14 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = reverseProxyConfig.Init()
|
||||
err = reverseProxyConfig.Init(nil)
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
err = reverseProxyConfig.Init()
|
||||
err = reverseProxyConfig.Init(nil)
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
// SSL配置
|
||||
var sslPolicy *sslconfigs.SSLPolicy
|
||||
if tlsConfig.SSLPolicyRef != nil && tlsConfig.SSLPolicyRef.SSLPolicyId > 0 {
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: tlsConfig.SSLPolicyRef.SSLPolicyId})
|
||||
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{
|
||||
SslPolicyId: tlsConfig.SSLPolicyRef.SSLPolicyId,
|
||||
IgnoreData: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -181,7 +181,7 @@ func (this *ServerHelper) createLogMenu(secondMenuItem string, serverIdString st
|
||||
|
||||
// 统计菜单
|
||||
func (this *ServerHelper) createStatMenu(secondMenuItem string, serverIdString string, serverConfig *serverconfigs.ServerConfig) []maps.Map {
|
||||
menuItems := []maps.Map{}
|
||||
var menuItems = []maps.Map{}
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "流量统计",
|
||||
"url": "/servers/server/stat?serverId=" + serverIdString,
|
||||
@@ -212,7 +212,7 @@ func (this *ServerHelper) createStatMenu(secondMenuItem string, serverIdString s
|
||||
|
||||
// 设置菜单
|
||||
func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdString string, serverConfig *serverconfigs.ServerConfig) (items []maps.Map) {
|
||||
menuItems := []maps.Map{
|
||||
var menuItems = []maps.Map{
|
||||
{
|
||||
"name": "基本信息",
|
||||
"url": "/servers/server/settings?serverId=" + serverIdString,
|
||||
@@ -249,10 +249,11 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
||||
"isOff": serverConfig.HTTPS != nil && !serverConfig.HTTPS.IsOn,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "源站",
|
||||
"url": "/servers/server/settings/reverseProxy?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "reverseProxy",
|
||||
"isOn": serverConfig.ReverseProxyRef != nil && serverConfig.ReverseProxyRef.IsOn,
|
||||
"name": "源站",
|
||||
"url": "/servers/server/settings/reverseProxy?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "reverseProxy",
|
||||
"isOn": serverConfig.ReverseProxyRef != nil && serverConfig.ReverseProxyRef.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeReverseProxy,
|
||||
})
|
||||
|
||||
menuItems = filterMenuItems(serverConfig, menuItems, serverIdString, secondMenuItem)
|
||||
@@ -263,10 +264,11 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
||||
"isActive": false,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "URL跳转",
|
||||
"url": "/servers/server/settings/redirects?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "redirects",
|
||||
"isOn": serverConfig.Web != nil && len(serverConfig.Web.HostRedirects) > 0,
|
||||
"name": "URL跳转",
|
||||
"url": "/servers/server/settings/redirects?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "redirects",
|
||||
"isOn": serverConfig.Web != nil && len(serverConfig.Web.HostRedirects) > 0,
|
||||
"configCode": serverconfigs.ConfigCodeHostRedirects,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "路由规则",
|
||||
@@ -287,58 +289,67 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.FirewallRef != nil && serverConfig.Web.FirewallRef.IsOn,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "缓存",
|
||||
"url": "/servers/server/settings/cache?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "cache",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Cache != nil && serverConfig.Web.Cache.IsOn,
|
||||
"name": "缓存",
|
||||
"url": "/servers/server/settings/cache?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "cache",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Cache != nil && serverConfig.Web.Cache.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeCache,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "访问鉴权",
|
||||
"url": "/servers/server/settings/access?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "access",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Auth != nil && serverConfig.Web.Auth.IsOn,
|
||||
"name": "访问鉴权",
|
||||
"url": "/servers/server/settings/access?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "access",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Auth != nil && serverConfig.Web.Auth.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeAuth,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "防盗链",
|
||||
"url": "/servers/server/settings/referers?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "referer",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Referers != nil && serverConfig.Web.Referers.IsOn,
|
||||
"name": "防盗链",
|
||||
"url": "/servers/server/settings/referers?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "referer",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Referers != nil && serverConfig.Web.Referers.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeReferers,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "UA名单",
|
||||
"url": "/servers/server/settings/userAgent?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "userAgent",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.UserAgent != nil && serverConfig.Web.UserAgent.IsOn,
|
||||
"name": "UA名单",
|
||||
"url": "/servers/server/settings/userAgent?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "userAgent",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.UserAgent != nil && serverConfig.Web.UserAgent.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeUserAgent,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "字符编码",
|
||||
"url": "/servers/server/settings/charset?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "charset",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Charset != nil && serverConfig.Web.Charset.IsOn,
|
||||
"name": "字符编码",
|
||||
"url": "/servers/server/settings/charset?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "charset",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Charset != nil && serverConfig.Web.Charset.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeCharset,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "访问日志",
|
||||
"url": "/servers/server/settings/accessLog?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "accessLog",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.AccessLogRef != nil && serverConfig.Web.AccessLogRef.IsOn,
|
||||
"name": "访问日志",
|
||||
"url": "/servers/server/settings/accessLog?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "accessLog",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.AccessLogRef != nil && serverConfig.Web.AccessLogRef.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeAccessLog,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "统计",
|
||||
"url": "/servers/server/settings/stat?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "stat",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.StatRef != nil && serverConfig.Web.StatRef.IsOn,
|
||||
"name": "统计",
|
||||
"url": "/servers/server/settings/stat?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "stat",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.StatRef != nil && serverConfig.Web.StatRef.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeStat,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "内容压缩",
|
||||
"url": "/servers/server/settings/compression?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "compression",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Compression != nil && serverConfig.Web.Compression.IsOn,
|
||||
"name": "内容压缩",
|
||||
"url": "/servers/server/settings/compression?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "compression",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Compression != nil && serverConfig.Web.Compression.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeCompression,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "自定义页面",
|
||||
"url": "/servers/server/settings/pages?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "pages",
|
||||
"isOn": serverConfig.Web != nil && (len(serverConfig.Web.Pages) > 0 || (serverConfig.Web.Shutdown != nil && serverConfig.Web.Shutdown.IsOn)),
|
||||
"name": "自定义页面",
|
||||
"url": "/servers/server/settings/pages?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "pages",
|
||||
"isOn": serverConfig.Web != nil && (len(serverConfig.Web.Pages) > 0 || (serverConfig.Web.Shutdown != nil && serverConfig.Web.Shutdown.IsOn)),
|
||||
"configCode": serverconfigs.ConfigCodePages,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "HTTP Header",
|
||||
@@ -347,23 +358,26 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
||||
"isOn": this.hasHTTPHeaders(serverConfig.Web),
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "Websocket",
|
||||
"url": "/servers/server/settings/websocket?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "websocket",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.WebsocketRef != nil && serverConfig.Web.WebsocketRef.IsOn,
|
||||
"name": "Websocket",
|
||||
"url": "/servers/server/settings/websocket?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "websocket",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.WebsocketRef != nil && serverConfig.Web.WebsocketRef.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeWebsocket,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "WebP",
|
||||
"url": "/servers/server/settings/webp?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "webp",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.WebP != nil && serverConfig.Web.WebP.IsOn,
|
||||
"name": "WebP",
|
||||
"url": "/servers/server/settings/webp?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "webp",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.WebP != nil && serverConfig.Web.WebP.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeWebp,
|
||||
})
|
||||
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "静态分发",
|
||||
"url": "/servers/server/settings/web?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "web",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Root != nil && serverConfig.Web.Root.IsOn,
|
||||
"name": "静态分发",
|
||||
"url": "/servers/server/settings/web?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "web",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.Root != nil && serverConfig.Web.Root.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeRoot,
|
||||
})
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "Fastcgi",
|
||||
@@ -379,17 +393,19 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
||||
})
|
||||
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "访客IP地址",
|
||||
"url": "/servers/server/settings/remoteAddr?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "remoteAddr",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.RemoteAddr != nil && serverConfig.Web.RemoteAddr.IsOn,
|
||||
"name": "访客IP地址",
|
||||
"url": "/servers/server/settings/remoteAddr?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "remoteAddr",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.RemoteAddr != nil && serverConfig.Web.RemoteAddr.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeRemoteAddr,
|
||||
})
|
||||
|
||||
menuItems = append(menuItems, maps.Map{
|
||||
"name": "请求限制",
|
||||
"url": "/servers/server/settings/requestLimit?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "requestLimit",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.RequestLimit != nil && serverConfig.Web.RequestLimit.IsOn,
|
||||
"name": "请求限制",
|
||||
"url": "/servers/server/settings/requestLimit?serverId=" + serverIdString,
|
||||
"isActive": secondMenuItem == "requestLimit",
|
||||
"isOn": serverConfig.Web != nil && serverConfig.Web.RequestLimit != nil && serverConfig.Web.RequestLimit.IsOn,
|
||||
"configCode": serverconfigs.ConfigCodeRequestLimit,
|
||||
})
|
||||
|
||||
menuItems = filterMenuItems2(serverConfig, menuItems, serverIdString, secondMenuItem)
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package adminserverutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/iwind/TeaGo"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ServerConfigIsChanged = false
|
||||
|
||||
const configFilename = "server.yaml"
|
||||
|
||||
// LoadServerConfig 读取当前服务配置
|
||||
func LoadServerConfig() (*TeaGo.ServerConfig, error) {
|
||||
configFile := Tea.ConfigFile(configFilename)
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var serverConfig = &TeaGo.ServerConfig{}
|
||||
err = yaml.Unmarshal(data, serverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return serverConfig, nil
|
||||
}
|
||||
|
||||
// WriteServerConfig 保存当前服务配置
|
||||
func WriteServerConfig(serverConfig *TeaGo.ServerConfig) error {
|
||||
data, err := yaml.Marshal(serverConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(Tea.ConfigFile(configFilename), data, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ServerConfigIsChanged = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadServerHTTPS 检查HTTPS地址
|
||||
func ReadServerHTTPS() (port int, err error) {
|
||||
config, err := LoadServerConfig()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if config == nil {
|
||||
return 0, errors.New("could not load server config")
|
||||
}
|
||||
|
||||
if config.Https.On && len(config.Https.Listen) > 0 {
|
||||
for _, listen := range config.Https.Listen {
|
||||
_, portString, splitErr := net.SplitHostPort(listen)
|
||||
if splitErr == nil {
|
||||
var portInt = types.Int(portString)
|
||||
if portInt > 0 {
|
||||
// 是否已经启动
|
||||
checkErr := func() error {
|
||||
conn, connErr := net.DialTimeout("tcp", ":"+portString, 1*time.Second)
|
||||
if connErr != nil {
|
||||
return connErr
|
||||
}
|
||||
_ = conn.Close()
|
||||
return nil
|
||||
}()
|
||||
if checkErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
port = portInt
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
@@ -13,9 +14,9 @@ func (this *IndexAction) Init() {
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
this.Data["serverIsChanged"] = serverConfigIsChanged
|
||||
this.Data["serverIsChanged"] = adminserverutils.ServerConfigIsChanged
|
||||
|
||||
serverConfig, err := loadServerConfig()
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net"
|
||||
)
|
||||
@@ -16,7 +17,7 @@ func (this *UpdateHTTPPopupAction) Init() {
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPPopupAction) RunGet(params struct{}) {
|
||||
serverConfig, err := loadServerConfig()
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
@@ -38,7 +39,7 @@ func (this *UpdateHTTPPopupAction) RunPost(params struct {
|
||||
this.Fail("请输入绑定地址")
|
||||
}
|
||||
|
||||
serverConfig, err := loadServerConfig()
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.Fail("保存失败:" + err.Error())
|
||||
}
|
||||
@@ -58,7 +59,7 @@ func (this *UpdateHTTPPopupAction) RunPost(params struct {
|
||||
}
|
||||
serverConfig.Http.Listen = listen
|
||||
|
||||
err = writeServerConfig(serverConfig)
|
||||
err = adminserverutils.WriteServerConfig(serverConfig)
|
||||
if err != nil {
|
||||
this.Fail("保存失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
adminserverutils "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server/admin-server-utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
@@ -21,7 +22,7 @@ func (this *UpdateHTTPSPopupAction) Init() {
|
||||
}
|
||||
|
||||
func (this *UpdateHTTPSPopupAction) RunGet(params struct{}) {
|
||||
serverConfig, err := loadServerConfig()
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
@@ -47,7 +48,7 @@ func (this *UpdateHTTPSPopupAction) RunGet(params struct{}) {
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
}
|
||||
_ = certConfig.Init()
|
||||
_ = certConfig.Init(nil)
|
||||
certConfig.CertData = nil
|
||||
certConfig.KeyData = nil
|
||||
certConfigs = append(certConfigs, certConfig)
|
||||
@@ -70,7 +71,7 @@ func (this *UpdateHTTPSPopupAction) RunPost(params struct {
|
||||
this.Fail("请输入绑定地址")
|
||||
}
|
||||
|
||||
serverConfig, err := loadServerConfig()
|
||||
serverConfig, err := adminserverutils.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.Fail("保存失败:" + err.Error())
|
||||
}
|
||||
@@ -133,7 +134,7 @@ func (this *UpdateHTTPSPopupAction) RunPost(params struct {
|
||||
serverConfig.Https.Cert = "configs/https.cert.pem"
|
||||
}
|
||||
|
||||
err = writeServerConfig(serverConfig)
|
||||
err = adminserverutils.WriteServerConfig(serverConfig)
|
||||
if err != nil {
|
||||
this.Fail("保存配置失败:" + err.Error())
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
)
|
||||
|
||||
var serverConfigIsChanged = false
|
||||
|
||||
// 读取当前服务配置
|
||||
func loadServerConfig() (*TeaGo.ServerConfig, error) {
|
||||
configFile := Tea.ConfigFile("server.yaml")
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverConfig := &TeaGo.ServerConfig{}
|
||||
err = yaml.Unmarshal(data, serverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return serverConfig, nil
|
||||
}
|
||||
|
||||
// 保存当前服务配置
|
||||
func writeServerConfig(serverConfig *TeaGo.ServerConfig) error {
|
||||
data, err := yaml.Marshal(serverConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(Tea.ConfigFile("server.yaml"), data, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverConfigIsChanged = true
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/setup/mysql/mysqlinstallers/utils"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"io"
|
||||
"net"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -106,6 +108,31 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// create symbolic links
|
||||
{
|
||||
var libFile = "/usr/lib64/libncurses.so.5"
|
||||
_, err = os.Stat(libFile)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
var latestLibFile = this.findLatestVersionFile("/usr/lib64", "libncurses.so.")
|
||||
if len(latestLibFile) > 0 {
|
||||
this.log("link '" + latestLibFile + "' to '" + libFile + "'")
|
||||
_ = os.Symlink(latestLibFile, libFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var libFile = "/usr/lib64/libtinfo.so.5"
|
||||
_, err = os.Stat(libFile)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
var latestLibFile = this.findLatestVersionFile("/usr/lib64", "libtinfo.so.")
|
||||
if len(latestLibFile) > 0 {
|
||||
this.log("link '" + latestLibFile + "' to '" + libFile + "'")
|
||||
_ = os.Symlink(latestLibFile, libFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create 'mysql' user group
|
||||
@@ -486,6 +513,15 @@ func (this *MySQLInstaller) Password() string {
|
||||
|
||||
// create my.cnf content
|
||||
func (this *MySQLInstaller) createMyCnf(baseDir string, dataDir string) string {
|
||||
var memoryTotalG = 1
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
memoryTotalG = this.sysMemoryGB() / 2
|
||||
if memoryTotalG <= 0 {
|
||||
memoryTotalG = 1
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
[mysqld]
|
||||
port=3306
|
||||
@@ -499,7 +535,8 @@ binlog_cache_size=1M
|
||||
binlog_stmt_cache_size=1M
|
||||
thread_cache_size=32
|
||||
binlog_expire_logs_seconds=604800
|
||||
`
|
||||
innodb_sort_buffer_size=8M
|
||||
innodb_buffer_pool_size=` + strconv.Itoa(memoryTotalG) + "G"
|
||||
}
|
||||
|
||||
// generate random password
|
||||
@@ -544,8 +581,8 @@ After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecStart=${BASE_DIR}/support-files/mysql.server start
|
||||
ExecStop=${BASE_DIR}/support-files/mysql.server stop
|
||||
ExecRestart=${BASE_DIR}/support-files/mysql.server restart
|
||||
@@ -610,3 +647,67 @@ func (this *MySQLInstaller) lookupUserAdd() (string, error) {
|
||||
}
|
||||
return "", errors.New("not found")
|
||||
}
|
||||
|
||||
func (this *MySQLInstaller) findLatestVersionFile(dir string, prefix string) string {
|
||||
files, err := filepath.Glob(filepath.Clean(dir + "/" + prefix + "*"))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var resultFile = ""
|
||||
var lastVersion = ""
|
||||
var reg = regexp.MustCompile(`\.([\d.]+)`)
|
||||
for _, file := range files {
|
||||
var filename = filepath.Base(file)
|
||||
var matches = reg.FindStringSubmatch(filename)
|
||||
if len(matches) > 1 {
|
||||
var version = matches[1]
|
||||
if len(lastVersion) == 0 || stringutil.VersionCompare(lastVersion, version) < 0 {
|
||||
lastVersion = version
|
||||
resultFile = file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultFile
|
||||
}
|
||||
|
||||
func (this *MySQLInstaller) sysMemoryGB() int {
|
||||
if runtime.GOOS != "linux" {
|
||||
return 0
|
||||
}
|
||||
meminfoData, err := os.ReadFile("/proc/meminfo")
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
for _, line := range bytes.Split(meminfoData, []byte{'\n'}) {
|
||||
line = bytes.TrimSpace(line)
|
||||
if bytes.Contains(line, []byte{':'}) {
|
||||
name, value, found := bytes.Cut(line, []byte{':'})
|
||||
if found {
|
||||
name = bytes.TrimSpace(name)
|
||||
if bytes.Equal(name, []byte("MemTotal")) {
|
||||
for _, unit := range []string{"gB", "mB", "kB"} {
|
||||
if bytes.Contains(value, []byte(unit)) {
|
||||
value = bytes.TrimSpace(bytes.ReplaceAll(value, []byte(unit), nil))
|
||||
valueInt, err := strconv.Atoi(string(value))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
switch unit {
|
||||
case "gB":
|
||||
return valueInt
|
||||
case "mB":
|
||||
return valueInt / 1024
|
||||
case "kB":
|
||||
return valueInt / 1024 / 1024
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func (this *ComponentsAction) RunGet(params struct{}) {
|
||||
|
||||
if !Tea.IsTesting() && len(componentsData) > 0 {
|
||||
this.AddHeader("Last-Modified", "Fri, 06 Sep 2019 08:29:50 GMT")
|
||||
this.Write(componentsData)
|
||||
_, _ = this.Write(componentsData)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
@@ -173,8 +174,19 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查指纹
|
||||
var clientFingerprint = session.GetString("@fingerprint")
|
||||
if len(clientFingerprint) > 0 && clientFingerprint != loginutils.CalculateClientFingerprint(action) {
|
||||
loginutils.UnsetCookie(action)
|
||||
session.Delete()
|
||||
|
||||
this.login(action)
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
if !configloaders.CheckAdmin(adminId) {
|
||||
loginutils.UnsetCookie(action)
|
||||
session.Delete()
|
||||
|
||||
this.login(action)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -53,35 +54,10 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN
|
||||
|
||||
// StoreAdmin 存储用户名到SESSION
|
||||
func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) {
|
||||
// 修改sid的时间
|
||||
if remember {
|
||||
cookie := &http.Cookie{
|
||||
Name: teaconst.CookieSID,
|
||||
Value: this.action.Session().Sid,
|
||||
Path: "/",
|
||||
MaxAge: 14 * 86400,
|
||||
HttpOnly: true,
|
||||
}
|
||||
if this.action.Request.TLS != nil {
|
||||
cookie.SameSite = http.SameSiteStrictMode
|
||||
cookie.Secure = true
|
||||
}
|
||||
this.action.AddCookie(cookie)
|
||||
} else {
|
||||
cookie := &http.Cookie{
|
||||
Name: teaconst.CookieSID,
|
||||
Value: this.action.Session().Sid,
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
HttpOnly: true,
|
||||
}
|
||||
if this.action.Request.TLS != nil {
|
||||
cookie.SameSite = http.SameSiteStrictMode
|
||||
cookie.Secure = true
|
||||
}
|
||||
this.action.AddCookie(cookie)
|
||||
}
|
||||
this.action.Session().Write("adminId", numberutils.FormatInt64(adminId))
|
||||
loginutils.SetCookie(this.action, remember)
|
||||
var session = this.action.Session()
|
||||
session.Write("adminId", numberutils.FormatInt64(adminId))
|
||||
session.Write("@fingerprint", loginutils.CalculateClientFingerprint(this.action))
|
||||
}
|
||||
|
||||
func (this *UserShouldAuth) IsUser() bool {
|
||||
@@ -93,5 +69,6 @@ func (this *UserShouldAuth) AdminId() int {
|
||||
}
|
||||
|
||||
func (this *UserShouldAuth) Logout() {
|
||||
loginutils.UnsetCookie(this.action)
|
||||
this.action.Session().Delete()
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -503,7 +503,7 @@ Vue.component("node-ddos-protection-config-box", {
|
||||
<prior-checkbox :v-config="config.tcp" v-if="isNode"></prior-checkbox>
|
||||
<tbody v-show="config.tcp.isPrior || !isNode">
|
||||
<tr>
|
||||
<td class="title">启用</td>
|
||||
<td class="title">启用DDoS防护</td>
|
||||
<td>
|
||||
<checkbox v-model="config.tcp.isOn"></checkbox>
|
||||
</td>
|
||||
@@ -1139,7 +1139,7 @@ Vue.component("message-row", {
|
||||
|
||||
<!-- 证书即将过期 -->
|
||||
<div v-if="message.type == 'SSLCertExpiring'" style="margin-top: 0.8em">
|
||||
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务»</a>
|
||||
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a><span v-if="params != null && params.acmeTaskId > 0"> | <a :href="'/servers/certs/acme'" target="_top">查看任务»</a></span>
|
||||
</div>
|
||||
|
||||
<!-- 证书续期成功 -->
|
||||
@@ -3616,7 +3616,11 @@ Vue.component("http-request-conds-box", {
|
||||
})
|
||||
|
||||
Vue.component("ssl-config-box", {
|
||||
props: ["v-ssl-policy", "v-protocol", "v-server-id"],
|
||||
props: [
|
||||
"v-ssl-policy",
|
||||
"v-protocol",
|
||||
"v-server-id"
|
||||
],
|
||||
created: function () {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
@@ -3724,12 +3728,23 @@ Vue.component("ssl-config-box", {
|
||||
selectedCertIds.push(cert.id.toString())
|
||||
})
|
||||
}
|
||||
teaweb.popup("/servers/certs/selectPopup?selectedCertIds=" + selectedCertIds, {
|
||||
let serverId = this.vServerId
|
||||
if (serverId == null) {
|
||||
serverId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/selectPopup?selectedCertIds=" + selectedCertIds + "&serverId=" + serverId, {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
if (resp.data.cert != null && resp.data.certRef != null) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null && resp.data.certRefs != null) {
|
||||
that.policy.certRefs.$pushAll(resp.data.certRefs)
|
||||
that.policy.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -3737,8 +3752,12 @@ Vue.component("ssl-config-box", {
|
||||
// 上传证书
|
||||
uploadCert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/certs/uploadPopup", {
|
||||
height: "28em",
|
||||
let serverId = this.vServerId
|
||||
if (typeof serverId != "number" && typeof serverId != "string") {
|
||||
serverId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadPopup?serverId=" + serverId, {
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("上传成功", function () {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
@@ -3748,6 +3767,28 @@ Vue.component("ssl-config-box", {
|
||||
})
|
||||
},
|
||||
|
||||
// 批量上传
|
||||
uploadBatch: function () {
|
||||
let that = this
|
||||
let serverId = this.vServerId
|
||||
if (typeof serverId != "number" && typeof serverId != "string") {
|
||||
serverId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadBatchPopup?serverId=" + serverId, {
|
||||
callback: function (resp) {
|
||||
if (resp.data.cert != null) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.policy.certRefs.$pushAll(resp.data.certRefs)
|
||||
that.policy.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 申请证书
|
||||
requestCert: function () {
|
||||
// 已经在证书中的域名
|
||||
@@ -3929,8 +3970,15 @@ Vue.component("ssl-config-box", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.policy.clientCARefs.push(resp.data.certRef)
|
||||
that.policy.clientCACerts.push(resp.data.cert)
|
||||
if (resp.data.cert != null && resp.data.certRef != null) {
|
||||
that.policy.clientCARefs.push(resp.data.certRef)
|
||||
that.policy.clientCACerts.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null && resp.data.certRefs != null) {
|
||||
that.policy.clientCARefs.$pushAll(resp.data.certRefs)
|
||||
that.policy.clientCACerts.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -3986,7 +4034,10 @@ Vue.component("ssl-config-box", {
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<button class="ui button tiny" type="button" @click.prevent="selectCert()">选择已有证书</button>
|
||||
<span class="disabled">|</span>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadCert()">上传新证书</button>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadBatch()">批量上传证书</button>
|
||||
<span class="disabled">|</span>
|
||||
<button class="ui button tiny" type="button" @click.prevent="requestCert()" v-if="vServerId != null && vServerId > 0">申请免费证书</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -4142,7 +4193,7 @@ Vue.component("ssl-config-box", {
|
||||
<td>客户端认证CA证书</td>
|
||||
<td>
|
||||
<div v-if="policy.clientCACerts != null && policy.clientCACerts.length > 0">
|
||||
<div class="ui label small" v-for="(cert, index) in policy.clientCACerts">
|
||||
<div class="ui label small basic" v-for="(cert, index) in policy.clientCACerts">
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeClientCACert()"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
@@ -4352,7 +4403,9 @@ Vue.component("ssl-certs-box", {
|
||||
"v-protocol", // 协议:https|tls
|
||||
"v-view-size", // 弹窗尺寸:normal, mini
|
||||
"v-single-mode", // 单证书模式
|
||||
"v-description" // 描述文字
|
||||
"v-description", // 描述文字
|
||||
"v-domains", // 搜索的域名列表或者函数
|
||||
"v-user-id" // 用户ID
|
||||
],
|
||||
data: function () {
|
||||
let certs = this.vCerts
|
||||
@@ -4390,8 +4443,8 @@ Vue.component("ssl-certs-box", {
|
||||
// 选择证书
|
||||
selectCert: function () {
|
||||
let that = this
|
||||
let width = "50em"
|
||||
let height = "30em"
|
||||
let width = "54em"
|
||||
let height = "32em"
|
||||
let viewSize = this.vViewSize
|
||||
if (viewSize == null) {
|
||||
viewSize = "normal"
|
||||
@@ -4400,11 +4453,41 @@ Vue.component("ssl-certs-box", {
|
||||
width = "35em"
|
||||
height = "20em"
|
||||
}
|
||||
teaweb.popup("/servers/certs/selectPopup?viewSize=" + viewSize, {
|
||||
|
||||
let searchingDomains = []
|
||||
if (this.vDomains != null) {
|
||||
if (typeof this.vDomains == "function") {
|
||||
let resultDomains = this.vDomains()
|
||||
if (resultDomains != null && typeof resultDomains == "object" && (resultDomains instanceof Array)) {
|
||||
searchingDomains = resultDomains
|
||||
}
|
||||
} else if (typeof this.vDomains == "object" && (this.vDomains instanceof Array)) {
|
||||
searchingDomains = this.vDomains
|
||||
}
|
||||
if (searchingDomains.length > 10000) {
|
||||
searchingDomains = searchingDomains.slice(0, 10000)
|
||||
}
|
||||
}
|
||||
|
||||
let selectedCertIds = this.certs.map(function (cert) {
|
||||
return cert.id
|
||||
})
|
||||
let userId = this.vUserId
|
||||
if (userId == null) {
|
||||
userId = 0
|
||||
}
|
||||
|
||||
teaweb.popup("/servers/certs/selectPopup?viewSize=" + viewSize + "&searchingDomains=" + window.encodeURIComponent(searchingDomains.join(",")) + "&selectedCertIds=" + selectedCertIds.join(",") + "&userId=" + userId, {
|
||||
width: width,
|
||||
height: height,
|
||||
callback: function (resp) {
|
||||
that.certs.push(resp.data.cert)
|
||||
if (resp.data.cert != null) {
|
||||
that.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -4412,16 +4495,46 @@ Vue.component("ssl-certs-box", {
|
||||
// 上传证书
|
||||
uploadCert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/certs/uploadPopup", {
|
||||
let userId = this.vUserId
|
||||
if (typeof userId != "number" && typeof userId != "string") {
|
||||
userId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadPopup?userId=" + userId, {
|
||||
height: "28em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("上传成功", function () {
|
||||
that.certs.push(resp.data.cert)
|
||||
if (resp.data.cert != null) {
|
||||
that.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 批量上传
|
||||
uploadBatch: function () {
|
||||
let that = this
|
||||
let userId = this.vUserId
|
||||
if (typeof userId != "number" && typeof userId != "string") {
|
||||
userId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadBatchPopup?userId=" + userId, {
|
||||
callback: function (resp) {
|
||||
if (resp.data.cert != null) {
|
||||
that.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime: function (timestamp) {
|
||||
return new Date(timestamp * 1000).format("Y-m-d")
|
||||
@@ -4447,7 +4560,9 @@ Vue.component("ssl-certs-box", {
|
||||
</div>
|
||||
<div v-if="buttonsVisible()">
|
||||
<button class="ui button tiny" type="button" @click.prevent="selectCert()">选择已有证书</button>
|
||||
<span class="disabled">|</span>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadCert()">上传新证书</button>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadBatch()">批量上传证书</button>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -6112,7 +6227,7 @@ Vue.component("http-firewall-checkpoint-cc", {
|
||||
<td>检查请求来源指纹</td>
|
||||
<td>
|
||||
<checkbox v-model="enableFingerprint"></checkbox>
|
||||
<p class="comment">在接收到HTTPS请求时尝试检查请求来源的指纹,用来检测代理服务和爬虫攻击。</p>
|
||||
<p class="comment">在接收到HTTPS请求时尝试检查请求来源的指纹,用来检测代理服务和爬虫攻击;如果你在网站前面放置了别的反向代理服务,请取消此选项。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -7026,80 +7141,102 @@ Vue.component("http-rewrite-labels-label", {
|
||||
})
|
||||
|
||||
Vue.component("server-name-box", {
|
||||
props: ["v-server-names"],
|
||||
data: function () {
|
||||
let serverNames = this.vServerNames;
|
||||
if (serverNames == null) {
|
||||
serverNames = []
|
||||
}
|
||||
return {
|
||||
serverNames: serverNames,
|
||||
isSearching: false,
|
||||
keyword: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addServerName: function () {
|
||||
window.UPDATING_SERVER_NAME = null
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
that.serverNames.push(serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
props: ["v-server-names"],
|
||||
data: function () {
|
||||
let serverNames = this.vServerNames;
|
||||
if (serverNames == null) {
|
||||
serverNames = []
|
||||
}
|
||||
return {
|
||||
serverNames: serverNames,
|
||||
isSearching: false,
|
||||
keyword: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addServerName: function () {
|
||||
window.UPDATING_SERVER_NAME = null
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
that.serverNames.push(serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeServerName: function (index) {
|
||||
this.serverNames.$remove(index)
|
||||
},
|
||||
removeServerName: function (index) {
|
||||
this.serverNames.$remove(index)
|
||||
},
|
||||
|
||||
updateServerName: function (index, serverName) {
|
||||
window.UPDATING_SERVER_NAME = serverName
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
Vue.set(that.serverNames, index, serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
showSearchBox: function () {
|
||||
this.isSearching = !this.isSearching
|
||||
if (this.isSearching) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.keywordRef.focus()
|
||||
}, 200)
|
||||
} else {
|
||||
this.keyword = ""
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
keyword: function (v) {
|
||||
this.serverNames.forEach(function (serverName) {
|
||||
if (v.length == 0) {
|
||||
serverName.isShowing = true
|
||||
return
|
||||
}
|
||||
if (serverName.subNames == null || serverName.subNames.length == 0) {
|
||||
if (!teaweb.match(serverName.name, v)) {
|
||||
serverName.isShowing = false
|
||||
}
|
||||
} else {
|
||||
let found = false
|
||||
serverName.subNames.forEach(function (subName) {
|
||||
if (teaweb.match(subName, v)) {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
serverName.isShowing = found
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
updateServerName: function (index, serverName) {
|
||||
window.UPDATING_SERVER_NAME = teaweb.clone(serverName)
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
Vue.set(that.serverNames, index, serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
showSearchBox: function () {
|
||||
this.isSearching = !this.isSearching
|
||||
if (this.isSearching) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.keywordRef.focus()
|
||||
}, 200)
|
||||
} else {
|
||||
this.keyword = ""
|
||||
}
|
||||
},
|
||||
allServerNames: function () {
|
||||
if (this.serverNames == null) {
|
||||
return []
|
||||
}
|
||||
let result = []
|
||||
this.serverNames.forEach(function (serverName) {
|
||||
if (serverName.subNames != null && serverName.subNames.length > 0) {
|
||||
serverName.subNames.forEach(function (subName) {
|
||||
if (subName != null && subName.length > 0) {
|
||||
if (!result.$contains(subName)) {
|
||||
result.push(subName)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (serverName.name != null && serverName.name.length > 0) {
|
||||
if (!result.$contains(serverName.name)) {
|
||||
result.push(serverName.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keyword: function (v) {
|
||||
this.serverNames.forEach(function (serverName) {
|
||||
if (v.length == 0) {
|
||||
serverName.isShowing = true
|
||||
return
|
||||
}
|
||||
if (serverName.subNames == null || serverName.subNames.length == 0) {
|
||||
if (!teaweb.match(serverName.name, v)) {
|
||||
serverName.isShowing = false
|
||||
}
|
||||
} else {
|
||||
let found = false
|
||||
serverName.subNames.forEach(function (subName) {
|
||||
if (teaweb.match(subName, v)) {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
serverName.isShowing = found
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="serverNames" :value="JSON.stringify(serverNames)"/>
|
||||
<div v-if="serverNames.length > 0">
|
||||
<div v-for="(serverName, index) in serverNames" class="ui label small basic" :class="{hidden: serverName.isShowing === false}">
|
||||
@@ -9626,10 +9763,20 @@ Vue.component("http-cc-config-box", {
|
||||
config = {
|
||||
isPrior: false,
|
||||
isOn: false,
|
||||
enableFingerprint: true,
|
||||
enableGET302: true,
|
||||
onlyURLPatterns: [],
|
||||
exceptURLPatterns: []
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof config.enableFingerprint != "boolean") {
|
||||
config.enableFingerprint = true
|
||||
}
|
||||
if (typeof config.enableGET302 != "boolean") {
|
||||
config.enableGET302 = true
|
||||
}
|
||||
|
||||
if (config.onlyURLPatterns == null) {
|
||||
config.onlyURLPatterns = []
|
||||
}
|
||||
@@ -9655,7 +9802,7 @@ Vue.component("http-cc-config-box", {
|
||||
<td class="title">启用CC无感防护</td>
|
||||
<td>
|
||||
<checkbox v-model="config.isOn"></checkbox>
|
||||
<p class="comment"><plus-label></plus-label>启用后,自动检测并拦截CC攻击,此功能不需要开启WAF功能。</p>
|
||||
<p class="comment"><plus-label></plus-label>启用后,自动检测并拦截CC攻击。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -9679,6 +9826,20 @@ Vue.component("http-cc-config-box", {
|
||||
<p class="comment">如果填写了支持URL,表示只对这些URL进行CC防护处理;如果不填则表示支持所有的URL。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>检查请求来源指纹</td>
|
||||
<td>
|
||||
<checkbox v-model="config.enableFingerprint"></checkbox>
|
||||
<p class="comment">在接收到HTTPS请求时尝试检查请求来源的指纹,用来检测代理服务和爬虫攻击;如果你在网站前面放置了别的反向代理服务,请取消此选项。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>启用GET302校验</td>
|
||||
<td>
|
||||
<checkbox v-model="config.enableGET302"></checkbox>
|
||||
<p class="comment">选中后,表示自动通过GET302方法来校验客户端。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -11562,6 +11723,27 @@ Vue.component("http-access-log-search-box", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("server-config-copy-link", {
|
||||
props: ["v-server-id", "v-config-code"],
|
||||
data: function () {
|
||||
return {
|
||||
serverId: this.vServerId,
|
||||
configCode: this.vConfigCode
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copy: function () {
|
||||
teaweb.popup("/servers/server/settings/copy?serverId=" + this.serverId + "&configCode=" + this.configCode, {
|
||||
height: "25em",
|
||||
callback: function () {
|
||||
teaweb.success("复制成功")
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
template: `<a href=\"" class="item" @click.prevent="copy" style="padding-right:0"><span style="font-size: 0.8em">复制</span> <i class="icon copy small"></i></a>`
|
||||
})
|
||||
|
||||
// 显示指标对象名
|
||||
Vue.component("metric-key-label", {
|
||||
props: ["v-key"],
|
||||
@@ -14691,7 +14873,8 @@ Vue.component("network-addresses-box", {
|
||||
addresses: addresses,
|
||||
protocol: protocol,
|
||||
name: name,
|
||||
from: from
|
||||
from: from,
|
||||
isEditing: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -14706,6 +14889,8 @@ Vue.component("network-addresses-box", {
|
||||
},
|
||||
methods: {
|
||||
addAddr: function () {
|
||||
this.isEditing = true
|
||||
|
||||
let that = this
|
||||
window.UPDATING_ADDR = null
|
||||
|
||||
@@ -14773,18 +14958,32 @@ Vue.component("network-addresses-box", {
|
||||
},
|
||||
supportRange: function () {
|
||||
return this.vSupportRange || (this.vServerType == "tcpProxy" || this.vServerType == "udpProxy")
|
||||
},
|
||||
edit: function () {
|
||||
this.isEditing = true
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" :name="name" :value="JSON.stringify(addresses)"/>
|
||||
<div v-if="addresses.length > 0">
|
||||
<div class="ui label small basic" v-for="(addr, index) in addresses">
|
||||
{{addr.protocol}}://<span v-if="addr.host.length > 0">{{addr.host.quoteIP()}}</span><span v-if="addr.host.length == 0">*</span>:<span v-if="addr.portRange.indexOf('-')<0">{{addr.portRange}}</span><span v-else style="font-style: italic">{{addr.portRange}}</span>
|
||||
<a href="" @click.prevent="updateAddr(index, addr)" title="修改"><i class="icon pencil small"></i></a>
|
||||
<a href="" @click.prevent="removeAddr(index)" title="删除"><i class="icon remove"></i></a> </div>
|
||||
<div class="ui divider"></div>
|
||||
<div v-show="!isEditing">
|
||||
<div v-if="addresses.length > 0">
|
||||
<div class="ui label small basic" v-for="(addr, index) in addresses">
|
||||
{{addr.protocol}}://<span v-if="addr.host.length > 0">{{addr.host.quoteIP()}}</span><span v-if="addr.host.length == 0">*</span>:<span v-if="addr.portRange.indexOf('-')<0">{{addr.portRange}}</span><span v-else style="font-style: italic">{{addr.portRange}}</span>
|
||||
</div>
|
||||
<a href="" @click.prevent="edit" style="font-size: 0.9em">[修改]</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isEditing || addresses.length == 0">
|
||||
<div v-if="addresses.length > 0">
|
||||
<div class="ui label small basic" v-for="(addr, index) in addresses">
|
||||
{{addr.protocol}}://<span v-if="addr.host.length > 0">{{addr.host.quoteIP()}}</span><span v-if="addr.host.length == 0">*</span>:<span v-if="addr.portRange.indexOf('-')<0">{{addr.portRange}}</span><span v-else style="font-style: italic">{{addr.portRange}}</span>
|
||||
<a href="" @click.prevent="updateAddr(index, addr)" title="修改"><i class="icon pencil small"></i></a>
|
||||
<a href="" @click.prevent="removeAddr(index)" title="删除"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<a href="" @click.prevent="addAddr()">[添加端口绑定]</a>
|
||||
</div>
|
||||
<a href="" @click.prevent="addAddr()">[添加端口绑定]</a>
|
||||
</div>`
|
||||
})
|
||||
|
||||
@@ -14876,7 +15075,7 @@ Vue.component("more-items-angle", {
|
||||
return false
|
||||
}
|
||||
},
|
||||
template: `<a href="" class="item" @click.prevent="show"><i class="icon angle" :class="{down: !visible, up: visible}"></i></a>`
|
||||
template: `<a href="" class="item" @click.prevent="show" style="padding-right: 0"><span style="font-size: 0.8em">切换</span><i class="icon angle" :class="{down: !visible, up: visible}"></i></a>`
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -14951,6 +15150,10 @@ Vue.component("link-red", {
|
||||
methods: {
|
||||
clickPrevent: function () {
|
||||
emitClick(this, arguments)
|
||||
|
||||
if (this.vHref.length > 0) {
|
||||
window.location = this.vHref
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<a :href="vHref" :title="title" style="border-bottom: 1px #db2828 dashed" @click.prevent="clickPrevent"><span class="red"><slot></slot></span></a>`
|
||||
@@ -15615,6 +15818,37 @@ Vue.component("loading-message", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("file-textarea", {
|
||||
props: ["value"],
|
||||
data: function () {
|
||||
let value = this.value
|
||||
if (typeof value != "string") {
|
||||
value = ""
|
||||
}
|
||||
return {
|
||||
realValue: value
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
},
|
||||
methods: {
|
||||
dragover: function () {},
|
||||
drop: function (e) {
|
||||
let that = this
|
||||
e.dataTransfer.items[0].getAsFile().text().then(function (data) {
|
||||
that.setValue(data)
|
||||
})
|
||||
},
|
||||
setValue: function (value) {
|
||||
this.realValue = value
|
||||
},
|
||||
focus: function () {
|
||||
this.$refs.textarea.focus()
|
||||
}
|
||||
},
|
||||
template: `<textarea @drop.prevent="drop" @dragover.prevent="dragover" ref="textarea" v-model="realValue"></textarea>`
|
||||
})
|
||||
|
||||
Vue.component("more-options-angle", {
|
||||
data: function () {
|
||||
return {
|
||||
@@ -15630,30 +15864,6 @@ Vue.component("more-options-angle", {
|
||||
template: `<a href="" @click.prevent="show()"><span v-if="!isVisible">更多选项</span><span v-if="isVisible">收起选项</span><i class="icon angle" :class="{down:!isVisible, up:isVisible}"></i></a>`
|
||||
})
|
||||
|
||||
/**
|
||||
* 菜单项
|
||||
*/
|
||||
Vue.component("inner-menu-item", {
|
||||
props: ["href", "active", "code"],
|
||||
data: function () {
|
||||
var active = this.active;
|
||||
if (typeof(active) =="undefined") {
|
||||
var itemCode = "";
|
||||
if (typeof (window.TEA.ACTION.data.firstMenuItem) != "undefined") {
|
||||
itemCode = window.TEA.ACTION.data.firstMenuItem;
|
||||
}
|
||||
active = (itemCode == this.code);
|
||||
}
|
||||
return {
|
||||
vHref: (this.href == null) ? "" : this.href,
|
||||
vActive: active
|
||||
};
|
||||
},
|
||||
template: '\
|
||||
<a :href="vHref" class="item right" style="color:#4183c4" :class="{active:vActive}">[<slot></slot>]</a> \
|
||||
'
|
||||
});
|
||||
|
||||
Vue.component("columns-grid", {
|
||||
props: [],
|
||||
mounted: function () {
|
||||
@@ -15726,6 +15936,30 @@ Vue.component("columns-grid", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
/**
|
||||
* 菜单项
|
||||
*/
|
||||
Vue.component("inner-menu-item", {
|
||||
props: ["href", "active", "code"],
|
||||
data: function () {
|
||||
var active = this.active;
|
||||
if (typeof(active) =="undefined") {
|
||||
var itemCode = "";
|
||||
if (typeof (window.TEA.ACTION.data.firstMenuItem) != "undefined") {
|
||||
itemCode = window.TEA.ACTION.data.firstMenuItem;
|
||||
}
|
||||
active = (itemCode == this.code);
|
||||
}
|
||||
return {
|
||||
vHref: (this.href == null) ? "" : this.href,
|
||||
vActive: active
|
||||
};
|
||||
},
|
||||
template: '\
|
||||
<a :href="vHref" class="item right" style="color:#4183c4" :class="{active:vActive}">[<slot></slot>]</a> \
|
||||
'
|
||||
});
|
||||
|
||||
Vue.component("health-check-config-box", {
|
||||
props: ["v-health-check-config", "v-check-domain-url"],
|
||||
data: function () {
|
||||
@@ -16336,15 +16570,15 @@ Vue.component("combo-box", {
|
||||
template: `<div style="display: inline; z-index: 10; background: white" class="combo-box">
|
||||
<!-- 搜索框 -->
|
||||
<div v-if="selectedItem == null">
|
||||
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
|
||||
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keydown.down.prevent="downItem" @keydown.up.prevent="upItem"/>
|
||||
</div>
|
||||
|
||||
<!-- 当前选中 -->
|
||||
<div v-if="selectedItem != null">
|
||||
<input type="hidden" :name="name" :value="selectedItem.value"/>
|
||||
<a href="" class="ui label basic" style="line-height: 1.4; font-weight: normal; font-size: 1em" ref="selectedLabel" @click.prevent="submitForm"><span><span v-if="title != null && title.length > 0">{{title}}:</span>{{selectedItem.name}}</span>
|
||||
<span title="清除" @click.prevent="reset"><i class="icon remove small"></i></span>
|
||||
</a>
|
||||
<span class="ui label basic" style="line-height: 1.4; font-weight: normal; font-size: 1em" ref="selectedLabel"><span><span v-if="title != null && title.length > 0">{{title}}:</span>{{selectedItem.name}}</span>
|
||||
<a href="" title="清除" @click.prevent="reset"><i class="icon remove small"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 菜单 -->
|
||||
|
||||
@@ -64,7 +64,7 @@ Vue.component("node-ddos-protection-config-box", {
|
||||
<prior-checkbox :v-config="config.tcp" v-if="isNode"></prior-checkbox>
|
||||
<tbody v-show="config.tcp.isPrior || !isNode">
|
||||
<tr>
|
||||
<td class="title">启用</td>
|
||||
<td class="title">启用DDoS防护</td>
|
||||
<td>
|
||||
<checkbox v-model="config.tcp.isOn"></checkbox>
|
||||
</td>
|
||||
|
||||
@@ -249,15 +249,15 @@ Vue.component("combo-box", {
|
||||
template: `<div style="display: inline; z-index: 10; background: white" class="combo-box">
|
||||
<!-- 搜索框 -->
|
||||
<div v-if="selectedItem == null">
|
||||
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keyup.down="downItem" @keyup.up="upItem"/>
|
||||
<input type="text" v-model="keyword" :placeholder="placeholder" :size="size" :style="{'width': styleWidth}" @input="changeKeyword" @focus="show" @blur="hide" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="searchBox" @keydown.down.prevent="downItem" @keydown.up.prevent="upItem"/>
|
||||
</div>
|
||||
|
||||
<!-- 当前选中 -->
|
||||
<div v-if="selectedItem != null">
|
||||
<input type="hidden" :name="name" :value="selectedItem.value"/>
|
||||
<a href="" class="ui label basic" style="line-height: 1.4; font-weight: normal; font-size: 1em" ref="selectedLabel" @click.prevent="submitForm"><span><span v-if="title != null && title.length > 0">{{title}}:</span>{{selectedItem.name}}</span>
|
||||
<span title="清除" @click.prevent="reset"><i class="icon remove small"></i></span>
|
||||
</a>
|
||||
<span class="ui label basic" style="line-height: 1.4; font-weight: normal; font-size: 1em" ref="selectedLabel"><span><span v-if="title != null && title.length > 0">{{title}}:</span>{{selectedItem.name}}</span>
|
||||
<a href="" title="清除" @click.prevent="reset"><i class="icon remove small"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 菜单 -->
|
||||
|
||||
30
web/public/js/components/common/file-textarea.js
Normal file
30
web/public/js/components/common/file-textarea.js
Normal file
@@ -0,0 +1,30 @@
|
||||
Vue.component("file-textarea", {
|
||||
props: ["value"],
|
||||
data: function () {
|
||||
let value = this.value
|
||||
if (typeof value != "string") {
|
||||
value = ""
|
||||
}
|
||||
return {
|
||||
realValue: value
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
},
|
||||
methods: {
|
||||
dragover: function () {},
|
||||
drop: function (e) {
|
||||
let that = this
|
||||
e.dataTransfer.items[0].getAsFile().text().then(function (data) {
|
||||
that.setValue(data)
|
||||
})
|
||||
},
|
||||
setValue: function (value) {
|
||||
this.realValue = value
|
||||
},
|
||||
focus: function () {
|
||||
this.$refs.textarea.focus()
|
||||
}
|
||||
},
|
||||
template: `<textarea @drop.prevent="drop" @dragover.prevent="dragover" ref="textarea" v-model="realValue"></textarea>`
|
||||
})
|
||||
@@ -24,6 +24,10 @@ Vue.component("link-red", {
|
||||
methods: {
|
||||
clickPrevent: function () {
|
||||
emitClick(this, arguments)
|
||||
|
||||
if (this.vHref.length > 0) {
|
||||
window.location = this.vHref
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<a :href="vHref" :title="title" style="border-bottom: 1px #db2828 dashed" @click.prevent="clickPrevent"><span class="red"><slot></slot></span></a>`
|
||||
|
||||
@@ -79,5 +79,5 @@ Vue.component("more-items-angle", {
|
||||
return false
|
||||
}
|
||||
},
|
||||
template: `<a href="" class="item" @click.prevent="show"><i class="icon angle" :class="{down: !visible, up: visible}"></i></a>`
|
||||
template: `<a href="" class="item" @click.prevent="show" style="padding-right: 0"><span style="font-size: 0.8em">切换</span><i class="icon angle" :class="{down: !visible, up: visible}"></i></a>`
|
||||
})
|
||||
@@ -24,7 +24,8 @@ Vue.component("network-addresses-box", {
|
||||
addresses: addresses,
|
||||
protocol: protocol,
|
||||
name: name,
|
||||
from: from
|
||||
from: from,
|
||||
isEditing: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -39,6 +40,8 @@ Vue.component("network-addresses-box", {
|
||||
},
|
||||
methods: {
|
||||
addAddr: function () {
|
||||
this.isEditing = true
|
||||
|
||||
let that = this
|
||||
window.UPDATING_ADDR = null
|
||||
|
||||
@@ -106,17 +109,31 @@ Vue.component("network-addresses-box", {
|
||||
},
|
||||
supportRange: function () {
|
||||
return this.vSupportRange || (this.vServerType == "tcpProxy" || this.vServerType == "udpProxy")
|
||||
},
|
||||
edit: function () {
|
||||
this.isEditing = true
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" :name="name" :value="JSON.stringify(addresses)"/>
|
||||
<div v-if="addresses.length > 0">
|
||||
<div class="ui label small basic" v-for="(addr, index) in addresses">
|
||||
{{addr.protocol}}://<span v-if="addr.host.length > 0">{{addr.host.quoteIP()}}</span><span v-if="addr.host.length == 0">*</span>:<span v-if="addr.portRange.indexOf('-')<0">{{addr.portRange}}</span><span v-else style="font-style: italic">{{addr.portRange}}</span>
|
||||
<a href="" @click.prevent="updateAddr(index, addr)" title="修改"><i class="icon pencil small"></i></a>
|
||||
<a href="" @click.prevent="removeAddr(index)" title="删除"><i class="icon remove"></i></a> </div>
|
||||
<div class="ui divider"></div>
|
||||
<div v-show="!isEditing">
|
||||
<div v-if="addresses.length > 0">
|
||||
<div class="ui label small basic" v-for="(addr, index) in addresses">
|
||||
{{addr.protocol}}://<span v-if="addr.host.length > 0">{{addr.host.quoteIP()}}</span><span v-if="addr.host.length == 0">*</span>:<span v-if="addr.portRange.indexOf('-')<0">{{addr.portRange}}</span><span v-else style="font-style: italic">{{addr.portRange}}</span>
|
||||
</div>
|
||||
<a href="" @click.prevent="edit" style="font-size: 0.9em">[修改]</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isEditing || addresses.length == 0">
|
||||
<div v-if="addresses.length > 0">
|
||||
<div class="ui label small basic" v-for="(addr, index) in addresses">
|
||||
{{addr.protocol}}://<span v-if="addr.host.length > 0">{{addr.host.quoteIP()}}</span><span v-if="addr.host.length == 0">*</span>:<span v-if="addr.portRange.indexOf('-')<0">{{addr.portRange}}</span><span v-else style="font-style: italic">{{addr.portRange}}</span>
|
||||
<a href="" @click.prevent="updateAddr(index, addr)" title="修改"><i class="icon pencil small"></i></a>
|
||||
<a href="" @click.prevent="removeAddr(index)" title="删除"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<a href="" @click.prevent="addAddr()">[添加端口绑定]</a>
|
||||
</div>
|
||||
<a href="" @click.prevent="addAddr()">[添加端口绑定]</a>
|
||||
</div>`
|
||||
})
|
||||
@@ -78,7 +78,7 @@ Vue.component("message-row", {
|
||||
|
||||
<!-- 证书即将过期 -->
|
||||
<div v-if="message.type == 'SSLCertExpiring'" style="margin-top: 0.8em">
|
||||
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> | <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务»</a>
|
||||
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a><span v-if="params != null && params.acmeTaskId > 0"> | <a :href="'/servers/certs/acme'" target="_top">查看任务»</a></span>
|
||||
</div>
|
||||
|
||||
<!-- 证书续期成功 -->
|
||||
|
||||
@@ -239,7 +239,7 @@ Vue.component("http-firewall-checkpoint-cc", {
|
||||
<td>检查请求来源指纹</td>
|
||||
<td>
|
||||
<checkbox v-model="enableFingerprint"></checkbox>
|
||||
<p class="comment">在接收到HTTPS请求时尝试检查请求来源的指纹,用来检测代理服务和爬虫攻击。</p>
|
||||
<p class="comment">在接收到HTTPS请求时尝试检查请求来源的指纹,用来检测代理服务和爬虫攻击;如果你在网站前面放置了别的反向代理服务,请取消此选项。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,78 +1,100 @@
|
||||
Vue.component("server-name-box", {
|
||||
props: ["v-server-names"],
|
||||
data: function () {
|
||||
let serverNames = this.vServerNames;
|
||||
if (serverNames == null) {
|
||||
serverNames = []
|
||||
}
|
||||
return {
|
||||
serverNames: serverNames,
|
||||
isSearching: false,
|
||||
keyword: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addServerName: function () {
|
||||
window.UPDATING_SERVER_NAME = null
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
that.serverNames.push(serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
props: ["v-server-names"],
|
||||
data: function () {
|
||||
let serverNames = this.vServerNames;
|
||||
if (serverNames == null) {
|
||||
serverNames = []
|
||||
}
|
||||
return {
|
||||
serverNames: serverNames,
|
||||
isSearching: false,
|
||||
keyword: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addServerName: function () {
|
||||
window.UPDATING_SERVER_NAME = null
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
that.serverNames.push(serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeServerName: function (index) {
|
||||
this.serverNames.$remove(index)
|
||||
},
|
||||
removeServerName: function (index) {
|
||||
this.serverNames.$remove(index)
|
||||
},
|
||||
|
||||
updateServerName: function (index, serverName) {
|
||||
window.UPDATING_SERVER_NAME = serverName
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
Vue.set(that.serverNames, index, serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
showSearchBox: function () {
|
||||
this.isSearching = !this.isSearching
|
||||
if (this.isSearching) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.keywordRef.focus()
|
||||
}, 200)
|
||||
} else {
|
||||
this.keyword = ""
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
keyword: function (v) {
|
||||
this.serverNames.forEach(function (serverName) {
|
||||
if (v.length == 0) {
|
||||
serverName.isShowing = true
|
||||
return
|
||||
}
|
||||
if (serverName.subNames == null || serverName.subNames.length == 0) {
|
||||
if (!teaweb.match(serverName.name, v)) {
|
||||
serverName.isShowing = false
|
||||
}
|
||||
} else {
|
||||
let found = false
|
||||
serverName.subNames.forEach(function (subName) {
|
||||
if (teaweb.match(subName, v)) {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
serverName.isShowing = found
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
updateServerName: function (index, serverName) {
|
||||
window.UPDATING_SERVER_NAME = teaweb.clone(serverName)
|
||||
let that = this
|
||||
teaweb.popup("/servers/addServerNamePopup", {
|
||||
callback: function (resp) {
|
||||
var serverName = resp.data.serverName
|
||||
Vue.set(that.serverNames, index, serverName)
|
||||
}
|
||||
});
|
||||
},
|
||||
showSearchBox: function () {
|
||||
this.isSearching = !this.isSearching
|
||||
if (this.isSearching) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.keywordRef.focus()
|
||||
}, 200)
|
||||
} else {
|
||||
this.keyword = ""
|
||||
}
|
||||
},
|
||||
allServerNames: function () {
|
||||
if (this.serverNames == null) {
|
||||
return []
|
||||
}
|
||||
let result = []
|
||||
this.serverNames.forEach(function (serverName) {
|
||||
if (serverName.subNames != null && serverName.subNames.length > 0) {
|
||||
serverName.subNames.forEach(function (subName) {
|
||||
if (subName != null && subName.length > 0) {
|
||||
if (!result.$contains(subName)) {
|
||||
result.push(subName)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (serverName.name != null && serverName.name.length > 0) {
|
||||
if (!result.$contains(serverName.name)) {
|
||||
result.push(serverName.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keyword: function (v) {
|
||||
this.serverNames.forEach(function (serverName) {
|
||||
if (v.length == 0) {
|
||||
serverName.isShowing = true
|
||||
return
|
||||
}
|
||||
if (serverName.subNames == null || serverName.subNames.length == 0) {
|
||||
if (!teaweb.match(serverName.name, v)) {
|
||||
serverName.isShowing = false
|
||||
}
|
||||
} else {
|
||||
let found = false
|
||||
serverName.subNames.forEach(function (subName) {
|
||||
if (teaweb.match(subName, v)) {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
serverName.isShowing = found
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="serverNames" :value="JSON.stringify(serverNames)"/>
|
||||
<div v-if="serverNames.length > 0">
|
||||
<div v-for="(serverName, index) in serverNames" class="ui label small basic" :class="{hidden: serverName.isShowing === false}">
|
||||
|
||||
@@ -5,7 +5,9 @@ Vue.component("ssl-certs-box", {
|
||||
"v-protocol", // 协议:https|tls
|
||||
"v-view-size", // 弹窗尺寸:normal, mini
|
||||
"v-single-mode", // 单证书模式
|
||||
"v-description" // 描述文字
|
||||
"v-description", // 描述文字
|
||||
"v-domains", // 搜索的域名列表或者函数
|
||||
"v-user-id" // 用户ID
|
||||
],
|
||||
data: function () {
|
||||
let certs = this.vCerts
|
||||
@@ -43,8 +45,8 @@ Vue.component("ssl-certs-box", {
|
||||
// 选择证书
|
||||
selectCert: function () {
|
||||
let that = this
|
||||
let width = "50em"
|
||||
let height = "30em"
|
||||
let width = "54em"
|
||||
let height = "32em"
|
||||
let viewSize = this.vViewSize
|
||||
if (viewSize == null) {
|
||||
viewSize = "normal"
|
||||
@@ -53,11 +55,41 @@ Vue.component("ssl-certs-box", {
|
||||
width = "35em"
|
||||
height = "20em"
|
||||
}
|
||||
teaweb.popup("/servers/certs/selectPopup?viewSize=" + viewSize, {
|
||||
|
||||
let searchingDomains = []
|
||||
if (this.vDomains != null) {
|
||||
if (typeof this.vDomains == "function") {
|
||||
let resultDomains = this.vDomains()
|
||||
if (resultDomains != null && typeof resultDomains == "object" && (resultDomains instanceof Array)) {
|
||||
searchingDomains = resultDomains
|
||||
}
|
||||
} else if (typeof this.vDomains == "object" && (this.vDomains instanceof Array)) {
|
||||
searchingDomains = this.vDomains
|
||||
}
|
||||
if (searchingDomains.length > 10000) {
|
||||
searchingDomains = searchingDomains.slice(0, 10000)
|
||||
}
|
||||
}
|
||||
|
||||
let selectedCertIds = this.certs.map(function (cert) {
|
||||
return cert.id
|
||||
})
|
||||
let userId = this.vUserId
|
||||
if (userId == null) {
|
||||
userId = 0
|
||||
}
|
||||
|
||||
teaweb.popup("/servers/certs/selectPopup?viewSize=" + viewSize + "&searchingDomains=" + window.encodeURIComponent(searchingDomains.join(",")) + "&selectedCertIds=" + selectedCertIds.join(",") + "&userId=" + userId, {
|
||||
width: width,
|
||||
height: height,
|
||||
callback: function (resp) {
|
||||
that.certs.push(resp.data.cert)
|
||||
if (resp.data.cert != null) {
|
||||
that.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -65,16 +97,46 @@ Vue.component("ssl-certs-box", {
|
||||
// 上传证书
|
||||
uploadCert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/certs/uploadPopup", {
|
||||
let userId = this.vUserId
|
||||
if (typeof userId != "number" && typeof userId != "string") {
|
||||
userId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadPopup?userId=" + userId, {
|
||||
height: "28em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("上传成功", function () {
|
||||
that.certs.push(resp.data.cert)
|
||||
if (resp.data.cert != null) {
|
||||
that.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 批量上传
|
||||
uploadBatch: function () {
|
||||
let that = this
|
||||
let userId = this.vUserId
|
||||
if (typeof userId != "number" && typeof userId != "string") {
|
||||
userId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadBatchPopup?userId=" + userId, {
|
||||
callback: function (resp) {
|
||||
if (resp.data.cert != null) {
|
||||
that.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime: function (timestamp) {
|
||||
return new Date(timestamp * 1000).format("Y-m-d")
|
||||
@@ -100,7 +162,9 @@ Vue.component("ssl-certs-box", {
|
||||
</div>
|
||||
<div v-if="buttonsVisible()">
|
||||
<button class="ui button tiny" type="button" @click.prevent="selectCert()">选择已有证书</button>
|
||||
<span class="disabled">|</span>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadCert()">上传新证书</button>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadBatch()">批量上传证书</button>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -1,5 +1,9 @@
|
||||
Vue.component("ssl-config-box", {
|
||||
props: ["v-ssl-policy", "v-protocol", "v-server-id"],
|
||||
props: [
|
||||
"v-ssl-policy",
|
||||
"v-protocol",
|
||||
"v-server-id"
|
||||
],
|
||||
created: function () {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
@@ -107,12 +111,23 @@ Vue.component("ssl-config-box", {
|
||||
selectedCertIds.push(cert.id.toString())
|
||||
})
|
||||
}
|
||||
teaweb.popup("/servers/certs/selectPopup?selectedCertIds=" + selectedCertIds, {
|
||||
let serverId = this.vServerId
|
||||
if (serverId == null) {
|
||||
serverId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/selectPopup?selectedCertIds=" + selectedCertIds + "&serverId=" + serverId, {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
if (resp.data.cert != null && resp.data.certRef != null) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null && resp.data.certRefs != null) {
|
||||
that.policy.certRefs.$pushAll(resp.data.certRefs)
|
||||
that.policy.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -120,8 +135,12 @@ Vue.component("ssl-config-box", {
|
||||
// 上传证书
|
||||
uploadCert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/certs/uploadPopup", {
|
||||
height: "28em",
|
||||
let serverId = this.vServerId
|
||||
if (typeof serverId != "number" && typeof serverId != "string") {
|
||||
serverId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadPopup?serverId=" + serverId, {
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("上传成功", function () {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
@@ -131,6 +150,28 @@ Vue.component("ssl-config-box", {
|
||||
})
|
||||
},
|
||||
|
||||
// 批量上传
|
||||
uploadBatch: function () {
|
||||
let that = this
|
||||
let serverId = this.vServerId
|
||||
if (typeof serverId != "number" && typeof serverId != "string") {
|
||||
serverId = 0
|
||||
}
|
||||
teaweb.popup("/servers/certs/uploadBatchPopup?serverId=" + serverId, {
|
||||
callback: function (resp) {
|
||||
if (resp.data.cert != null) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null) {
|
||||
that.policy.certRefs.$pushAll(resp.data.certRefs)
|
||||
that.policy.certs.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 申请证书
|
||||
requestCert: function () {
|
||||
// 已经在证书中的域名
|
||||
@@ -312,8 +353,15 @@ Vue.component("ssl-config-box", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.policy.clientCARefs.push(resp.data.certRef)
|
||||
that.policy.clientCACerts.push(resp.data.cert)
|
||||
if (resp.data.cert != null && resp.data.certRef != null) {
|
||||
that.policy.clientCARefs.push(resp.data.certRef)
|
||||
that.policy.clientCACerts.push(resp.data.cert)
|
||||
}
|
||||
if (resp.data.certs != null && resp.data.certRefs != null) {
|
||||
that.policy.clientCARefs.$pushAll(resp.data.certRefs)
|
||||
that.policy.clientCACerts.$pushAll(resp.data.certs)
|
||||
}
|
||||
that.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -369,7 +417,10 @@ Vue.component("ssl-config-box", {
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<button class="ui button tiny" type="button" @click.prevent="selectCert()">选择已有证书</button>
|
||||
<span class="disabled">|</span>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadCert()">上传新证书</button>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadBatch()">批量上传证书</button>
|
||||
<span class="disabled">|</span>
|
||||
<button class="ui button tiny" type="button" @click.prevent="requestCert()" v-if="vServerId != null && vServerId > 0">申请免费证书</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -525,7 +576,7 @@ Vue.component("ssl-config-box", {
|
||||
<td>客户端认证CA证书</td>
|
||||
<td>
|
||||
<div v-if="policy.clientCACerts != null && policy.clientCACerts.length > 0">
|
||||
<div class="ui label small" v-for="(cert, index) in policy.clientCACerts">
|
||||
<div class="ui label small basic" v-for="(cert, index) in policy.clientCACerts">
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeClientCACert()"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
@@ -140,27 +140,42 @@ window.teaweb = {
|
||||
}
|
||||
return (Math.round(bytes * 100 / Math.pow(1024, 6)) / 100) + "EB";
|
||||
},
|
||||
formatBits: function (bits) {
|
||||
formatBits: function (bits, decimal) {
|
||||
bits = Math.ceil(bits);
|
||||
let div = 10000
|
||||
switch (decimal) {
|
||||
case 1:
|
||||
div = 10
|
||||
break
|
||||
case 2:
|
||||
div = 100
|
||||
break
|
||||
case 3:
|
||||
div = 1000
|
||||
break
|
||||
case 4:
|
||||
div = 10000
|
||||
break
|
||||
}
|
||||
if (bits < Math.pow(1024, 1)) {
|
||||
return bits + "bps";
|
||||
}
|
||||
if (bits < Math.pow(1024, 2)) {
|
||||
return (Math.round(bits * 10000 / Math.pow(1024, 1)) / 10000) + "Kbps";
|
||||
return (Math.round(bits * div / Math.pow(1024, 1)) / div) + "Kbps";
|
||||
}
|
||||
if (bits < Math.pow(1024, 3)) {
|
||||
return (Math.round(bits * 10000 / Math.pow(1024, 2)) / 10000) + "Mbps";
|
||||
return (Math.round(bits * div / Math.pow(1024, 2)) / div) + "Mbps";
|
||||
}
|
||||
if (bits < Math.pow(1024, 4)) {
|
||||
return (Math.round(bits * 10000 / Math.pow(1024, 3)) / 10000) + "Gbps";
|
||||
return (Math.round(bits * div / Math.pow(1024, 3)) / div) + "Gbps";
|
||||
}
|
||||
if (bits < Math.pow(1024, 5)) {
|
||||
return (Math.round(bits * 10000 / Math.pow(1024, 4)) / 10000) + "Tbps";
|
||||
return (Math.round(bits * div / Math.pow(1024, 4)) / div) + "Tbps";
|
||||
}
|
||||
if (bits < Math.pow(1024, 6)) {
|
||||
return (Math.round(bits * 10000 / Math.pow(1024, 5)) / 10000) + "Pbps";
|
||||
return (Math.round(bits * div / Math.pow(1024, 5)) / div) + "Pbps";
|
||||
}
|
||||
return (Math.round(bits * 10000 / Math.pow(1024, 6)) / 10000) + "Ebps";
|
||||
return (Math.round(bits * div / Math.pow(1024, 6)) / div) + "Ebps";
|
||||
},
|
||||
formatNumber: function (x) {
|
||||
if (x == null) {
|
||||
|
||||
@@ -218,7 +218,7 @@ tbody {
|
||||
}
|
||||
p.comment,
|
||||
div.comment {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: #959da6;
|
||||
padding-top: 0.4em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -84,7 +84,7 @@ tbody {
|
||||
}
|
||||
|
||||
p.comment, div.comment {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: #959da6;
|
||||
padding-top: 0.4em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ tbody {
|
||||
}
|
||||
p.comment,
|
||||
div.comment {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
color: #959da6;
|
||||
padding-top: 0.4em;
|
||||
font-weight: normal;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sources":["@layout_popup.less"],"names":[],"mappings":";AACA;EACC,WAAA;;AAGD;EACC,aAAA;;AAGD;EACC,qBAAA;;AAGD,CAAC;AAAW,CAAC,SAAS;AAAQ,CAAC,SAAS;AAAS,IAAI;EACpD,WAAA;;AAGD,CAAC;AAAU,IAAI;AAAU,IAAI;EAC5B,cAAA;;AAGD,IAAI;AAAO,KAAK;AAAO,CAAC;EACvB,sBAAA;;AAGD,CAAC;EACA,iBAAA;;AAGD,IAAI;AAAM,GAAG;EACZ,cAAA;;AAGD,IAAI;EACH,cAAA;;AAGD,GAAG,IAAI;EACN,mBAAmB,8CAAnB;;AAGD;EACC,uBAAA;;AAGD,MAAM;EACL,sBAAA;;AAGD,MAAM;EACL,sBAAA;;AAGD,MAAM;EACL,sBAAA;;AAGD,MAAO;AAAI,MAAO;EACjB,gBAAA;;AAGD,CAAC;AAAU,GAAG;EACb,yBAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;;AAGD,CAAC,QAAS;AAAI,GAAG,QAAS;EACzB,6BAAA;;AAGD;EACC,mBAAA;EACA,2BAAA;EACA,gBAAA;EACA,uBAAA;;AAGD,GAAG;AAAS,CAAC;EACZ,eAAA;;;AAID,GAAG;EACF,UAAA;;AAGD,GAAG;EACF,YAAA;;AAGD,GAAG;EACF,UAAA;;AAGD,GAAG;EACF,WAAA;;;AAID,MAAM;EACL,cAAA;;;AAID;EACC,kBAAA;EACA,UAAA;EACA,UAAA;EACA,mBAAA;EACA,kBAAA;EACA,UAAA;;AAGD,mBAAqC;EACpC;IACC,SAAA;;;AAIF,KAAK;EACJ,SAAA;;AAGD,KAAK;EACJ,UAAA;;AAGD,mBAAqC;EACpC,KAAK;IACJ,SAAA;;;AAIF,KAAM,MAAM,GAAE;EACb,WAAA;;AAGD,KAAM,MAAM,GAAE;EACb,WAAA;;AAGD,KAAM,MAAM;EACX,mBAAA;;AAGD,KAAM,GAAE;EACP,8BAAA;;AAGD,KAAM,MAAM,GAAE;EACb,mBAAA;;AAGD,KAAM,MAAM,GAAE;EACb,sBAAA;;AAGD,KAAM,MAAM,GAAE,aAAc;EAC3B,mBAAA;;AAGD,KAAM,MAAM,GAAG;EACd,mBAAA;EACA,kBAAA;EACA,gBAAA;;AAGD,KAAM;EACL,mBAAA;EACA,iBAAA;;AAGD,KAAM,GAAG;EACR,gBAAA;;AAGD,KAAM,GAAG,KAAI;EACZ,cAAA;;AAGD,KAAM,GAAG;EACR,gBAAA;EACA,0BAAA;EACA,UAAA;;AAGD,KAAM,GAAG,EAAC;EACT,SAAS,GAAT;;AAGD,KAAM,GAAG,EAAC;EACT,SAAS,GAAT;;AAGD,KAAM;EACL,mBAAA;;AAGD,KAAM,GAAG,KAAI;EACZ,gBAAA;;AAGD,KAAM,QAAO;EACZ,gBAAA;EACA,cAAA;EACA,gBAAA;;;AAID,KAAK;EACJ,gBAAA;;AAGD,KAAK,KAAK;EACT,UAAA;EACA,WAAA;;;AAID;EACC,wBAAA;;;AAID,iBAAkB;EACjB,gBAAA;;AAGD,iBAAkB,MAAK;EACtB,UAAA;;AAGD,iBAAkB,MAAM;EACvB,2BAAA;;AAGD,MAAM;EACL,sBAAA;;;AAID,mBAAqC;EACpC,OAAO,IAAI;IACV,sBAAA;;;;AAKF,KAAK;EACJ,0BAAA;;AAGD,KAAK;EACJ,cAAA;;;AAOD,WAAY,MAAK;EAChB,wBAAA;EACA,2BAAA;;AAGD,WAAY;EACX,wBAAA;EACA,2BAAA;;AAGD,YAAa,MAAK;EACjB,wBAAA;EACA,2BAAA;;AAGD,YAAa,MAAK,KAAM;EACvB,kBAAA;;AAGD,YAAa;EACZ,wBAAA;;AAGD,KAAM;EACL,aAAA;;;AAID,IAAI;AAAQ,GAAG;EACd,cAAA;;AAGD,IAAI;EACH,8BAAA;;;AAID,QAAS;EACR,WAAA;EACA,kBAAA;;;AAID,SAAU,MAAM;AAAG,SAAU;EAC5B,gBAAA;;;AAID;EACC,eAAA;EAEA,2BAAA;;AAHD,KAKC;EACC,qBAAA;EACA,mBAAA;EACA,WAAA;EACA,iBAAA;EACA,SAAA;EACA,gBAAA;EACA,sBAAA;EACA,cAAA;;AAbF,KAgBC,EAAC;EACA,mBAAA;EACA,YAAA;;AAlBF,KAqBC,EAAC;EACA,gBAAA;;AAtBF,KAyBC;EACC,kBAAA;EACA,qBAAA;;AAKF;EACC,kBAAA;;AAGD,cAAc;AAAQ,aAAa;AAAQ,YAAY;EACtD,sBAAA;;AAGD;AAAgB;AAAe;EAC9B,sBAAA;;AAGD;EACC,2BAAA;;AAID,KAAK;EACJ,yBAAA;;AAID,QAAQ;EACP,4BAA4B,wBAA5B;EACA,gBAAA;;AAID,UAAW;EACV,gBAAA;EACA,gBAAA;EACA,kBAAA;EACA,2CAAA;EACA,aAAA;EACA,YAAA;;AAGD,UAAW,MAAK;EACf,UAAA;;AAID;EACC,gBAAA;EACA,wCAAA;EACA,0BAAA;EACA,wBAAA;EACA,YAAA;EACA,gBAAA;EACA,iBAAA;EACA,cAAA;EACA,qBAAA;EACA,gBAAA;EACA,wBAAA","file":"@layout_popup.css"}
|
||||
{"version":3,"sources":["@layout_popup.less"],"names":[],"mappings":";AACA;EACC,WAAA;;AAGD;EACC,aAAA;;AAGD;EACC,qBAAA;;AAGD,CAAC;AAAW,CAAC,SAAS;AAAQ,CAAC,SAAS;AAAS,IAAI;EACpD,WAAA;;AAGD,CAAC;AAAU,IAAI;AAAU,IAAI;EAC5B,cAAA;;AAGD,IAAI;AAAO,KAAK;AAAO,CAAC;EACvB,sBAAA;;AAGD,CAAC;EACA,iBAAA;;AAGD,IAAI;AAAM,GAAG;EACZ,cAAA;;AAGD,IAAI;EACH,cAAA;;AAGD,GAAG,IAAI;EACN,mBAAmB,8CAAnB;;AAGD;EACC,uBAAA;;AAGD,MAAM;EACL,sBAAA;;AAGD,MAAM;EACL,sBAAA;;AAGD,MAAM;EACL,sBAAA;;AAGD,MAAO;AAAI,MAAO;EACjB,gBAAA;;AAGD,CAAC;AAAU,GAAG;EACb,cAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;;AAGD,CAAC,QAAS;AAAI,GAAG,QAAS;EACzB,6BAAA;;AAGD;EACC,mBAAA;EACA,2BAAA;EACA,gBAAA;EACA,uBAAA;;AAGD,GAAG;AAAS,CAAC;EACZ,eAAA;;;AAID,GAAG;EACF,UAAA;;AAGD,GAAG;EACF,YAAA;;AAGD,GAAG;EACF,UAAA;;AAGD,GAAG;EACF,WAAA;;;AAID,MAAM;EACL,cAAA;;;AAID;EACC,kBAAA;EACA,UAAA;EACA,UAAA;EACA,mBAAA;EACA,kBAAA;EACA,UAAA;;AAGD,mBAAqC;EACpC;IACC,SAAA;;;AAIF,KAAK;EACJ,SAAA;;AAGD,KAAK;EACJ,UAAA;;AAGD,mBAAqC;EACpC,KAAK;IACJ,SAAA;;;AAIF,KAAM,MAAM,GAAE;EACb,WAAA;;AAGD,KAAM,MAAM,GAAE;EACb,WAAA;;AAGD,KAAM,MAAM;EACX,mBAAA;;AAGD,KAAM,GAAE;EACP,8BAAA;;AAGD,KAAM,MAAM,GAAE;EACb,mBAAA;;AAGD,KAAM,MAAM,GAAE;EACb,sBAAA;;AAGD,KAAM,MAAM,GAAE,aAAc;EAC3B,mBAAA;;AAGD,KAAM,MAAM,GAAG;EACd,mBAAA;EACA,kBAAA;EACA,gBAAA;;AAGD,KAAM;EACL,mBAAA;EACA,iBAAA;;AAGD,KAAM,GAAG;EACR,gBAAA;;AAGD,KAAM,GAAG,KAAI;EACZ,cAAA;;AAGD,KAAM,GAAG;EACR,gBAAA;EACA,0BAAA;EACA,UAAA;;AAGD,KAAM,GAAG,EAAC;EACT,SAAS,GAAT;;AAGD,KAAM,GAAG,EAAC;EACT,SAAS,GAAT;;AAGD,KAAM;EACL,mBAAA;;AAGD,KAAM,GAAG,KAAI;EACZ,gBAAA;;AAGD,KAAM,QAAO;EACZ,gBAAA;EACA,cAAA;EACA,gBAAA;;;AAID,KAAK;EACJ,gBAAA;;AAGD,KAAK,KAAK;EACT,UAAA;EACA,WAAA;;;AAID;EACC,wBAAA;;;AAID,iBAAkB;EACjB,gBAAA;;AAGD,iBAAkB,MAAK;EACtB,UAAA;;AAGD,iBAAkB,MAAM;EACvB,2BAAA;;AAGD,MAAM;EACL,sBAAA;;;AAID,mBAAqC;EACpC,OAAO,IAAI;IACV,sBAAA;;;;AAKF,KAAK;EACJ,0BAAA;;AAGD,KAAK;EACJ,cAAA;;;AAOD,WAAY,MAAK;EAChB,wBAAA;EACA,2BAAA;;AAGD,WAAY;EACX,wBAAA;EACA,2BAAA;;AAGD,YAAa,MAAK;EACjB,wBAAA;EACA,2BAAA;;AAGD,YAAa,MAAK,KAAM;EACvB,kBAAA;;AAGD,YAAa;EACZ,wBAAA;;AAGD,KAAM;EACL,aAAA;;;AAID,IAAI;AAAQ,GAAG;EACd,cAAA;;AAGD,IAAI;EACH,8BAAA;;;AAID,QAAS;EACR,WAAA;EACA,kBAAA;;;AAID,SAAU,MAAM;AAAG,SAAU;EAC5B,gBAAA;;;AAID;EACC,eAAA;EAEA,2BAAA;;AAHD,KAKC;EACC,qBAAA;EACA,mBAAA;EACA,WAAA;EACA,iBAAA;EACA,SAAA;EACA,gBAAA;EACA,sBAAA;EACA,cAAA;;AAbF,KAgBC,EAAC;EACA,mBAAA;EACA,YAAA;;AAlBF,KAqBC,EAAC;EACA,gBAAA;;AAtBF,KAyBC;EACC,kBAAA;EACA,qBAAA;;AAKF;EACC,kBAAA;;AAGD,cAAc;AAAQ,aAAa;AAAQ,YAAY;EACtD,sBAAA;;AAGD;AAAgB;AAAe;EAC9B,sBAAA;;AAGD;EACC,2BAAA;;AAID,KAAK;EACJ,yBAAA;;AAID,QAAQ;EACP,4BAA4B,wBAA5B;EACA,gBAAA;;AAID,UAAW;EACV,gBAAA;EACA,gBAAA;EACA,kBAAA;EACA,2CAAA;EACA,aAAA;EACA,YAAA;;AAGD,UAAW,MAAK;EACf,UAAA;;AAID;EACC,gBAAA;EACA,wCAAA;EACA,0BAAA;EACA,wBAAA;EACA,YAAA;EACA,gBAAA;EACA,iBAAA;EACA,cAAA;EACA,qBAAA;EACA,gBAAA;EACA,wBAAA","file":"@layout_popup.css"}
|
||||
@@ -60,7 +60,7 @@ tbody {
|
||||
}
|
||||
|
||||
p.comment, div.comment {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
color: #959da6;
|
||||
padding-top: 0.4em;
|
||||
font-weight: normal;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<first-menu>
|
||||
<menu-item @click.prevent="createAdmin">创建</menu-item>
|
||||
<menu-item @click.prevent="createAdmin">[创建管理员]</menu-item>
|
||||
</first-menu>
|
||||
@@ -33,17 +33,17 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否允许登录</td>
|
||||
<td>允许登录</td>
|
||||
<td>
|
||||
<checkbox name="canLogin" value="1"></checkbox>
|
||||
<p class="comment">选中后才可以登录当前的管理平台。</p>
|
||||
<p class="comment">选中后,当前管理员才可以登录当前的管理平台。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否为超级管理员</td>
|
||||
<td>超级管理员</td>
|
||||
<td>
|
||||
<checkbox name="isSuper" v-model="isSuper"></checkbox>
|
||||
<p class="comment">超级管理员自动拥有所有的管理权限。</p>
|
||||
<p class="comment">选中后,表示当前管理员为超级管理员;超级管理员自动拥有所有的管理权限。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!isSuper">
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<table class="ui table selectable">
|
||||
<div class="margin"></div>
|
||||
<form class="ui form" method="get" action="/admins" v-show="!hasWeakPassword">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" placeholder="用户名、全名 ..." v-model="keyword"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
|
||||
<a href="/admins" v-if="keyword.length > 0">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-if="admins.length == 0">
|
||||
<div class="margin"></div>
|
||||
<p class="comment">暂时还没有<span v-if="keyword.length > 0">跟关键词匹配</span>管理员。</p>
|
||||
</div>
|
||||
|
||||
<div v-if="hasWeakPassword">
|
||||
<span class="ui label small basic blue">当前正在筛选弱密码管理员 <a href="/admins"><i class="icon remove small"></i></a></span>
|
||||
</div>
|
||||
|
||||
<table class="ui table selectable" v-show="admins.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
@@ -13,12 +36,13 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="admin in admins">
|
||||
<td :class="{disabled:!admin.isOn}"><a :href="'/admins/admin?adminId=' + admin.id">{{admin.username}}</a>
|
||||
<div v-if="admin.isSuper" style="margin-top: 0.5em">
|
||||
<tiny-basic-label class="olive">超级管理员</tiny-basic-label>
|
||||
<td :class="{disabled:!admin.isOn}"><a :href="'/admins/admin?adminId=' + admin.id"><keyword :v-word="keyword">{{admin.username}}</keyword></a>
|
||||
<div v-if="admin.isSuper || admin.hasWeakPassword" style="margin-top: 0.5em">
|
||||
<tiny-basic-label class="olive" v-if="admin.isSuper">超级管理员</tiny-basic-label>
|
||||
<a :href="'/admins/update?adminId=' + admin.id" v-if="admin.hasWeakPassword"><tiny-basic-label class="red" title="当前管理员已设置密码为弱密码,有极大的安全风险,请及时修改">弱密码</tiny-basic-label></a>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="{disabled:!admin.isOn}">{{admin.fullname}}</td>
|
||||
<td :class="{disabled:!admin.isOn}"><keyword :v-word="keyword">{{admin.fullname}}</keyword></td>
|
||||
<td>
|
||||
<span v-if="admin.canLogin" class="green">Y</span>
|
||||
<span v-else class="disabled">N</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Tea.context(function () {
|
||||
this.createAdmin = function () {
|
||||
teaweb.popup("/admins/createPopup", {
|
||||
height: "22em",
|
||||
height: "30em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
|
||||
@@ -33,17 +33,17 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否允许登录</td>
|
||||
<td>允许登录</td>
|
||||
<td>
|
||||
<checkbox name="canLogin" value="1" v-model="admin.canLogin"></checkbox>
|
||||
<p class="comment">选中后才可以登录当前的管理平台。</p>
|
||||
<p class="comment">选中后,当前管理员才可以登录当前的管理平台。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否为超级管理员</td>
|
||||
<td>超级管理员</td>
|
||||
<td>
|
||||
<checkbox name="isSuper" v-model="admin.isSuper"></checkbox>
|
||||
<p class="comment">超级管理员自动拥有所有的管理权限。</p>
|
||||
<p class="comment">选中后,表示当前管理员为超级管理员;超级管理员自动拥有所有的管理权限。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!admin.isSuper">
|
||||
|
||||
@@ -174,10 +174,13 @@ Tea.context(function () {
|
||||
|
||||
this.defaultIP = ""
|
||||
this.changeName = function () {
|
||||
if (this.validateIP(this.name)) {
|
||||
this.defaultIP = this.name
|
||||
} else {
|
||||
this.defaultIP = ""
|
||||
let matchIP = this.name.match(/(\d{1,3}\.){3}\d{1,3}/)
|
||||
if (matchIP != null) {
|
||||
if (this.validateIP(matchIP[0])) {
|
||||
this.defaultIP = matchIP[0]
|
||||
} else {
|
||||
this.defaultIP = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
<p class="comment" v-if="!dnsIsExcludingLnNode">通过设置A记录可以将集群上的服务请求转发到不同线路的节点上。</p>
|
||||
</div>
|
||||
<p class="comment" v-if="dnsIsExcludingLnNode"><span class="red">当前集群DNS已设置不解析Ln节点,所以当前节点不会加入DNS解析;如需加入,请修改"集群设置" -- "DNS设置" -- "更多选项" -- "包含Ln节点"。</span></p>
|
||||
<p class="comment" v-if="!node.isInstalled"><span class="red">当前节点尚未完成安装,DNS解析记录将不会生效,<a :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id">[点此安装]</a>。</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
<!-- 未安装 -->
|
||||
<div v-if="!node.isInstalled">
|
||||
<div>
|
||||
<span class="red">在当前节点完成安装前,相关DNS解析记录将不会生效,<link-red href="" @click.prevent="updateNodeIsInstalled(true)">已完成安装</link-red> 。</span>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
|
||||
<h4>方法1:通过SSH自动安装</h4>
|
||||
|
||||
<div v-if="installStatus != null && (installStatus.isRunning || installStatus.isFinished)"
|
||||
@@ -69,7 +74,8 @@ secret: "{{node.secret}}"</source-code-box>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a href="" @click.prevent="updateNodeIsInstalled(true)">[修改为已安装状态]</a>
|
||||
<div class="ui divider"></div>
|
||||
<a href="" @click.prevent="updateNodeIsInstalled(true)">[修改当前节点为已安装状态]</a>
|
||||
</div>
|
||||
|
||||
<!-- 已安装 -->
|
||||
|
||||
@@ -20,7 +20,8 @@ Tea.context(function () {
|
||||
|
||||
// 设置节点安装状态
|
||||
this.updateNodeIsInstalled = function (isInstalled) {
|
||||
teaweb.confirm("确定要将当前节点修改为未安装状态?", function () {
|
||||
let msg = isInstalled ? "html:确定要将当前节点修改为<strong>已安装</strong>状态?" : "html:确定要将当前节点修改为<strong>未安装</strong>状态?"
|
||||
teaweb.confirm(msg, function () {
|
||||
this.$post("/clusters/cluster/node/updateInstallStatus")
|
||||
.params({
|
||||
nodeId: this.nodeId,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
.table th.value-column {
|
||||
width: 7em;
|
||||
}
|
||||
.table .label {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sources":["nodes.less"],"names":[],"mappings":"AAAA,MAAO;EACN,oBAAA;;AAGD,CACC;EACC,iCAAA;;AAIF,WACC;EACC,kBAAA;EACA,aAAA;;AAIF,WAAW,UACV;EACC,eAAA;EACA,cAAA;;AAIF,WAAW,MACV;EACC,eAAA;;AAIF,CAAC;EACA,gBAAA;EACA,kBAAA;;AAGD;EACC,kBAAA;;AADD,aAGC,MAAK;EACJ,aAAA;EACA,kBAAA;EACA,UAAA;EACA,QAAA;EACA,gBAAA;;AAIF,aAAa,MACZ,MAAK;EACJ,eAAA","file":"nodes.css"}
|
||||
{"version":3,"sources":["nodes.less"],"names":[],"mappings":"AAAA,MACC,GAAE;EACD,UAAA;;AAFF,MAKC;EACC,oBAAA;;AAIF,CACC;EACC,iCAAA;;AAIF,WACC;EACC,kBAAA;EACA,aAAA;;AAIF,WAAW,UACV;EACC,eAAA;EACA,cAAA;;AAIF,WAAW,MACV;EACC,eAAA;;AAIF,CAAC;EACA,gBAAA;EACA,kBAAA;;AAGD;EACC,kBAAA;;AADD,aAGC,MAAK;EACJ,aAAA;EACA,kBAAA;EACA,UAAA;EACA,QAAA;EACA,gBAAA;;AAIF,aAAa,MACZ,MAAK;EACJ,eAAA","file":"nodes.css"}
|
||||
@@ -64,16 +64,16 @@
|
||||
<th>节点名称</th>
|
||||
<th>IP</th>
|
||||
<th class="width10">DNS线路</th>
|
||||
<th class="width5 center" v-if="windowWidth < miniWidth || windowWidth > columnWidth1">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
|
||||
<th class="width5 center" v-if="windowWidth < miniWidth || windowWidth > columnWidth2">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
|
||||
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">下行带宽<sort-arrow name="trafficOutOrder"></sort-arrow></th>
|
||||
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">连接数<sort-arrow name="connectionsOrder"></sort-arrow></th>
|
||||
<th class="center" style="width: 7em" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">负载<sort-arrow name="loadOrder"></sort-arrow></th>
|
||||
<th class="two wide center">状态</th>
|
||||
<th class="value-column center" v-if="windowWidth < miniWidth || windowWidth > columnWidth1">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
|
||||
<th class="value-column center" v-if="windowWidth < miniWidth || windowWidth > columnWidth2">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
|
||||
<th class="value-column center" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">下行带宽<sort-arrow name="trafficOutOrder"></sort-arrow></th>
|
||||
<th class="value-column center" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">连接数<sort-arrow name="connectionsOrder"></sort-arrow></th>
|
||||
<th class="value-column center" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">负载<sort-arrow name="loadOrder"></sort-arrow></th>
|
||||
<th class="width6 center">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="node in nodes">
|
||||
<tr v-for="(node, nodeIndex) in nodes">
|
||||
<td class="node-name-td"><a :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}<sup v-if="node.level > 1"><span class="blue"> L{{node.level}}</span></sup></a>
|
||||
|
||||
<a :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + node.id" title="设置"><i class="icon setting grey"></i></a>
|
||||
@@ -91,7 +91,7 @@
|
||||
<td>
|
||||
<span v-if="node.ipAddresses.length == 0" class="disabled">-</span>
|
||||
<div v-else class="address-box">
|
||||
<div v-for="addr in node.ipAddresses" style="margin-bottom:0.3em">
|
||||
<div v-for="(addr, index) in node.ipAddresses" v-if="node.ipAddresses.length < mostIPVisible || node.ipAddressesVisible || index < mostIPVisible" style="margin-bottom:0.3em">
|
||||
<div class="ui label tiny basic">{{addr.ip}}
|
||||
<span class="small" v-if="addr.name.length > 0">({{addr.name}}<span v-if="!addr.canAccess">,不可访问</span>)</span>
|
||||
<span class="small" v-if="addr.name.length == 0 && !addr.canAccess">(不可访问)</span>
|
||||
@@ -102,6 +102,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="" @click.prevent="showMoreIP(nodeIndex, node)" v-show="node.ipAddresses.length > mostIPVisible">
|
||||
<span class="small grey" v-if="!node.ipAddressesVisible">更多IP...</span>
|
||||
<span class="small grey" v-if="node.ipAddressesVisible">...收起</span>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="routes-box" :class="{'show-link': node.dnsRouteNames.length == 0 && hasClusterDNS}">
|
||||
@@ -127,11 +131,11 @@
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth3">
|
||||
<span v-if="node.status.isActive && node.status.trafficOutBytes > 0">{{teaweb.formatBits(node.status.trafficOutBytes * 8/60)}}</span>
|
||||
<span v-if="node.status.isActive && node.status.trafficOutBytes > 0">{{teaweb.formatBits(node.status.trafficOutBytes * 8/60, 2)}}</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth4">
|
||||
<span v-if="node.status.isActive && node.status.countConnections > 0">{{node.status.countConnections}}</span>
|
||||
<span v-if="node.status.isActive && node.status.countConnections > 0">{{teaweb.formatCount(node.status.countConnections)}}</span>
|
||||
<span v-else class="disabled">-</span>
|
||||
</td>
|
||||
<td class="center" v-if="windowWidth < miniWidth || windowWidth > columnWidth5">
|
||||
|
||||
@@ -48,4 +48,17 @@ Tea.context(function () {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示和隐藏IP
|
||||
*/
|
||||
this.mostIPVisible = 4
|
||||
|
||||
this.showMoreIP = function (nodeIndex, node) {
|
||||
if (typeof node.ipAddressesVisible != "boolean") {
|
||||
node.ipAddressesVisible = false
|
||||
}
|
||||
node.ipAddressesVisible = !node.ipAddressesVisible
|
||||
Vue.set(this.nodes, nodeIndex, node)
|
||||
}
|
||||
})
|
||||
@@ -1,5 +1,11 @@
|
||||
.table .label {
|
||||
margin-bottom: 0.5em;
|
||||
.table {
|
||||
th.value-column {
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@@ -122,6 +122,17 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4>其他</h4>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">支持低版本HTTP</td>
|
||||
<td>
|
||||
<checkbox name="httpAllSupportsLowVersionHTTP" v-model="config.httpAll.supportsLowVersionHTTP"></checkbox>
|
||||
<p class="comment">选中后,表示支持HTTP/1.0、HTTP/0.9等低于HTTP/1.1版本的HTTP协议。低版本HTTP协议不支持分段传输内容,且无法保持连接,对系统性能有严重的负面影响。建议只有在你的用户正在使用非常老旧的设备时才启用此选项。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,4 +1,5 @@
|
||||
<first-menu>
|
||||
<menu-item href="/clusters/grants" code="index">认证列表</menu-item>
|
||||
<menu-item href="/clusters/grants/create" code="create">创建认证</menu-item>
|
||||
<span class="item disabled">|</span>
|
||||
<menu-item href="/clusters/grants/create" code="create">[创建认证]</menu-item>
|
||||
</first-menu>
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
<tr>
|
||||
<td>RSA私钥 *</td>
|
||||
<td>
|
||||
<textarea name="privateKey" spellcheck="false"></textarea>
|
||||
<p class="comment">用来生成登录SSH公钥的私钥</p>
|
||||
<file-textarea name="privateKey" spellcheck="false" placeholder="填入RSA私钥内容或者拖动私钥文件到当前框中"></file-textarea>
|
||||
<p class="comment">用来生成登录SSH公钥的私钥。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user