Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42148a66bd | ||
|
|
96878715bf | ||
|
|
1a5f3342e7 | ||
|
|
3613d13a2b | ||
|
|
7786140d85 | ||
|
|
3a23b57f1b | ||
|
|
5a6e6fba69 | ||
|
|
910b3a6162 | ||
|
|
0dc19bed45 | ||
|
|
a7bdb64301 | ||
|
|
4739072a85 | ||
|
|
07bdae2488 | ||
|
|
b84035d821 | ||
|
|
dc0a7b9dae | ||
|
|
2937bd8de0 | ||
|
|
37315ef4d9 | ||
|
|
1986fece07 | ||
|
|
16b1657f35 | ||
|
|
c115c62cd9 | ||
|
|
d2df7f8d5b | ||
|
|
6cb79864e6 | ||
|
|
982d28c7b4 | ||
|
|
0f57516fdc | ||
|
|
75a89defcb | ||
|
|
22a6c52060 | ||
|
|
37607e4a41 | ||
|
|
5936155998 | ||
|
|
8d76de935f | ||
|
|
9baa530064 | ||
|
|
103414b338 | ||
|
|
72fe68ebfe | ||
|
|
cfed31958b | ||
|
|
3d5fca2d36 | ||
|
|
a5710286ec | ||
|
|
d0ce0c6c58 | ||
|
|
779e2cf0f2 | ||
|
|
2108474777 | ||
|
|
e25e0f1747 | ||
|
|
e8e74b639c | ||
|
|
a14fcd1e50 | ||
|
|
485c0e0891 | ||
|
|
00a19e9d43 | ||
|
|
67d0dc0783 | ||
|
|
3718c35842 | ||
|
|
567ffc80b6 | ||
|
|
5d15a08ac8 | ||
|
|
2b84037346 | ||
|
|
536f11e617 | ||
|
|
5d367a384e | ||
|
|
04933e6bf0 | ||
|
|
12abe9aa69 | ||
|
|
cba642a4bc | ||
|
|
9fce0ac0aa | ||
|
|
681812b619 | ||
|
|
6d0be57698 | ||
|
|
1d521602e1 | ||
|
|
2f67e7937a | ||
|
|
e0078a42dc | ||
|
|
3ec875d49d | ||
|
|
35028d1310 | ||
|
|
7ba3d7c4bb | ||
|
|
c05e64098c | ||
|
|
e82ee56a2c | ||
|
|
5681b61aea | ||
|
|
d6ce7eab25 | ||
|
|
bf597fe41c | ||
|
|
6a920f964f | ||
|
|
977b66ba4e | ||
|
|
4659c29358 | ||
|
|
e3bc95b275 | ||
|
|
bf51255e13 | ||
|
|
915fe6837b | ||
|
|
d1237215c0 | ||
|
|
8ba5cfdfa6 | ||
|
|
0d1097425d | ||
|
|
0789a9ecc8 | ||
|
|
400f764b74 | ||
|
|
46e036e3a4 | ||
|
|
17e6264af8 | ||
|
|
468b6ae125 | ||
|
|
e5f5ee4f6a | ||
|
|
3695082ec2 | ||
|
|
f384d86014 | ||
|
|
34bf5028c3 | ||
|
|
8b727aa939 | ||
|
|
9432600de6 | ||
|
|
bb790ec687 | ||
|
|
8bad658d7c | ||
|
|
a66cc9c08a | ||
|
|
0dc24fb342 | ||
|
|
b965ac6232 | ||
|
|
08d61013c8 | ||
|
|
7796f65814 | ||
|
|
02a3afb9ad |
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
|
"github.com/TeaOSLab/EdgeAdmin/internal/apps"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||||
@@ -9,10 +10,13 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
|
"github.com/TeaOSLab/EdgeAdmin/internal/nodes"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/gosock/pkg/gosock"
|
"github.com/iwind/gosock/pkg/gosock"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,6 +57,20 @@ func main() {
|
|||||||
fmt.Println("[ERROR]reset failed: " + err.Error())
|
fmt.Println("[ERROR]reset failed: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset local api
|
||||||
|
var apiNodeExe = Tea.Root + "/edge-api/bin/edge-api"
|
||||||
|
_, err = os.Stat(apiNodeExe)
|
||||||
|
if err == nil {
|
||||||
|
var cmd = exec.Command(apiNodeExe, "reset")
|
||||||
|
var stderr = &bytes.Buffer{}
|
||||||
|
cmd.Stderr = stderr
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("reset api node failed: " + stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("done")
|
fmt.Println("done")
|
||||||
})
|
})
|
||||||
app.On("recover", func() {
|
app.On("recover", func() {
|
||||||
|
|||||||
1
docker/.gitignore
vendored
Normal file
1
docker/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.zip
|
||||||
36
docker/Dockerfile
Normal file
36
docker/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
FROM alpine:latest
|
||||||
|
LABEL maintainer="iwind.liu@gmail.com"
|
||||||
|
ENV TZ "Asia/Shanghai"
|
||||||
|
ENV VERSION 0.5.7
|
||||||
|
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"
|
||||||
|
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
|
||||||
|
RUN apk add wget
|
||||||
|
RUN mkdir ${ROOT_DIR}; \
|
||||||
|
cd ${ROOT_DIR}; \
|
||||||
|
wget ${TAR_URL} -O ${TAR_FILE}; \
|
||||||
|
apk add unzip; \
|
||||||
|
unzip ${TAR_FILE}; \
|
||||||
|
rm -f ${TAR_FILE}
|
||||||
|
|
||||||
|
RUN apk add mysql mysql-client; \
|
||||||
|
sed -e "s/\[mysqld\]/\[mysqld\]\n\ndatadir=\/var\/lib\/mysql\nport=3306\ninnodb_flush_log_at_trx_commit=2\nmax_connections=256\nmax_prepared_stmt_count=65535\nbinlog_cache_size=1M\nbinlog_stmt_cache_size=1M\nthread_cache_size=32\nbinlog_expire_logs_seconds=1209600\n\n/" /etc/my.cnf > /tmp/my.cnf; \
|
||||||
|
cp /tmp/my.cnf /etc/my.cnf; \
|
||||||
|
sed -e "s/skip-networking/#skip-networking/" /etc/my.cnf.d/mariadb-server.cnf > /tmp/mariadb-server.cnf; \
|
||||||
|
cp /tmp/mariadb-server.cnf /etc/my.cnf.d/mariadb-server.cnf; \
|
||||||
|
mysql_install_db --user=mysql
|
||||||
|
RUN mysqld_safe --user=mysql & \
|
||||||
|
sleep 5; \
|
||||||
|
mysql -uroot -hlocalhost --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';"
|
||||||
|
|
||||||
|
RUN echo -e "#!/usr/bin/env sh\n\nmysqld_safe --user=mysql &\n/usr/local/goedge/edge-admin/bin/edge-admin\n" > ${ROOT_DIR}/run.sh; \
|
||||||
|
chmod u+x ${ROOT_DIR}/run.sh
|
||||||
|
|
||||||
|
EXPOSE 7788
|
||||||
|
EXPOSE 8001
|
||||||
|
EXPOSE 3306
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/usr/local/goedge/run.sh" ]
|
||||||
5
docker/build.sh
Executable file
5
docker/build.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
VERSION=latest
|
||||||
|
|
||||||
|
docker build --no-cache -t goedge/edge-admin:${VERSION} .
|
||||||
5
docker/run.sh
Executable file
5
docker/run.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
VERSION=latest
|
||||||
|
|
||||||
|
docker run -d -p 7788:7788 -p 8001:8001 -p 3306:3306 --name edge-admin goedge/edge-admin:${VERSION}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package configloaders
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
|
||||||
"github.com/iwind/TeaGo/logs"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sharedUserUIConfig *systemconfigs.UserUIConfig = nil
|
|
||||||
|
|
||||||
const (
|
|
||||||
UserUISettingName = "userUIConfig"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LoadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
|
|
||||||
locker.Lock()
|
|
||||||
defer locker.Unlock()
|
|
||||||
|
|
||||||
config, err := loadUserUIConfig()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.UserUIConfig)
|
|
||||||
return &v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateUserUIConfig(uiConfig *systemconfigs.UserUIConfig) error {
|
|
||||||
locker.Lock()
|
|
||||||
defer locker.Unlock()
|
|
||||||
|
|
||||||
var rpcClient, err = rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
valueJSON, err := json.Marshal(uiConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = rpcClient.SysSettingRPC().UpdateSysSetting(rpcClient.Context(0), &pb.UpdateSysSettingRequest{
|
|
||||||
Code: UserUISettingName,
|
|
||||||
ValueJSON: valueJSON,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sharedUserUIConfig = uiConfig
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadUserUIConfig() (*systemconfigs.UserUIConfig, error) {
|
|
||||||
if sharedUserUIConfig != nil {
|
|
||||||
return sharedUserUIConfig, nil
|
|
||||||
}
|
|
||||||
var rpcClient, err = rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(rpcClient.Context(0), &pb.ReadSysSettingRequest{
|
|
||||||
Code: UserUISettingName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(resp.ValueJSON) == 0 {
|
|
||||||
sharedUserUIConfig = defaultUserUIConfig()
|
|
||||||
return sharedUserUIConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
config := &systemconfigs.UserUIConfig{}
|
|
||||||
err = json.Unmarshal(resp.ValueJSON, config)
|
|
||||||
if err != nil {
|
|
||||||
logs.Println("[UI_MANAGER]" + err.Error())
|
|
||||||
sharedUserUIConfig = defaultUserUIConfig()
|
|
||||||
return sharedUserUIConfig, nil
|
|
||||||
}
|
|
||||||
sharedUserUIConfig = config
|
|
||||||
return sharedUserUIConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultUserUIConfig() *systemconfigs.UserUIConfig {
|
|
||||||
return &systemconfigs.UserUIConfig{
|
|
||||||
ProductName: "GoEdge",
|
|
||||||
UserSystemName: "GoEdge用户系统",
|
|
||||||
ShowOpenSourceInfo: true,
|
|
||||||
ShowVersion: true,
|
|
||||||
ShowFinance: true,
|
|
||||||
BandwidthUnit: systemconfigs.BandwidthUnitBit,
|
|
||||||
ShowBandwidthCharts: true,
|
|
||||||
ShowTrafficCharts: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -61,10 +61,11 @@ func LoadAPIConfig() (*APIConfig, error) {
|
|||||||
|
|
||||||
// ResetAPIConfig 重置配置
|
// ResetAPIConfig 重置配置
|
||||||
func ResetAPIConfig() error {
|
func ResetAPIConfig() error {
|
||||||
filename := "api.yaml"
|
var filename = "api.yaml"
|
||||||
|
|
||||||
|
// 重置 configs/api.yaml
|
||||||
{
|
{
|
||||||
configFile := Tea.ConfigFile(filename)
|
var configFile = Tea.ConfigFile(filename)
|
||||||
stat, err := os.Stat(configFile)
|
stat, err := os.Stat(configFile)
|
||||||
if err == nil && !stat.IsDir() {
|
if err == nil && !stat.IsDir() {
|
||||||
err = os.Remove(configFile)
|
err = os.Remove(configFile)
|
||||||
@@ -77,7 +78,7 @@ func ResetAPIConfig() error {
|
|||||||
// 重置 ~/.edge-admin/api.yaml
|
// 重置 ~/.edge-admin/api.yaml
|
||||||
homeDir, homeErr := os.UserHomeDir()
|
homeDir, homeErr := os.UserHomeDir()
|
||||||
if homeErr == nil {
|
if homeErr == nil {
|
||||||
configFile := homeDir + "/." + teaconst.ProcessName + "/" + filename
|
var configFile = homeDir + "/." + teaconst.ProcessName + "/" + filename
|
||||||
stat, err := os.Stat(configFile)
|
stat, err := os.Stat(configFile)
|
||||||
if err == nil && !stat.IsDir() {
|
if err == nil && !stat.IsDir() {
|
||||||
err = os.Remove(configFile)
|
err = os.Remove(configFile)
|
||||||
@@ -89,7 +90,7 @@ func ResetAPIConfig() error {
|
|||||||
|
|
||||||
// 重置 /etc/edge-admin/api.yaml
|
// 重置 /etc/edge-admin/api.yaml
|
||||||
{
|
{
|
||||||
configFile := "/etc/" + teaconst.ProcessName + "/" + filename
|
var configFile = "/etc/" + teaconst.ProcessName + "/" + filename
|
||||||
stat, err := os.Stat(configFile)
|
stat, err := os.Stat(configFile)
|
||||||
if err == nil && !stat.IsDir() {
|
if err == nil && !stat.IsDir() {
|
||||||
err = os.Remove(configFile)
|
err = os.Remove(configFile)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.5.3"
|
Version = "0.5.8"
|
||||||
|
|
||||||
APINodeVersion = "0.5.3"
|
APINodeVersion = "0.5.8"
|
||||||
|
|
||||||
ProductName = "Edge Admin"
|
ProductName = "Edge Admin"
|
||||||
ProcessName = "edge-admin"
|
ProcessName = "edge-admin"
|
||||||
|
|||||||
@@ -98,10 +98,6 @@ func (this *RPCClient) NodeRegionRPC() pb.NodeRegionServiceClient {
|
|||||||
return pb.NewNodeRegionServiceClient(this.pickConn())
|
return pb.NewNodeRegionServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *RPCClient) NodePriceItemRPC() pb.NodePriceItemServiceClient {
|
|
||||||
return pb.NewNodePriceItemServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
|
func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
|
||||||
return pb.NewNodeIPAddressServiceClient(this.pickConn())
|
return pb.NewNodeIPAddressServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
@@ -174,18 +170,10 @@ func (this *RPCClient) APIMethodStatRPC() pb.APIMethodStatServiceClient {
|
|||||||
return pb.NewAPIMethodStatServiceClient(this.pickConn())
|
return pb.NewAPIMethodStatServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient {
|
|
||||||
return pb.NewUserNodeServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) DBNodeRPC() pb.DBNodeServiceClient {
|
func (this *RPCClient) DBNodeRPC() pb.DBNodeServiceClient {
|
||||||
return pb.NewDBNodeServiceClient(this.pickConn())
|
return pb.NewDBNodeServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *RPCClient) MonitorNodeRPC() pb.MonitorNodeServiceClient {
|
|
||||||
return pb.NewMonitorNodeServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) DBRPC() pb.DBServiceClient {
|
func (this *RPCClient) DBRPC() pb.DBServiceClient {
|
||||||
return pb.NewDBServiceClient(this.pickConn())
|
return pb.NewDBServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
@@ -407,26 +395,6 @@ func (this *RPCClient) UserRPC() pb.UserServiceClient {
|
|||||||
return pb.NewUserServiceClient(this.pickConn())
|
return pb.NewUserServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
|
|
||||||
return pb.NewUserBillServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) ServerBillRPC() pb.ServerBillServiceClient {
|
|
||||||
return pb.NewServerBillServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) UserAccountRPC() pb.UserAccountServiceClient {
|
|
||||||
return pb.NewUserAccountServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) UserAccountLogRPC() pb.UserAccountLogServiceClient {
|
|
||||||
return pb.NewUserAccountLogServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) UserAccountDailyStatRPC() pb.UserAccountDailyStatServiceClient {
|
|
||||||
return pb.NewUserAccountDailyStatServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
|
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
|
||||||
return pb.NewUserAccessKeyServiceClient(this.pickConn())
|
return pb.NewUserAccessKeyServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
@@ -483,14 +451,6 @@ func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceC
|
|||||||
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
|
return pb.NewServerStatBoardChartServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *RPCClient) PlanRPC() pb.PlanServiceClient {
|
|
||||||
return pb.NewPlanServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) UserPlanRPC() pb.UserPlanServiceClient {
|
|
||||||
return pb.NewUserPlanServiceClient(this.pickConn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
|
func (this *RPCClient) TrafficDailyStatRPC() pb.TrafficDailyStatServiceClient {
|
||||||
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
|
return pb.NewTrafficDailyStatServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
@@ -611,41 +571,30 @@ func (this *RPCClient) pickConn() *grpc.ClientConn {
|
|||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
// 检查连接状态
|
// 检查连接状态
|
||||||
if len(this.conns) > 0 {
|
var countConns = len(this.conns)
|
||||||
var availableConns = []*grpc.ClientConn{}
|
if countConns > 0 {
|
||||||
for _, state := range []connectivity.State{connectivity.Ready, connectivity.Idle, connectivity.Connecting} {
|
if countConns == 1 {
|
||||||
|
return this.conns[0]
|
||||||
|
}
|
||||||
|
for _, state := range []connectivity.State{
|
||||||
|
connectivity.Ready,
|
||||||
|
connectivity.Idle,
|
||||||
|
connectivity.Connecting,
|
||||||
|
connectivity.TransientFailure,
|
||||||
|
} {
|
||||||
|
var availableConns = []*grpc.ClientConn{}
|
||||||
for _, conn := range this.conns {
|
for _, conn := range this.conns {
|
||||||
if conn.GetState() == state {
|
if conn.GetState() == state {
|
||||||
availableConns = append(availableConns, conn)
|
availableConns = append(availableConns, conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(availableConns) > 0 {
|
if len(availableConns) > 0 {
|
||||||
break
|
return this.randConn(availableConns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(availableConns) > 0 {
|
|
||||||
return availableConns[rands.Int(0, len(availableConns)-1)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭
|
|
||||||
for _, conn := range this.conns {
|
|
||||||
_ = conn.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新初始化
|
return this.randConn(this.conns)
|
||||||
err := this.init()
|
|
||||||
if err != nil {
|
|
||||||
// 错误提示已经在构造对象时打印过,所以这里不再重复打印
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(this.conns) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.conns[rands.Int(0, len(this.conns)-1)]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close 关闭
|
// Close 关闭
|
||||||
@@ -679,3 +628,14 @@ func (this *RPCClient) localIPAddrs() []string {
|
|||||||
}
|
}
|
||||||
return localIPAddrs
|
return localIPAddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) randConn(conns []*grpc.ClientConn) *grpc.ClientConn {
|
||||||
|
var l = len(conns)
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if l == 1 {
|
||||||
|
return conns[0]
|
||||||
|
}
|
||||||
|
return conns[rands.Int(0, l-1)]
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package numberutils
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FormatInt64(value int64) string {
|
func FormatInt64(value int64) string {
|
||||||
@@ -28,17 +30,35 @@ func FormatBytes(bytes int64) string {
|
|||||||
if bytes < Pow1024(1) {
|
if bytes < Pow1024(1) {
|
||||||
return FormatInt64(bytes) + "B"
|
return FormatInt64(bytes) + "B"
|
||||||
} else if bytes < Pow1024(2) {
|
} else if bytes < Pow1024(2) {
|
||||||
return fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1)))
|
return TrimZeroSuffix(fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1))))
|
||||||
} else if bytes < Pow1024(3) {
|
} else if bytes < Pow1024(3) {
|
||||||
return fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2)))
|
return TrimZeroSuffix(fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2))))
|
||||||
} else if bytes < Pow1024(4) {
|
} else if bytes < Pow1024(4) {
|
||||||
return fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3)))
|
return TrimZeroSuffix(fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3))))
|
||||||
} else if bytes < Pow1024(5) {
|
} else if bytes < Pow1024(5) {
|
||||||
return fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4)))
|
return TrimZeroSuffix(fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4))))
|
||||||
} else if bytes < Pow1024(6) {
|
} else if bytes < Pow1024(6) {
|
||||||
return fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5)))
|
return TrimZeroSuffix(fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5))))
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6)))
|
return TrimZeroSuffix(fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatBits(bits int64) string {
|
||||||
|
if bits < Pow1024(1) {
|
||||||
|
return FormatInt64(bits) + "bps"
|
||||||
|
} else if bits < Pow1024(2) {
|
||||||
|
return TrimZeroSuffix(fmt.Sprintf("%.4fKbps", float64(bits)/float64(Pow1024(1))))
|
||||||
|
} else if bits < Pow1024(3) {
|
||||||
|
return TrimZeroSuffix(fmt.Sprintf("%.4fMbps", float64(bits)/float64(Pow1024(2))))
|
||||||
|
} else if bits < Pow1024(4) {
|
||||||
|
return TrimZeroSuffix(fmt.Sprintf("%.4fGbps", float64(bits)/float64(Pow1024(3))))
|
||||||
|
} else if bits < Pow1024(5) {
|
||||||
|
return TrimZeroSuffix(fmt.Sprintf("%.4fTbps", float64(bits)/float64(Pow1024(4))))
|
||||||
|
} else if bits < Pow1024(6) {
|
||||||
|
return TrimZeroSuffix(fmt.Sprintf("%.4fPbps", float64(bits)/float64(Pow1024(5))))
|
||||||
|
} else {
|
||||||
|
return TrimZeroSuffix(fmt.Sprintf("%.4fEbps", float64(bits)/float64(Pow1024(6))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +82,19 @@ func FormatFloat(f interface{}, decimal int) string {
|
|||||||
switch x := f.(type) {
|
switch x := f.(type) {
|
||||||
case float32, float64:
|
case float32, float64:
|
||||||
var s = fmt.Sprintf("%."+types.String(decimal)+"f", x)
|
var s = fmt.Sprintf("%."+types.String(decimal)+"f", x)
|
||||||
|
|
||||||
|
// 分隔
|
||||||
|
var dotIndex = strings.Index(s, ".")
|
||||||
|
if dotIndex > 0 {
|
||||||
|
var d = s[:dotIndex]
|
||||||
|
var f2 = s[dotIndex:]
|
||||||
|
f2 = strings.TrimRight(strings.TrimRight(f2, "0"), ".")
|
||||||
|
return formatDigit(d) + f2
|
||||||
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||||
return types.String(x)
|
return formatDigit(types.String(x))
|
||||||
case string:
|
case string:
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
@@ -74,3 +104,44 @@ func FormatFloat(f interface{}, decimal int) string {
|
|||||||
func FormatFloat2(f interface{}) string {
|
func FormatFloat2(f interface{}) string {
|
||||||
return FormatFloat(f, 2)
|
return FormatFloat(f, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var decimalReg = regexp.MustCompile(`^(\d+\.\d+)([a-zA-Z]+)?$`)
|
||||||
|
|
||||||
|
// TrimZeroSuffix 去除小数数字尾部多余的0
|
||||||
|
func TrimZeroSuffix(s string) string {
|
||||||
|
var matches = decimalReg.FindStringSubmatch(s)
|
||||||
|
if len(matches) < 3 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return strings.TrimRight(strings.TrimRight(matches[1], "0"), ".") + matches[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatDigit(d string) string {
|
||||||
|
if len(d) == 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = ""
|
||||||
|
if d[0] < '0' || d[0] > '9' {
|
||||||
|
prefix = d[:1]
|
||||||
|
d = d[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
var l = len(d)
|
||||||
|
if l > 3 {
|
||||||
|
var pieces = l / 3
|
||||||
|
var commIndex = l - pieces*3
|
||||||
|
var d2 = ""
|
||||||
|
if commIndex > 0 {
|
||||||
|
d2 = d[:commIndex] + ", "
|
||||||
|
}
|
||||||
|
for i := 0; i < pieces; i++ {
|
||||||
|
d2 += d[commIndex+i*3 : commIndex+i*3+3]
|
||||||
|
if i != pieces-1 {
|
||||||
|
d2 += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix + d2
|
||||||
|
}
|
||||||
|
return prefix + d
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,4 +33,35 @@ func TestFormatFloat(t *testing.T) {
|
|||||||
t.Log(numberutils.FormatFloat(100.23456, 2))
|
t.Log(numberutils.FormatFloat(100.23456, 2))
|
||||||
t.Log(numberutils.FormatFloat(100.000023, 2))
|
t.Log(numberutils.FormatFloat(100.000023, 2))
|
||||||
t.Log(numberutils.FormatFloat(100.012, 2))
|
t.Log(numberutils.FormatFloat(100.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(123.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(1234.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(12345.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(123456.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(1234567.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(12345678.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(123456789.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(1234567890.012, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(123, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(1234, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(1234.00001, 4))
|
||||||
|
t.Log(numberutils.FormatFloat(1234.56700, 4))
|
||||||
|
t.Log(numberutils.FormatFloat(-1234.56700, 2))
|
||||||
|
t.Log(numberutils.FormatFloat(-221745.12, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrimZeroSuffix(t *testing.T) {
|
||||||
|
for _, s := range []string{
|
||||||
|
"1",
|
||||||
|
"1.0000",
|
||||||
|
"1.10",
|
||||||
|
"100",
|
||||||
|
"100.0000",
|
||||||
|
"100.0",
|
||||||
|
"100.0123",
|
||||||
|
"100.0010",
|
||||||
|
"100.000KB",
|
||||||
|
"100.010MB",
|
||||||
|
} {
|
||||||
|
t.Log(s, "=>", numberutils.TrimZeroSuffix(s))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ func (this *ServiceManager) installSystemdService(systemd, exePath string, args
|
|||||||
shortName := teaconst.SystemdServiceName
|
shortName := teaconst.SystemdServiceName
|
||||||
longName := "GoEdge Admin" // TODO 将来可以修改
|
longName := "GoEdge Admin" // TODO 将来可以修改
|
||||||
|
|
||||||
desc := `# Provides: ` + shortName + `
|
desc := `### BEGIN INIT INFO
|
||||||
|
# Provides: ` + shortName + `
|
||||||
# Required-Start: $all
|
# Required-Start: $all
|
||||||
# Required-Stop:
|
# Required-Stop:
|
||||||
# Default-Start: 2 3 4 5
|
# Default-Start: 2 3 4 5
|
||||||
|
|||||||
55
internal/utils/time.go
Normal file
55
internal/utils/time.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RangeTimes 计算时间点
|
||||||
|
func RangeTimes(timeFrom string, timeTo string, everyMinutes int32) (result []string, err error) {
|
||||||
|
if everyMinutes <= 0 {
|
||||||
|
return nil, errors.New("invalid 'everyMinutes'")
|
||||||
|
}
|
||||||
|
|
||||||
|
var reg = regexp.MustCompile(`^\d{4}$`)
|
||||||
|
if !reg.MatchString(timeFrom) {
|
||||||
|
return nil, errors.New("invalid timeFrom '" + timeFrom + "'")
|
||||||
|
}
|
||||||
|
if !reg.MatchString(timeTo) {
|
||||||
|
return nil, errors.New("invalid timeTo '" + timeTo + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeFrom > timeTo {
|
||||||
|
// swap
|
||||||
|
timeFrom, timeTo = timeTo, timeFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
var everyMinutesInt = int(everyMinutes)
|
||||||
|
|
||||||
|
var fromHour = types.Int(timeFrom[:2])
|
||||||
|
var fromMinute = types.Int(timeFrom[2:])
|
||||||
|
var toHour = types.Int(timeTo[:2])
|
||||||
|
var toMinute = types.Int(timeTo[2:])
|
||||||
|
|
||||||
|
if fromMinute%everyMinutesInt == 0 {
|
||||||
|
result = append(result, timeFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
fromMinute += everyMinutesInt
|
||||||
|
if fromMinute > 59 {
|
||||||
|
fromHour += fromMinute / 60
|
||||||
|
fromMinute = fromMinute % 60
|
||||||
|
}
|
||||||
|
if fromHour > toHour || (fromHour == toHour && fromMinute > toMinute) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result = append(result, fmt.Sprintf("%02d%02d", fromHour, fromMinute))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -49,7 +49,11 @@ func (this *ParentAction) ErrorText(err string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *ParentAction) NotFound(name string, itemId int64) {
|
func (this *ParentAction) NotFound(name string, itemId int64) {
|
||||||
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
|
if itemId > 0 {
|
||||||
|
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
|
||||||
|
} else {
|
||||||
|
this.ErrorPage(errors.New(name + " is not found"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
|
func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
|
||||||
@@ -126,11 +130,8 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AdminContext 获取Context
|
// AdminContext 获取Context
|
||||||
|
// 每个请求的context都必须是一个新的实例
|
||||||
func (this *ParentAction) AdminContext() context.Context {
|
func (this *ParentAction) AdminContext() context.Context {
|
||||||
if this.ctx != nil {
|
|
||||||
return this.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.rpcClient == nil {
|
if this.rpcClient == nil {
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func init() {
|
|||||||
Post("/options", new(OptionsAction)).
|
Post("/options", new(OptionsAction)).
|
||||||
|
|
||||||
// AccessKeys
|
// AccessKeys
|
||||||
Prefix("/admins/accessKeys").
|
Prefix("/admins/accesskeys").
|
||||||
Get("", new(accesskeys.IndexAction)).
|
Get("", new(accesskeys.IndexAction)).
|
||||||
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
|
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
|
||||||
Post("/delete", new(accesskeys.DeleteAction)).
|
Post("/delete", new(accesskeys.DeleteAction)).
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreatePopupAction struct {
|
type CreatePopupAction struct {
|
||||||
@@ -40,11 +42,11 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
Field("name", params.Name).
|
Field("name", params.Name).
|
||||||
Require("请输入API节点名称")
|
Require("请输入API节点名称")
|
||||||
|
|
||||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
var httpConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||||
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
|
var httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||||
|
|
||||||
// 监听地址
|
// 监听地址
|
||||||
listens := []*serverconfigs.NetworkAddressConfig{}
|
var listens = []*serverconfigs.NetworkAddressConfig{}
|
||||||
err := json.Unmarshal(params.ListensJSON, &listens)
|
err := json.Unmarshal(params.ListensJSON, &listens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
@@ -64,15 +66,19 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rest监听地址
|
// Rest监听地址
|
||||||
restHTTPConfig := &serverconfigs.HTTPProtocolConfig{}
|
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||||
restHTTPSConfig := &serverconfigs.HTTPSProtocolConfig{}
|
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||||
if params.RestIsOn {
|
if params.RestIsOn {
|
||||||
restListens := []*serverconfigs.NetworkAddressConfig{}
|
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||||
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(restListens) == 0 {
|
||||||
|
this.Fail("请至少添加一个HTTP API监听端口")
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, addr := range restListens {
|
for _, addr := range restListens {
|
||||||
if addr.Protocol.IsHTTPFamily() {
|
if addr.Protocol.IsHTTPFamily() {
|
||||||
restHTTPConfig.IsOn = true
|
restHTTPConfig.IsOn = true
|
||||||
@@ -82,10 +88,35 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否有端口冲突
|
||||||
|
var rpcAddresses = []string{}
|
||||||
|
for _, listen := range listens {
|
||||||
|
err := listen.Init()
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, listen := range restListens {
|
||||||
|
err := listen.Init()
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, address := range listen.Addresses() {
|
||||||
|
if lists.ContainsString(rpcAddresses, address) {
|
||||||
|
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突,请修改后提交")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 证书
|
// 证书
|
||||||
certIds := []int64{}
|
var certIds = []int64{}
|
||||||
if len(params.CertIdsJSON) > 0 {
|
if len(params.CertIdsJSON) > 0 {
|
||||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -97,7 +128,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
this.Fail("请添加至少一个证书")
|
this.Fail("请添加至少一个证书")
|
||||||
}
|
}
|
||||||
|
|
||||||
certRefs := []*sslconfigs.SSLCertRef{}
|
var certRefs = []*sslconfigs.SSLCertRef{}
|
||||||
for _, certId := range certIds {
|
for _, certId := range certIds {
|
||||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
@@ -131,7 +162,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 访问地址
|
// 访问地址
|
||||||
accessAddrs := []*serverconfigs.NetworkAddressConfig{}
|
var accessAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||||
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
|
err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -175,12 +177,16 @@ func (this *UpdateAction) RunPost(params struct {
|
|||||||
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
var restHTTPConfig = &serverconfigs.HTTPProtocolConfig{}
|
||||||
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
var restHTTPSConfig = &serverconfigs.HTTPSProtocolConfig{}
|
||||||
if params.RestIsOn {
|
if params.RestIsOn {
|
||||||
restListens := []*serverconfigs.NetworkAddressConfig{}
|
var restListens = []*serverconfigs.NetworkAddressConfig{}
|
||||||
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
err = json.Unmarshal(params.RestListensJSON, &restListens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(restListens) == 0 {
|
||||||
|
this.Fail("请至少添加一个HTTP API监听端口")
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, addr := range restListens {
|
for _, addr := range restListens {
|
||||||
if addr.Protocol.IsHTTPFamily() {
|
if addr.Protocol.IsHTTPFamily() {
|
||||||
restHTTPConfig.IsOn = true
|
restHTTPConfig.IsOn = true
|
||||||
@@ -190,6 +196,31 @@ func (this *UpdateAction) RunPost(params struct {
|
|||||||
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
restHTTPSConfig.Listen = append(restHTTPSConfig.Listen, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否有端口冲突
|
||||||
|
var rpcAddresses = []string{}
|
||||||
|
for _, listen := range listens {
|
||||||
|
err := listen.Init()
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rpcAddresses = append(rpcAddresses, listen.Addresses()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, listen := range restListens {
|
||||||
|
err := listen.Init()
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("校验配置失败:" + configutils.QuoteIP(listen.Host) + ":" + listen.PortRange + ": " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, address := range listen.Addresses() {
|
||||||
|
if lists.ContainsString(rpcAddresses, address) {
|
||||||
|
this.Fail("HTTP API地址 '" + address + "' 和 GRPC地址冲突,请修改后提交")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 证书
|
// 证书
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
"time"
|
"time"
|
||||||
@@ -300,6 +301,22 @@ func (this *DetailAction) RunGet(params struct {
|
|||||||
lnAddrs = []string{}
|
lnAddrs = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API节点地址
|
||||||
|
var apiNodeAddrStrings = []string{}
|
||||||
|
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||||
|
if len(node.ApiNodeAddrsJSON) > 0 {
|
||||||
|
err = json.Unmarshal(node.ApiNodeAddrsJSON, &apiNodeAddrs)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, addr := range apiNodeAddrs {
|
||||||
|
if addr.Init() == nil {
|
||||||
|
apiNodeAddrStrings = append(apiNodeAddrStrings, addr.FullAddresses()...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.Data["node"] = maps.Map{
|
this.Data["node"] = maps.Map{
|
||||||
"id": node.Id,
|
"id": node.Id,
|
||||||
"name": node.Name,
|
"name": node.Name,
|
||||||
@@ -318,6 +335,8 @@ func (this *DetailAction) RunGet(params struct {
|
|||||||
"level": node.Level,
|
"level": node.Level,
|
||||||
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
|
"levelInfo": nodeconfigs.FindNodeLevel(int(node.Level)),
|
||||||
"lnAddrs": lnAddrs,
|
"lnAddrs": lnAddrs,
|
||||||
|
"enableIPLists": node.EnableIPLists,
|
||||||
|
"apiNodeAddrs": apiNodeAddrStrings,
|
||||||
|
|
||||||
"status": maps.Map{
|
"status": maps.Map{
|
||||||
"isActive": status.IsActive,
|
"isActive": status.IsActive,
|
||||||
@@ -337,6 +356,8 @@ func (this *DetailAction) RunGet(params struct {
|
|||||||
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
|
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
|
||||||
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
|
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
|
||||||
"exePath": status.ExePath,
|
"exePath": status.ExePath,
|
||||||
|
"apiSuccessPercent": status.APISuccessPercent,
|
||||||
|
"apiAvgCostSeconds": status.APIAvgCostSeconds,
|
||||||
},
|
},
|
||||||
|
|
||||||
"group": groupMap,
|
"group": groupMap,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -57,9 +58,19 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var diskSubDirs = []*serverconfigs.CacheDir{}
|
||||||
|
if len(node.CacheDiskSubDirsJSON) > 0 {
|
||||||
|
err = json.Unmarshal(node.CacheDiskSubDirsJSON, &diskSubDirs)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var nodeMap = this.Data["node"].(maps.Map)
|
var nodeMap = this.Data["node"].(maps.Map)
|
||||||
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
|
nodeMap["maxCacheDiskCapacity"] = maxCacheDiskCapacity
|
||||||
nodeMap["cacheDiskDir"] = node.CacheDiskDir
|
nodeMap["cacheDiskDir"] = node.CacheDiskDir
|
||||||
|
nodeMap["cacheDiskSubDirs"] = diskSubDirs
|
||||||
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
|
nodeMap["maxCacheMemoryCapacity"] = maxCacheMemoryCapacity
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
@@ -69,6 +80,7 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
NodeId int64
|
NodeId int64
|
||||||
MaxCacheDiskCapacityJSON []byte
|
MaxCacheDiskCapacityJSON []byte
|
||||||
CacheDiskDir string
|
CacheDiskDir string
|
||||||
|
CacheDiskSubDirsJSON []byte
|
||||||
MaxCacheMemoryCapacityJSON []byte
|
MaxCacheMemoryCapacityJSON []byte
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
@@ -105,10 +117,20 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(params.CacheDiskSubDirsJSON) > 0 {
|
||||||
|
var cacheSubDirs = []*serverconfigs.CacheDir{}
|
||||||
|
err := json.Unmarshal(params.CacheDiskSubDirsJSON, &cacheSubDirs)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
|
_, err := this.RPC().NodeRPC().UpdateNodeCache(this.AdminContext(), &pb.UpdateNodeCacheRequest{
|
||||||
NodeId: params.NodeId,
|
NodeId: params.NodeId,
|
||||||
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
|
MaxCacheDiskCapacity: pbMaxCacheDiskCapacity,
|
||||||
CacheDiskDir: params.CacheDiskDir,
|
CacheDiskDir: params.CacheDiskDir,
|
||||||
|
CacheDiskSubDirsJSON: params.CacheDiskSubDirsJSON,
|
||||||
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
|
MaxCacheMemoryCapacity: pbMaxCacheMemoryCapacity,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
@@ -50,6 +51,22 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
this.Data["dnsResolverConfig"] = dnsResolverConfig
|
this.Data["dnsResolverConfig"] = dnsResolverConfig
|
||||||
|
|
||||||
|
// API相关
|
||||||
|
apiConfigResp, err := this.RPC().NodeRPC().FindNodeAPIConfig(this.AdminContext(), &pb.FindNodeAPIConfigRequest{NodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||||
|
if len(apiConfigResp.ApiNodeAddrsJSON) > 0 {
|
||||||
|
err = json.Unmarshal(apiConfigResp.ApiNodeAddrsJSON, &apiNodeAddrs)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Data["apiNodeAddrs"] = apiNodeAddrs
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +76,8 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
|
|
||||||
DnsResolverJSON []byte
|
DnsResolverJSON []byte
|
||||||
|
|
||||||
|
ApiNodeAddrsJSON []byte
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
@@ -68,6 +87,7 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
this.Fail("CPU线程数不能小于0")
|
this.Fail("CPU线程数不能小于0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 系统设置
|
||||||
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
|
_, err := this.RPC().NodeRPC().UpdateNodeSystem(this.AdminContext(), &pb.UpdateNodeSystemRequest{
|
||||||
NodeId: params.NodeId,
|
NodeId: params.NodeId,
|
||||||
MaxCPU: params.MaxCPU,
|
MaxCPU: params.MaxCPU,
|
||||||
@@ -77,6 +97,7 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNS解析设置
|
||||||
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
|
var dnsResolverConfig = nodeconfigs.DefaultDNSResolverConfig()
|
||||||
err = json.Unmarshal(params.DnsResolverJSON, dnsResolverConfig)
|
err = json.Unmarshal(params.DnsResolverJSON, dnsResolverConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -98,5 +119,28 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API节点设置
|
||||||
|
var apiNodeAddrs = []*serverconfigs.NetworkAddressConfig{}
|
||||||
|
if len(params.ApiNodeAddrsJSON) > 0 {
|
||||||
|
err = json.Unmarshal(params.ApiNodeAddrsJSON, &apiNodeAddrs)
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("API节点地址校验错误:" + err.Error())
|
||||||
|
}
|
||||||
|
for _, addr := range apiNodeAddrs {
|
||||||
|
err = addr.Init()
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("API节点地址校验错误:" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = this.RPC().NodeRPC().UpdateNodeAPIConfig(this.AdminContext(), &pb.UpdateNodeAPIConfigRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
ApiNodeAddrsJSON: params.ApiNodeAddrsJSON,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,14 +100,15 @@ func (this *UpdateAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var nodeMap = maps.Map{
|
var nodeMap = maps.Map{
|
||||||
"id": node.Id,
|
"id": node.Id,
|
||||||
"name": node.Name,
|
"name": node.Name,
|
||||||
"ipAddresses": ipAddressMaps,
|
"ipAddresses": ipAddressMaps,
|
||||||
"cluster": clusterMap,
|
"cluster": clusterMap,
|
||||||
"isOn": node.IsOn,
|
"isOn": node.IsOn,
|
||||||
"group": groupMap,
|
"group": groupMap,
|
||||||
"region": regionMap,
|
"region": regionMap,
|
||||||
"level": node.Level,
|
"level": node.Level,
|
||||||
|
"enableIPLists": node.EnableIPLists,
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.LnAddrs == nil {
|
if node.LnAddrs == nil {
|
||||||
@@ -157,6 +158,7 @@ func (this *UpdateAction) RunPost(params struct {
|
|||||||
IsOn bool
|
IsOn bool
|
||||||
Level int32
|
Level int32
|
||||||
LnAddrs []string
|
LnAddrs []string
|
||||||
|
EnableIPLists bool
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
}) {
|
}) {
|
||||||
@@ -234,6 +236,7 @@ func (this *UpdateAction) RunPost(params struct {
|
|||||||
IsOn: params.IsOn,
|
IsOn: params.IsOn,
|
||||||
Level: params.Level,
|
Level: params.Level,
|
||||||
LnAddrs: lnAddrs,
|
LnAddrs: lnAddrs,
|
||||||
|
EnableIPLists: params.EnableIPLists,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
HttpAllAllowNodeIP bool
|
HttpAllAllowNodeIP bool
|
||||||
HttpAllDefaultDomain string
|
HttpAllDefaultDomain string
|
||||||
|
|
||||||
|
HttpAccessLogEnableRequestHeaders bool
|
||||||
|
HttpAccessLogEnableResponseHeaders bool
|
||||||
|
HttpAccessLogCommonRequestHeadersOnly bool
|
||||||
|
HttpAccessLogEnableCookies bool
|
||||||
|
|
||||||
LogRecordServerError bool
|
LogRecordServerError bool
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
@@ -123,6 +128,11 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
|
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
|
||||||
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
|
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
|
||||||
|
|
||||||
|
config.HTTPAccessLog.EnableRequestHeaders = params.HttpAccessLogEnableRequestHeaders
|
||||||
|
config.HTTPAccessLog.EnableResponseHeaders = params.HttpAccessLogEnableResponseHeaders
|
||||||
|
config.HTTPAccessLog.CommonRequestHeadersOnly = params.HttpAccessLogCommonRequestHeadersOnly
|
||||||
|
config.HTTPAccessLog.EnableCookies = params.HttpAccessLogEnableCookies
|
||||||
|
|
||||||
config.Log.RecordServerError = params.LogRecordServerError
|
config.Log.RecordServerError = params.LogRecordServerError
|
||||||
|
|
||||||
err = config.Init()
|
err = config.Init()
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
grant := grantResp.NodeGrant
|
var grant = grantResp.NodeGrant
|
||||||
if grant != nil {
|
if grant != nil {
|
||||||
grantMap = maps.Map{
|
grantMap = maps.Map{
|
||||||
"id": grant.Id,
|
"id": grant.Id,
|
||||||
@@ -79,6 +79,16 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSH参数
|
||||||
|
var sshParams = nodeconfigs.DefaultSSHParams()
|
||||||
|
if len(cluster.SshParamsJSON) > 0 {
|
||||||
|
err = json.Unmarshal(cluster.SshParamsJSON, sshParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.Data["cluster"] = maps.Map{
|
this.Data["cluster"] = maps.Map{
|
||||||
"id": cluster.Id,
|
"id": cluster.Id,
|
||||||
"name": cluster.Name,
|
"name": cluster.Name,
|
||||||
@@ -89,6 +99,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
"clock": clockConfig,
|
"clock": clockConfig,
|
||||||
"autoRemoteStart": cluster.AutoRemoteStart,
|
"autoRemoteStart": cluster.AutoRemoteStart,
|
||||||
"autoInstallNftables": cluster.AutoInstallNftables,
|
"autoInstallNftables": cluster.AutoInstallNftables,
|
||||||
|
"sshParams": sshParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认值
|
// 默认值
|
||||||
@@ -104,12 +115,14 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
ClusterId int64
|
ClusterId int64
|
||||||
Name string
|
Name string
|
||||||
GrantId int64
|
GrantId int64
|
||||||
|
SshParamsPort int
|
||||||
InstallDir string
|
InstallDir string
|
||||||
TimeZone string
|
TimeZone string
|
||||||
NodeMaxThreads int32
|
NodeMaxThreads int32
|
||||||
AutoOpenPorts bool
|
AutoOpenPorts bool
|
||||||
ClockAutoSync bool
|
ClockAutoSync bool
|
||||||
ClockServer string
|
ClockServer string
|
||||||
|
ClockCheckChrony bool
|
||||||
AutoRemoteStart bool
|
AutoRemoteStart bool
|
||||||
AutoInstallNftables bool
|
AutoInstallNftables bool
|
||||||
|
|
||||||
@@ -129,9 +142,20 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
Lte(int64(nodeconfigs.DefaultMaxThreadsMax), "单节点最大线程数最大值不能大于"+types.String(nodeconfigs.DefaultMaxThreadsMax))
|
Lte(int64(nodeconfigs.DefaultMaxThreadsMax), "单节点最大线程数最大值不能大于"+types.String(nodeconfigs.DefaultMaxThreadsMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ssh
|
||||||
|
var sshParams = nodeconfigs.DefaultSSHParams()
|
||||||
|
sshParams.Port = params.SshParamsPort
|
||||||
|
sshParamsJSON, err := json.Marshal(sshParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// clock
|
||||||
var clockConfig = nodeconfigs.DefaultClockConfig()
|
var clockConfig = nodeconfigs.DefaultClockConfig()
|
||||||
clockConfig.AutoSync = params.ClockAutoSync
|
clockConfig.AutoSync = params.ClockAutoSync
|
||||||
clockConfig.Server = params.ClockServer
|
clockConfig.Server = params.ClockServer
|
||||||
|
clockConfig.CheckChrony = params.ClockCheckChrony
|
||||||
clockConfigJSON, err := json.Marshal(clockConfig)
|
clockConfigJSON, err := json.Marshal(clockConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
@@ -155,6 +179,7 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
ClockJSON: clockConfigJSON,
|
ClockJSON: clockConfigJSON,
|
||||||
AutoRemoteStart: params.AutoRemoteStart,
|
AutoRemoteStart: params.AutoRemoteStart,
|
||||||
AutoInstallNftables: params.AutoInstallNftables,
|
AutoInstallNftables: params.AutoInstallNftables,
|
||||||
|
SshParamsJSON: sshParamsJSON,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
|||||||
var tabbar = actionutils.NewTabbar()
|
var tabbar = actionutils.NewTabbar()
|
||||||
tabbar.Add("集群列表", "", "/clusters", "", false)
|
tabbar.Add("集群列表", "", "/clusters", "", false)
|
||||||
if teaconst.IsPlus {
|
if teaconst.IsPlus {
|
||||||
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board")
|
tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "chart line area", selectedTabbar == "board")
|
||||||
}
|
}
|
||||||
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
|
tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node")
|
||||||
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
|
tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type IndexAction struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
func (this *IndexAction) Init() {
|
||||||
this.Nav("", "", "region")
|
this.Nav("", "", "index")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
func (this *IndexAction) RunGet(params struct{}) {
|
||||||
@@ -20,7 +20,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
regionMaps := []maps.Map{}
|
var regionMaps = []maps.Map{}
|
||||||
for _, region := range regionsResp.NodeRegions {
|
for _, region := range regionsResp.NodeRegions {
|
||||||
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeRegionId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeRegionIdRequest{NodeRegionId: region.Id})
|
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeRegionId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeRegionIdRequest{NodeRegionId: region.Id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,9 +18,13 @@ func init() {
|
|||||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||||
Post("/delete", new(DeleteAction)).
|
Post("/delete", new(DeleteAction)).
|
||||||
Post("/sort", new(SortAction)).
|
Post("/sort", new(SortAction)).
|
||||||
|
Get("/nodes", new(NodesAction)).
|
||||||
|
GetPost("/updateNodeRegionPopup", new(UpdateNodeRegionPopupAction)).
|
||||||
|
|
||||||
|
//
|
||||||
GetPost("/selectPopup", new(SelectPopupAction)).
|
GetPost("/selectPopup", new(SelectPopupAction)).
|
||||||
GetPost("/prices", new(PricesAction)).
|
|
||||||
GetPost("/updatePricePopup", new(UpdatePricePopupAction)).
|
//
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
package items
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CreatePopupAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CreatePopupAction) Init() {
|
|
||||||
this.Nav("", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CreatePopupAction) RunPost(params struct {
|
|
||||||
Name string
|
|
||||||
BitsFrom int64
|
|
||||||
BitsTo int64
|
|
||||||
|
|
||||||
Must *actions.Must
|
|
||||||
CSRF *actionutils.CSRF
|
|
||||||
}) {
|
|
||||||
params.Must.
|
|
||||||
Field("name", params.Name).
|
|
||||||
Require("请输入名称").
|
|
||||||
Field("bitsFrom", params.BitsFrom).
|
|
||||||
Gte(0, "请输入不小于0的整数").
|
|
||||||
Field("bitsTo", params.BitsTo).
|
|
||||||
Gte(0, "请输入不小于0的整数")
|
|
||||||
|
|
||||||
createResp, err := this.RPC().NodePriceItemRPC().CreateNodePriceItem(this.AdminContext(), &pb.CreateNodePriceItemRequest{
|
|
||||||
Name: params.Name,
|
|
||||||
Type: regionutils.PriceTypeTraffic,
|
|
||||||
BitsFrom: params.BitsFrom * 1000 * 1000,
|
|
||||||
BitsTo: params.BitsTo * 1000 * 1000,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer this.CreateLogInfo("创建流量价格项目", createResp.NodePriceItemId)
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package items
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DeleteAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *DeleteAction) RunPost(params struct {
|
|
||||||
ItemId int64
|
|
||||||
}) {
|
|
||||||
defer this.CreateLogInfo("删除流量价格项目 %d", params.ItemId)
|
|
||||||
|
|
||||||
_, err := this.RPC().NodePriceItemRPC().DeleteNodePriceItem(this.AdminContext(), &pb.DeleteNodePriceItemRequest{NodePriceItemId: params.ItemId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package items
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
|
||||||
"github.com/iwind/TeaGo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
|
||||||
server.
|
|
||||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
|
||||||
Data("teaMenu", "clusters").
|
|
||||||
Data("teaSubMenu", "region").
|
|
||||||
Prefix("/clusters/regions/items").
|
|
||||||
GetPost("/createPopup", new(CreatePopupAction)).
|
|
||||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
|
||||||
Post("/delete", new(DeleteAction)).
|
|
||||||
EndAll()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package items
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UpdatePopupAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UpdatePopupAction) Init() {
|
|
||||||
this.Nav("", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UpdatePopupAction) RunGet(params struct {
|
|
||||||
ItemId int64
|
|
||||||
}) {
|
|
||||||
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
item := itemResp.NodePriceItem
|
|
||||||
if item == nil {
|
|
||||||
this.NotFound("nodePriceItem", params.ItemId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Data["item"] = maps.Map{
|
|
||||||
"id": item.Id,
|
|
||||||
"name": item.Name,
|
|
||||||
"bitsFrom": item.BitsFrom,
|
|
||||||
"bitsTo": item.BitsTo,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UpdatePopupAction) RunPost(params struct {
|
|
||||||
ItemId int64
|
|
||||||
Name string
|
|
||||||
BitsFrom int64
|
|
||||||
BitsTo int64
|
|
||||||
|
|
||||||
Must *actions.Must
|
|
||||||
CSRF *actionutils.CSRF
|
|
||||||
}) {
|
|
||||||
defer this.CreateLogInfo("修改流量价格项目 %d", params.ItemId)
|
|
||||||
|
|
||||||
params.Must.
|
|
||||||
Field("name", params.Name).
|
|
||||||
Require("请输入名称").
|
|
||||||
Field("bitsFrom", params.BitsFrom).
|
|
||||||
Gte(0, "请输入不小于0的整数").
|
|
||||||
Field("bitsTo", params.BitsTo).
|
|
||||||
Gte(0, "请输入不小于0的整数")
|
|
||||||
|
|
||||||
_, err := this.RPC().NodePriceItemRPC().UpdateNodePriceItem(this.AdminContext(), &pb.UpdateNodePriceItemRequest{
|
|
||||||
NodePriceItemId: params.ItemId,
|
|
||||||
Name: params.Name,
|
|
||||||
BitsFrom: params.BitsFrom * 1000 * 1000,
|
|
||||||
BitsTo: params.BitsTo * 1000 * 1000,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
92
internal/web/actions/default/clusters/regions/nodes.go
Normal file
92
internal/web/actions/default/clusters/regions/nodes.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package regions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NodesAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *NodesAction) Init() {
|
||||||
|
this.Nav("", "", "node")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *NodesAction) RunGet(params struct {
|
||||||
|
RegionId int64
|
||||||
|
}) {
|
||||||
|
this.Data["regionId"] = params.RegionId
|
||||||
|
|
||||||
|
// 所有区域
|
||||||
|
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var regionMaps = []maps.Map{}
|
||||||
|
for _, region := range regionsResp.NodeRegions {
|
||||||
|
regionMaps = append(regionMaps, maps.Map{
|
||||||
|
"id": region.Id,
|
||||||
|
"name": region.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["regions"] = regionMaps
|
||||||
|
|
||||||
|
// 节点数量
|
||||||
|
countResp, err := this.RPC().NodeRPC().CountAllNodeRegionInfo(this.AdminContext(), &pb.CountAllNodeRegionInfoRequest{NodeRegionId: params.RegionId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var page = this.NewPage(countResp.Count)
|
||||||
|
this.Data["page"] = page.AsHTML()
|
||||||
|
|
||||||
|
// 节点列表
|
||||||
|
var hasNodesWithoutRegion = false
|
||||||
|
nodesResp, err := this.RPC().NodeRPC().ListNodeRegionInfo(this.AdminContext(), &pb.ListNodeRegionInfoRequest{
|
||||||
|
NodeRegionId: params.RegionId,
|
||||||
|
Offset: page.Offset,
|
||||||
|
Size: page.Size,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var nodeMaps = []maps.Map{}
|
||||||
|
for _, node := range nodesResp.InfoList {
|
||||||
|
// region
|
||||||
|
var regionMap maps.Map
|
||||||
|
if node.NodeRegion != nil {
|
||||||
|
regionMap = maps.Map{
|
||||||
|
"id": node.NodeRegion.Id,
|
||||||
|
"name": node.NodeRegion.Name,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasNodesWithoutRegion = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// cluster
|
||||||
|
var clusterMap maps.Map
|
||||||
|
if node.NodeCluster != nil {
|
||||||
|
clusterMap = maps.Map{
|
||||||
|
"id": node.NodeCluster.Id,
|
||||||
|
"name": node.NodeCluster.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMaps = append(nodeMaps, maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
"region": regionMap,
|
||||||
|
"cluster": clusterMap,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["nodes"] = nodeMaps
|
||||||
|
this.Data["hasNodesWithoutRegion"] = hasNodesWithoutRegion
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package regions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/regionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PricesAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *PricesAction) Init() {
|
|
||||||
this.Nav("", "", "price")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *PricesAction) RunGet(params struct{}) {
|
|
||||||
// 所有价格项目
|
|
||||||
itemsResp, err := this.RPC().NodePriceItemRPC().FindAllAvailableNodePriceItems(this.AdminContext(), &pb.FindAllAvailableNodePriceItemsRequest{Type: regionutils.PriceTypeTraffic})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
itemMaps := []maps.Map{}
|
|
||||||
for _, item := range itemsResp.NodePriceItems {
|
|
||||||
|
|
||||||
itemMaps = append(itemMaps, maps.Map{
|
|
||||||
"id": item.Id,
|
|
||||||
"name": item.Name,
|
|
||||||
"bitsFromString": this.formatBits(item.BitsFrom),
|
|
||||||
"bitsToString": this.formatBits(item.BitsTo),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.Data["items"] = itemMaps
|
|
||||||
|
|
||||||
// 所有区域
|
|
||||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledNodeRegions(this.AdminContext(), &pb.FindAllEnabledNodeRegionsRequest{})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
regionMaps := []maps.Map{}
|
|
||||||
for _, region := range regionsResp.NodeRegions {
|
|
||||||
pricesMap := map[string]float32{}
|
|
||||||
if len(region.PricesJSON) > 0 {
|
|
||||||
err = json.Unmarshal(region.PricesJSON, &pricesMap)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
regionMaps = append(regionMaps, maps.Map{
|
|
||||||
"id": region.Id,
|
|
||||||
"isOn": region.IsOn,
|
|
||||||
"name": region.Name,
|
|
||||||
"prices": pricesMap,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.Data["regions"] = regionMaps
|
|
||||||
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *PricesAction) formatBits(bits int64) string {
|
|
||||||
sizeHuman := ""
|
|
||||||
if bits < 1000 {
|
|
||||||
sizeHuman = numberutils.FormatInt64(bits) + "BPS"
|
|
||||||
} else if bits < 1_000_000 {
|
|
||||||
sizeHuman = fmt.Sprintf("%.2fKBPS", float64(bits)/1000)
|
|
||||||
} else if bits < 1_000_000_000 {
|
|
||||||
sizeHuman = fmt.Sprintf("%.2fMBPS", float64(bits)/1000/1000)
|
|
||||||
} else if bits < 1_000_000_000_000 {
|
|
||||||
sizeHuman = fmt.Sprintf("%.2fGBPS", float64(bits)/1000/1000/1000)
|
|
||||||
} else if bits < 1_000_000_000_000_000 {
|
|
||||||
sizeHuman = fmt.Sprintf("%.2fTBPS", float64(bits)/1000/1000/1000/1000)
|
|
||||||
} else if bits < 1_000_000_000_000_000_000 {
|
|
||||||
sizeHuman = fmt.Sprintf("%.2fPBPS", float64(bits)/1000/1000/1000/1000/1000)
|
|
||||||
} else {
|
|
||||||
sizeHuman = fmt.Sprintf("%.2fEBPS", float64(bits)/1000/1000/1000/1000/1000/1000)
|
|
||||||
}
|
|
||||||
return sizeHuman
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package regionutils
|
|
||||||
|
|
||||||
const (
|
|
||||||
PriceTypeTraffic = "traffic"
|
|
||||||
)
|
|
||||||
@@ -40,12 +40,18 @@ func (this *SelectPopupAction) RunPost(params struct {
|
|||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
|
if params.RegionId <= 0 {
|
||||||
|
this.Data["region"] = nil
|
||||||
|
this.Success()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
region := regionResp.NodeRegion
|
var region = regionResp.NodeRegion
|
||||||
if region == nil {
|
if region == nil {
|
||||||
this.NotFound("nodeRegion", params.RegionId)
|
this.NotFound("nodeRegion", params.RegionId)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package regions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateNodeRegionPopupAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateNodeRegionPopupAction) Init() {
|
||||||
|
this.Nav("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateNodeRegionPopupAction) RunGet(params struct {
|
||||||
|
NodeId int64
|
||||||
|
RegionId int64
|
||||||
|
}) {
|
||||||
|
// node
|
||||||
|
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var node = nodeResp.Node
|
||||||
|
if node == nil {
|
||||||
|
this.NotFound("node", params.NodeId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["node"] = maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// region
|
||||||
|
this.Data["region"] = maps.Map{
|
||||||
|
"id": 0,
|
||||||
|
"name": "",
|
||||||
|
}
|
||||||
|
if params.RegionId > 0 {
|
||||||
|
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var region = regionResp.NodeRegion
|
||||||
|
if region != nil {
|
||||||
|
this.Data["region"] = maps.Map{
|
||||||
|
"id": region.Id,
|
||||||
|
"name": region.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all regions
|
||||||
|
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.AdminContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var regionMaps = []maps.Map{}
|
||||||
|
for _, region := range regionsResp.NodeRegions {
|
||||||
|
regionMaps = append(regionMaps, maps.Map{
|
||||||
|
"id": region.Id,
|
||||||
|
"name": region.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["regions"] = regionMaps
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateNodeRegionPopupAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
RegionId int64
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
CSRF *actionutils.CSRF
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("修改节点 %d 区域到 %d", params.RegionId)
|
||||||
|
|
||||||
|
_, err := this.RPC().NodeRPC().UpdateNodeRegionInfo(this.AdminContext(), &pb.UpdateNodeRegionInfoRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
NodeRegionId: params.RegionId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package regions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UpdatePricePopupAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UpdatePricePopupAction) Init() {
|
|
||||||
this.Nav("", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UpdatePricePopupAction) RunGet(params struct {
|
|
||||||
RegionId int64
|
|
||||||
ItemId int64
|
|
||||||
}) {
|
|
||||||
// 区域
|
|
||||||
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.AdminContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
region := regionResp.NodeRegion
|
|
||||||
if region == nil {
|
|
||||||
this.NotFound("nodeRegion", params.RegionId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.Data["region"] = maps.Map{
|
|
||||||
"id": region.Id,
|
|
||||||
"isOn": region.IsOn,
|
|
||||||
"name": region.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前价格
|
|
||||||
pricesMap := map[string]float32{}
|
|
||||||
if len(region.PricesJSON) > 0 {
|
|
||||||
err = json.Unmarshal(region.PricesJSON, &pricesMap)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.Data["price"] = pricesMap[numberutils.FormatInt64(params.ItemId)]
|
|
||||||
|
|
||||||
// 价格项
|
|
||||||
itemResp, err := this.RPC().NodePriceItemRPC().FindEnabledNodePriceItem(this.AdminContext(), &pb.FindEnabledNodePriceItemRequest{NodePriceItemId: params.ItemId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
item := itemResp.NodePriceItem
|
|
||||||
if item == nil {
|
|
||||||
this.NotFound("nodePriceItem", params.ItemId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Data["item"] = maps.Map{
|
|
||||||
"id": item.Id,
|
|
||||||
"name": item.Name,
|
|
||||||
"bitsFrom": item.BitsFrom,
|
|
||||||
"bitsTo": item.BitsTo,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UpdatePricePopupAction) RunPost(params struct {
|
|
||||||
RegionId int64
|
|
||||||
ItemId int64
|
|
||||||
Price float32
|
|
||||||
|
|
||||||
Must *actions.Must
|
|
||||||
CSRF *actionutils.CSRF
|
|
||||||
}) {
|
|
||||||
defer this.CreateLogInfo("修改区域 %d-价格项 %d 的价格", params.RegionId, params.ItemId)
|
|
||||||
|
|
||||||
_, err := this.RPC().NodeRegionRPC().UpdateNodeRegionPrice(this.AdminContext(), &pb.UpdateNodeRegionPriceRequest{
|
|
||||||
NodeRegionId: params.RegionId,
|
|
||||||
NodeItemId: params.ItemId,
|
|
||||||
Price: params.Price,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -76,16 +76,14 @@ func (this *IndexAction) RunPost(params struct{}) {
|
|||||||
this.Data["dashboard"] = maps.Map{
|
this.Data["dashboard"] = maps.Map{
|
||||||
"defaultClusterId": resp.DefaultNodeClusterId,
|
"defaultClusterId": resp.DefaultNodeClusterId,
|
||||||
|
|
||||||
"countServers": resp.CountServers,
|
"countServers": resp.CountServers,
|
||||||
"countNodeClusters": resp.CountNodeClusters,
|
"countNodeClusters": resp.CountNodeClusters,
|
||||||
"countNodes": resp.CountNodes,
|
"countNodes": resp.CountNodes,
|
||||||
"countOfflineNodes": resp.CountOfflineNodes,
|
"countOfflineNodes": resp.CountOfflineNodes,
|
||||||
"countUsers": resp.CountUsers,
|
"countUsers": resp.CountUsers,
|
||||||
"countAPINodes": resp.CountAPINodes,
|
"countAPINodes": resp.CountAPINodes,
|
||||||
"countOfflineAPINodes": resp.CountOfflineAPINodes,
|
"countOfflineAPINodes": resp.CountOfflineAPINodes,
|
||||||
"countDBNodes": resp.CountDBNodes,
|
"countDBNodes": resp.CountDBNodes,
|
||||||
"countUserNodes": resp.CountUserNodes,
|
|
||||||
"countOfflineUserNodes": resp.CountOfflineUserNodes,
|
|
||||||
|
|
||||||
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
|
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
|
||||||
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
|
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
var defaultRoute = dnsResp.DefaultRoute
|
var defaultRoute = dnsResp.DefaultRoute
|
||||||
domainName := ""
|
domainName := ""
|
||||||
dnsMap := maps.Map{
|
var dnsMap = maps.Map{
|
||||||
"dnsName": dnsResp.Name,
|
"dnsName": dnsResp.Name,
|
||||||
"domainId": 0,
|
"domainId": 0,
|
||||||
"domainName": "",
|
"domainName": "",
|
||||||
@@ -76,14 +76,14 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nodeMaps := []maps.Map{}
|
var nodeMaps = []maps.Map{}
|
||||||
for _, node := range nodesResp.Nodes {
|
for _, node := range nodesResp.Nodes {
|
||||||
if len(node.Routes) > 0 {
|
if len(node.Routes) > 0 {
|
||||||
for _, route := range node.Routes {
|
for _, route := range node.Routes {
|
||||||
// 检查是否已解析
|
// 检查是否已解析
|
||||||
isResolved := false
|
var isResolved = false
|
||||||
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
if cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
|
||||||
recordType := "A"
|
var recordType = "A"
|
||||||
if utils.IsIPv6(node.IpAddr) {
|
if utils.IsIPv6(node.IpAddr) {
|
||||||
recordType = "AAAA"
|
recordType = "AAAA"
|
||||||
}
|
}
|
||||||
@@ -102,9 +102,10 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodeMaps = append(nodeMaps, maps.Map{
|
nodeMaps = append(nodeMaps, maps.Map{
|
||||||
"id": node.Id,
|
"id": node.Id,
|
||||||
"name": node.Name,
|
"name": node.Name,
|
||||||
"ipAddr": node.IpAddr,
|
"ipAddr": node.IpAddr,
|
||||||
|
"ipAddrId": node.NodeIPAddressId,
|
||||||
"route": maps.Map{
|
"route": maps.Map{
|
||||||
"name": route.Name,
|
"name": route.Name,
|
||||||
"code": route.Code,
|
"code": route.Code,
|
||||||
@@ -117,7 +118,7 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
// 默认线路
|
// 默认线路
|
||||||
var isResolved = false
|
var isResolved = false
|
||||||
if len(defaultRoute) > 0 {
|
if len(defaultRoute) > 0 {
|
||||||
recordType := "A"
|
var recordType = "A"
|
||||||
if utils.IsIPv6(node.IpAddr) {
|
if utils.IsIPv6(node.IpAddr) {
|
||||||
recordType = "AAAA"
|
recordType = "AAAA"
|
||||||
}
|
}
|
||||||
@@ -135,9 +136,10 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
isResolved = checkResp.IsOk
|
isResolved = checkResp.IsOk
|
||||||
}
|
}
|
||||||
nodeMaps = append(nodeMaps, maps.Map{
|
nodeMaps = append(nodeMaps, maps.Map{
|
||||||
"id": node.Id,
|
"id": node.Id,
|
||||||
"name": node.Name,
|
"name": node.Name,
|
||||||
"ipAddr": node.IpAddr,
|
"ipAddr": node.IpAddr,
|
||||||
|
"ipAddrId": node.NodeIPAddressId,
|
||||||
"route": maps.Map{
|
"route": maps.Map{
|
||||||
"name": "",
|
"name": "",
|
||||||
"code": "",
|
"code": "",
|
||||||
@@ -155,7 +157,7 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serverMaps := []maps.Map{}
|
var serverMaps = []maps.Map{}
|
||||||
for _, server := range serversResp.Servers {
|
for _, server := range serversResp.Servers {
|
||||||
// 检查是否已解析
|
// 检查是否已解析
|
||||||
isResolved := false
|
isResolved := false
|
||||||
@@ -198,7 +200,7 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
issueMaps := []maps.Map{}
|
var issueMaps = []maps.Map{}
|
||||||
for _, issue := range issuesResp.Issues {
|
for _, issue := range issuesResp.Issues {
|
||||||
issueMaps = append(issueMaps, maps.Map{
|
issueMaps = append(issueMaps, maps.Map{
|
||||||
"target": issue.Target,
|
"target": issue.Target,
|
||||||
@@ -218,7 +220,7 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
taskMaps := []maps.Map{}
|
var taskMaps = []maps.Map{}
|
||||||
for _, task := range resp.DnsTasks {
|
for _, task := range resp.DnsTasks {
|
||||||
var clusterMap maps.Map = nil
|
var clusterMap maps.Map = nil
|
||||||
var nodeMap maps.Map = nil
|
var nodeMap maps.Map = nil
|
||||||
|
|||||||
@@ -71,7 +71,10 @@ func ValidateRecordName(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pieces := strings.Split(name, ".")
|
pieces := strings.Split(name, ".")
|
||||||
for _, piece := range pieces {
|
for index, piece := range pieces {
|
||||||
|
if index == 0 && piece == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if piece == "-" ||
|
if piece == "-" ||
|
||||||
strings.HasPrefix(piece, "-") ||
|
strings.HasPrefix(piece, "-") ||
|
||||||
strings.HasSuffix(piece, "-") ||
|
strings.HasSuffix(piece, "-") ||
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ func (this *UpdateNodePopupAction) Init() {
|
|||||||
func (this *UpdateNodePopupAction) RunGet(params struct {
|
func (this *UpdateNodePopupAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
NodeId int64
|
NodeId int64
|
||||||
|
IpAddrId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["nodeId"] = params.NodeId
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
|
||||||
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
|
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
|
||||||
NodeId: params.NodeId,
|
NodeId: params.NodeId,
|
||||||
NodeClusterId: params.ClusterId,
|
NodeClusterId: params.ClusterId,
|
||||||
|
NodeIPAddrId: params.IpAddrId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
@@ -39,12 +41,13 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.Data["ipAddr"] = dnsInfo.IpAddr
|
this.Data["ipAddr"] = dnsInfo.IpAddr
|
||||||
|
this.Data["ipAddrId"] = dnsInfo.NodeIPAddressId
|
||||||
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
|
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
|
||||||
this.Data["domainId"] = dnsInfo.DnsDomainId
|
this.Data["domainId"] = dnsInfo.DnsDomainId
|
||||||
this.Data["domainName"] = dnsInfo.DnsDomainName
|
this.Data["domainName"] = dnsInfo.DnsDomainName
|
||||||
|
|
||||||
// 读取所有线路
|
// 读取所有线路
|
||||||
allRouteMaps := []maps.Map{}
|
var allRouteMaps = []maps.Map{}
|
||||||
if dnsInfo.DnsDomainId > 0 {
|
if dnsInfo.DnsDomainId > 0 {
|
||||||
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
|
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,6 +78,7 @@ func (this *UpdateNodePopupAction) RunGet(params struct {
|
|||||||
func (this *UpdateNodePopupAction) RunPost(params struct {
|
func (this *UpdateNodePopupAction) RunPost(params struct {
|
||||||
NodeId int64
|
NodeId int64
|
||||||
IpAddr string
|
IpAddr string
|
||||||
|
IpAddrId int64
|
||||||
DomainId int64
|
DomainId int64
|
||||||
DnsRoutesJSON []byte
|
DnsRoutesJSON []byte
|
||||||
|
|
||||||
@@ -84,7 +88,7 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
|
|||||||
// 操作日志
|
// 操作日志
|
||||||
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
|
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
|
||||||
|
|
||||||
routes := []string{}
|
var routes = []string{}
|
||||||
if len(params.DnsRoutesJSON) > 0 {
|
if len(params.DnsRoutesJSON) > 0 {
|
||||||
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
|
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,10 +107,11 @@ func (this *UpdateNodePopupAction) RunPost(params struct {
|
|||||||
|
|
||||||
// 执行修改
|
// 执行修改
|
||||||
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
|
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
|
||||||
NodeId: params.NodeId,
|
NodeId: params.NodeId,
|
||||||
IpAddr: params.IpAddr,
|
IpAddr: params.IpAddr,
|
||||||
DnsDomainId: params.DomainId,
|
NodeIPAddressId: params.IpAddrId,
|
||||||
Routes: routes,
|
DnsDomainId: params.DomainId,
|
||||||
|
Routes: routes,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
var providerMaps = []maps.Map{}
|
var providerMaps = []maps.Map{}
|
||||||
for _, provider := range providersResp.DnsProviders {
|
for _, provider := range providersResp.DnsProviders {
|
||||||
dataUpdatedTime := ""
|
var dataUpdatedTime = ""
|
||||||
if provider.DataUpdatedAt > 0 {
|
if provider.DataUpdatedAt > 0 {
|
||||||
dataUpdatedTime = timeutil.FormatTime("Y-m-d H:i:s", provider.DataUpdatedAt)
|
dataUpdatedTime = timeutil.FormatTime("Y-m-d H:i:s", provider.DataUpdatedAt)
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
countDomains := countDomainsResp.Count
|
var countDomains = countDomainsResp.Count
|
||||||
|
|
||||||
providerMaps = append(providerMaps, maps.Map{
|
providerMaps = append(providerMaps, maps.Map{
|
||||||
"id": provider.Id,
|
"id": provider.Id,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package acme
|
package acme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
@@ -25,7 +26,7 @@ func (this *UpdateTaskPopupAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
task := taskResp.AcmeTask
|
var task = taskResp.AcmeTask
|
||||||
if task == nil {
|
if task == nil {
|
||||||
this.NotFound("acmeTask", params.TaskId)
|
this.NotFound("acmeTask", params.TaskId)
|
||||||
return
|
return
|
||||||
@@ -74,7 +75,7 @@ func (this *UpdateTaskPopupAction) RunGet(params struct {
|
|||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
providerMaps := []maps.Map{}
|
var providerMaps = []maps.Map{}
|
||||||
for _, provider := range providersResp.DnsProviders {
|
for _, provider := range providersResp.DnsProviders {
|
||||||
providerMaps = append(providerMaps, maps.Map{
|
providerMaps = append(providerMaps, maps.Map{
|
||||||
"id": provider.Id,
|
"id": provider.Id,
|
||||||
@@ -93,7 +94,7 @@ func (this *UpdateTaskPopupAction) RunPost(params struct {
|
|||||||
AcmeUserId int64
|
AcmeUserId int64
|
||||||
DnsProviderId int64
|
DnsProviderId int64
|
||||||
DnsDomain string
|
DnsDomain string
|
||||||
Domains []string
|
DomainsJSON []byte
|
||||||
AutoRenew bool
|
AutoRenew bool
|
||||||
AuthURL string
|
AuthURL string
|
||||||
|
|
||||||
@@ -123,11 +124,20 @@ func (this *UpdateTaskPopupAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(params.Domains) == 0 {
|
var domains = []string{}
|
||||||
|
if len(params.DomainsJSON) > 0 {
|
||||||
|
err := json.Unmarshal(params.DomainsJSON, &domains)
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("解析域名数据失败:" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(domains) == 0 {
|
||||||
this.Fail("请输入证书域名列表")
|
this.Fail("请输入证书域名列表")
|
||||||
}
|
}
|
||||||
realDomains := []string{}
|
var realDomains = []string{}
|
||||||
for _, domain := range params.Domains {
|
for _, domain := range domains {
|
||||||
domain = strings.ToLower(domain)
|
domain = strings.ToLower(domain)
|
||||||
if params.AuthType == "dns" {
|
if params.AuthType == "dns" {
|
||||||
if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {
|
if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ func (this *CreatePopupAction) Init() {
|
|||||||
this.Nav("", "", "")
|
this.Nav("", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
func (this *CreatePopupAction) RunGet(params struct {
|
||||||
|
ProviderCode string
|
||||||
|
}) {
|
||||||
|
this.Data["providerCode"] = params.ProviderCode
|
||||||
|
|
||||||
// 服务商
|
// 服务商
|
||||||
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
|
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ func (this *SelectPopupAction) RunGet(params struct {
|
|||||||
// TODO 列出常用和最新的证书供用户选择
|
// TODO 列出常用和最新的证书供用户选择
|
||||||
|
|
||||||
this.Data["keyword"] = params.Keyword
|
this.Data["keyword"] = params.Keyword
|
||||||
|
this.Data["selectedCertIds"] = params.SelectedCertIds
|
||||||
|
|
||||||
// 已经选择的证书
|
// 已经选择的证书
|
||||||
selectedCertIds := []string{}
|
var selectedCertIds = []string{}
|
||||||
if len(params.SelectedCertIds) > 0 {
|
if len(params.SelectedCertIds) > 0 {
|
||||||
selectedCertIds = strings.Split(params.SelectedCertIds, ",")
|
selectedCertIds = strings.Split(params.SelectedCertIds, ",")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ func (this *CreateRulePopupAction) RunGet(params struct {
|
|||||||
"params": checkpoint.Params,
|
"params": checkpoint.Params,
|
||||||
"options": checkpoint.Options,
|
"options": checkpoint.Options,
|
||||||
"isComposed": checkpoint.IsComposed,
|
"isComposed": checkpoint.IsComposed,
|
||||||
|
"dataType": checkpoint.DataType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
internal/web/actions/default/servers/headers/options.go
Normal file
26
internal/web/actions/default/servers/headers/options.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package headers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OptionsAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *OptionsAction) RunPost(params struct {
|
||||||
|
Type string
|
||||||
|
}) {
|
||||||
|
if params.Type == "request" {
|
||||||
|
this.Data["headers"] = serverconfigs.AllHTTPCommonRequestHeaders
|
||||||
|
} else if params.Type == "response" {
|
||||||
|
this.Data["headers"] = serverconfigs.AllHTTPCommonResponseHeaders
|
||||||
|
} else {
|
||||||
|
this.Data["headers"] = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package servers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
@@ -90,15 +89,16 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
|
|
||||||
// 服务列表
|
// 服务列表
|
||||||
serversResp, err := this.RPC().ServerRPC().ListEnabledServersMatch(this.AdminContext(), &pb.ListEnabledServersMatchRequest{
|
serversResp, err := this.RPC().ServerRPC().ListEnabledServersMatch(this.AdminContext(), &pb.ListEnabledServersMatchRequest{
|
||||||
Offset: page.Offset,
|
Offset: page.Offset,
|
||||||
Size: page.Size,
|
Size: page.Size,
|
||||||
NodeClusterId: params.ClusterId,
|
NodeClusterId: params.ClusterId,
|
||||||
ServerGroupId: params.GroupId,
|
ServerGroupId: params.GroupId,
|
||||||
Keyword: params.Keyword,
|
Keyword: params.Keyword,
|
||||||
AuditingFlag: params.AuditingFlag,
|
AuditingFlag: params.AuditingFlag,
|
||||||
TrafficOutDesc: params.TrafficOutOrder == "desc",
|
TrafficOutDesc: params.TrafficOutOrder == "desc",
|
||||||
TrafficOutAsc: params.TrafficOutOrder == "asc",
|
TrafficOutAsc: params.TrafficOutOrder == "asc",
|
||||||
UserId: params.UserId,
|
UserId: params.UserId,
|
||||||
|
IgnoreServerNames: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
@@ -176,27 +176,35 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 域名列表
|
// 域名列表
|
||||||
var serverNames = []*serverconfigs.ServerNameConfig{}
|
|
||||||
if server.IsAuditing || (server.AuditingResult != nil && !server.AuditingResult.IsOk) {
|
if server.IsAuditing || (server.AuditingResult != nil && !server.AuditingResult.IsOk) {
|
||||||
server.ServerNamesJSON = server.AuditingServerNamesJSON
|
server.ServerNamesJSON = server.AuditingServerNamesJSON
|
||||||
|
|
||||||
|
if len(config.ServerNames) == 0 {
|
||||||
|
// 审核中的域名
|
||||||
|
if len(server.ServerNamesJSON) > 0 {
|
||||||
|
var serverNames = []*serverconfigs.ServerNameConfig{}
|
||||||
|
err = json.Unmarshal(server.ServerNamesJSON, &serverNames)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.ServerNames = serverNames
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var auditingIsOk = true
|
var auditingIsOk = true
|
||||||
if !server.IsAuditing && server.AuditingResult != nil && !server.AuditingResult.IsOk {
|
if !server.IsAuditing && server.AuditingResult != nil && !server.AuditingResult.IsOk {
|
||||||
auditingIsOk = false
|
auditingIsOk = false
|
||||||
}
|
}
|
||||||
if len(server.ServerNamesJSON) > 0 {
|
var firstServerName = ""
|
||||||
err = json.Unmarshal(server.ServerNamesJSON, &serverNames)
|
for _, serverNameConfig := range config.ServerNames {
|
||||||
if err != nil {
|
if len(serverNameConfig.Name) > 0 {
|
||||||
this.ErrorPage(err)
|
firstServerName = serverNameConfig.Name
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
}
|
if len(serverNameConfig.SubNames) > 0 {
|
||||||
var countServerNames = 0
|
firstServerName = serverNameConfig.SubNames[0]
|
||||||
for _, serverName := range serverNames {
|
break
|
||||||
if len(serverName.SubNames) == 0 {
|
|
||||||
countServerNames++
|
|
||||||
} else {
|
|
||||||
countServerNames += len(serverName.SubNames)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,9 +224,9 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 统计数据
|
// 统计数据
|
||||||
var bandwidth = ""
|
var bandwidthBits int64 = 0
|
||||||
if server.BandwidthBytes > 0 {
|
if server.BandwidthBytes > 0 {
|
||||||
bandwidth = numberutils.FormatBytes(server.BandwidthBytes)
|
bandwidthBits = server.BandwidthBytes * 8
|
||||||
}
|
}
|
||||||
|
|
||||||
serverMaps = append(serverMaps, maps.Map{
|
serverMaps = append(serverMaps, maps.Map{
|
||||||
@@ -232,13 +240,13 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
"ports": portMaps,
|
"ports": portMaps,
|
||||||
"serverTypeName": serverconfigs.FindServerType(server.Type).GetString("name"),
|
"serverTypeName": serverconfigs.FindServerType(server.Type).GetString("name"),
|
||||||
"groups": groupMaps,
|
"groups": groupMaps,
|
||||||
"serverNames": serverNames,
|
"firstServerName": firstServerName,
|
||||||
"countServerNames": countServerNames,
|
"countServerNames": server.CountServerNames,
|
||||||
"isAuditing": server.IsAuditing,
|
"isAuditing": server.IsAuditing,
|
||||||
"auditingIsOk": auditingIsOk,
|
"auditingIsOk": auditingIsOk,
|
||||||
"user": userMap,
|
"user": userMap,
|
||||||
"auditingTime": auditingTime,
|
"auditingTime": auditingTime,
|
||||||
"bandwidth": bandwidth,
|
"bandwidthBits": bandwidthBits,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.Data["servers"] = serverMaps
|
this.Data["servers"] = serverMaps
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package servers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/headers"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/users"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/users"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||||
"github.com/iwind/TeaGo"
|
"github.com/iwind/TeaGo"
|
||||||
@@ -26,10 +27,13 @@ func init() {
|
|||||||
Get("/serverNamesPopup", new(ServerNamesPopupAction)).
|
Get("/serverNamesPopup", new(ServerNamesPopupAction)).
|
||||||
Post("/status", new(StatusAction)).
|
Post("/status", new(StatusAction)).
|
||||||
|
|
||||||
//
|
// user
|
||||||
Post("/users/options", new(users.OptionsAction)).
|
Post("/users/options", new(users.OptionsAction)).
|
||||||
Post("/users/plans", new(users.PlansAction)).
|
Post("/users/plans", new(users.PlansAction)).
|
||||||
|
|
||||||
|
// header
|
||||||
|
Post("/headers/options", new(headers.OptionsAction)).
|
||||||
|
|
||||||
//
|
//
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -21,21 +21,21 @@ func (this *IndexAction) Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct {
|
func (this *IndexAction) RunGet(params struct {
|
||||||
Ip string
|
Keyword string
|
||||||
GlobalOnly bool
|
GlobalOnly bool
|
||||||
Unread bool
|
Unread bool
|
||||||
EventLevel string
|
EventLevel string
|
||||||
ListType string
|
ListType string
|
||||||
}) {
|
}) {
|
||||||
this.Data["type"] = ""
|
this.Data["type"] = ""
|
||||||
this.Data["ip"] = params.Ip
|
this.Data["keyword"] = params.Keyword
|
||||||
this.Data["globalOnly"] = params.GlobalOnly
|
this.Data["globalOnly"] = params.GlobalOnly
|
||||||
this.Data["unread"] = params.Unread
|
this.Data["unread"] = params.Unread
|
||||||
this.Data["eventLevel"] = params.EventLevel
|
this.Data["eventLevel"] = params.EventLevel
|
||||||
this.Data["listType"] = params.ListType
|
this.Data["listType"] = params.ListType
|
||||||
|
|
||||||
countUnreadResp, err := this.RPC().IPItemRPC().CountAllEnabledIPItems(this.AdminContext(), &pb.CountAllEnabledIPItemsRequest{
|
countUnreadResp, err := this.RPC().IPItemRPC().CountAllEnabledIPItems(this.AdminContext(), &pb.CountAllEnabledIPItemsRequest{
|
||||||
Ip: params.Ip,
|
Keyword: params.Keyword,
|
||||||
GlobalOnly: params.GlobalOnly,
|
GlobalOnly: params.GlobalOnly,
|
||||||
Unread: true,
|
Unread: true,
|
||||||
EventLevel: params.EventLevel,
|
EventLevel: params.EventLevel,
|
||||||
@@ -48,7 +48,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
this.Data["countUnread"] = countUnreadResp.Count
|
this.Data["countUnread"] = countUnreadResp.Count
|
||||||
|
|
||||||
countResp, err := this.RPC().IPItemRPC().CountAllEnabledIPItems(this.AdminContext(), &pb.CountAllEnabledIPItemsRequest{
|
countResp, err := this.RPC().IPItemRPC().CountAllEnabledIPItems(this.AdminContext(), &pb.CountAllEnabledIPItemsRequest{
|
||||||
Ip: params.Ip,
|
Keyword: params.Keyword,
|
||||||
GlobalOnly: params.GlobalOnly,
|
GlobalOnly: params.GlobalOnly,
|
||||||
Unread: params.Unread,
|
Unread: params.Unread,
|
||||||
EventLevel: params.EventLevel,
|
EventLevel: params.EventLevel,
|
||||||
@@ -63,7 +63,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
this.Data["page"] = page.AsHTML()
|
this.Data["page"] = page.AsHTML()
|
||||||
|
|
||||||
itemsResp, err := this.RPC().IPItemRPC().ListAllEnabledIPItems(this.AdminContext(), &pb.ListAllEnabledIPItemsRequest{
|
itemsResp, err := this.RPC().IPItemRPC().ListAllEnabledIPItems(this.AdminContext(), &pb.ListAllEnabledIPItemsRequest{
|
||||||
Ip: params.Ip,
|
Keyword: params.Keyword,
|
||||||
GlobalOnly: params.GlobalOnly,
|
GlobalOnly: params.GlobalOnly,
|
||||||
Unread: params.Unread,
|
Unread: params.Unread,
|
||||||
EventLevel: params.EventLevel,
|
EventLevel: params.EventLevel,
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ func (this *CreateDeletePopupAction) Init() {
|
|||||||
|
|
||||||
func (this *CreateDeletePopupAction) RunGet(params struct {
|
func (this *CreateDeletePopupAction) RunGet(params struct {
|
||||||
HeaderPolicyId int64
|
HeaderPolicyId int64
|
||||||
|
Type string
|
||||||
}) {
|
}) {
|
||||||
this.Data["headerPolicyId"] = params.HeaderPolicyId
|
this.Data["headerPolicyId"] = params.HeaderPolicyId
|
||||||
|
this.Data["type"] = params.Type
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 删除Header
|
// DeleteAction 删除Header
|
||||||
type DeleteAction struct {
|
type DeleteAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,34 +65,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 套餐
|
// 套餐
|
||||||
var userPlanMap = maps.Map{"id": server.UserPlanId, "dayTo": "", "plan": maps.Map{}}
|
this.initUserPlan(server)
|
||||||
if server.UserPlanId > 0 {
|
|
||||||
userPlanResp, err := this.RPC().UserPlanRPC().FindEnabledUserPlan(this.AdminContext(), &pb.FindEnabledUserPlanRequest{UserPlanId: server.UserPlanId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var userPlan = userPlanResp.UserPlan
|
|
||||||
if userPlan != nil {
|
|
||||||
planResp, err := this.RPC().PlanRPC().FindEnabledPlan(this.AdminContext(), &pb.FindEnabledPlanRequest{PlanId: userPlan.PlanId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var plan = planResp.Plan
|
|
||||||
if plan != nil {
|
|
||||||
userPlanMap = maps.Map{
|
|
||||||
"id": userPlan.Id,
|
|
||||||
"dayTo": userPlan.DayTo,
|
|
||||||
"plan": maps.Map{
|
|
||||||
"id": plan.Id,
|
|
||||||
"name": plan.Name,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.Data["userPlan"] = userPlanMap
|
|
||||||
|
|
||||||
// 集群
|
// 集群
|
||||||
clusterId := int64(0)
|
clusterId := int64(0)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build !plus
|
||||||
|
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *IndexAction) initUserPlan(server *pb.Server) {
|
||||||
|
var userPlanMap = maps.Map{"id": server.UserPlanId, "dayTo": "", "plan": maps.Map{}}
|
||||||
|
this.Data["userPlan"] = userPlanMap
|
||||||
|
}
|
||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreatePopupAction struct {
|
type CreatePopupAction struct {
|
||||||
@@ -27,6 +27,9 @@ func (this *CreatePopupAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreatePopupAction) RunPost(params struct {
|
func (this *CreatePopupAction) RunPost(params struct {
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// URL
|
||||||
Mode string
|
Mode string
|
||||||
BeforeURL string
|
BeforeURL string
|
||||||
AfterURL string
|
AfterURL string
|
||||||
@@ -34,51 +37,131 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
MatchRegexp bool
|
MatchRegexp bool
|
||||||
KeepRequestURI bool
|
KeepRequestURI bool
|
||||||
KeepArgs bool
|
KeepArgs bool
|
||||||
Status int
|
|
||||||
CondsJSON []byte
|
// 域名
|
||||||
IsOn bool
|
DomainsAll bool
|
||||||
|
DomainsBeforeJSON []byte
|
||||||
|
DomainBeforeIgnorePorts bool
|
||||||
|
DomainAfter string
|
||||||
|
DomainAfterScheme string
|
||||||
|
|
||||||
|
// 端口
|
||||||
|
PortsAll bool
|
||||||
|
PortsBefore []string
|
||||||
|
PortAfter int
|
||||||
|
PortAfterScheme string
|
||||||
|
|
||||||
|
Status int
|
||||||
|
CondsJSON []byte
|
||||||
|
IsOn bool
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
params.Must.
|
var config = &serverconfigs.HTTPHostRedirectConfig{}
|
||||||
Field("beforeURL", params.BeforeURL).
|
config.Type = params.Type
|
||||||
Require("请填写跳转前的URL")
|
config.Status = params.Status
|
||||||
|
config.IsOn = params.IsOn
|
||||||
|
|
||||||
|
switch params.Type {
|
||||||
|
case serverconfigs.HTTPHostRedirectTypeURL:
|
||||||
|
params.Must.
|
||||||
|
Field("beforeURL", params.BeforeURL).
|
||||||
|
Require("请填写跳转前的URL")
|
||||||
|
|
||||||
|
// 校验格式
|
||||||
|
if params.MatchRegexp {
|
||||||
|
_, err := regexp.Compile(params.BeforeURL)
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("跳转前URL正则表达式错误:" + err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
u, err := url.Parse(params.BeforeURL)
|
||||||
|
if err != nil {
|
||||||
|
this.FailField("beforeURL", "请输入正确的跳转前URL")
|
||||||
|
}
|
||||||
|
if (u.Scheme != "http" && u.Scheme != "https") ||
|
||||||
|
len(u.Host) == 0 {
|
||||||
|
this.FailField("beforeURL", "请输入正确的跳转前URL")
|
||||||
|
}
|
||||||
|
|
||||||
// 校验格式
|
|
||||||
if params.MatchRegexp {
|
|
||||||
_, err := regexp.Compile(params.BeforeURL)
|
|
||||||
if err != nil {
|
|
||||||
this.Fail("跳转前URL正则表达式错误:" + err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
u, err := url.Parse(params.BeforeURL)
|
|
||||||
if err != nil {
|
|
||||||
this.FailField("beforeURL", "请输入正确的跳转前URL")
|
|
||||||
}
|
|
||||||
if (u.Scheme != "http" && u.Scheme != "https") ||
|
|
||||||
len(u.Host) == 0 {
|
|
||||||
this.FailField("beforeURL", "请输入正确的跳转前URL")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
params.Must.
|
||||||
|
Field("afterURL", params.AfterURL).
|
||||||
|
Require("请填写跳转后URL")
|
||||||
|
|
||||||
params.Must.
|
// 校验格式
|
||||||
Field("afterURL", params.AfterURL).
|
if params.MatchRegexp {
|
||||||
Require("请填写跳转后URL")
|
// 正则表达式情况下不做校验
|
||||||
|
} else {
|
||||||
|
u, err := url.Parse(params.AfterURL)
|
||||||
|
if err != nil {
|
||||||
|
this.FailField("afterURL", "请输入正确的跳转后URL")
|
||||||
|
}
|
||||||
|
if (u.Scheme != "http" && u.Scheme != "https") ||
|
||||||
|
len(u.Host) == 0 {
|
||||||
|
this.FailField("afterURL", "请输入正确的跳转后URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 校验格式
|
config.Mode = params.Mode
|
||||||
if params.MatchRegexp {
|
config.BeforeURL = params.BeforeURL
|
||||||
// 正则表达式情况下不做校验
|
config.AfterURL = params.AfterURL
|
||||||
} else {
|
config.MatchPrefix = params.MatchPrefix
|
||||||
u, err := url.Parse(params.AfterURL)
|
config.MatchRegexp = params.MatchRegexp
|
||||||
if err != nil {
|
config.KeepRequestURI = params.KeepRequestURI
|
||||||
this.FailField("afterURL", "请输入正确的跳转后URL")
|
config.KeepArgs = params.KeepArgs
|
||||||
|
case serverconfigs.HTTPHostRedirectTypeDomain:
|
||||||
|
config.DomainsAll = params.DomainsAll
|
||||||
|
var domainsBefore = []string{}
|
||||||
|
if len(params.DomainsBeforeJSON) > 0 {
|
||||||
|
err := json.Unmarshal(params.DomainsBeforeJSON, &domainsBefore)
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("错误的域名格式:" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (u.Scheme != "http" && u.Scheme != "https") ||
|
config.DomainsBefore = domainsBefore
|
||||||
len(u.Host) == 0 {
|
if !params.DomainsAll {
|
||||||
this.FailField("afterURL", "请输入正确的跳转后URL")
|
if len(domainsBefore) == 0 {
|
||||||
|
this.Fail("请输入跳转前域名")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
config.DomainBeforeIgnorePorts = params.DomainBeforeIgnorePorts
|
||||||
|
if len(params.DomainAfter) == 0 {
|
||||||
|
this.FailField("domainAfter", "请输入跳转后域名")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.DomainAfter = params.DomainAfter
|
||||||
|
config.DomainAfterScheme = params.DomainAfterScheme
|
||||||
|
case serverconfigs.HTTPHostRedirectTypePort:
|
||||||
|
config.PortsAll = params.PortsAll
|
||||||
|
|
||||||
|
config.PortsBefore = params.PortsBefore
|
||||||
|
var portReg = regexp.MustCompile(`^\d+$`)
|
||||||
|
var portRangeReg = regexp.MustCompile(`^\d+-\d+$`)
|
||||||
|
if !config.PortsAll {
|
||||||
|
for _, port := range params.PortsBefore {
|
||||||
|
port = strings.ReplaceAll(port, " ", "")
|
||||||
|
if !portReg.MatchString(port) && !portRangeReg.MatchString(port) {
|
||||||
|
this.Fail("端口号" + port + "填写错误(请输入单个端口号或一个端口范围)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(params.PortsBefore) == 0 {
|
||||||
|
this.Fail("请输入跳转前端口")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.PortAfter <= 0 {
|
||||||
|
this.FailField("portAfter", "请输入跳转后端口")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.PortAfter = params.PortAfter
|
||||||
|
config.PortAfterScheme = params.PortAfterScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
params.Must.
|
params.Must.
|
||||||
@@ -99,19 +182,16 @@ func (this *CreatePopupAction) RunPost(params struct {
|
|||||||
this.Fail("匹配条件校验失败:" + err.Error())
|
this.Fail("匹配条件校验失败:" + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
config.Conds = conds
|
||||||
|
|
||||||
this.Data["redirect"] = maps.Map{
|
// 校验配置
|
||||||
"mode": params.Mode,
|
err := config.Init()
|
||||||
"status": params.Status,
|
if err != nil {
|
||||||
"beforeURL": params.BeforeURL,
|
this.Fail("配置校验失败:" + err.Error())
|
||||||
"afterURL": params.AfterURL,
|
return
|
||||||
"matchPrefix": params.MatchPrefix,
|
|
||||||
"matchRegexp": params.MatchRegexp,
|
|
||||||
"keepRequestURI": params.KeepRequestURI,
|
|
||||||
"keepArgs": params.KeepArgs,
|
|
||||||
"conds": conds,
|
|
||||||
"isOn": params.IsOn,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Data["redirect"] = config
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
//go:build !plus
|
||||||
|
|
||||||
package users
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,33 +16,6 @@ func (this *PlansAction) RunPost(params struct {
|
|||||||
UserId int64
|
UserId int64
|
||||||
ServerId int64
|
ServerId int64
|
||||||
}) {
|
}) {
|
||||||
if !teaconst.IsPlus || params.UserId <= 0 {
|
this.Data["plans"] = []maps.Map{}
|
||||||
this.Data["plans"] = []maps.Map{}
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO 优化用户套餐查询
|
|
||||||
userPlansResp, err := this.RPC().UserPlanRPC().FindAllEnabledUserPlansForServer(this.AdminContext(), &pb.FindAllEnabledUserPlansForServerRequest{
|
|
||||||
UserId: params.UserId,
|
|
||||||
ServerId: params.ServerId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var userPlanMaps = []maps.Map{}
|
|
||||||
for _, userPlan := range userPlansResp.UserPlans {
|
|
||||||
if userPlan.Plan == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
userPlanMaps = append(userPlanMaps, maps.Map{
|
|
||||||
"id": userPlan.Id,
|
|
||||||
"name": userPlan.Plan.Name,
|
|
||||||
"dayTo": userPlan.DayTo,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.Data["plans"] = userPlanMaps
|
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,181 +0,0 @@
|
|||||||
package userui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IndexAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
|
||||||
this.Nav("", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
|
||||||
config, err := configloaders.LoadUserUIConfig()
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.Data["config"] = config
|
|
||||||
|
|
||||||
// 时区
|
|
||||||
this.Data["timeZoneGroups"] = nodeconfigs.FindAllTimeZoneGroups()
|
|
||||||
this.Data["timeZoneLocations"] = nodeconfigs.FindAllTimeZoneLocations()
|
|
||||||
|
|
||||||
if len(config.TimeZone) == 0 {
|
|
||||||
config.TimeZone = nodeconfigs.DefaultTimeZoneLocation
|
|
||||||
}
|
|
||||||
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(config.TimeZone)
|
|
||||||
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IndexAction) RunPost(params struct {
|
|
||||||
ProductName string
|
|
||||||
UserSystemName string
|
|
||||||
ShowOpenSourceInfo bool
|
|
||||||
ShowVersion bool
|
|
||||||
Version string
|
|
||||||
ShowFinance bool
|
|
||||||
FaviconFile *actions.File
|
|
||||||
LogoFile *actions.File
|
|
||||||
TimeZone string
|
|
||||||
|
|
||||||
ShowTrafficCharts bool
|
|
||||||
ShowBandwidthCharts bool
|
|
||||||
BandwidthUnit string
|
|
||||||
|
|
||||||
Must *actions.Must
|
|
||||||
CSRF *actionutils.CSRF
|
|
||||||
}) {
|
|
||||||
params.Must.
|
|
||||||
Field("productName", params.ProductName).
|
|
||||||
Require("请输入产品名称").
|
|
||||||
Field("userSystemName", params.UserSystemName).
|
|
||||||
Require("请输入管理员系统名称")
|
|
||||||
|
|
||||||
config, err := configloaders.LoadUserUIConfig()
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
config.ProductName = params.ProductName
|
|
||||||
config.UserSystemName = params.UserSystemName
|
|
||||||
config.ShowOpenSourceInfo = params.ShowOpenSourceInfo
|
|
||||||
config.ShowVersion = params.ShowVersion
|
|
||||||
config.Version = params.Version
|
|
||||||
config.ShowFinance = params.ShowFinance
|
|
||||||
config.ShowTrafficCharts = params.ShowTrafficCharts
|
|
||||||
config.ShowBandwidthCharts = params.ShowBandwidthCharts
|
|
||||||
config.BandwidthUnit = params.BandwidthUnit
|
|
||||||
config.TimeZone = params.TimeZone
|
|
||||||
|
|
||||||
// 上传Favicon文件
|
|
||||||
if params.FaviconFile != nil {
|
|
||||||
createResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
|
|
||||||
Filename: params.FaviconFile.Filename,
|
|
||||||
Size: params.FaviconFile.Size,
|
|
||||||
IsPublic: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fileId := createResp.FileId
|
|
||||||
|
|
||||||
// 上传内容
|
|
||||||
buf := make([]byte, 512*1024)
|
|
||||||
reader, err := params.FaviconFile.OriginFile.Open()
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
n, err := reader.Read(buf)
|
|
||||||
if n > 0 {
|
|
||||||
_, err = this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
|
|
||||||
FileId: fileId,
|
|
||||||
Data: buf[:n],
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.Fail("上传失败:" + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
this.Fail("上传失败:" + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 置为已完成
|
|
||||||
_, err = this.RPC().FileRPC().UpdateFileFinished(this.AdminContext(), &pb.UpdateFileFinishedRequest{FileId: fileId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
}
|
|
||||||
config.FaviconFileId = fileId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传Logo文件
|
|
||||||
if params.LogoFile != nil {
|
|
||||||
createResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
|
|
||||||
Filename: params.LogoFile.Filename,
|
|
||||||
Size: params.LogoFile.Size,
|
|
||||||
IsPublic: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fileId := createResp.FileId
|
|
||||||
|
|
||||||
// 上传内容
|
|
||||||
buf := make([]byte, 512*1024)
|
|
||||||
reader, err := params.LogoFile.OriginFile.Open()
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
n, err := reader.Read(buf)
|
|
||||||
if n > 0 {
|
|
||||||
_, err = this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
|
|
||||||
FileId: fileId,
|
|
||||||
Data: buf[:n],
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.Fail("上传失败:" + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
this.Fail("上传失败:" + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 置为已完成
|
|
||||||
_, err = this.RPC().FileRPC().UpdateFileFinished(this.AdminContext(), &pb.UpdateFileFinishedRequest{FileId: fileId})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
}
|
|
||||||
config.LogoFileId = fileId
|
|
||||||
}
|
|
||||||
|
|
||||||
err = configloaders.UpdateUserUIConfig(config)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package userui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
|
||||||
"github.com/iwind/TeaGo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
|
||||||
server.
|
|
||||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
|
|
||||||
Helper(settingutils.NewHelper("userUI")).
|
|
||||||
Prefix("/settings/user-ui").
|
|
||||||
GetPost("", new(IndexAction)).
|
|
||||||
EndAll()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/dbs"
|
"github.com/iwind/TeaGo/dbs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -31,10 +32,21 @@ func (this *DetectDBAction) RunPost(params struct{}) {
|
|||||||
localPort = "3306"
|
localPort = "3306"
|
||||||
|
|
||||||
var username = "root"
|
var username = "root"
|
||||||
for _, pass := range []string{"", "123456", "654321", "Aa_123456"} {
|
var passwords = []string{"", "123456", "654321", "Aa_123456", "111111"}
|
||||||
|
|
||||||
|
// 使用 foolish-mysql 安装的MySQL
|
||||||
|
localGeneratedPasswordData, err := os.ReadFile("/usr/local/mysql/generated-password.txt")
|
||||||
|
if err == nil {
|
||||||
|
var localGeneratedPassword = strings.TrimSpace(string(localGeneratedPasswordData))
|
||||||
|
if len(localGeneratedPassword) > 0 {
|
||||||
|
passwords = append(passwords, localGeneratedPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pass := range passwords {
|
||||||
db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{
|
db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{
|
||||||
Driver: "mysql",
|
Driver: "mysql",
|
||||||
Dsn: username + ":" + pass + "@tcp(" + configutils.QuoteIP(localHost) + ":" + localPort + ")/edges11111",
|
Dsn: username + ":" + pass + "@tcp(" + configutils.QuoteIP(localHost) + ":" + localPort + ")/edges",
|
||||||
Prefix: "",
|
Prefix: "",
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
|||||||
currentHost = host
|
currentHost = host
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if net.ParseIP(currentHost) != nil {
|
if net.ParseIP(currentHost) != nil && currentHost != "localhost" && currentHost != "127.0.0.1" {
|
||||||
this.Data["currentHost"] = currentHost
|
this.Data["currentHost"] = currentHost
|
||||||
} else {
|
} else {
|
||||||
this.Data["currentHost"] = ""
|
this.Data["currentHost"] = ""
|
||||||
|
|||||||
@@ -17,13 +17,17 @@ import (
|
|||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/gosock/pkg/gosock"
|
"github.com/iwind/gosock/pkg/gosock"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InstallAction struct {
|
type InstallAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
|
|
||||||
|
apiSetupFinished bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *InstallAction) RunPost(params struct {
|
func (this *InstallAction) RunPost(params struct {
|
||||||
@@ -40,7 +44,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
|
|
||||||
// API节点配置
|
// API节点配置
|
||||||
currentStatusText = "正在检查API节点配置"
|
currentStatusText = "正在检查API节点配置"
|
||||||
apiNodeMap := maps.Map{}
|
var apiNodeMap = maps.Map{}
|
||||||
err := json.Unmarshal(params.ApiNodeJSON, &apiNodeMap)
|
err := json.Unmarshal(params.ApiNodeJSON, &apiNodeMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("API节点配置数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error())
|
this.Fail("API节点配置数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error())
|
||||||
@@ -48,7 +52,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
|
|
||||||
// 数据库
|
// 数据库
|
||||||
currentStatusText = "正在检查数据库配置"
|
currentStatusText = "正在检查数据库配置"
|
||||||
dbMap := maps.Map{}
|
var dbMap = maps.Map{}
|
||||||
err = json.Unmarshal(params.DbJSON, &dbMap)
|
err = json.Unmarshal(params.DbJSON, &dbMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("数据库配置数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error())
|
this.Fail("数据库配置数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error())
|
||||||
@@ -56,14 +60,14 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
|
|
||||||
// 管理员
|
// 管理员
|
||||||
currentStatusText = "正在检查管理员配置"
|
currentStatusText = "正在检查管理员配置"
|
||||||
adminMap := maps.Map{}
|
var adminMap = maps.Map{}
|
||||||
err = json.Unmarshal(params.AdminJSON, &adminMap)
|
err = json.Unmarshal(params.AdminJSON, &adminMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("管理员数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error())
|
this.Fail("管理员数据解析错误,请刷新页面后重新尝试安装,错误信息:" + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安装API节点
|
// 安装API节点
|
||||||
mode := apiNodeMap.GetString("mode")
|
var mode = apiNodeMap.GetString("mode")
|
||||||
if mode == "new" {
|
if mode == "new" {
|
||||||
currentStatusText = "准备启动新API节点"
|
currentStatusText = "准备启动新API节点"
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
// ...
|
// ...
|
||||||
|
|
||||||
// 检查环境
|
// 检查环境
|
||||||
apiNodeDir := Tea.Root + "/edge-api"
|
var apiNodeDir = Tea.Root + "/edge-api"
|
||||||
for _, dir := range []string{"edge-api", "edge-api/configs", "edge-api/bin"} {
|
for _, dir := range []string{"edge-api", "edge-api/configs", "edge-api/bin"} {
|
||||||
apiNodeDir := Tea.Root + "/" + dir
|
apiNodeDir := Tea.Root + "/" + dir
|
||||||
_, err = os.Stat(apiNodeDir)
|
_, err = os.Stat(apiNodeDir)
|
||||||
@@ -87,7 +91,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存数据库配置
|
// 保存数据库配置
|
||||||
dsn := dbMap.GetString("username") + ":" + dbMap.GetString("password") + "@tcp(" + configutils.QuoteIP(dbMap.GetString("host")) + ":" + dbMap.GetString("port") + ")/" + dbMap.GetString("database") + "?charset=utf8mb4&timeout=30s"
|
var dsn = dbMap.GetString("username") + ":" + dbMap.GetString("password") + "@tcp(" + configutils.QuoteIP(dbMap.GetString("host")) + ":" + dbMap.GetString("port") + ")/" + dbMap.GetString("database") + "?charset=utf8mb4&timeout=30s"
|
||||||
dbConfig := &dbs.Config{
|
dbConfig := &dbs.Config{
|
||||||
DBs: map[string]*dbs.DBConfig{
|
DBs: map[string]*dbs.DBConfig{
|
||||||
"prod": {
|
"prod": {
|
||||||
@@ -108,7 +112,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
|
|
||||||
// 生成备份文件
|
// 生成备份文件
|
||||||
homeDir, _ := os.UserHomeDir()
|
homeDir, _ := os.UserHomeDir()
|
||||||
backupDirs := []string{"/etc/edge-api"}
|
var backupDirs = []string{"/etc/edge-api"}
|
||||||
if len(homeDir) > 0 {
|
if len(homeDir) > 0 {
|
||||||
backupDirs = append(backupDirs, homeDir+"/.edge-api")
|
backupDirs = append(backupDirs, homeDir+"/.edge-api")
|
||||||
}
|
}
|
||||||
@@ -151,15 +155,21 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
var resultMap = maps.Map{}
|
var resultMap = maps.Map{}
|
||||||
logs.Println("[INSTALL]setup edge-api")
|
logs.Println("[INSTALL]setup edge-api")
|
||||||
{
|
{
|
||||||
cmd := exec.Command(apiNodeDir+"/bin/edge-api", "setup", "-api-node-protocol=http", "-api-node-host=\""+apiNodeMap.GetString("newHost")+"\"", "-api-node-port=\""+apiNodeMap.GetString("newPort")+"\"")
|
this.apiSetupFinished = false
|
||||||
output := bytes.NewBuffer([]byte{})
|
var cmd = exec.Command(apiNodeDir+"/bin/edge-api", "setup", "-api-node-protocol=http", "-api-node-host=\""+apiNodeMap.GetString("newHost")+"\"", "-api-node-port=\""+apiNodeMap.GetString("newPort")+"\"")
|
||||||
|
var output = bytes.NewBuffer([]byte{})
|
||||||
cmd.Stdout = output
|
cmd.Stdout = output
|
||||||
|
|
||||||
|
// 试图读取执行日志
|
||||||
|
go this.startReadingAPIInstallLog()
|
||||||
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
|
this.apiSetupFinished = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("安装失败:" + err.Error())
|
this.Fail("安装失败:" + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
resultData := output.Bytes()
|
var resultData = output.Bytes()
|
||||||
err = json.Unmarshal(resultData, &resultMap)
|
err = json.Unmarshal(resultData, &resultMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("安装节点时返回数据错误:" + err.Error() + "(" + string(resultData) + ")")
|
this.Fail("安装节点时返回数据错误:" + err.Error() + "(" + string(resultData) + ")")
|
||||||
@@ -175,7 +185,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
// 关闭正在运行的API节点,防止冲突
|
// 关闭正在运行的API节点,防止冲突
|
||||||
logs.Println("[INSTALL]stop edge-api")
|
logs.Println("[INSTALL]stop edge-api")
|
||||||
{
|
{
|
||||||
cmd := exec.Command(apiNodeDir+"/bin/edge-api", "stop")
|
var cmd = exec.Command(apiNodeDir+"/bin/edge-api", "stop")
|
||||||
_ = cmd.Run()
|
_ = cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +193,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
currentStatusText = "正在启动API节点"
|
currentStatusText = "正在启动API节点"
|
||||||
logs.Println("[INSTALL]start edge-api")
|
logs.Println("[INSTALL]start edge-api")
|
||||||
{
|
{
|
||||||
cmd := exec.Command(apiNodeDir + "/bin/edge-api")
|
var cmd = exec.Command(apiNodeDir + "/bin/edge-api")
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail("API节点启动失败:" + err.Error())
|
this.Fail("API节点启动失败:" + err.Error())
|
||||||
@@ -220,7 +230,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 写入API节点配置,完成安装
|
// 写入API节点配置,完成安装
|
||||||
apiConfig := &configs.APIConfig{
|
var apiConfig = &configs.APIConfig{
|
||||||
RPC: struct {
|
RPC: struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
DisableUpdate bool `yaml:"disableUpdate"`
|
DisableUpdate bool `yaml:"disableUpdate"`
|
||||||
@@ -283,7 +293,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
this.Success()
|
this.Success()
|
||||||
} else if mode == "old" {
|
} else if mode == "old" {
|
||||||
// 构造RPC
|
// 构造RPC
|
||||||
apiConfig := &configs.APIConfig{
|
var apiConfig = &configs.APIConfig{
|
||||||
RPC: struct {
|
RPC: struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
DisableUpdate bool `yaml:"disableUpdate"`
|
DisableUpdate bool `yaml:"disableUpdate"`
|
||||||
@@ -303,7 +313,7 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 设置管理员
|
// 设置管理员
|
||||||
ctx := client.APIContext(0)
|
var ctx = client.APIContext(0)
|
||||||
_, err = client.AdminRPC().CreateOrUpdateAdmin(ctx, &pb.CreateOrUpdateAdminRequest{
|
_, err = client.AdminRPC().CreateOrUpdateAdmin(ctx, &pb.CreateOrUpdateAdminRequest{
|
||||||
Username: adminMap.GetString("username"),
|
Username: adminMap.GetString("username"),
|
||||||
Password: adminMap.GetString("password"),
|
Password: adminMap.GetString("password"),
|
||||||
@@ -344,3 +354,73 @@ func (this *InstallAction) RunPost(params struct {
|
|||||||
this.Fail("错误的API节点模式:'" + mode + "'")
|
this.Fail("错误的API节点模式:'" + mode + "'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取API安装时的日志,以便于显示当前正在执行的任务
|
||||||
|
func (this *InstallAction) startReadingAPIInstallLog() {
|
||||||
|
var tmpDir = os.TempDir()
|
||||||
|
if len(tmpDir) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var logFile = tmpDir + "/edge-install.log"
|
||||||
|
|
||||||
|
var logFp *os.File
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// 尝试5秒钟
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
logFp, err = os.Open(logFile)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.apiSetupFinished {
|
||||||
|
_ = logFp.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
_ = logFp.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var ticker = time.NewTicker(1 * time.Second)
|
||||||
|
var logBuf = make([]byte, 256)
|
||||||
|
for range ticker.C {
|
||||||
|
if this.apiSetupFinished {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = logFp.Seek(-256, io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
currentStatusText = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := logFp.Read(logBuf)
|
||||||
|
if err != nil {
|
||||||
|
currentStatusText = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
var logData = string(logBuf[:n])
|
||||||
|
var lines = strings.Split(logData, "\n")
|
||||||
|
if len(lines) >= 3 {
|
||||||
|
var line = strings.TrimSpace(lines[len(lines)-2])
|
||||||
|
if len(line) > 0 {
|
||||||
|
if !this.apiSetupFinished {
|
||||||
|
currentStatusText = "正在执行 " + line + " ..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func init() {
|
|||||||
Get("/otpQrcode", new(OtpQrcodeAction)).
|
Get("/otpQrcode", new(OtpQrcodeAction)).
|
||||||
|
|
||||||
// AccessKeys
|
// AccessKeys
|
||||||
Prefix("/users/accessKeys").
|
Prefix("/users/accesskeys").
|
||||||
Get("", new(accesskeys.IndexAction)).
|
Get("", new(accesskeys.IndexAction)).
|
||||||
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
|
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
|
||||||
Post("/delete", new(accesskeys.DeleteAction)).
|
Post("/delete", new(accesskeys.DeleteAction)).
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
||||||
nodes "github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
nodes "github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
@@ -145,7 +146,7 @@ func checkRequestSecurity(securityConfig *systemconfigs.SecurityConfig, req *htt
|
|||||||
if err == nil && len(realDomain) > 0 {
|
if err == nil && len(realDomain) > 0 {
|
||||||
domain = realDomain
|
domain = realDomain
|
||||||
}
|
}
|
||||||
if !lists.ContainsString(securityConfig.AllowDomains, domain) {
|
if !configutils.MatchDomains(securityConfig.AllowDomains, domain) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/logs"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/logs"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/regions/items"
|
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/tasks"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/tasks"
|
||||||
|
|
||||||
// 通用
|
// 通用
|
||||||
@@ -125,7 +124,6 @@ import (
|
|||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ui"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ui"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/updates"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/updates"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/upgrade"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/upgrade"
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui"
|
|
||||||
|
|
||||||
// 恢复
|
// 恢复
|
||||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/recover"
|
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/recover"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
1
web/public/js/components/.gitignore
vendored
Normal file
1
web/public/js/components/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*-plus.js
|
||||||
@@ -41,7 +41,7 @@ Vue.component("api-node-addresses-box", {
|
|||||||
<input type="hidden" :name="vName" :value="JSON.stringify(addrs)"/>
|
<input type="hidden" :name="vName" :value="JSON.stringify(addrs)"/>
|
||||||
<div v-if="addrs.length > 0">
|
<div v-if="addrs.length > 0">
|
||||||
<div>
|
<div>
|
||||||
<div v-for="(addr, index) in addrs" class="ui label small">
|
<div v-for="(addr, index) in addrs" class="ui label small basic">
|
||||||
{{addr.protocol}}://{{addr.host.quoteIP()}}:{{addr.portRange}}</span>
|
{{addr.protocol}}://{{addr.host.quoteIP()}}:{{addr.portRange}}</span>
|
||||||
<a href="" title="修改" @click.prevent="updateAddr(index, addr)"><i class="icon pencil small"></i></a>
|
<a href="" title="修改" @click.prevent="updateAddr(index, addr)"><i class="icon pencil small"></i></a>
|
||||||
<a href="" title="删除" @click.prevent="removeAddr(index)"><i class="icon remove"></i></a>
|
<a href="" title="删除" @click.prevent="removeAddr(index)"><i class="icon remove"></i></a>
|
||||||
|
|||||||
16
web/public/js/components/common/bits-var.js
Normal file
16
web/public/js/components/common/bits-var.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Vue.component("bits-var", {
|
||||||
|
props: ["v-bits"],
|
||||||
|
data: function () {
|
||||||
|
let bits = this.vBits
|
||||||
|
if (typeof bits != "number") {
|
||||||
|
bits = 0
|
||||||
|
}
|
||||||
|
let format = teaweb.splitFormat(teaweb.formatBits(bits))
|
||||||
|
return {
|
||||||
|
format: format
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template:`<var class="normal">
|
||||||
|
<span>{{format[0]}}</span>{{format[1]}}
|
||||||
|
</var>`
|
||||||
|
})
|
||||||
16
web/public/js/components/common/bytes-var.js
Normal file
16
web/public/js/components/common/bytes-var.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Vue.component("bytes-var", {
|
||||||
|
props: ["v-bytes"],
|
||||||
|
data: function () {
|
||||||
|
let bytes = this.vBytes
|
||||||
|
if (typeof bytes != "number") {
|
||||||
|
bytes = 0
|
||||||
|
}
|
||||||
|
let format = teaweb.splitFormat(teaweb.formatBytes(bytes))
|
||||||
|
return {
|
||||||
|
format: format
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template:`<var class="normal">
|
||||||
|
<span>{{format[0]}}</span>{{format[1]}}
|
||||||
|
</var>`
|
||||||
|
})
|
||||||
@@ -35,7 +35,7 @@ Vue.component("checkbox", {
|
|||||||
this.newValue = ""
|
this.newValue = ""
|
||||||
},
|
},
|
||||||
isChecked: function () {
|
isChecked: function () {
|
||||||
return this.newValue == this.elementValue
|
return (typeof (this.newValue) == "boolean" && this.newValue) || this.newValue == this.elementValue
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Vue.component("datepicker", {
|
Vue.component("datepicker", {
|
||||||
props: ["v-name", "v-value", "v-bottom-left"],
|
props: ["value", "v-name", "name", "v-value", "v-bottom-left", "placeholder"],
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
let that = this
|
let that = this
|
||||||
teaweb.datepicker(this.$refs.dayInput, function (v) {
|
teaweb.datepicker(this.$refs.dayInput, function (v) {
|
||||||
@@ -9,26 +9,51 @@ Vue.component("datepicker", {
|
|||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
let name = this.vName
|
let name = this.vName
|
||||||
|
if (name == null) {
|
||||||
|
name = this.name
|
||||||
|
}
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
name = "day"
|
name = "day"
|
||||||
}
|
}
|
||||||
|
|
||||||
let day = this.vValue
|
let day = this.vValue
|
||||||
if (day == null) {
|
if (day == null) {
|
||||||
day = ""
|
day = this.value
|
||||||
|
if (day == null) {
|
||||||
|
day = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let placeholder = "YYYY-MM-DD"
|
||||||
|
if (this.placeholder != null) {
|
||||||
|
placeholder = this.placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: name,
|
realName: name,
|
||||||
|
realPlaceholder: placeholder,
|
||||||
day: day
|
day: day
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
value: function (v) {
|
||||||
|
this.day = v
|
||||||
|
|
||||||
|
let picker = this.$refs.dayInput.picker
|
||||||
|
if (picker != null) {
|
||||||
|
if (v != null && /^\d+-\d+-\d+$/.test(v)) {
|
||||||
|
picker.setDate(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change: function () {
|
change: function () {
|
||||||
|
this.$emit("input", this.day) // support v-model,事件触发需要在 change 之前
|
||||||
this.$emit("change", this.day)
|
this.$emit("change", this.day)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `<div style="display: inline-block">
|
template: `<div style="display: inline-block">
|
||||||
<input type="text" :name="name" v-model="day" placeholder="YYYY-MM-DD" style="width:8.6em" maxlength="10" @input="change" ref="dayInput" autocomplete="off"/>
|
<input type="text" :name="realName" v-model="day" :placeholder="realPlaceholder" style="width:8.6em" maxlength="10" @input="change" ref="dayInput" autocomplete="off"/>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
@@ -54,8 +54,6 @@ Vue.component("datetime-input", {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change: function () {
|
change: function () {
|
||||||
let date = new Date()
|
|
||||||
|
|
||||||
// day
|
// day
|
||||||
if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) {
|
if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) {
|
||||||
this.hasDayError = true
|
this.hasDayError = true
|
||||||
@@ -63,21 +61,18 @@ Vue.component("datetime-input", {
|
|||||||
}
|
}
|
||||||
let pieces = this.day.split("-")
|
let pieces = this.day.split("-")
|
||||||
let year = parseInt(pieces[0])
|
let year = parseInt(pieces[0])
|
||||||
date.setFullYear(year)
|
|
||||||
|
|
||||||
let month = parseInt(pieces[1])
|
let month = parseInt(pieces[1])
|
||||||
if (month < 1 || month > 12) {
|
if (month < 1 || month > 12) {
|
||||||
this.hasDayError = true
|
this.hasDayError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
date.setMonth(month - 1)
|
|
||||||
|
|
||||||
let day = parseInt(pieces[2])
|
let day = parseInt(pieces[2])
|
||||||
if (day < 1 || day > 32) {
|
if (day < 1 || day > 32) {
|
||||||
this.hasDayError = true
|
this.hasDayError = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
date.setDate(day)
|
|
||||||
|
|
||||||
this.hasDayError = false
|
this.hasDayError = false
|
||||||
|
|
||||||
@@ -96,7 +91,6 @@ Vue.component("datetime-input", {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hasHourError = false
|
this.hasHourError = false
|
||||||
date.setHours(hour)
|
|
||||||
|
|
||||||
// minute
|
// minute
|
||||||
if (!/^\d+$/.test(this.minute)) {
|
if (!/^\d+$/.test(this.minute)) {
|
||||||
@@ -113,7 +107,6 @@ Vue.component("datetime-input", {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hasMinuteError = false
|
this.hasMinuteError = false
|
||||||
date.setMinutes(minute)
|
|
||||||
|
|
||||||
// second
|
// second
|
||||||
if (!/^\d+$/.test(this.second)) {
|
if (!/^\d+$/.test(this.second)) {
|
||||||
@@ -130,8 +123,8 @@ Vue.component("datetime-input", {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.hasSecondError = false
|
this.hasSecondError = false
|
||||||
date.setSeconds(second)
|
|
||||||
|
|
||||||
|
let date = new Date(year, month - 1, day, hour, minute, second)
|
||||||
this.timestamp = Math.floor(date.getTime() / 1000)
|
this.timestamp = Math.floor(date.getTime() / 1000)
|
||||||
},
|
},
|
||||||
leadingZero: function (s, l) {
|
leadingZero: function (s, l) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Vue.component("download-link", {
|
|||||||
} else {
|
} else {
|
||||||
let e = document.getElementById(this.vElement)
|
let e = document.getElementById(this.vElement)
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
teaweb.warn("找不到要下载的内容")
|
// 不提示错误,因为此时可能页面未加载完整
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
text = e.innerText
|
text = e.innerText
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 启用状态标签
|
// 启用状态标签
|
||||||
Vue.component("label-on", {
|
Vue.component("label-on", {
|
||||||
props: ["v-is-on"],
|
props: ["v-is-on"],
|
||||||
template: '<div><span v-if="vIsOn" class="ui label tiny green basic">已启用</span><span v-if="!vIsOn" class="ui label tiny red basic">已停用</span></div>'
|
template: '<div><span v-if="vIsOn" class="green">已启用</span><span v-if="!vIsOn" class="red">已停用</span></div>'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 文字代码标签
|
// 文字代码标签
|
||||||
|
|||||||
79
web/public/js/components/node/node-cache-disk-dirs-box.js
Normal file
79
web/public/js/components/node/node-cache-disk-dirs-box.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
Vue.component("node-cache-disk-dirs-box", {
|
||||||
|
props: ["value", "name"],
|
||||||
|
data: function () {
|
||||||
|
let dirs = this.value
|
||||||
|
if (dirs == null) {
|
||||||
|
dirs = []
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
dirs: dirs,
|
||||||
|
|
||||||
|
isEditing: false,
|
||||||
|
isAdding: false,
|
||||||
|
|
||||||
|
addingPath: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
add: function () {
|
||||||
|
this.isAdding = true
|
||||||
|
let that = this
|
||||||
|
setTimeout(function () {
|
||||||
|
that.$refs.addingPath.focus()
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
confirm: function () {
|
||||||
|
let addingPath = this.addingPath.trim()
|
||||||
|
if (addingPath.length == 0) {
|
||||||
|
let that = this
|
||||||
|
teaweb.warn("请输入要添加的缓存目录", function () {
|
||||||
|
that.$refs.addingPath.focus()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (addingPath[0] != "/") {
|
||||||
|
addingPath = "/" + addingPath
|
||||||
|
}
|
||||||
|
this.dirs.push({
|
||||||
|
path: addingPath
|
||||||
|
})
|
||||||
|
this.cancel()
|
||||||
|
},
|
||||||
|
cancel: function () {
|
||||||
|
this.addingPath = ""
|
||||||
|
this.isAdding = false
|
||||||
|
this.isEditing = false
|
||||||
|
},
|
||||||
|
remove: function (index) {
|
||||||
|
let that = this
|
||||||
|
teaweb.confirm("确定要删除此目录吗?", function () {
|
||||||
|
that.dirs.$remove(index)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<div>
|
||||||
|
<input type="hidden" :name="name" :value="JSON.stringify(dirs)"/>
|
||||||
|
<div style="margin-bottom: 0.3em">
|
||||||
|
<span class="ui label small basic" v-for="(dir, index) in dirs">
|
||||||
|
<i class="icon folder"></i>{{dir.path}} <a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加 -->
|
||||||
|
<div v-if="isAdding">
|
||||||
|
<div class="ui fields inline">
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" style="width: 30em" v-model="addingPath" @keyup.enter="confirm()" @keypress.enter.prevent="1" @keydown.esc="cancel()" ref="addingPath" placeholder="新的缓存目录,比如 /mnt/cache"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<button class="ui button small" type="button" @click.prevent="confirm">确定</button>
|
||||||
|
<a href="" title="取消" @click.prevent="cancel"><i class="icon remove small"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!isAdding">
|
||||||
|
<button class="ui button tiny" type="button" @click.prevent="add">+</button>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
@@ -13,9 +13,9 @@ Vue.component("ns-access-log-box", {
|
|||||||
if (accessLog.recordValue == null || accessLog.recordValue.length == 0) {
|
if (accessLog.recordValue == null || accessLog.recordValue.length == 0) {
|
||||||
isFailure = true
|
isFailure = true
|
||||||
}
|
}
|
||||||
} else if (accessLog.nsRecordId == null || accessLog.nsRecordId == 0) {
|
|
||||||
isFailure = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 没有找到记录的不需要高亮显示,防止管理员看到红色就心理恐慌
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
Vue.component("plan-bandwidth-ranges", {
|
|
||||||
props: ["v-ranges"],
|
|
||||||
data: function () {
|
|
||||||
let ranges = this.vRanges
|
|
||||||
if (ranges == null) {
|
|
||||||
ranges = []
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ranges: ranges,
|
|
||||||
isAdding: false,
|
|
||||||
|
|
||||||
minMB: "",
|
|
||||||
maxMB: "",
|
|
||||||
pricePerMB: "",
|
|
||||||
totalPrice: "",
|
|
||||||
addingRange: {
|
|
||||||
minMB: 0,
|
|
||||||
maxMB: 0,
|
|
||||||
pricePerMB: 0,
|
|
||||||
totalPrice: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
add: function () {
|
|
||||||
this.isAdding = !this.isAdding
|
|
||||||
let that = this
|
|
||||||
setTimeout(function () {
|
|
||||||
that.$refs.minMB.focus()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cancelAdding: function () {
|
|
||||||
this.isAdding = false
|
|
||||||
},
|
|
||||||
confirm: function () {
|
|
||||||
this.isAdding = false
|
|
||||||
this.minMB = ""
|
|
||||||
this.maxMB = ""
|
|
||||||
this.pricePerMB = ""
|
|
||||||
this.totalPrice = ""
|
|
||||||
this.ranges.push(this.addingRange)
|
|
||||||
this.ranges.$sort(function (v1, v2) {
|
|
||||||
if (v1.minMB < v2.minMB) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if (v1.minMB == v2.minMB) {
|
|
||||||
if (v2.maxMB == 0 || v1.maxMB < v2.maxMB) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
})
|
|
||||||
this.change()
|
|
||||||
this.addingRange = {
|
|
||||||
minMB: 0,
|
|
||||||
maxMB: 0,
|
|
||||||
pricePerMB: 0,
|
|
||||||
totalPrice: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
remove: function (index) {
|
|
||||||
this.ranges.$remove(index)
|
|
||||||
this.change()
|
|
||||||
},
|
|
||||||
change: function () {
|
|
||||||
this.$emit("change", this.ranges)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
minMB: function (v) {
|
|
||||||
let minMB = parseInt(v.toString())
|
|
||||||
if (isNaN(minMB) || minMB < 0) {
|
|
||||||
minMB = 0
|
|
||||||
}
|
|
||||||
this.addingRange.minMB = minMB
|
|
||||||
},
|
|
||||||
maxMB: function (v) {
|
|
||||||
let maxMB = parseInt(v.toString())
|
|
||||||
if (isNaN(maxMB) || maxMB < 0) {
|
|
||||||
maxMB = 0
|
|
||||||
}
|
|
||||||
this.addingRange.maxMB = maxMB
|
|
||||||
},
|
|
||||||
pricePerMB: function (v) {
|
|
||||||
let pricePerMB = parseFloat(v.toString())
|
|
||||||
if (isNaN(pricePerMB) || pricePerMB < 0) {
|
|
||||||
pricePerMB = 0
|
|
||||||
}
|
|
||||||
this.addingRange.pricePerMB = pricePerMB
|
|
||||||
},
|
|
||||||
totalPrice: function (v) {
|
|
||||||
let totalPrice = parseFloat(v.toString())
|
|
||||||
if (isNaN(totalPrice) || totalPrice < 0) {
|
|
||||||
totalPrice = 0
|
|
||||||
}
|
|
||||||
this.addingRange.totalPrice = totalPrice
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `<div>
|
|
||||||
<!-- 已有价格 -->
|
|
||||||
<div v-if="ranges.length > 0">
|
|
||||||
<div class="ui label basic small" v-for="(range, index) in ranges" style="margin-bottom: 0.5em">
|
|
||||||
{{range.minMB}}MB - <span v-if="range.maxMB > 0">{{range.maxMB}}MB</span><span v-else>∞</span> 价格:<span v-if="range.totalPrice > 0">{{range.totalPrice}}元</span><span v-else="">{{range.pricePerMB}}元/MB</span>
|
|
||||||
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 添加 -->
|
|
||||||
<div v-if="isAdding">
|
|
||||||
<table class="ui table">
|
|
||||||
<tr>
|
|
||||||
<td class="title">带宽下限 *</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" placeholder="最小带宽" style="width: 7em" maxlength="10" ref="minMB" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="minMB"/>
|
|
||||||
<span class="ui label">MB</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="title">带宽上限 *</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" placeholder="最大带宽" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="maxMB"/>
|
|
||||||
<span class="ui label">MB</span>
|
|
||||||
</div>
|
|
||||||
<p class="comment">如果填0,表示上不封顶。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>总价格</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" placeholder="总价格" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="totalPrice"/>
|
|
||||||
<span class="ui label">元/MB</span>
|
|
||||||
</div>
|
|
||||||
<p class="comment">和单位价格二选一。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="title">单位价格</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" placeholder="单位价格" style="width: 7em" maxlength="10" @keyup.enter="confirm()" @keypress.enter.prevent="1" v-model="pricePerMB"/>
|
|
||||||
<span class="ui label">元/MB</span>
|
|
||||||
</div>
|
|
||||||
<p class="comment">和总价格二选一。如果设置了单位价格,那么"总价格 = 单位价格 x 带宽"。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<button class="ui button small" type="button" @click.prevent="confirm">确定</button>
|
|
||||||
<a href="" title="取消" @click.prevent="cancelAdding"><i class="icon remove small"></i></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按钮 -->
|
|
||||||
<div v-if="!isAdding">
|
|
||||||
<button class="ui button small" type="button" @click.prevent="add">+</button>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
})
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
// 套餐价格配置
|
|
||||||
Vue.component("plan-price-config-box", {
|
|
||||||
props: ["v-price-type", "v-monthly-price", "v-seasonally-price", "v-yearly-price", "v-traffic-price", "v-bandwidth-price", "v-disable-period"],
|
|
||||||
data: function () {
|
|
||||||
let priceType = this.vPriceType
|
|
||||||
if (priceType == null) {
|
|
||||||
priceType = "bandwidth"
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按时间周期计费
|
|
||||||
let monthlyPriceNumber = 0
|
|
||||||
let monthlyPrice = this.vMonthlyPrice
|
|
||||||
if (monthlyPrice == null || monthlyPrice <= 0) {
|
|
||||||
monthlyPrice = ""
|
|
||||||
} else {
|
|
||||||
monthlyPrice = monthlyPrice.toString()
|
|
||||||
monthlyPriceNumber = parseFloat(monthlyPrice)
|
|
||||||
if (isNaN(monthlyPriceNumber)) {
|
|
||||||
monthlyPriceNumber = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let seasonallyPriceNumber = 0
|
|
||||||
let seasonallyPrice = this.vSeasonallyPrice
|
|
||||||
if (seasonallyPrice == null || seasonallyPrice <= 0) {
|
|
||||||
seasonallyPrice = ""
|
|
||||||
} else {
|
|
||||||
seasonallyPrice = seasonallyPrice.toString()
|
|
||||||
seasonallyPriceNumber = parseFloat(seasonallyPrice)
|
|
||||||
if (isNaN(seasonallyPriceNumber)) {
|
|
||||||
seasonallyPriceNumber = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let yearlyPriceNumber = 0
|
|
||||||
let yearlyPrice = this.vYearlyPrice
|
|
||||||
if (yearlyPrice == null || yearlyPrice <= 0) {
|
|
||||||
yearlyPrice = ""
|
|
||||||
} else {
|
|
||||||
yearlyPrice = yearlyPrice.toString()
|
|
||||||
yearlyPriceNumber = parseFloat(yearlyPrice)
|
|
||||||
if (isNaN(yearlyPriceNumber)) {
|
|
||||||
yearlyPriceNumber = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按流量计费
|
|
||||||
let trafficPrice = this.vTrafficPrice
|
|
||||||
let trafficPriceBaseNumber = 0
|
|
||||||
if (trafficPrice != null) {
|
|
||||||
trafficPriceBaseNumber = trafficPrice.base
|
|
||||||
} else {
|
|
||||||
trafficPrice = {
|
|
||||||
base: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let trafficPriceBase = ""
|
|
||||||
if (trafficPriceBaseNumber > 0) {
|
|
||||||
trafficPriceBase = trafficPriceBaseNumber.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按带宽计费
|
|
||||||
let bandwidthPrice = this.vBandwidthPrice
|
|
||||||
if (bandwidthPrice == null) {
|
|
||||||
bandwidthPrice = {
|
|
||||||
percentile: 95,
|
|
||||||
ranges: []
|
|
||||||
}
|
|
||||||
} else if (bandwidthPrice.ranges == null) {
|
|
||||||
bandwidthPrice.ranges = []
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
priceType: priceType,
|
|
||||||
monthlyPrice: monthlyPrice,
|
|
||||||
seasonallyPrice: seasonallyPrice,
|
|
||||||
yearlyPrice: yearlyPrice,
|
|
||||||
|
|
||||||
monthlyPriceNumber: monthlyPriceNumber,
|
|
||||||
seasonallyPriceNumber: seasonallyPriceNumber,
|
|
||||||
yearlyPriceNumber: yearlyPriceNumber,
|
|
||||||
|
|
||||||
trafficPriceBase: trafficPriceBase,
|
|
||||||
trafficPrice: trafficPrice,
|
|
||||||
|
|
||||||
bandwidthPrice: bandwidthPrice,
|
|
||||||
bandwidthPercentile: bandwidthPrice.percentile
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
changeBandwidthPriceRanges: function (ranges) {
|
|
||||||
this.bandwidthPrice.ranges = ranges
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
monthlyPrice: function (v) {
|
|
||||||
let price = parseFloat(v)
|
|
||||||
if (isNaN(price)) {
|
|
||||||
price = 0
|
|
||||||
}
|
|
||||||
this.monthlyPriceNumber = price
|
|
||||||
},
|
|
||||||
seasonallyPrice: function (v) {
|
|
||||||
let price = parseFloat(v)
|
|
||||||
if (isNaN(price)) {
|
|
||||||
price = 0
|
|
||||||
}
|
|
||||||
this.seasonallyPriceNumber = price
|
|
||||||
},
|
|
||||||
yearlyPrice: function (v) {
|
|
||||||
let price = parseFloat(v)
|
|
||||||
if (isNaN(price)) {
|
|
||||||
price = 0
|
|
||||||
}
|
|
||||||
this.yearlyPriceNumber = price
|
|
||||||
},
|
|
||||||
trafficPriceBase: function (v) {
|
|
||||||
let price = parseFloat(v)
|
|
||||||
if (isNaN(price)) {
|
|
||||||
price = 0
|
|
||||||
}
|
|
||||||
this.trafficPrice.base = price
|
|
||||||
},
|
|
||||||
bandwidthPercentile: function (v) {
|
|
||||||
let percentile = parseInt(v)
|
|
||||||
if (isNaN(percentile) || percentile <= 0) {
|
|
||||||
percentile = 95
|
|
||||||
} else if (percentile > 100) {
|
|
||||||
percentile = 100
|
|
||||||
}
|
|
||||||
this.bandwidthPrice.percentile = percentile
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `<div>
|
|
||||||
<input type="hidden" name="priceType" :value="priceType"/>
|
|
||||||
<input type="hidden" name="monthlyPrice" :value="monthlyPriceNumber"/>
|
|
||||||
<input type="hidden" name="seasonallyPrice" :value="seasonallyPriceNumber"/>
|
|
||||||
<input type="hidden" name="yearlyPrice" :value="yearlyPriceNumber"/>
|
|
||||||
<input type="hidden" name="trafficPriceJSON" :value="JSON.stringify(trafficPrice)"/>
|
|
||||||
<input type="hidden" name="bandwidthPriceJSON" :value="JSON.stringify(bandwidthPrice)"/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<radio :v-value="'bandwidth'" :value="priceType" v-model="priceType"> 按带宽</radio>
|
|
||||||
<radio :v-value="'traffic'" :value="priceType" v-model="priceType"> 按流量</radio>
|
|
||||||
<radio :v-value="'period'" :value="priceType" v-model="priceType" v-show="typeof(vDisablePeriod) != 'boolean' || !vDisablePeriod"> 按时间周期</radio>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按时间周期 -->
|
|
||||||
<div v-show="priceType == 'period'">
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<table class="ui table">
|
|
||||||
<tr>
|
|
||||||
<td class="title">月度价格</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" style="width: 7em" maxlength="10" v-model="monthlyPrice"/>
|
|
||||||
<span class="ui label">元</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="title">季度价格</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" style="width: 7em" maxlength="10" v-model="seasonallyPrice"/>
|
|
||||||
<span class="ui label">元</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="title">年度价格</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" style="width: 7em" maxlength="10" v-model="yearlyPrice"/>
|
|
||||||
<span class="ui label">元</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按流量 -->
|
|
||||||
<div v-show="priceType =='traffic'">
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<table class="ui table">
|
|
||||||
<tr>
|
|
||||||
<td class="title">基础流量费用 *</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" v-model="trafficPriceBase" maxlength="10" style="width: 7em"/>
|
|
||||||
<span class="ui label">元/GB</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按带宽 -->
|
|
||||||
<div v-show="priceType == 'bandwidth'">
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<table class="ui table">
|
|
||||||
<tr>
|
|
||||||
<td class="title">带宽百分位 *</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" style="width: 4em" maxlength="3" v-model="bandwidthPercentile"/>
|
|
||||||
<span class="ui label">th</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>带宽价格</td>
|
|
||||||
<td>
|
|
||||||
<plan-bandwidth-ranges :v-ranges="bandwidthPrice.ranges" @change="changeBandwidthPriceRanges"></plan-bandwidth-ranges>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
})
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// 域名列表
|
// 域名列表
|
||||||
Vue.component("domains-box", {
|
Vue.component("domains-box", {
|
||||||
props: ["v-domains", "name"],
|
props: ["v-domains", "name", "v-support-wildcard"],
|
||||||
data: function () {
|
data: function () {
|
||||||
let domains = this.vDomains
|
let domains = this.vDomains
|
||||||
if (domains == null) {
|
if (domains == null) {
|
||||||
@@ -11,11 +11,47 @@ Vue.component("domains-box", {
|
|||||||
if (this.name != null && typeof this.name == "string") {
|
if (this.name != null && typeof this.name == "string") {
|
||||||
realName = this.name
|
realName = this.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let supportWildcard = true
|
||||||
|
if (typeof this.vSupportWildcard == "boolean") {
|
||||||
|
supportWildcard = this.vSupportWildcard
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domains: domains,
|
domains: domains,
|
||||||
|
|
||||||
|
mode: "single", // single | batch
|
||||||
|
batchDomains: "",
|
||||||
|
|
||||||
isAdding: false,
|
isAdding: false,
|
||||||
addingDomain: "",
|
addingDomain: "",
|
||||||
realName: realName
|
|
||||||
|
isEditing: false,
|
||||||
|
editingIndex: -1,
|
||||||
|
|
||||||
|
realName: realName,
|
||||||
|
supportWildcard: supportWildcard
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
vSupportWildcard: function (v) {
|
||||||
|
if (typeof v == "boolean") {
|
||||||
|
this.supportWildcard = v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: function (mode) {
|
||||||
|
let that = this
|
||||||
|
setTimeout(function () {
|
||||||
|
if (mode == "single") {
|
||||||
|
if (that.$refs.addingDomain != null) {
|
||||||
|
that.$refs.addingDomain.focus()
|
||||||
|
}
|
||||||
|
} else if (mode == "batch") {
|
||||||
|
if (that.$refs.batchDomains != null) {
|
||||||
|
that.$refs.batchDomains.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -27,6 +63,11 @@ Vue.component("domains-box", {
|
|||||||
}, 100)
|
}, 100)
|
||||||
},
|
},
|
||||||
confirm: function () {
|
confirm: function () {
|
||||||
|
if (this.mode == "batch") {
|
||||||
|
this.confirmBatch()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let that = this
|
let that = this
|
||||||
|
|
||||||
// 删除其中的空格
|
// 删除其中的空格
|
||||||
@@ -39,54 +80,155 @@ Vue.component("domains-box", {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 基本校验
|
// 基本校验
|
||||||
if (this.addingDomain[0] == "~") {
|
if (this.supportWildcard) {
|
||||||
let expr = this.addingDomain.substring(1)
|
if (this.addingDomain[0] == "~") {
|
||||||
try {
|
let expr = this.addingDomain.substring(1)
|
||||||
new RegExp(expr)
|
try {
|
||||||
} catch (e) {
|
new RegExp(expr)
|
||||||
teaweb.warn("正则表达式错误:" + e.message, function () {
|
} catch (e) {
|
||||||
|
teaweb.warn("正则表达式错误:" + e.message, function () {
|
||||||
|
that.$refs.addingDomain.focus()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (/[*~^]/.test(this.addingDomain)) {
|
||||||
|
teaweb.warn("当前只支持添加普通域名,域名中不能含有特殊符号", function () {
|
||||||
that.$refs.addingDomain.focus()
|
that.$refs.addingDomain.focus()
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.domains.push(this.addingDomain)
|
if (this.isEditing && this.editingIndex >= 0) {
|
||||||
|
this.domains[this.editingIndex] = this.addingDomain
|
||||||
|
} else {
|
||||||
|
this.domains.push(this.addingDomain)
|
||||||
|
}
|
||||||
this.cancel()
|
this.cancel()
|
||||||
|
this.change()
|
||||||
|
},
|
||||||
|
confirmBatch: function () {
|
||||||
|
let domains = this.batchDomains.split("\n")
|
||||||
|
let realDomains = []
|
||||||
|
let that = this
|
||||||
|
let hasProblems = false
|
||||||
|
domains.forEach(function (domain) {
|
||||||
|
if (hasProblems) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (domain.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (that.supportWildcard) {
|
||||||
|
if (domain == "~") {
|
||||||
|
let expr = domain.substring(1)
|
||||||
|
try {
|
||||||
|
new RegExp(expr)
|
||||||
|
} catch (e) {
|
||||||
|
hasProblems = true
|
||||||
|
teaweb.warn("正则表达式错误:" + e.message, function () {
|
||||||
|
that.$refs.batchDomains.focus()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (/[*~^]/.test(domain)) {
|
||||||
|
hasProblems = true
|
||||||
|
teaweb.warn("当前只支持添加普通域名,域名中不能含有特殊符号", function () {
|
||||||
|
that.$refs.batchDomains.focus()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
realDomains.push(domain)
|
||||||
|
})
|
||||||
|
if (hasProblems) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (realDomains.length == 0) {
|
||||||
|
teaweb.warn("请输入要添加的域名", function () {
|
||||||
|
that.$refs.batchDomains.focus()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
realDomains.forEach(function (domain) {
|
||||||
|
that.domains.push(domain)
|
||||||
|
})
|
||||||
|
this.cancel()
|
||||||
|
this.change()
|
||||||
|
},
|
||||||
|
edit: function (index) {
|
||||||
|
this.addingDomain = this.domains[index]
|
||||||
|
this.isEditing = true
|
||||||
|
this.editingIndex = index
|
||||||
|
let that = this
|
||||||
|
setTimeout(function () {
|
||||||
|
that.$refs.addingDomain.focus()
|
||||||
|
}, 50)
|
||||||
},
|
},
|
||||||
remove: function (index) {
|
remove: function (index) {
|
||||||
this.domains.$remove(index)
|
this.domains.$remove(index)
|
||||||
|
this.change()
|
||||||
},
|
},
|
||||||
cancel: function () {
|
cancel: function () {
|
||||||
this.isAdding = false
|
this.isAdding = false
|
||||||
|
this.mode = "single"
|
||||||
|
this.batchDomains = ""
|
||||||
|
this.isEditing = false
|
||||||
|
this.editingIndex = -1
|
||||||
this.addingDomain = ""
|
this.addingDomain = ""
|
||||||
|
},
|
||||||
|
change: function () {
|
||||||
|
this.$emit("change", this.domains)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<input type="hidden" :name="realName" :value="JSON.stringify(domains)"/>
|
<input type="hidden" :name="realName" :value="JSON.stringify(domains)"/>
|
||||||
<div v-if="domains.length > 0">
|
<div v-if="domains.length > 0">
|
||||||
<span class="ui label small basic" v-for="(domain, index) in domains">
|
<span class="ui label small basic" v-for="(domain, index) in domains" :class="{blue: index == editingIndex}">
|
||||||
<span v-if="domain.length > 0 && domain[0] == '~'" class="grey" style="font-style: normal">[正则]</span>
|
<span v-if="domain.length > 0 && domain[0] == '~'" class="grey" style="font-style: normal">[正则]</span>
|
||||||
<span v-if="domain.length > 0 && domain[0] == '.'" class="grey" style="font-style: normal">[后缀]</span>
|
<span v-if="domain.length > 0 && domain[0] == '.'" class="grey" style="font-style: normal">[后缀]</span>
|
||||||
<span v-if="domain.length > 0 && domain[0] == '*'" class="grey" style="font-style: normal">[泛域名]</span>
|
<span v-if="domain.length > 0 && domain[0] == '*'" class="grey" style="font-style: normal">[泛域名]</span>
|
||||||
{{domain}}
|
{{domain}}
|
||||||
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
<span v-if="!isAdding && !isEditing">
|
||||||
|
<a href="" title="修改" @click.prevent="edit(index)"><i class="icon pencil small"></i></a>
|
||||||
|
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||||
|
</span>
|
||||||
|
<span v-if="isAdding || isEditing">
|
||||||
|
<a class="disabled"><i class="icon pencil small"></i></a>
|
||||||
|
<a class="disabled"><i class="icon remove small"></i></a>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isAdding">
|
<div v-if="isAdding || isEditing">
|
||||||
<div class="ui fields">
|
<div class="ui fields">
|
||||||
|
<div class="ui field" v-if="isAdding">
|
||||||
|
<select class="ui dropdown" v-model="mode">
|
||||||
|
<option value="single">单个</option>
|
||||||
|
<option value="batch">批量</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" v-model="addingDomain" @keyup.enter="confirm()" @keypress.enter.prevent="1" ref="addingDomain" placeholder="*.xxx.com" size="30"/>
|
<div v-show="mode == 'single'">
|
||||||
|
<input type="text" v-model="addingDomain" @keyup.enter="confirm()" @keypress.enter.prevent="1" @keydown.esc="cancel()" ref="addingDomain" :placeholder="supportWildcard ? 'example.com、*.example.com' : 'example.com、www.example.com'" size="30" maxlength="100"/>
|
||||||
|
</div>
|
||||||
|
<div v-show="mode == 'batch'">
|
||||||
|
<textarea cols="30" v-model="batchDomains" placeholder="example1.com\nexample2.com\n每行一个域名" ref="batchDomains"></textarea>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<button class="ui button tiny" type="button" @click.prevent="confirm">确定</button>
|
<button class="ui button tiny" type="button" @click.prevent="confirm">确定</button>
|
||||||
<a href="" title="取消" @click.prevent="cancel"><i class="icon remove small"></i></a>
|
<a href="" title="取消" @click.prevent="cancel"><i class="icon remove small"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">支持普通域名(<code-label>example.com</code-label>)、泛域名(<code-label>*.example.com</code-label>)、域名后缀(以点号开头,如<code-label>.example.com</code-label>)和正则表达式(以波浪号开头,如<code-label>~.*.example.com</code-label>)。</p>
|
<p class="comment" v-if="supportWildcard">支持普通域名(<code-label>example.com</code-label>)、泛域名(<code-label>*.example.com</code-label>)<span v-if="vSupportWildcard == undefined">、域名后缀(以点号开头,如<code-label>.example.com</code-label>)和正则表达式(以波浪号开头,如<code-label>~.*.example.com</code-label>)</span>;如果域名后有端口,请加上端口号。</p>
|
||||||
|
<p class="comment" v-if="!supportWildcard">只支持普通域名(<code-label>example.com</code-label>、<code-label>www.example.com</code-label>)。</p>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 0.5em" v-if="!isAdding">
|
<div style="margin-top: 0.5em" v-if="!isAdding">
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ Vue.component("firewall-syn-flood-config-box", {
|
|||||||
<td class="title">启用</td>
|
<td class="title">启用</td>
|
||||||
<td>
|
<td>
|
||||||
<checkbox v-model="config.isOn"></checkbox>
|
<checkbox v-model="config.isOn"></checkbox>
|
||||||
<p class="comment">启用后,WAF将会尝试自动检测并阻止SYN Flood攻击。此功能需要节点已安装并启用Firewalld。</p>
|
<p class="comment">启用后,WAF将会尝试自动检测并阻止SYN Flood攻击。此功能需要节点已安装并启用nftables或Firewalld。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ Vue.component("http-access-log-config-box", {
|
|||||||
<prior-checkbox :v-config="accessLog" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
<prior-checkbox :v-config="accessLog" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||||
<tbody v-show="(!vIsLocation && !vIsGroup) || accessLog.isPrior">
|
<tbody v-show="(!vIsLocation && !vIsGroup) || accessLog.isPrior">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">开启访问日志</td>
|
<td class="title">启用访问日志</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" v-model="accessLog.isOn"/>
|
<input type="checkbox" v-model="accessLog.isOn"/>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ Vue.component("http-access-log-search-box", {
|
|||||||
<a class="ui label basic" :class="{disabled: keyword.length == 0}" @click.prevent="cleanKeyword"><i class="icon remove small"></i></a>
|
<a class="ui label basic" :class="{disabled: keyword.length == 0}" @click.prevent="cleanKeyword"><i class="icon remove small"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field"><tip-icon content="一些特殊的关键词:<br/>单个状态码:status:200<br/>状态码范围:status:500-504<br/>查询IP:ip:192.168.1.100<br/>查询URL:https://goedge.cn/docs<br/>查询路径部分:requestPath:/hello/world<br/>查询协议版本:proto:HTTP/1.1<br/>协议:scheme:http"></tip-icon></div>
|
<div class="ui field"><tip-icon content="一些特殊的关键词:<br/>单个状态码:status:200<br/>状态码范围:status:500-504<br/>查询IP:ip:192.168.1.100<br/>查询URL:https://goedge.cn/docs<br/>查询路径部分:requestPath:/hello/world<br/>查询协议版本:proto:HTTP/1.1<br/>协议:scheme:http<br/>请求方法:method:POST"></tip-icon></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui fields inline" style="margin-top: 0.5em">
|
<div class="ui fields inline" style="margin-top: 0.5em">
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Vue.component("http-charsets-box", {
|
|||||||
<prior-checkbox :v-config="charsetConfig" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
<prior-checkbox :v-config="charsetConfig" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||||
<tbody v-show="(!vIsLocation && !vIsGroup) || charsetConfig.isPrior">
|
<tbody v-show="(!vIsLocation && !vIsGroup) || charsetConfig.isPrior">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">启用</td>
|
<td class="title">启用字符编码</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" v-model="charsetConfig.isOn"/>
|
<input type="checkbox" v-model="charsetConfig.isOn"/>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Vue.component("http-compression-config-box", {
|
|||||||
brotliRef: null,
|
brotliRef: null,
|
||||||
minLength: {count: 0, "unit": "kb"},
|
minLength: {count: 0, "unit": "kb"},
|
||||||
maxLength: {count: 0, "unit": "kb"},
|
maxLength: {count: 0, "unit": "kb"},
|
||||||
mimeTypes: ["text/*", "application/*", "font/*"],
|
mimeTypes: ["text/*", "application/javascript", "application/json", "application/atom+xml", "application/rss+xml", "application/xhtml+xml", "font/*", "image/svg+xml"],
|
||||||
extensions: [".js", ".json", ".html", ".htm", ".xml", ".css", ".woff2", ".txt"],
|
extensions: [".js", ".json", ".html", ".htm", ".xml", ".css", ".woff2", ".txt"],
|
||||||
conds: null
|
conds: null
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ Vue.component("http-compression-config-box", {
|
|||||||
<prior-checkbox :v-config="config" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
<prior-checkbox :v-config="config" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||||
<tbody v-show="(!vIsLocation && !vIsGroup) || config.isPrior">
|
<tbody v-show="(!vIsLocation && !vIsGroup) || config.isPrior">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">启用</td>
|
<td class="title">启用内容压缩</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" value="1" v-model="config.isOn"/>
|
<input type="checkbox" value="1" v-model="config.isOn"/>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ Vue.component("http-firewall-rule-label", {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showErr: function (err) {
|
showErr: function (err) {
|
||||||
|
|
||||||
teaweb.popupTip("规则校验错误,请修正:<span class=\"red\">" + teaweb.encodeHTML(err) + "</span>")
|
teaweb.popupTip("规则校验错误,请修正:<span class=\"red\">" + teaweb.encodeHTML(err) + "</span>")
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -24,7 +23,8 @@ Vue.component("http-firewall-rule-label", {
|
|||||||
|
|
||||||
<!-- refererBlock -->
|
<!-- refererBlock -->
|
||||||
<span v-if="rule.param == '\${refererBlock}'">
|
<span v-if="rule.param == '\${refererBlock}'">
|
||||||
{{rule.checkpointOptions.allowDomains}}
|
<span v-if="rule.checkpointOptions.allowDomains != null && rule.checkpointOptions.allowDomains.length > 0">允许{{rule.checkpointOptions.allowDomains}}</span>
|
||||||
|
<span v-if="rule.checkpointOptions.denyDomains != null && rule.checkpointOptions.denyDomains.length > 0">禁止{{rule.checkpointOptions.denyDomains}}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-else>
|
<span v-else>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Vue.component("http-firewall-rules-box", {
|
|||||||
window.UPDATING_RULE = null
|
window.UPDATING_RULE = null
|
||||||
let that = this
|
let that = this
|
||||||
teaweb.popup("/servers/components/waf/createRulePopup?type=" + this.vType, {
|
teaweb.popup("/servers/components/waf/createRulePopup?type=" + this.vType, {
|
||||||
|
height: "30em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
that.rules.push(resp.data.rule)
|
that.rules.push(resp.data.rule)
|
||||||
}
|
}
|
||||||
@@ -23,6 +24,7 @@ Vue.component("http-firewall-rules-box", {
|
|||||||
window.UPDATING_RULE = rule
|
window.UPDATING_RULE = rule
|
||||||
let that = this
|
let that = this
|
||||||
teaweb.popup("/servers/components/waf/createRulePopup?type=" + this.vType, {
|
teaweb.popup("/servers/components/waf/createRulePopup?type=" + this.vType, {
|
||||||
|
height: "30em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
Vue.set(that.rules, index, resp.data.rule)
|
Vue.set(that.rules, index, resp.data.rule)
|
||||||
}
|
}
|
||||||
@@ -48,7 +50,8 @@ Vue.component("http-firewall-rules-box", {
|
|||||||
|
|
||||||
<!-- refererBlock -->
|
<!-- refererBlock -->
|
||||||
<span v-if="rule.param == '\${refererBlock}'">
|
<span v-if="rule.param == '\${refererBlock}'">
|
||||||
{{rule.checkpointOptions.allowDomains}}
|
<span v-if="rule.checkpointOptions.allowDomains != null && rule.checkpointOptions.allowDomains.length > 0">允许{{rule.checkpointOptions.allowDomains}}</span>
|
||||||
|
<span v-if="rule.checkpointOptions.denyDomains != null && rule.checkpointOptions.denyDomains.length > 0">禁止{{rule.checkpointOptions.denyDomains}}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-else>
|
<span v-else>
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ Vue.component("http-firewall-checkpoint-referer-block", {
|
|||||||
let allowEmpty = true
|
let allowEmpty = true
|
||||||
let allowSameDomain = true
|
let allowSameDomain = true
|
||||||
let allowDomains = []
|
let allowDomains = []
|
||||||
|
let denyDomains = []
|
||||||
|
|
||||||
let options = {}
|
let options = {}
|
||||||
if (window.parent.UPDATING_RULE != null) {
|
if (window.parent.UPDATING_RULE != null) {
|
||||||
@@ -254,6 +255,9 @@ Vue.component("http-firewall-checkpoint-referer-block", {
|
|||||||
if (options.allowDomains != null && typeof (options.allowDomains) == "object") {
|
if (options.allowDomains != null && typeof (options.allowDomains) == "object") {
|
||||||
allowDomains = options.allowDomains
|
allowDomains = options.allowDomains
|
||||||
}
|
}
|
||||||
|
if (options.denyDomains != null && typeof (options.denyDomains) == "object") {
|
||||||
|
denyDomains = options.denyDomains
|
||||||
|
}
|
||||||
|
|
||||||
let that = this
|
let that = this
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
@@ -264,6 +268,7 @@ Vue.component("http-firewall-checkpoint-referer-block", {
|
|||||||
allowEmpty: allowEmpty,
|
allowEmpty: allowEmpty,
|
||||||
allowSameDomain: allowSameDomain,
|
allowSameDomain: allowSameDomain,
|
||||||
allowDomains: allowDomains,
|
allowDomains: allowDomains,
|
||||||
|
denyDomains: denyDomains,
|
||||||
options: {},
|
options: {},
|
||||||
value: 0
|
value: 0
|
||||||
}
|
}
|
||||||
@@ -281,6 +286,10 @@ Vue.component("http-firewall-checkpoint-referer-block", {
|
|||||||
this.allowDomains = values
|
this.allowDomains = values
|
||||||
this.change()
|
this.change()
|
||||||
},
|
},
|
||||||
|
changeDenyDomains: function (values) {
|
||||||
|
this.denyDomains = values
|
||||||
|
this.change()
|
||||||
|
},
|
||||||
change: function () {
|
change: function () {
|
||||||
this.vCheckpoint.options = [
|
this.vCheckpoint.options = [
|
||||||
{
|
{
|
||||||
@@ -294,7 +303,11 @@ Vue.component("http-firewall-checkpoint-referer-block", {
|
|||||||
{
|
{
|
||||||
code: "allowDomains",
|
code: "allowDomains",
|
||||||
value: this.allowDomains
|
value: this.allowDomains
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
code: "denyDomains",
|
||||||
|
value: this.denyDomains
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -323,6 +336,13 @@ Vue.component("http-firewall-checkpoint-referer-block", {
|
|||||||
<p class="comment">允许的来源域名列表,比如<code-label>example.com</code-label>、<code-label>*.example.com</code-label>。单个星号<code-label>*</code-label>表示允许所有域名。</p>
|
<p class="comment">允许的来源域名列表,比如<code-label>example.com</code-label>、<code-label>*.example.com</code-label>。单个星号<code-label>*</code-label>表示允许所有域名。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>禁止的来源域名</td>
|
||||||
|
<td>
|
||||||
|
<values-box :values="denyDomains" @change="changeDenyDomains"></values-box>
|
||||||
|
<p class="comment">禁止的来源域名列表,比如<code-label>example.org</code-label>、<code-label>*.example.org</code-label>;除了这些禁止的来源域名外,其他域名都会被允许,除非限定了允许的来源域名。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
44
web/public/js/components/server/http-header-assitant.js
Normal file
44
web/public/js/components/server/http-header-assitant.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
Vue.component("http-header-assistant", {
|
||||||
|
props: ["v-type", "v-value"],
|
||||||
|
mounted: function () {
|
||||||
|
let that = this
|
||||||
|
Tea.action("/servers/headers/options?type=" + this.vType)
|
||||||
|
.post()
|
||||||
|
.success(function (resp) {
|
||||||
|
that.allHeaders = resp.data.headers
|
||||||
|
})
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
allHeaders: [],
|
||||||
|
matchedHeaders: [],
|
||||||
|
|
||||||
|
selectedHeaderName: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
vValue: function (v) {
|
||||||
|
if (v != this.selectedHeaderName) {
|
||||||
|
this.selectedHeaderName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.length == 0) {
|
||||||
|
this.matchedHeaders = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.matchedHeaders = this.allHeaders.filter(function (header) {
|
||||||
|
return teaweb.match(header, v)
|
||||||
|
}).slice(0, 5)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
select: function (header) {
|
||||||
|
this.$emit("select", header)
|
||||||
|
this.selectedHeaderName = header
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `<span v-if="selectedHeaderName.length == 0">
|
||||||
|
<a href="" v-for="header in matchedHeaders" class="ui label basic tiny blue" style="font-weight: normal" @click.prevent="select(header)">{{header}}</a>
|
||||||
|
<span v-if="matchedHeaders.length > 0"> </span>
|
||||||
|
</span>`
|
||||||
|
})
|
||||||
@@ -100,10 +100,9 @@ Vue.component("http-host-redirect-box", {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 1em"></th>
|
<th style="width: 1em"></th>
|
||||||
<th>跳转前URL</th>
|
<th>跳转前</th>
|
||||||
<th style="width: 1em"></th>
|
<th style="width: 1em"></th>
|
||||||
<th>跳转后URL</th>
|
<th>跳转后</th>
|
||||||
<th>匹配模式</th>
|
|
||||||
<th>HTTP状态码</th>
|
<th>HTTP状态码</th>
|
||||||
<th class="two wide">状态</th>
|
<th class="two wide">状态</th>
|
||||||
<th class="two op">操作</th>
|
<th class="two op">操作</th>
|
||||||
@@ -113,17 +112,48 @@ Vue.component("http-host-redirect-box", {
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="text-align: center;"><i class="icon bars handle grey"></i> </td>
|
<td style="text-align: center;"><i class="icon bars handle grey"></i> </td>
|
||||||
<td>
|
<td>
|
||||||
{{redirect.beforeURL}}
|
<div v-if="redirect.type == '' || redirect.type == 'url'">
|
||||||
|
{{redirect.beforeURL}}
|
||||||
|
<div style="margin-top: 0.4em">
|
||||||
|
<grey-label><strong>URL跳转</strong></grey-label>
|
||||||
|
<grey-label v-if="redirect.matchPrefix">匹配前缀</grey-label>
|
||||||
|
<grey-label v-if="redirect.matchRegexp">正则匹配</grey-label>
|
||||||
|
<grey-label v-if="!redirect.matchPrefix && !redirect.matchRegexp">精准匹配</grey-label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="redirect.type == 'domain'">
|
||||||
|
<span v-if="redirect.domainsAll">所有域名</span>
|
||||||
|
<span v-if="!redirect.domainsAll && redirect.domainsBefore != null">
|
||||||
|
<span v-if="redirect.domainsBefore.length == 1">{{redirect.domainsBefore[0]}}</span>
|
||||||
|
<span v-if="redirect.domainsBefore.length > 1">{{redirect.domainsBefore[0]}}等{{redirect.domainsBefore.length}}个域名</span>
|
||||||
|
</span>
|
||||||
|
<div style="margin-top: 0.4em">
|
||||||
|
<grey-label><strong>域名跳转</strong></grey-label>
|
||||||
|
<grey-label v-if="redirect.domainAfterScheme != null && redirect.domainAfterScheme.length > 0">{{redirect.domainAfterScheme}}</grey-label>
|
||||||
|
<grey-label v-if="redirect.domainBeforeIgnorePorts">忽略端口</grey-label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="redirect.type == 'port'">
|
||||||
|
<span v-if="redirect.portsAll">所有端口</span>
|
||||||
|
<span v-if="!redirect.portsAll && redirect.portsBefore != null">
|
||||||
|
<span v-if="redirect.portsBefore.length <= 5">{{redirect.portsBefore.join(", ")}}</span>
|
||||||
|
<span v-if="redirect.portsBefore.length > 5">{{redirect.portsBefore.slice(0, 5).join(", ")}}等{{redirect.portsBefore.length}}个端口</span>
|
||||||
|
</span>
|
||||||
|
<div style="margin-top: 0.4em">
|
||||||
|
<grey-label><strong>端口跳转</strong></grey-label>
|
||||||
|
<grey-label v-if="redirect.portAfterScheme != null && redirect.portAfterScheme.length > 0">{{redirect.portAfterScheme}}</grey-label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 0.5em" v-if="redirect.conds != null && redirect.conds.groups != null && redirect.conds.groups.length > 0">
|
<div style="margin-top: 0.5em" v-if="redirect.conds != null && redirect.conds.groups != null && redirect.conds.groups.length > 0">
|
||||||
<span class="ui label text basic tiny">匹配条件</span>
|
<grey-label>匹配条件</grey-label>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td nowrap="">-></td>
|
<td nowrap="">-></td>
|
||||||
<td>{{redirect.afterURL}}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<span v-if="redirect.matchPrefix">匹配前缀</span>
|
<span v-if="redirect.type == '' || redirect.type == 'url'">{{redirect.afterURL}}</span>
|
||||||
<span v-if="redirect.matchRegexp">正则匹配</span>
|
<span v-if="redirect.type == 'domain'">{{redirect.domainAfter}}</span>
|
||||||
<span v-if="!redirect.matchPrefix && !redirect.matchRegexp">精准匹配</span>
|
<span v-if="redirect.type == 'port'">{{redirect.portAfter}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-if="redirect.status > 0">{{redirect.status}}</span>
|
<span v-if="redirect.status > 0">{{redirect.status}}</span>
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ Vue.component("http-referers-config-box", {
|
|||||||
isOn: false,
|
isOn: false,
|
||||||
allowEmpty: true,
|
allowEmpty: true,
|
||||||
allowSameDomain: true,
|
allowSameDomain: true,
|
||||||
allowDomains: []
|
allowDomains: [],
|
||||||
|
denyDomains: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (config.allowDomains == null) {
|
if (config.allowDomains == null) {
|
||||||
config.allowDomains = []
|
config.allowDomains = []
|
||||||
}
|
}
|
||||||
|
if (config.denyDomains == null) {
|
||||||
|
config.denyDomains = []
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
config: config
|
config: config
|
||||||
}
|
}
|
||||||
@@ -23,6 +27,8 @@ Vue.component("http-referers-config-box", {
|
|||||||
return ((!this.vIsLocation && !this.vIsGroup) || this.config.isPrior) && this.config.isOn
|
return ((!this.vIsLocation && !this.vIsGroup) || this.config.isPrior) && this.config.isOn
|
||||||
},
|
},
|
||||||
changeAllowDomains: function (domains) {
|
changeAllowDomains: function (domains) {
|
||||||
|
},
|
||||||
|
changeDenyDomains: function (domains) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `<div>
|
template: `<div>
|
||||||
@@ -59,10 +65,17 @@ Vue.component("http-referers-config-box", {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>允许的来源域名</td>
|
<td>允许的来源域名</td>
|
||||||
<td>
|
<td>
|
||||||
<values-box :values="config.allowDomains" @change="changeAllowDomains"></values-box>
|
<domains-box :v-domains="config.allowDomains" @change="changeAllowDomains">></domains-box>
|
||||||
<p class="comment">允许的其他来源域名列表,比如<code-label>example.com</code-label>、<code-label>*.example.com</code-label>。单个星号<code-label>*</code-label>表示允许所有域名。</p>
|
<p class="comment">允许的其他来源域名列表,比如<code-label>example.com</code-label>、<code-label>*.example.com</code-label>。单个星号<code-label>*</code-label>表示允许所有域名。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>禁止的来源域名</td>
|
||||||
|
<td>
|
||||||
|
<domains-box :v-domains="config.denyDomains" @change="changeDenyDomains"></domains-box>
|
||||||
|
<p class="comment">禁止的来源域名列表,比如<code-label>example.org</code-label>、<code-label>*.example.org</code-label>;除了这些禁止的来源域名外,其他域名都会被允许,除非限定了允许的来源域名。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="ui margin"></div>
|
<div class="ui margin"></div>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ Vue.component("http-request-limit-config-box", {
|
|||||||
<td>单IP最大并发连接数</td>
|
<td>单IP最大并发连接数</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" maxlength="6" v-model="maxConnsPerIP"/>
|
<input type="text" maxlength="6" v-model="maxConnsPerIP"/>
|
||||||
<p class="comment">单IP最大连接数,统计单个IP总连接数时不区分服务,超出此限制则响应用户<code-label>429</code-label>代码。为0表示不限制。<span v-if="maxConnsPerIP <= 3" class="red">当前设置的并发连接数过低,可能会影响正常用户访问,建议不小于3。</span></p>
|
<p class="comment">单IP最大连接数,统计单个IP总连接数时不区分服务,超出此限制则响应用户<code-label>429</code-label>代码。为0表示不限制。<span v-if="maxConnsPerIP > 0 && maxConnsPerIP <= 3" class="red">当前设置的并发连接数过低,可能会影响正常用户访问,建议不小于3。</span></p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Vue.component("http-stat-config-box", {
|
|||||||
<prior-checkbox :v-config="stat" v-if="vIsLocation || vIsGroup" ></prior-checkbox>
|
<prior-checkbox :v-config="stat" v-if="vIsLocation || vIsGroup" ></prior-checkbox>
|
||||||
<tbody v-show="(!vIsLocation && !vIsGroup) || stat.isPrior">
|
<tbody v-show="(!vIsLocation && !vIsGroup) || stat.isPrior">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">是否开启统计</td>
|
<td class="title">启用统计</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" v-model="stat.isOn"/>
|
<input type="checkbox" v-model="stat.isOn"/>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Vue.component("http-web-root-box", {
|
|||||||
<prior-checkbox :v-config="rootConfig" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
<prior-checkbox :v-config="rootConfig" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||||
<tbody v-show="(!vIsLocation && !vIsGroup) || rootConfig.isPrior">
|
<tbody v-show="(!vIsLocation && !vIsGroup) || rootConfig.isPrior">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">是否开启静态资源分发</td>
|
<td class="title">启用静态资源分发</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" v-model="rootConfig.isOn"/>
|
<input type="checkbox" v-model="rootConfig.isOn"/>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Vue.component("http-webp-config-box", {
|
|||||||
<prior-checkbox :v-config="config" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
<prior-checkbox :v-config="config" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||||
<tbody v-show="(!vIsLocation && !vIsGroup) || config.isPrior">
|
<tbody v-show="(!vIsLocation && !vIsGroup) || config.isPrior">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">启用</td>
|
<td class="title">启用WebP压缩</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" value="1" v-model="config.isOn"/>
|
<input type="checkbox" value="1" v-model="config.isOn"/>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ Vue.component("http-websocket-box", {
|
|||||||
<prior-checkbox :v-config="websocketRef" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
<prior-checkbox :v-config="websocketRef" v-if="vIsLocation || vIsGroup"></prior-checkbox>
|
||||||
<tbody v-show="((!vIsLocation && !vIsGroup) || websocketRef.isPrior)">
|
<tbody v-show="((!vIsLocation && !vIsGroup) || websocketRef.isPrior)">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">启用配置</td>
|
<td class="title">启用Websocket</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" v-model="websocketRef.isOn"/>
|
<input type="checkbox" v-model="websocketRef.isOn"/>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Vue.component("origin-list-box", {
|
|||||||
methods: {
|
methods: {
|
||||||
createPrimaryOrigin: function () {
|
createPrimaryOrigin: function () {
|
||||||
teaweb.popup("/servers/server/settings/origins/addPopup?originType=primary&" + this.vParams, {
|
teaweb.popup("/servers/server/settings/origins/addPopup?originType=primary&" + this.vParams, {
|
||||||
|
width: "45em",
|
||||||
height: "27em",
|
height: "27em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
teaweb.success("保存成功", function () {
|
teaweb.success("保存成功", function () {
|
||||||
@@ -19,6 +20,7 @@ Vue.component("origin-list-box", {
|
|||||||
},
|
},
|
||||||
createBackupOrigin: function () {
|
createBackupOrigin: function () {
|
||||||
teaweb.popup("/servers/server/settings/origins/addPopup?originType=backup&" + this.vParams, {
|
teaweb.popup("/servers/server/settings/origins/addPopup?originType=backup&" + this.vParams, {
|
||||||
|
width: "45em",
|
||||||
height: "27em",
|
height: "27em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
teaweb.success("保存成功", function () {
|
teaweb.success("保存成功", function () {
|
||||||
@@ -29,6 +31,7 @@ Vue.component("origin-list-box", {
|
|||||||
},
|
},
|
||||||
updateOrigin: function (originId, originType) {
|
updateOrigin: function (originId, originType) {
|
||||||
teaweb.popup("/servers/server/settings/origins/updatePopup?originType=" + originType + "&" + this.vParams + "&originId=" + originId, {
|
teaweb.popup("/servers/server/settings/origins/updatePopup?originType=" + originType + "&" + this.vParams + "&originId=" + originId, {
|
||||||
|
width: "45em",
|
||||||
height: "27em",
|
height: "27em",
|
||||||
callback: function (resp) {
|
callback: function (resp) {
|
||||||
teaweb.success("保存成功", function () {
|
teaweb.success("保存成功", function () {
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ Vue.component("ssl-config-box", {
|
|||||||
|
|
||||||
<!-- HSTS -->
|
<!-- HSTS -->
|
||||||
<tr v-show="vProtocol == 'https'">
|
<tr v-show="vProtocol == 'https'">
|
||||||
<td :class="{'color-border':hsts.isOn}">是否开启HSTS</td>
|
<td :class="{'color-border':hsts.isOn}">开启HSTS</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="hstsOn" v-model="hsts.isOn" value="1"/>
|
<input type="checkbox" name="hstsOn" v-model="hsts.isOn" value="1"/>
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ window.teaweb = {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (typeof (element) == "string") {
|
if (typeof (element) == "string") {
|
||||||
element = document.getElementById(element);
|
element = document.getElementById(element);
|
||||||
}
|
}
|
||||||
@@ -112,8 +111,9 @@ window.teaweb = {
|
|||||||
},
|
},
|
||||||
reposition: !bottomLeft
|
reposition: !bottomLeft
|
||||||
})
|
})
|
||||||
},
|
|
||||||
|
|
||||||
|
element.picker = picker
|
||||||
|
},
|
||||||
formatBytes: function (bytes) {
|
formatBytes: function (bytes) {
|
||||||
bytes = Math.ceil(bytes);
|
bytes = Math.ceil(bytes);
|
||||||
if (bytes < Math.pow(1024, 1)) {
|
if (bytes < Math.pow(1024, 1)) {
|
||||||
@@ -136,6 +136,28 @@ window.teaweb = {
|
|||||||
}
|
}
|
||||||
return (Math.round(bytes * 100 / Math.pow(1024, 6)) / 100) + "EB";
|
return (Math.round(bytes * 100 / Math.pow(1024, 6)) / 100) + "EB";
|
||||||
},
|
},
|
||||||
|
formatBits: function (bits) {
|
||||||
|
bits = Math.ceil(bits);
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
if (bits < Math.pow(1024, 3)) {
|
||||||
|
return (Math.round(bits * 10000 / Math.pow(1024, 2)) / 10000) + "Mbps";
|
||||||
|
}
|
||||||
|
if (bits < Math.pow(1024, 4)) {
|
||||||
|
return (Math.round(bits * 10000 / Math.pow(1024, 3)) / 10000) + "Gbps";
|
||||||
|
}
|
||||||
|
if (bits < Math.pow(1024, 5)) {
|
||||||
|
return (Math.round(bits * 10000 / Math.pow(1024, 4)) / 10000) + "Tbps";
|
||||||
|
}
|
||||||
|
if (bits < Math.pow(1024, 6)) {
|
||||||
|
return (Math.round(bits * 10000 / Math.pow(1024, 5)) / 10000) + "Pbps";
|
||||||
|
}
|
||||||
|
return (Math.round(bits * 10000 / Math.pow(1024, 6)) / 10000) + "Ebps";
|
||||||
|
},
|
||||||
formatNumber: function (x) {
|
formatNumber: function (x) {
|
||||||
if (x == null) {
|
if (x == null) {
|
||||||
return "null"
|
return "null"
|
||||||
@@ -205,6 +227,19 @@ window.teaweb = {
|
|||||||
divider: divider
|
divider: divider
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
bitsAxis: function (stats, countFunc) {
|
||||||
|
let axis = this.bytesAxis(stats, countFunc)
|
||||||
|
let unit = axis.unit
|
||||||
|
if (unit == "B") {
|
||||||
|
unit = "bps"
|
||||||
|
} else {
|
||||||
|
unit += "bps"
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
unit: unit,
|
||||||
|
divider: axis.divider
|
||||||
|
}
|
||||||
|
},
|
||||||
countAxis: function (stats, countFunc) {
|
countAxis: function (stats, countFunc) {
|
||||||
let max = Math.max.apply(this, stats.map(countFunc))
|
let max = Math.max.apply(this, stats.map(countFunc))
|
||||||
let divider = 1
|
let divider = 1
|
||||||
@@ -559,12 +594,22 @@ window.teaweb = {
|
|||||||
let max = options.max
|
let max = options.max
|
||||||
let interval = options.interval
|
let interval = options.interval
|
||||||
|
|
||||||
|
let left = options.left
|
||||||
|
if (typeof left != "number") {
|
||||||
|
left = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let right = options.right
|
||||||
|
if (typeof right != "number") {
|
||||||
|
right = 0
|
||||||
|
}
|
||||||
|
|
||||||
let chartBox = document.getElementById(chartId)
|
let chartBox = document.getElementById(chartId)
|
||||||
if (chartBox == null) {
|
if (chartBox == null) {
|
||||||
console.error("chart id '" + chartId + "' not found")
|
console.error("chart id '" + chartId + "' not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let chart = this.initChart(chartBox)
|
let chart = this.initChart(chartBox, options.cache)
|
||||||
let option = {
|
let option = {
|
||||||
xAxis: {
|
xAxis: {
|
||||||
data: values.map(xFunc),
|
data: values.map(xFunc),
|
||||||
@@ -591,9 +636,9 @@ window.teaweb = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: 40,
|
left: 40 + left,
|
||||||
top: 10,
|
top: 10,
|
||||||
right: 20,
|
right: 20 + right,
|
||||||
bottom: 20
|
bottom: 20
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
@@ -605,7 +650,8 @@ window.teaweb = {
|
|||||||
color: this.DefaultChartColor
|
color: this.DefaultChartColor
|
||||||
},
|
},
|
||||||
areaStyle: {},
|
areaStyle: {},
|
||||||
smooth: true
|
smooth: true,
|
||||||
|
markLine: options.markLine
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
animation: true,
|
animation: true,
|
||||||
@@ -893,7 +939,11 @@ window.teaweb = {
|
|||||||
return [40, -20]
|
return [40, -20]
|
||||||
},
|
},
|
||||||
chartMap: {}, // dom id => chart
|
chartMap: {}, // dom id => chart
|
||||||
initChart: function (dom) {
|
initChart: function (dom, cache) {
|
||||||
|
if (typeof(cache) != "boolean") {
|
||||||
|
cache = true
|
||||||
|
}
|
||||||
|
|
||||||
let domId = dom.getAttribute("id")
|
let domId = dom.getAttribute("id")
|
||||||
if (domId != null && domId.length > 0 && typeof (this.chartMap[domId]) == "object") {
|
if (domId != null && domId.length > 0 && typeof (this.chartMap[domId]) == "object") {
|
||||||
return this.chartMap[domId]
|
return this.chartMap[domId]
|
||||||
@@ -902,7 +952,9 @@ window.teaweb = {
|
|||||||
window.addEventListener("resize", function () {
|
window.addEventListener("resize", function () {
|
||||||
instance.resize()
|
instance.resize()
|
||||||
})
|
})
|
||||||
this.chartMap[domId] = instance
|
if (cache) {
|
||||||
|
this.chartMap[domId] = instance
|
||||||
|
}
|
||||||
return instance
|
return instance
|
||||||
},
|
},
|
||||||
encodeHTML: function (s) {
|
encodeHTML: function (s) {
|
||||||
|
|||||||
@@ -731,6 +731,9 @@ var.olive {
|
|||||||
var.dash {
|
var.dash {
|
||||||
border-bottom: 1px dashed grey;
|
border-bottom: 1px dashed grey;
|
||||||
}
|
}
|
||||||
|
var.normal {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
/** checkbox **/
|
/** checkbox **/
|
||||||
.checkbox label a,
|
.checkbox label a,
|
||||||
.checkbox label {
|
.checkbox label {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user