Compare commits

..

56 Commits

Author SHA1 Message Date
刘祥超
7af5e828bb 在选择线路弹窗可以从域名中获取线路 2021-09-06 09:16:19 +08:00
刘祥超
98d53c49a0 修复打开网站服务菜单时无法点击系统设置的Bug 2021-09-06 08:53:51 +08:00
刘祥超
bb32849e47 安装时延长API节点初始化等待时间 2021-09-06 08:53:26 +08:00
刘祥超
13221faee0 实现区域监控节点运行日志和运行结果 2021-09-06 08:53:08 +08:00
刘祥超
abda14f543 实现基本的区域监控终端管理功能 2021-09-05 11:10:07 +08:00
刘祥超
3f0e459181 删除不需要的文件 2021-09-04 10:27:45 +08:00
刘祥超
3df61381a4 删除部分不需要的文件/阶段性提交 2021-09-04 10:17:14 +08:00
刘祥超
daf8ae2192 修复IP地址保存后跳转错误的问题 2021-09-04 08:27:24 +08:00
刘祥超
3bf374ea5a 优化URL跳转交互 2021-09-01 08:49:59 +08:00
刘祥超
94b3559eee 增加独立的IP地址管理功能 2021-08-31 17:24:30 +08:00
刘祥超
1c93b42681 企业认证信息中增加节点数显示 2021-08-30 18:57:35 +08:00
刘祥超
1d9d44ecc3 数据看板和节点看板都改成异步加载 2021-08-30 15:23:31 +08:00
刘祥超
19178f963e 服务看板改成异步 2021-08-30 11:26:23 +08:00
刘祥超
4dc54f95b6 优化数据库节点管理 2021-08-30 10:56:03 +08:00
刘祥超
0790403923 优化交互 2021-08-30 09:52:09 +08:00
刘祥超
c55008d302 看板在健康检查失败离线时提供手动上线按钮 2021-08-29 16:56:37 +08:00
刘祥超
df9df94bb8 节点选择集群时可以根据关键词搜索 2021-08-29 16:41:59 +08:00
刘祥超
502ce4e414 健康检查连续下线次数默认值从1次改为3次 2021-08-29 15:26:56 +08:00
刘祥超
f5f07c0e96 可以在节点列表中直接修改节点所属线路 2021-08-29 14:00:59 +08:00
刘祥超
9549424835 缓存策略里的默认缓存条件增加、修改或者删除后自动保存 2021-08-29 09:40:21 +08:00
刘祥超
062b9baeab 修复缓存条件状态码无法修改的问题 2021-08-29 09:22:02 +08:00
刘祥超
08d1fcaf03 服务 -- 缓存设置中增加清理和预热功能 2021-08-29 09:21:44 +08:00
刘祥超
ad7523a500 修改accessKeys package因为大小写而无法编译的问题 2021-08-26 14:02:53 +08:00
刘祥超
28aa0f3b76 删除不需要的文件 2021-08-26 14:01:48 +08:00
刘祥超
8247843936 添加IP到IP名单时,可以选择批量输入 2021-08-26 10:28:52 +08:00
刘祥超
09ff5a37a9 DNS服务商支持搜索 2021-08-25 18:46:57 +08:00
刘祥超
663ec7710c 集群支持使用域名搜索 2021-08-25 18:39:35 +08:00
刘祥超
0dad0b10e6 选择DNS线路时增加搜索/节点如果没有设置DNS线路就使用默认线路 2021-08-25 17:16:14 +08:00
刘祥超
8c352813c4 优化HTTP Header管理界面交互 2021-08-25 14:09:37 +08:00
刘祥超
745f5d9759 优化集群选择DNS设置交互 2021-08-25 11:55:06 +08:00
刘祥超
6536ace4f7 默认集群没有节点时,在看板提示添加 2021-08-25 11:40:57 +08:00
刘祥超
67e6e06e72 新安装检查数据库权限后删除测试表 2021-08-25 11:27:09 +08:00
刘祥超
d0bf85de17 创建集群时自动创建缓存策略和WAF策略/优化界面 2021-08-25 11:17:24 +08:00
刘祥超
14cb3bdc67 增加忽略相似消息周期设置 2021-08-24 20:45:07 +08:00
刘祥超
e9dadd5571 消息接收人可以设置接收消息时间段 2021-08-24 17:46:00 +08:00
刘祥超
d5eceb05ce 消息发送任务队列可以删除单个任务 2021-08-24 16:36:47 +08:00
刘祥超
b510c88f84 通知媒介可以设置发送频率 2021-08-24 15:46:37 +08:00
刘祥超
16e409e5f5 通知媒介增加任务队列查看功能 2021-08-24 14:22:15 +08:00
刘祥超
ad257fcd0e 优化js 2021-08-24 09:31:14 +08:00
刘祥超
e84087d6ec 修复指标table类型图表的js错误 2021-08-24 09:31:07 +08:00
刘祥超
218c2385f0 在服务日志中增加WAF日志筛选 2021-08-22 16:49:15 +08:00
刘祥超
4ef8c2bc77 全局访问日志增加WAF日志/优化交互/数据看板--WAF--最新拦截记录增加更多链接 2021-08-22 16:34:20 +08:00
刘祥超
d9bc1bdfca Dashboard可以提示API节点升级 2021-08-21 19:44:03 +08:00
刘祥超
a5c287e8d6 自建DNS支持递归查询 2021-08-21 16:46:49 +08:00
刘祥超
1e9f5c9c56 DNS访问日志显示匹配的线路 2021-08-20 11:27:06 +08:00
刘祥超
fbcf439f89 集群界面数据改成异步加载 2021-08-20 10:51:01 +08:00
刘祥超
766c3666d8 添加DNS账号时自动读取DNS服务商下域名 2021-08-19 14:26:25 +08:00
刘祥超
f08289351d IP名单批量导入IP支持CIDR 2021-08-19 10:50:15 +08:00
刘祥超
e426bba8b5 优化界面 2021-08-18 17:09:09 +08:00
刘祥超
4b200762a5 节点IP地址可以设置阈值 2021-08-18 16:19:07 +08:00
刘祥超
fd203ed436 节点IP增加是否启用、是否在线状态 2021-08-18 09:24:03 +08:00
刘祥超
04cea4dbd6 优化界面 2021-08-17 10:48:38 +08:00
刘祥超
a8f1056c3c 调整版本为0.3.0 2021-08-17 09:44:14 +08:00
刘祥超
c2576f4c62 优化界面 2021-08-16 14:44:32 +08:00
刘祥超
1da5d1094f WAF设置处增加文字提示 2021-08-16 10:03:44 +08:00
刘祥超
34e6122407 修复可能edgeTest无法升级的问题 2021-08-16 10:03:14 +08:00
283 changed files with 4486 additions and 2412 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*_plus.go
*-plus.sh
*-plus.sh
*@plus.js

View File

@@ -1,91 +0,0 @@
#!/usr/bin/env bash
function build() {
ROOT=$(dirname $0)
NAME="edge-admin"
DIST=$ROOT/"../dist/${NAME}"
OS=${1}
ARCH=${2}
if [ -z $OS ]; then
echo "usage: build.sh OS ARCH"
exit
fi
if [ -z $ARCH ]; then
echo "usage: build.sh OS ARCH"
exit
fi
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
ZIP="${NAME}-${OS}-${ARCH}-v${VERSION}.zip"
# check edge-api
APINodeVersion=$(lookup-version $ROOT"/../../EdgeAPI/internal/const/const.go")
echo "building edge-api v${APINodeVersion} ..."
EDGE_API_BUILD_SCRIPT=$ROOT"/../../EdgeAPI/build/build.sh"
if [ ! -f $EDGE_API_BUILD_SCRIPT ]; then
echo "unable to find edge-api build script 'EdgeAPI/build/build.sh'"
exit
fi
cd $ROOT"/../../EdgeAPI/build"
echo "=============================="
./build.sh $OS $ARCH
echo "=============================="
cd -
# create dir & copy files
echo "copying ..."
if [ ! -d $DIST ]; then
mkdir $DIST
mkdir $DIST/bin
mkdir $DIST/configs
mkdir $DIST/logs
fi
cp -R $ROOT/../web $DIST/
rm -f $DIST/web/tmp/*
cp $ROOT/configs/server.template.yaml $DIST/configs/
EDGE_API_ZIP_FILE=$ROOT"/../../EdgeAPI/dist/edge-api-${OS}-${ARCH}-v${APINodeVersion}.zip"
cp $EDGE_API_ZIP_FILE $DIST/
cd $DIST/
unzip -q $(basename $EDGE_API_ZIP_FILE)
rm -f $(basename $EDGE_API_ZIP_FILE)
cd -
# build
echo "building "${NAME}" ..."
env GOOS=$OS GOARCH=$GOARCH go build -ldflags="-s -w" -tags demo -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go
# delete hidden files
find $DIST -name ".DS_Store" -delete
find $DIST -name ".gitignore" -delete
# zip
echo "zip files ..."
cd "${DIST}/../" || exit
if [ -f "${ZIP}" ]; then
rm -f "${ZIP}"
fi
zip -r -X -q "${ZIP}" ${NAME}/
rm -rf ${NAME}
cd - || exit
echo "[done]"
}
function lookup-version() {
FILE=$1
VERSION_DATA=$(cat $FILE)
re="Version[ ]+=[ ]+\"([0-9.]+)\""
if [[ $VERSION_DATA =~ $re ]]; then
VERSION=${BASH_REMATCH[1]}
echo $VERSION
else
echo "could not match version"
exit
fi
}
build $1 $2

View File

@@ -1,7 +1,9 @@
package teaconst
const (
Version = "0.2.9"
Version = "0.3.0"
APINodeVersion = "0.3.0"
ProductName = "Edge Admin"
ProcessName = "edge-admin"

View File

@@ -99,6 +99,10 @@ func (this *RPCClient) NodeIPAddressRPC() pb.NodeIPAddressServiceClient {
return pb.NewNodeIPAddressServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressLogRPC() pb.NodeIPAddressLogServiceClient {
return pb.NewNodeIPAddressLogServiceClient(this.pickConn())
}
func (this *RPCClient) NodeValueRPC() pb.NodeValueServiceClient {
return pb.NewNodeValueServiceClient(this.pickConn())
}
@@ -296,6 +300,14 @@ func (this *RPCClient) IPListRPC() pb.IPListServiceClient {
return pb.NewIPListServiceClient(this.pickConn())
}
func (this *RPCClient) ReportNodeRPC() pb.ReportNodeServiceClient {
return pb.NewReportNodeServiceClient(this.pickConn())
}
func (this *RPCClient) ReportResultRPC() pb.ReportResultServiceClient {
return pb.NewReportResultServiceClient(this.pickConn())
}
func (this *RPCClient) IPItemRPC() pb.IPItemServiceClient {
return pb.NewIPItemServiceClient(this.pickConn())
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"regexp"
)
type CreatePopupAction struct {
@@ -29,6 +30,14 @@ func (this *CreatePopupAction) RunPost(params struct {
GroupIds string
Description string
TimeFromHour string
TimeFromMinute string
TimeFromSecond string
TimeToHour string
TimeToMinute string
TimeToSecond string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -40,12 +49,26 @@ func (this *CreatePopupAction) RunPost(params struct {
groupIds := utils.SplitNumbers(params.GroupIds)
var digitReg = regexp.MustCompile(`^\d+$`)
var timeFrom = ""
if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
}
var timeTo = ""
if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
}
resp, err := this.RPC().MessageRecipientRPC().CreateMessageRecipient(this.AdminContext(), &pb.CreateMessageRecipientRequest{
AdminId: params.AdminId,
MessageMediaInstanceId: params.InstanceId,
User: params.User,
MessageRecipientGroupIds: groupIds,
Description: params.Description,
TimeFrom: timeFrom,
TimeTo: timeTo,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -3,6 +3,7 @@ package instances
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -65,6 +66,11 @@ func (this *CreatePopupAction) RunPost(params struct {
TelegramToken string
RateMinutes int32
RateCount int32
HashLife int32
Description string
Must *actions.Must
@@ -231,11 +237,23 @@ func (this *CreatePopupAction) RunPost(params struct {
return
}
var rateConfig = &monitorconfigs.RateConfig{
Minutes: params.RateMinutes,
Count: params.RateCount,
}
rateJSON, err := json.Marshal(rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
resp, err := this.RPC().MessageMediaInstanceRPC().CreateMessageMediaInstance(this.AdminContext(), &pb.CreateMessageMediaInstanceRequest{
Name: params.Name,
MediaType: params.MediaType,
ParamsJSON: optionsJSON,
Description: params.Description,
RateJSON: rateJSON,
HashLife: params.HashLife,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -3,6 +3,7 @@ package instances
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
@@ -38,6 +39,16 @@ func (this *InstanceAction) RunGet(params struct {
}
}
// 频率
var rateConfig = &monitorconfigs.RateConfig{}
if len(instance.RateJSON) > 0 {
err = json.Unmarshal(instance.RateJSON, rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["instance"] = maps.Map{
"id": instance.Id,
"name": instance.Name,
@@ -48,6 +59,8 @@ func (this *InstanceAction) RunGet(params struct {
},
"description": instance.Description,
"params": mediaParams,
"rate": rateConfig,
"hashLife": instance.HashLife,
}
this.Show()

View File

@@ -3,6 +3,7 @@ package instances
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/monitorconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
@@ -39,6 +40,15 @@ func (this *UpdateAction) RunGet(params struct {
}
}
var rateConfig = &monitorconfigs.RateConfig{}
if len(instance.RateJSON) > 0 {
err = json.Unmarshal(instance.RateJSON, rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["instance"] = maps.Map{
"id": instance.Id,
"name": instance.Name,
@@ -49,6 +59,8 @@ func (this *UpdateAction) RunGet(params struct {
},
"description": instance.Description,
"params": mediaParams,
"rate": rateConfig,
"hashLife": instance.HashLife,
}
this.Show()
@@ -104,6 +116,11 @@ func (this *UpdateAction) RunPost(params struct {
Description string
IsOn bool
RateMinutes int32
RateCount int32
HashLife int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -270,12 +287,24 @@ func (this *UpdateAction) RunPost(params struct {
return
}
var rateConfig = &monitorconfigs.RateConfig{
Minutes: params.RateMinutes,
Count: params.RateCount,
}
rateJSON, err := json.Marshal(rateConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().MessageMediaInstanceRPC().UpdateMessageMediaInstance(this.AdminContext(), &pb.UpdateMessageMediaInstanceRequest{
MessageMediaInstanceId: params.InstanceId,
Name: params.Name,
MediaType: params.MediaType,
ParamsJSON: optionsJSON,
Description: params.Description,
RateJSON: rateJSON,
HashLife: params.HashLife,
IsOn: params.IsOn,
})
if err != nil {

View File

@@ -44,6 +44,8 @@ func (this *RecipientAction) RunGet(params struct {
},
"user": recipient.User,
"description": recipient.Description,
"timeFrom": recipient.TimeFrom,
"timeTo": recipient.TimeTo,
}
this.Show()

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package tasks
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo("删除消息发送任务 %d", params.TaskId)
_, err := this.RPC().MessageTaskRPC().DeleteMessageTask(this.AdminContext(), &pb.DeleteMessageTaskRequest{MessageTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,103 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package tasks
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "task")
}
func (this *IndexAction) RunGet(params struct {
Status int32
}) {
this.Data["status"] = params.Status
if params.Status > 3 {
params.Status = 0
}
countWaitingResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusNone})
if err != nil {
this.ErrorPage(err)
return
}
var countWaiting = countWaitingResp.Count
this.Data["countWaiting"] = countWaiting
countFailedResp, err := this.RPC().MessageTaskRPC().CountMessageTasksWithStatus(this.AdminContext(), &pb.CountMessageTasksWithStatusRequest{Status: pb.CountMessageTasksWithStatusRequest_MessageTaskStatusFailed})
if err != nil {
this.ErrorPage(err)
return
}
var countFailed = countFailedResp.Count
this.Data["countFailed"] = countFailed
// 列表
var total = int64(0)
switch params.Status {
case 0:
total = countWaiting
case 3:
total = countFailed
}
page := this.NewPage(total)
this.Data["page"] = page.AsHTML()
var taskMaps = []maps.Map{}
tasksResp, err := this.RPC().MessageTaskRPC().ListMessageTasksWithStatus(this.AdminContext(), &pb.ListMessageTasksWithStatusRequest{
Status: pb.ListMessageTasksWithStatusRequest_Status(params.Status),
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
for _, task := range tasksResp.MessageTasks {
var resultMap = maps.Map{}
var result = task.Result
if result != nil {
resultMap = maps.Map{
"isOk": result.IsOk,
"error": result.Error,
"response": result.Response,
}
}
//var recipients = []string{}
var user = ""
var instanceMap maps.Map
if task.MessageRecipient != nil {
user = task.MessageRecipient.User
if task.MessageRecipient.MessageMediaInstance != nil {
instanceMap = maps.Map{
"id": task.MessageRecipient.MessageMediaInstance.Id,
"name": task.MessageRecipient.MessageMediaInstance.Name,
}
}
}
taskMaps = append(taskMaps, maps.Map{
"id": task.Id,
"subject": task.Subject,
"body": task.Body,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
"result": resultMap,
"status": task.Status,
"user": user,
"instance": instanceMap,
})
}
this.Data["tasks"] = taskMaps
this.Show()
}

View File

@@ -13,7 +13,9 @@ func init() {
Data("teaMenu", "admins").
Data("teaSubMenu", "recipients").
Prefix("/admins/recipients/tasks").
Get("", new(IndexAction)).
Post("/taskInfo", new(TaskInfoAction)).
Post("/delete", new(DeleteAction)).
EndAll()
})
}

View File

@@ -6,6 +6,8 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"regexp"
"strings"
)
type UpdateAction struct {
@@ -30,6 +32,27 @@ func (this *UpdateAction) RunGet(params struct {
return
}
var timeFromHour = ""
var timeFromMinute = ""
var timeFromSecond = ""
if len(recipient.TimeFrom) > 0 {
var pieces = strings.Split(recipient.TimeFrom, ":")
timeFromHour = pieces[0]
timeFromMinute = pieces[1]
timeFromSecond = pieces[2]
}
var timeToHour = ""
var timeToMinute = ""
var timeToSecond = ""
if len(recipient.TimeTo) > 0 {
var pieces = strings.Split(recipient.TimeTo, ":")
timeToHour = pieces[0]
timeToMinute = pieces[1]
timeToSecond = pieces[2]
}
this.Data["recipient"] = maps.Map{
"id": recipient.Id,
"admin": maps.Map{
@@ -43,8 +66,14 @@ func (this *UpdateAction) RunGet(params struct {
"id": recipient.MessageMediaInstance.Id,
"name": recipient.MessageMediaInstance.Name,
},
"user": recipient.User,
"description": recipient.Description,
"user": recipient.User,
"description": recipient.Description,
"timeFromHour": timeFromHour,
"timeFromMinute": timeFromMinute,
"timeFromSecond": timeFromSecond,
"timeToHour": timeToHour,
"timeToMinute": timeToMinute,
"timeToSecond": timeToSecond,
}
this.Show()
@@ -61,6 +90,14 @@ func (this *UpdateAction) RunPost(params struct {
Description string
IsOn bool
TimeFromHour string
TimeFromMinute string
TimeFromSecond string
TimeToHour string
TimeToMinute string
TimeToSecond string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
@@ -74,6 +111,18 @@ func (this *UpdateAction) RunPost(params struct {
groupIds := utils.SplitNumbers(params.GroupIds)
var digitReg = regexp.MustCompile(`^\d+$`)
var timeFrom = ""
if digitReg.MatchString(params.TimeFromHour) && digitReg.MatchString(params.TimeFromMinute) && digitReg.MatchString(params.TimeFromSecond) {
timeFrom = params.TimeFromHour + ":" + params.TimeFromMinute + ":" + params.TimeFromSecond
}
var timeTo = ""
if digitReg.MatchString(params.TimeToHour) && digitReg.MatchString(params.TimeToMinute) && digitReg.MatchString(params.TimeToSecond) {
timeTo = params.TimeToHour + ":" + params.TimeToMinute + ":" + params.TimeToSecond
}
_, err := this.RPC().MessageRecipientRPC().UpdateMessageRecipient(this.AdminContext(), &pb.UpdateMessageRecipientRequest{
MessageRecipientId: params.RecipientId,
AdminId: params.AdminId,
@@ -82,6 +131,8 @@ func (this *UpdateAction) RunPost(params struct {
MessageRecipientGroupIds: groupIds,
Description: params.Description,
IsOn: params.IsOn,
TimeFrom: timeFrom,
TimeTo: timeTo,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -1,182 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"strconv"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "board", "")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
if !teaconst.IsPlus {
this.RedirectURL("/clusters/cluster?clusterId=" + strconv.FormatInt(params.ClusterId, 10))
return
}
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeClusterBoard(this.AdminContext(), &pb.ComposeServerStatNodeClusterBoardRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"countUsers": resp.CountUsers,
"countActiveNodes": resp.CountActiveNodes,
"countInactiveNodes": resp.CountInactiveNodes,
"countServers": resp.CountServers,
}
// 24小时流量趋势
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
})
}
this.Data["hourlyStats"] = statMaps
}
// 15天流量趋势
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
})
}
this.Data["dailyStats"] = statMaps
}
// 节点排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNodeStats {
statMaps = append(statMaps, maps.Map{
"nodeId": stat.NodeId,
"nodeName": stat.NodeName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topNodeStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopDomainStats {
statMaps = append(statMaps, maps.Map{
"serverId": stat.ServerId,
"domain": stat.Domain,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["loadValues"] = statMaps
}
// 指标
{
var chartMaps = []maps.Map{}
for _, chart := range resp.MetricDataCharts {
var statMaps = []maps.Map{}
for _, stat := range chart.MetricStats {
statMaps = append(statMaps, maps.Map{
"keys": stat.Keys,
"time": stat.Time,
"value": stat.Value,
"count": stat.SumCount,
"total": stat.SumTotal,
})
}
chartMaps = append(chartMaps, maps.Map{
"chart": maps.Map{
"id": chart.MetricChart.Id,
"name": chart.MetricChart.Name,
"widthDiv": chart.MetricChart.WidthDiv,
"isOn": chart.MetricChart.IsOn,
"maxItems": chart.MetricChart.MaxItems,
"type": chart.MetricChart.Type,
},
"item": maps.Map{
"id": chart.MetricChart.MetricItem.Id,
"name": chart.MetricChart.MetricItem.Name,
"period": chart.MetricChart.MetricItem.Period,
"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
"valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"valueTypeName": serverconfigs.FindMetricValueName(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"keys": chart.MetricChart.MetricItem.Keys,
},
"stats": statMaps,
})
}
this.Data["metricCharts"] = chartMaps
}
this.Show()
}

View File

@@ -171,16 +171,23 @@ func (this *CreateNodeAction) RunPost(params struct {
addressId := address.GetInt64("id")
if addressId > 0 {
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
AddressId: addressId,
NodeId: nodeId,
NodeIPAddressId: addressId,
NodeId: nodeId,
})
} else {
var thresholdsJSON = []byte{}
var thresholds = address.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, _ = json.Marshal(thresholds)
}
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: address.GetString("name"),
Ip: address.GetString("ip"),
CanAccess: address.GetBool("canAccess"),
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: address.GetString("name"),
Ip: address.GetString("ip"),
CanAccess: address.GetBool("canAccess"),
ThresholdsJSON: thresholdsJSON,
})
}
if err != nil {

View File

@@ -2,10 +2,8 @@ package cluster
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/boards"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node"
nodeboards "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/boards"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
@@ -46,7 +44,8 @@ func init() {
Post("/up", new(node.UpAction)).
Get("/thresholds", new(thresholds.IndexAction)).
Get("/detail", new(node.DetailAction)).
GetPost("/boards", new(nodeboards.IndexAction)).
GetPost("/updateDNSPopup", new(node.UpdateDNSPopupAction)).
Post("/syncDomain", new(node.SyncDomainAction)).
// 分组相关
Prefix("/clusters/cluster/groups").
@@ -56,10 +55,6 @@ func init() {
Post("/delete", new(groups.DeleteAction)).
Post("/sort", new(groups.SortAction)).
GetPost("/selectPopup", new(groups.SelectPopupAction)).
// 看板相关
Prefix("/clusters/cluster/boards").
Get("", new(boards.IndexAction)).
EndAll()
})
}

View File

@@ -1,232 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"strconv"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "node", "board")
this.SecondMenu("nodes")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
err := nodeutils.InitNodeInfo(this, params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
if !teaconst.IsPlus {
this.RedirectURL("/clusters/cluster/node?clusterId=" + strconv.FormatInt(params.ClusterId, 10) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
return
}
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeBoard(this.AdminContext(), &pb.ComposeServerStatNodeBoardRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"isActive": resp.IsActive,
"trafficInBytes": resp.TrafficInBytes,
"trafficOutBytes": resp.TrafficOutBytes,
"countConnections": resp.CountConnections,
"countRequests": resp.CountRequests,
"countAttackRequests": resp.CountAttackRequests,
"cpuUsage": resp.CpuUsage,
"memoryUsage": resp.MemoryUsage,
"memoryTotalSize": resp.MemoryTotalSize,
"load": resp.Load,
"cacheDiskSize": resp.CacheDiskSize,
"cacheMemorySize": resp.CacheMemorySize,
}
// 24小时流量趋势
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
})
}
this.Data["hourlyStats"] = statMaps
}
// 15天流量趋势
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
})
}
this.Data["dailyStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopDomainStats {
statMaps = append(statMaps, maps.Map{
"serverId": stat.ServerId,
"domain": stat.Domain,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["loadValues"] = statMaps
}
// CacheDirs
{
var statMaps = []maps.Map{}
for _, stat := range resp.CacheDirsValues {
var m = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &m)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": m,
})
}
this.Data["cacheDirValues"] = statMaps
}
// 指标
{
var chartMaps = []maps.Map{}
for _, chart := range resp.MetricDataCharts {
var statMaps = []maps.Map{}
for _, stat := range chart.MetricStats {
statMaps = append(statMaps, maps.Map{
"keys": stat.Keys,
"time": stat.Time,
"value": stat.Value,
"count": stat.SumCount,
"total": stat.SumTotal,
})
}
chartMaps = append(chartMaps, maps.Map{
"chart": maps.Map{
"id": chart.MetricChart.Id,
"name": chart.MetricChart.Name,
"widthDiv": chart.MetricChart.WidthDiv,
"isOn": chart.MetricChart.IsOn,
"maxItems": chart.MetricChart.MaxItems,
"type": chart.MetricChart.Type,
},
"item": maps.Map{
"id": chart.MetricChart.MetricItem.Id,
"name": chart.MetricChart.MetricItem.Name,
"period": chart.MetricChart.MetricItem.Period,
"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
"valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"valueTypeName": serverconfigs.FindMetricValueName(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"keys": chart.MetricChart.MetricItem.Keys,
},
"stats": statMaps,
})
}
this.Data["metricCharts"] = chartMaps
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
NodeId int64
}) {
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeBoard(this.AdminContext(), &pb.ComposeServerStatNodeBoardRequest{NodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"isActive": resp.IsActive,
"trafficInBytes": resp.TrafficInBytes,
"trafficOutBytes": resp.TrafficOutBytes,
"countConnections": resp.CountConnections,
"countRequests": resp.CountRequests,
"countAttackRequests": resp.CountAttackRequests,
"cpuUsage": resp.CpuUsage,
"memoryUsage": resp.MemoryUsage,
"memoryTotalSize": resp.MemoryTotalSize,
"load": resp.Load,
"cacheDiskSize": resp.CacheDiskSize,
"cacheMemorySize": resp.CacheMemorySize,
}
this.Success()
}

View File

@@ -79,11 +79,22 @@ func (this *DetailAction) RunGet(params struct {
var ipAddresses = ipAddressesResp.Addresses
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if len(addr.ThresholdsJSON) > 0 {
err = json.Unmarshal(addr.ThresholdsJSON, &thresholds)
if err != nil {
this.ErrorPage(err)
return
}
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"thresholds": thresholds,
})
}
@@ -121,7 +132,7 @@ func (this *DetailAction) RunGet(params struct {
}
for _, addr := range ipAddresses {
if !addr.CanAccess {
if !addr.CanAccess || !addr.IsUp || !addr.IsOn {
continue
}
for _, route := range dnsInfo.Routes {

View File

@@ -24,6 +24,8 @@ func InitNodeInfo(action actionutils.ActionInterface, nodeId int64) error {
action.ViewData()["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
"isOn": node.IsOn,
"isUp": node.IsUp,
}
if node.NodeCluster != nil {
action.ViewData()["clusterId"] = node.NodeCluster.Id

View File

@@ -0,0 +1,33 @@
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SyncDomainAction struct {
actionutils.ParentAction
}
func (this *SyncDomainAction) RunPost(params struct {
DomainId int64
}) {
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "同步DNS域名数据 %d", params.DomainId)
// 执行同步
resp, err := this.RPC().DNSDomainRPC().SyncDNSDomainData(this.AdminContext(), &pb.SyncDNSDomainDataRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if resp.IsOk {
this.Success()
} else {
this.Data["shouldFix"] = resp.ShouldFix
this.Fail(resp.Error)
}
this.Success()
}

View File

@@ -57,11 +57,23 @@ func (this *UpdateAction) RunGet(params struct {
}
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if len(addr.ThresholdsJSON) > 0 {
err = json.Unmarshal(addr.ThresholdsJSON, &thresholds)
if err != nil {
this.ErrorPage(err)
return
}
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"id": addr.Id,
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
"thresholds": thresholds,
})
}

View File

@@ -0,0 +1,115 @@
package node
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
)
type UpdateDNSPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateDNSPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateDNSPopupAction) RunGet(params struct {
ClusterId int64
NodeId int64
}) {
this.Data["nodeId"] = params.NodeId
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
dnsInfo := dnsInfoResp.Node
if dnsInfo == nil {
this.NotFound("node", params.NodeId)
return
}
this.Data["ipAddr"] = dnsInfo.IpAddr
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
this.Data["domainId"] = dnsInfo.DnsDomainId
this.Data["domainName"] = dnsInfo.DnsDomainName
// 读取所有线路
allRouteMaps := []maps.Map{}
if dnsInfo.DnsDomainId > 0 {
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: dnsInfo.DnsDomainId})
if err != nil {
this.ErrorPage(err)
return
}
if len(routesResp.Routes) > 0 {
for _, route := range routesResp.Routes {
allRouteMaps = append(allRouteMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"domainName": dnsInfo.DnsDomainName,
"domainId": dnsInfo.DnsDomainId,
})
}
// 筛选
var routes = domainutils.FilterRoutes(dnsInfo.Routes, routesResp.Routes)
dnsInfo.Routes = routes
this.Data["routes"] = domainutils.ConvertRoutesToMaps(dnsInfo)
}
}
this.Data["allRoutes"] = allRouteMaps
this.Show()
}
func (this *UpdateDNSPopupAction) RunPost(params struct {
NodeId int64
IpAddr string
DomainId int64
DnsRoutesJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 操作日志
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d 的DNS设置", params.NodeId)
routes := []string{}
err := json.Unmarshal(params.DnsRoutesJSON, &routes)
if err != nil {
this.ErrorPage(err)
return
}
params.Must.
Field("ipAddr", params.IpAddr).
Require("请输入IP地址")
if net.ParseIP(params.IpAddr) == nil {
this.FailField("ipAddr", "请输入正确的IP地址")
}
// 执行修改
_, err = this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
NodeId: params.NodeId,
IpAddr: params.IpAddr,
DnsDomainId: params.DomainId,
Routes: routes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -41,6 +41,15 @@ func (this *NodesAction) RunGet(params struct {
this.Data["activeState"] = params.ActiveState
this.Data["keyword"] = params.Keyword
// 集群是否已经设置了线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasClusterDNS"] = clusterDNSResp.Domain != nil
// 数量
countAllResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{
NodeClusterId: params.ClusterId,
})
@@ -129,6 +138,8 @@ func (this *NodesAction) RunGet(params struct {
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isUp": addr.IsUp,
"isOn": addr.IsOn,
})
}

View File

@@ -19,6 +19,7 @@ func (this *CreateAction) Init() {
}
func (this *CreateAction) RunGet(params struct{}) {
// 是否有域名
hasDomainsResp, err := this.RPC().DNSDomainRPC().ExistAvailableDomains(this.AdminContext(), &pb.ExistAvailableDomainsRequest{})
if err != nil {
this.ErrorPage(err)
@@ -53,13 +54,6 @@ func (this *CreateAction) RunPost(params struct {
Field("name", params.Name).
Require("请输入集群名称")
if params.CachePolicyId <= 0 {
this.Fail("请选择或者创建缓存策略")
}
if params.HttpFirewallPolicyId <= 0 {
this.Fail("请选择或者创建WAF策略")
}
// 检查DNS名称
if len(params.DnsName) > 0 {
if !domainutils.ValidateDomainFormat(params.DnsName) {
@@ -113,5 +107,7 @@ func (this *CreateAction) RunPost(params struct {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "创建节点集群:%d", createResp.NodeClusterId)
this.Data["clusterId"] = createResp.NodeClusterId
this.Success()
}

View File

@@ -208,6 +208,8 @@ func (this *IndexAction) searchNodes(keyword string) {
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}

View File

@@ -20,21 +20,28 @@ func (this *SelectPopupAction) Init() {
func (this *SelectPopupAction) RunGet(params struct {
SelectedClusterIds string
Keyword string
}) {
this.Data["keyword"] = params.Keyword
var selectedIds = utils.SplitNumbers(params.SelectedClusterIds)
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
page.Size = 6
this.Data["page"] = page.AsHTML()
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -1,133 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type DnsAction struct {
actionutils.ParentAction
}
func (this *DnsAction) Init() {
this.Nav("", "", "dns")
}
func (this *DnsAction) RunGet(params struct{}) {
if !teaconst.IsPlus {
this.RedirectURL("/dashboard")
return
}
resp, err := this.RPC().NSRPC().ComposeNSBoard(this.AdminContext(), &pb.ComposeNSBoardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"countDomains": resp.CountNSDomains,
"countRecords": resp.CountNSRecords,
"countClusters": resp.CountNSClusters,
"countNodes": resp.CountNSNodes,
"countOfflineNodes": resp.CountOfflineNSNodes,
}
// 流量排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["hourlyStats"] = statMaps
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["dailyStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSDomainStats {
statMaps = append(statMaps, maps.Map{
"domainId": stat.NsDomainId,
"domainName": stat.NsDomainName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// 节点排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSNodeStats {
statMaps = append(statMaps, maps.Map{
"clusterId": stat.NsClusterId,
"nodeId": stat.NsNodeId,
"nodeName": stat.NsNodeName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topNodeStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["loadValues"] = statMaps
}
this.Show()
}

View File

@@ -1,249 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
"regexp"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
if !teaconst.IsPlus {
this.RedirectURL("/dashboard")
return
}
// 取得用户的权限
module, ok := configloaders.FindFirstAdminModule(this.AdminId())
if ok {
if module != "dashboard" {
for _, m := range configloaders.AllModuleMaps() {
if m.GetString("code") == module {
this.RedirectURL(m.GetString("url"))
return
}
}
}
}
// 读取看板数据
resp, err := this.RPC().AdminRPC().ComposeAdminDashboard(this.AdminContext(), &pb.ComposeAdminDashboardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["dashboard"] = maps.Map{
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,
"countUsers": resp.CountUsers,
"countAPINodes": resp.CountAPINodes,
"countDBNodes": resp.CountDBNodes,
"countUserNodes": resp.CountUserNodes,
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
"canGoSettings": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeSetting),
"canGoUsers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeUser),
}
// 今日流量
todayTrafficBytes := int64(0)
if len(resp.DailyTrafficStats) > 0 {
todayTrafficBytes = resp.DailyTrafficStats[len(resp.DailyTrafficStats)-1].Bytes
}
todayTrafficString := numberutils.FormatBytes(todayTrafficBytes)
result := regexp.MustCompile(`^(?U)(.+)([a-zA-Z]+)$`).FindStringSubmatch(todayTrafficString)
if len(result) > 2 {
this.Data["todayTraffic"] = result[1]
this.Data["todayTrafficUnit"] = result[2]
} else {
this.Data["todayTraffic"] = todayTrafficString
this.Data["todayTrafficUnit"] = ""
}
// 24小时流量趋势
{
statMaps := []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
})
}
this.Data["hourlyTrafficStats"] = statMaps
}
// 15天流量趋势
{
statMaps := []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"cachedBytes": stat.CachedBytes,
"countRequests": stat.CountRequests,
"countCachedRequests": stat.CountCachedRequests,
"countAttackRequests": stat.CountAttackRequests,
"attackBytes": stat.AttackBytes,
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
})
}
this.Data["dailyTrafficStats"] = statMaps
}
// 节点排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNodeStats {
statMaps = append(statMaps, maps.Map{
"nodeId": stat.NodeId,
"nodeName": stat.NodeName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topNodeStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopDomainStats {
statMaps = append(statMaps, maps.Map{
"serverId": stat.ServerId,
"domain": stat.Domain,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// 版本升级
if resp.NodeUpgradeInfo != nil {
this.Data["nodeUpgradeInfo"] = maps.Map{
"count": resp.NodeUpgradeInfo.CountNodes,
"version": resp.NodeUpgradeInfo.NewVersion,
}
} else {
this.Data["nodeUpgradeInfo"] = maps.Map{
"count": 0,
"version": "",
}
}
if resp.MonitorNodeUpgradeInfo != nil {
this.Data["monitorNodeUpgradeInfo"] = maps.Map{
"count": resp.MonitorNodeUpgradeInfo.CountNodes,
"version": resp.MonitorNodeUpgradeInfo.NewVersion,
}
} else {
this.Data["monitorNodeUpgradeInfo"] = maps.Map{
"count": 0,
"version": "",
}
}
if resp.ApiNodeUpgradeInfo != nil {
this.Data["apiNodeUpgradeInfo"] = maps.Map{
"count": resp.ApiNodeUpgradeInfo.CountNodes,
"version": resp.ApiNodeUpgradeInfo.NewVersion,
}
} else {
this.Data["apiNodeUpgradeInfo"] = maps.Map{
"count": 0,
"version": "",
}
}
if resp.UserNodeUpgradeInfo != nil {
this.Data["userNodeUpgradeInfo"] = maps.Map{
"count": resp.UserNodeUpgradeInfo.CountNodes,
"version": resp.UserNodeUpgradeInfo.NewVersion,
}
} else {
this.Data["userNodeUpgradeInfo"] = maps.Map{
"count": 0,
"version": 0,
}
}
if resp.AuthorityNodeUpgradeInfo != nil {
this.Data["authorityNodeUpgradeInfo"] = maps.Map{
"count": resp.AuthorityNodeUpgradeInfo.CountNodes,
"version": resp.AuthorityNodeUpgradeInfo.NewVersion,
}
} else {
this.Data["authorityNodeUpgradeInfo"] = maps.Map{
"count": 0,
"version": "",
}
}
if resp.NsNodeUpgradeInfo != nil {
this.Data["nsNodeUpgradeInfo"] = maps.Map{
"count": resp.NsNodeUpgradeInfo.CountNodes,
"version": resp.NsNodeUpgradeInfo.NewVersion,
}
} else {
this.Data["nsNodeUpgradeInfo"] = maps.Map{
"count": 0,
"version": "",
}
}
// 指标
{
var chartMaps = []maps.Map{}
for _, chart := range resp.MetricDataCharts {
var statMaps = []maps.Map{}
for _, stat := range chart.MetricStats {
statMaps = append(statMaps, maps.Map{
"keys": stat.Keys,
"time": stat.Time,
"value": stat.Value,
"count": stat.SumCount,
"total": stat.SumTotal,
})
}
chartMaps = append(chartMaps, maps.Map{
"chart": maps.Map{
"id": chart.MetricChart.Id,
"name": chart.MetricChart.Name,
"widthDiv": chart.MetricChart.WidthDiv,
"isOn": chart.MetricChart.IsOn,
"maxItems": chart.MetricChart.MaxItems,
"type": chart.MetricChart.Type,
},
"item": maps.Map{
"id": chart.MetricChart.MetricItem.Id,
"name": chart.MetricChart.MetricItem.Name,
"period": chart.MetricChart.MetricItem.Period,
"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
"valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"valueTypeName": serverconfigs.FindMetricValueName(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
"keys": chart.MetricChart.MetricItem.Keys,
},
"stats": statMaps,
})
}
this.Data["metricCharts"] = chartMaps
}
this.Show()
}

View File

@@ -1,103 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UserAction struct {
actionutils.ParentAction
}
func (this *UserAction) Init() {
this.Nav("", "", "user")
}
func (this *UserAction) RunGet(params struct{}) {
if !teaconst.IsPlus {
this.RedirectURL("/dashboard")
return
}
resp, err := this.RPC().UserRPC().ComposeUserGlobalBoard(this.AdminContext(), &pb.ComposeUserGlobalBoardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"totalUsers": resp.TotalUsers,
"countTodayUsers": resp.CountTodayUsers,
"countWeeklyUsers": resp.CountWeeklyUsers,
"countUserNodes": resp.CountUserNodes,
"countOfflineUserNodes": resp.CountOfflineUserNodes,
}
{
statMaps := []maps.Map{}
for _, stat := range resp.DailyStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Day,
"count": stat.Count,
})
}
this.Data["dailyStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["loadValues"] = statMaps
}
// 流量排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopTrafficStats {
statMaps = append(statMaps, maps.Map{
"userId": stat.UserId,
"userName": stat.UserName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topTrafficStats"] = statMaps
}
this.Show()
}

View File

@@ -1,76 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type WafAction struct {
actionutils.ParentAction
}
func (this *WafAction) Init() {
this.Nav("", "", "waf")
}
func (this *WafAction) RunGet(params struct{}) {
if !teaconst.IsPlus {
this.RedirectURL("/dashboard")
return
}
resp, err := this.RPC().FirewallRPC().ComposeFirewallGlobalBoard(this.AdminContext(), &pb.ComposeFirewallGlobalBoardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"countDailyLogs": resp.CountDailyLogs,
"countDailyBlocks": resp.CountDailyBlocks,
"countDailyCaptcha": resp.CountDailyCaptcha,
"countWeeklyBlocks": resp.CountWeeklyBlocks,
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyStats {
statMaps = append(statMaps, maps.Map{
"hour": stat.Hour,
"countLogs": stat.CountLogs,
"countCaptcha": stat.CountCaptcha,
"countBlocks": stat.CountBlocks,
})
}
this.Data["hourlyStats"] = statMaps
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Day,
"countLogs": stat.CountLogs,
"countCaptcha": stat.CountCaptcha,
"countBlocks": stat.CountBlocks,
})
}
this.Data["dailyStats"] = statMaps
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.HttpFirewallRuleGroups {
statMaps = append(statMaps, maps.Map{
"name": stat.HttpFirewallRuleGroup.Name,
"count": stat.Count,
})
}
this.Data["groupStats"] = statMaps
}
this.Show()
}

View File

@@ -1,28 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package boards
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type WafLogsAction struct {
actionutils.ParentAction
}
func (this *WafLogsAction) RunPost(params struct{}) {
resp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
HasFirewallPolicy: true,
Reverse: false,
Day: timeutil.Format("Ymd"),
Size: 5,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["accessLogs"] = resp.HttpAccessLogs
this.Success()
}

View File

@@ -41,12 +41,16 @@ func (this *IndexAction) RunGet(params struct{}) {
}
// 读取看板数据
resp, err := this.RPC().AdminRPC().ComposeAdminDashboard(this.AdminContext(), &pb.ComposeAdminDashboardRequest{})
resp, err := this.RPC().AdminRPC().ComposeAdminDashboard(this.AdminContext(), &pb.ComposeAdminDashboardRequest{
ApiVersion: teaconst.APINodeVersion,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["dashboard"] = maps.Map{
"defaultClusterId": resp.DefaultNodeClusterId,
"countServers": resp.CountServers,
"countNodeClusters": resp.CountNodeClusters,
"countNodes": resp.CountNodes,

View File

@@ -2,7 +2,6 @@ package dashboard
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard/boards"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
@@ -13,15 +12,6 @@ func init() {
Data("teaMenu", "dashboard").
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
GetPost("", new(IndexAction)).
// 看板
Prefix("/dashboard/boards").
Get("", new(boards.IndexAction)).
Get("/waf", new(boards.WafAction)).
Post("/wafLogs", new(boards.WafLogsAction)).
Get("/dns", new(boards.DnsAction)).
Get("/user", new(boards.UserAction)).
EndAll()
})
}

View File

@@ -3,28 +3,35 @@ package db
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db/dbnodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CleanPopupAction struct {
type CleanAction struct {
actionutils.ParentAction
}
func (this *CleanPopupAction) Init() {
this.Nav("", "", "")
func (this *CleanAction) Init() {
this.Nav("", "", "clean")
}
func (this *CleanPopupAction) RunGet(params struct {
func (this *CleanAction) RunGet(params struct {
NodeId int64
}) {
_, err := dbnodeutils.InitNode(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
this.Show()
}
func (this *CleanPopupAction) RunPost(params struct {
func (this *CleanAction) RunPost(params struct {
NodeId int64
Must *actions.Must
@@ -33,8 +40,7 @@ func (this *CleanPopupAction) RunPost(params struct {
DbNodeId: params.NodeId,
})
if err != nil {
this.ErrorPage(err)
return
this.Fail("查询数据时出错了:" + err.Error())
}
tableMaps := []maps.Map{}

View File

@@ -0,0 +1,34 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package dbnodeutils
import (
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
// InitNode 初始化指标信息
func InitNode(parent *actionutils.ParentAction, nodeId int64) (*pb.DBNode, error) {
client, err := rpc.SharedRPC()
if err != nil {
return nil, err
}
resp, err := client.DBNodeRPC().FindEnabledDBNode(parent.AdminContext(), &pb.FindEnabledDBNodeRequest{DbNodeId: nodeId})
if err != nil {
return nil, err
}
var node = resp.DbNode
if node == nil {
return nil, errors.New("not found db node with id '" + types.String(nodeId) + "'")
}
parent.Data["node"] = maps.Map{
"id": node.Id,
"name": node.Name,
}
return node, nil
}

View File

@@ -42,9 +42,10 @@ func (this *IndexAction) RunGet(params struct{}) {
"port": node.Port,
"database": node.Database,
"status": maps.Map{
"isOk": node.Status.IsOk,
"error": node.Status.Error,
"size": numberutils.FormatBytes(node.Status.Size),
"isOk": node.Status.IsOk,
"error": node.Status.Error,
"size": numberutils.FormatBytes(node.Status.Size),
"version": node.Status.Version,
},
})
}

View File

@@ -16,12 +16,14 @@ func init() {
Prefix("/db").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
GetPost("/update", new(UpdateAction)).
Post("/delete", new(DeleteAction)).
GetPost("/cleanPopup", new(CleanPopupAction)).
GetPost("/clean", new(CleanAction)).
Post("/deleteTable", new(DeleteTableAction)).
Post("/truncateTable", new(TruncateTableAction)).
Get("/node", new(NodeAction)).
Get("/logs", new(LogsAction)).
Post("/status", new(StatusAction)).
EndAll()
})
}

View File

@@ -0,0 +1,82 @@
package db
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db/dbnodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type LogsAction struct {
actionutils.ParentAction
}
func (this *LogsAction) Init() {
this.Nav("", "", "log")
}
func (this *LogsAction) RunGet(params struct {
NodeId int64
DayFrom string
DayTo string
Keyword string
Level string
}) {
_, err := dbnodeutils.InitNode(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["nodeId"] = params.NodeId
this.Data["dayFrom"] = params.DayFrom
this.Data["dayTo"] = params.DayTo
this.Data["keyword"] = params.Keyword
this.Data["level"] = params.Level
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
Role: nodeconfigs.NodeRoleDatabase,
NodeId: params.NodeId,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count, 20)
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
NodeId: params.NodeId,
Role: nodeconfigs.NodeRoleDatabase,
DayFrom: params.DayFrom,
DayTo: params.DayTo,
Keyword: params.Keyword,
Level: params.Level,
Offset: page.Offset,
Size: page.Size,
})
logs := []maps.Map{}
for _, log := range logsResp.NodeLogs {
logs = append(logs, maps.Map{
"tag": log.Tag,
"description": log.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
"level": log.Level,
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
})
}
this.Data["logs"] = logs
this.Data["page"] = page.AsHTML()
this.Show()
}

View File

@@ -0,0 +1,41 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package db
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/db/dbnodeutils"
"github.com/iwind/TeaGo/maps"
)
type NodeAction struct {
actionutils.ParentAction
}
func (this *NodeAction) Init() {
this.Nav("", "", "node")
}
func (this *NodeAction) RunGet(params struct {
NodeId int64
}) {
node, err := dbnodeutils.InitNode(this.Parent(), params.NodeId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["node"] = maps.Map{
"id": node.Id,
"isOn": node.IsOn,
"name": node.Name,
"database": node.Database,
"host": node.Host,
"port": node.Port,
"username": node.Username,
"password": node.Password,
"description": node.Description,
}
this.Show()
}

View File

@@ -0,0 +1,38 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package db
import (
"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/maps"
)
type StatusAction struct {
actionutils.ParentAction
}
func (this *StatusAction) RunPost(params struct {
NodeId int64
}) {
statusResp, err := this.RPC().DBNodeRPC().CheckDBNodeStatus(this.AdminContext(), &pb.CheckDBNodeStatusRequest{DbNodeId: params.NodeId})
if err != nil {
this.ErrorPage(err)
return
}
var status = statusResp.DbNodeStatus
if status != nil {
this.Data["status"] = maps.Map{
"isOk": status.IsOk,
"error": status.Error,
"size": numberutils.FormatBytes(status.Size),
"version": status.Version,
}
} else {
this.Data["status"] = nil
}
this.Success()
}

View File

@@ -8,15 +8,15 @@ import (
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
func (this *UpdateAction) Init() {
this.Nav("", "", "update")
}
func (this *UpdatePopupAction) RunGet(params struct {
func (this *UpdateAction) RunGet(params struct {
NodeId int64
}) {
nodeResp, err := this.RPC().DBNodeRPC().FindEnabledDBNode(this.AdminContext(), &pb.FindEnabledDBNodeRequest{DbNodeId: params.NodeId})
@@ -46,7 +46,7 @@ func (this *UpdatePopupAction) RunGet(params struct {
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
func (this *UpdateAction) RunPost(params struct {
NodeId int64
Name string

View File

@@ -40,6 +40,7 @@ func (this *ClusterAction) RunGet(params struct {
this.ErrorPage(err)
return
}
var defaultRoute = dnsResp.DefaultRoute
domainName := ""
dnsMap := maps.Map{
"dnsName": dnsResp.Name,
@@ -106,6 +107,26 @@ func (this *ClusterAction) RunGet(params struct {
})
}
} else {
// 默认线路
var isResolved = false
if len(defaultRoute) > 0 {
recordType := "A"
if utils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
DnsDomainId: cluster.DnsDomainId,
Name: cluster.DnsName,
Type: recordType,
Route: defaultRoute,
Value: node.IpAddr,
})
if err != nil {
this.ErrorPage(err)
return
}
isResolved = checkResp.IsOk
}
nodeMaps = append(nodeMaps, maps.Map{
"id": node.Id,
"name": node.Name,
@@ -115,7 +136,7 @@ func (this *ClusterAction) RunGet(params struct {
"code": "",
},
"clusterId": node.NodeClusterId,
"isResolved": false,
"isResolved": isResolved,
})
}
}

View File

@@ -14,8 +14,14 @@ func (this *IndexAction) Init() {
this.Nav("dns", "dns", "")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{})
func (this *IndexAction) RunGet(params struct {
Keyword string
}) {
this.Data["keyword"] = params.Keyword
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
@@ -24,8 +30,9 @@ func (this *IndexAction) RunGet(params struct{}) {
this.Data["page"] = page.AsHTML()
clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -34,6 +34,7 @@ func init() {
GetPost("/updatePopup", new(providers.UpdatePopupAction)).
Post("/delete", new(providers.DeleteAction)).
Get("/provider", new(providers.ProviderAction)).
Post("/syncDomains", new(providers.SyncDomainsAction)).
EndData().
// 域名
@@ -55,8 +56,8 @@ func init() {
Data("teaSubMenu", "issue").
GetPost("", new(issues.IndexAction)).
GetPost("/updateNodePopup", new(issues.UpdateNodePopupAction)).
Post("/syncDomain", new(issues.SyncDomainAction)).
EndData().
EndAll()
})
}

View File

@@ -0,0 +1,33 @@
package issues
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SyncDomainAction struct {
actionutils.ParentAction
}
func (this *SyncDomainAction) RunPost(params struct {
DomainId int64
}) {
// 记录日志
defer this.CreateLog(oplogs.LevelInfo, "同步DNS域名数据 %d", params.DomainId)
// 执行同步
resp, err := this.RPC().DNSDomainRPC().SyncDNSDomainData(this.AdminContext(), &pb.SyncDNSDomainDataRequest{DnsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if resp.IsOk {
this.Success()
} else {
this.Data["shouldFix"] = resp.ShouldFix
this.Fail(resp.Error)
}
this.Success()
}

View File

@@ -15,9 +15,14 @@ func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
func (this *IndexAction) RunGet(params struct {
Keyword string
}) {
this.Data["keyword"] = params.Keyword
countResp, err := this.RPC().DNSProviderRPC().CountAllEnabledDNSProviders(this.AdminContext(), &pb.CountAllEnabledDNSProvidersRequest{
AdminId: this.AdminId(),
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
@@ -29,6 +34,7 @@ func (this *IndexAction) RunGet(params struct{}) {
providersResp, err := this.RPC().DNSProviderRPC().ListEnabledDNSProviders(this.AdminContext(), &pb.ListEnabledDNSProvidersRequest{
AdminId: this.AdminId(),
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})

View File

@@ -82,6 +82,7 @@ func (this *ProviderAction) RunGet(params struct {
"id": domain.Id,
"name": domain.Name,
"isOn": domain.IsOn,
"isUp": domain.IsUp,
"dataUpdatedTime": dataUpdatedTime,
"countRoutes": len(domain.Routes),
"countServerRecords": domain.CountServerRecords,

View File

@@ -0,0 +1,25 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package providers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type SyncDomainsAction struct {
actionutils.ParentAction
}
func (this *SyncDomainsAction) RunPost(params struct {
ProviderId int64
}) {
resp, err := this.RPC().DNSDomainRPC().SyncDNSDomainsFromProvider(this.AdminContext(), &pb.SyncDNSDomainsFromProviderRequest{DnsProviderId: params.ProviderId})
if err != nil {
this.Fail("更新域名失败:" + err.Error())
}
this.Data["hasChanges"] = resp.HasChanges
this.Success()
}

View File

@@ -1,7 +1,10 @@
package ipAddresses
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
@@ -15,19 +18,22 @@ func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
func (this *CreatePopupAction) RunGet(params struct {
SupportThresholds bool
}) {
this.Data["supportThresholds"] = params.SupportThresholds
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
IP string `alias:"ip"`
CanAccess bool
Name string
IP string `alias:"ip"`
CanAccess bool
Name string
ThresholdsJSON []byte
Must *actions.Must
}) {
// TODO 严格校验IP地址
ip := net.ParseIP(params.IP)
if len(ip) == 0 {
this.Fail("请输入正确的IP")
@@ -37,11 +43,19 @@ func (this *CreatePopupAction) RunPost(params struct {
Field("ip", params.IP).
Require("请输入IP地址")
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if teaconst.IsPlus && len(params.ThresholdsJSON) > 0 {
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
}
this.Data["ipAddress"] = maps.Map{
"name": params.Name,
"canAccess": params.CanAccess,
"ip": params.IP,
"id": 0,
"name": params.Name,
"canAccess": params.CanAccess,
"ip": params.IP,
"id": 0,
"isOn": true,
"isUp": true,
"thresholds": thresholds,
}
this.Success()
}

View File

@@ -16,24 +16,40 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
return err
}
for _, addr := range addresses {
var thresholdsJSON = []byte{}
var thresholds = addr.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, _ = json.Marshal(thresholds)
}
addrId := addr.GetInt64("id")
if addrId > 0 {
var isOn = false
if !addr.Has("isOn") { // 兼容老版本
isOn = true
} else {
isOn = addr.GetBool("isOn")
}
_, err = parentAction.RPC().NodeIPAddressRPC().UpdateNodeIPAddress(parentAction.AdminContext(), &pb.UpdateNodeIPAddressRequest{
AddressId: addrId,
Ip: addr.GetString("ip"),
Name: addr.GetString("name"),
CanAccess: addr.GetBool("canAccess"),
NodeIPAddressId: addrId,
Ip: addr.GetString("ip"),
Name: addr.GetString("name"),
CanAccess: addr.GetBool("canAccess"),
IsOn: isOn,
ThresholdsJSON: thresholdsJSON,
})
if err != nil {
return err
}
} else {
_, err = parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
ThresholdsJSON: thresholdsJSON,
})
if err != nil {
return err

View File

@@ -1,7 +1,11 @@
package ipAddresses
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"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"
"github.com/iwind/TeaGo/maps"
"net"
@@ -16,35 +20,61 @@ func (this *UpdatePopupAction) Init() {
}
func (this *UpdatePopupAction) RunGet(params struct {
AddressId int64
AddressId int64
SupportThresholds bool
}) {
this.Data["supportThresholds"] = params.SupportThresholds
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
AddressId int64
IP string `alias:"ip"`
Name string
CanAccess bool
AddressId int64
IP string `alias:"ip"`
Name string
CanAccess bool
IsOn bool
ThresholdsJSON []byte
Must *actions.Must
}) {
// TODO 严格校验IP地址
params.Must.
Field("ip", params.IP).
Require("请输入IP地址")
// 获取IP地址信息
var isUp = true
if params.AddressId > 0 {
addressResp, err := this.RPC().NodeIPAddressRPC().FindEnabledNodeIPAddress(this.AdminContext(), &pb.FindEnabledNodeIPAddressRequest{NodeIPAddressId: params.AddressId})
if err != nil {
this.ErrorPage(err)
return
}
var address = addressResp.NodeIPAddress
if address == nil {
this.Fail("找不到要修改的地址")
}
isUp = address.IsUp
}
ip := net.ParseIP(params.IP)
if len(ip) == 0 {
this.Fail("请输入正确的IP")
}
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if teaconst.IsPlus && len(params.ThresholdsJSON) > 0 {
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
}
this.Data["ipAddress"] = maps.Map{
"name": params.Name,
"ip": params.IP,
"id": params.AddressId,
"canAccess": params.CanAccess,
"name": params.Name,
"ip": params.IP,
"id": params.AddressId,
"canAccess": params.CanAccess,
"isOn": params.IsOn,
"isUp": isUp,
"thresholds": thresholds,
}
this.Success()

View File

@@ -120,6 +120,9 @@ func (this *IndexAction) RunGet(params struct {
}
if resp.IpRegionMap != nil {
for ip, region := range resp.IpRegionMap {
if len(region.Isp) > 0 {
region.Summary += " | " + region.Isp
}
regionMap[ip] = region.Summary
}
}

View File

@@ -83,8 +83,8 @@ func (this *CreateNodeAction) RunPost(params struct {
addressId := address.GetInt64("id")
if addressId > 0 {
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
AddressId: addressId,
NodeId: nodeId,
NodeIPAddressId: addressId,
NodeId: nodeId,
})
} else {
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{

View File

@@ -96,6 +96,8 @@ func (this *IndexAction) RunGet(params struct {
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}

View File

@@ -71,6 +71,8 @@ func (this *IndexAction) RunGet(params struct {
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}

View File

@@ -61,6 +61,8 @@ func (this *UpdateAction) RunGet(params struct {
"name": addr.Name,
"ip": addr.Ip,
"canAccess": addr.CanAccess,
"isOn": addr.IsOn,
"isUp": addr.IsUp,
})
}

View File

@@ -1,6 +1,6 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cluster
package accessLog
import (
"encoding/json"

View File

@@ -1,4 +1,4 @@
package cluster
package accessLog
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"

View File

@@ -0,0 +1,68 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package recursion
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "")
this.SecondMenu("recursion")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
}) {
this.Data["clusterId"] = params.ClusterId
resp, err := this.RPC().NSClusterRPC().FindNSClusterRecursionConfig(this.AdminContext(), &pb.FindNSClusterRecursionConfigRequest{NsClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var config = &dnsconfigs.RecursionConfig{}
if len(resp.RecursionJSON) > 0 {
err = json.Unmarshal(resp.RecursionJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
} else {
config.UseLocalHosts = true
}
this.Data["config"] = config
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
RecursionJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo("修改DNS集群 %d 的递归DNS设置", params.ClusterId)
// TODO 校验域名
_, err := this.RPC().NSClusterRPC().UpdateNSClusterRecursionConfig(this.AdminContext(), &pb.UpdateNSClusterRecursionConfigRequest{
NsClusterId: params.ClusterId,
RecursionJSON: params.RecursionJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,21 @@
package recursion
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Helper(new(clusterutils.ClusterHelper)).
Data("teaMenu", "ns").
Data("teaSubMenu", "cluster").
Prefix("/ns/clusters/cluster/settings/recursion").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -78,15 +78,21 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
// 设置菜单
func (this *ClusterHelper) createSettingMenu(cluster *pb.NSCluster, selectedItem string) (items []maps.Map) {
clusterId := numberutils.FormatInt64(cluster.Id)
items = append(items, maps.Map{
"name": "基础设置",
"url": "/ns/clusters/cluster/settings?clusterId=" + clusterId,
"isActive": selectedItem == "basic",
})
items = append(items, maps.Map{
"name": "访问日志",
"url": "/ns/clusters/cluster/settings/accessLog?clusterId=" + clusterId,
"isActive": selectedItem == "accessLog",
})
return
return []maps.Map{
{
"name": "基础设置",
"url": "/ns/clusters/cluster/settings?clusterId=" + clusterId,
"isActive": selectedItem == "basic",
},
{
"name": "访问日志",
"url": "/ns/clusters/cluster/settings/accessLog?clusterId=" + clusterId,
"isActive": selectedItem == "accessLog",
},
{
"name": "递归DNS",
"url": "/ns/clusters/cluster/settings/recursion?clusterId=" + clusterId,
"isActive": selectedItem == "recursion",
},
}
}

View File

@@ -12,7 +12,7 @@ type IndexAction struct {
}
func (this *IndexAction) Init() {
this.FirstMenu("domain")
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct {

View File

@@ -1,127 +0,0 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "dns")
}
func (this *IndexAction) RunGet(params struct{}) {
resp, err := this.RPC().NSRPC().ComposeNSBoard(this.AdminContext(), &pb.ComposeNSBoardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"countDomains": resp.CountNSDomains,
"countRecords": resp.CountNSRecords,
"countClusters": resp.CountNSClusters,
"countNodes": resp.CountNSNodes,
"countOfflineNodes": resp.CountOfflineNSNodes,
}
// 流量排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["hourlyStats"] = statMaps
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["dailyStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSDomainStats {
statMaps = append(statMaps, maps.Map{
"domainId": stat.NsDomainId,
"domainName": stat.NsDomainName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// 节点排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSNodeStats {
statMaps = append(statMaps, maps.Map{
"clusterId": stat.NsClusterId,
"nodeId": stat.NsNodeId,
"nodeName": stat.NsNodeName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topNodeStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": types.Float32(string(stat.ValueJSON)),
})
}
this.Data["loadValues"] = statMaps
}
this.Show()
}

View File

@@ -1,52 +0,0 @@
package ns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/keys"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/domains/records"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/settings"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNS)).
Data("teaMenu", "ns").
Prefix("/ns").
Get("", new(IndexAction)).
// 域名相关
Prefix("/ns/domains").
Data("teaSubMenu", "domain").
Get("", new(domains.IndexAction)).
GetPost("/create", new(domains.CreateAction)).
Post("/delete", new(domains.DeleteAction)).
Get("/domain", new(domains.DomainAction)).
GetPost("/update", new(domains.UpdateAction)).
GetPost("/tsig", new(domains.TsigAction)).
// 域名密钥
Prefix("/ns/domains/keys").
Data("teaSubMenu", "domain").
Get("", new(keys.IndexAction)).
GetPost("/createPopup", new(keys.CreatePopupAction)).
GetPost("/updatePopup", new(keys.UpdatePopupAction)).
Post("/delete", new(keys.DeleteAction)).
Post("/generateSecret", new(keys.GenerateSecretAction)).
// 记录相关
Prefix("/ns/domains/records").
Get("", new(records.IndexAction)).
GetPost("/createPopup", new(records.CreatePopupAction)).
GetPost("/updatePopup", new(records.UpdatePopupAction)).
Post("/delete", new(records.DeleteAction)).
// 设置
Prefix("/ns/settings").
Get("", new(settings.IndexAction)).
EndAll()
})
}

View File

@@ -28,7 +28,7 @@ func init() {
Post("/testWrite", new(TestWriteAction)).
Get("/selectPopup", new(SelectPopupAction)).
Post("/count", new(CountAction)).
Post("/updateRefs", new(UpdateRefsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,35 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdateRefsAction struct {
actionutils.ParentAction
}
func (this *UpdateRefsAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateRefsAction) RunPost(params struct {
CachePolicyId int64
RefsJSON []byte
}) {
// 修改缓存条件
if params.CachePolicyId > 0 && len(params.RefsJSON) > 0 {
_, err := this.RPC().HTTPCachePolicyRPC().UpdateHTTPCachePolicyRefs(this.AdminContext(), &pb.UpdateHTTPCachePolicyRefsRequest{
HttpCachePolicyId: params.CachePolicyId,
RefsJSON: params.RefsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -45,11 +45,9 @@ func init() {
GetPost("/ipadmin", new(ipadmin.IndexAction)).
GetPost("/ipadmin/provinces", new(ipadmin.ProvincesAction)).
Get("/ipadmin/lists", new(ipadmin.ListsAction)).
GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)).
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
GetPost("/ipadmin/test", new(ipadmin.TestAction)).
EndAll()
})
}

View File

@@ -1,105 +0,0 @@
package ipadmin
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreateIPPopupAction struct {
actionutils.ParentAction
}
func (this *CreateIPPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateIPPopupAction) RunGet(params struct {
FirewallPolicyId int64
Type string
}) {
this.Data["listType"] = params.Type
listId, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledPolicyIPListIdWithType(this.AdminContext(), params.FirewallPolicyId, params.Type)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["listId"] = listId
this.Show()
}
func (this *CreateIPPopupAction) RunPost(params struct {
FirewallPolicyId int64
ListId int64
IpFrom string
IpTo string
ExpiredAt int64
Reason string
Type string
EventLevel string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// TODO 校验ListId所属用户
switch params.Type {
case "ipv4":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入开始IP")
// 校验IP格式ipFrom/ipTo
var ipFromLong uint64
if !utils.IsIPv4(params.IpFrom) {
this.Fail("请输入正确的开始IP")
}
ipFromLong = utils.IP2Long(params.IpFrom)
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
}
case "ipv6":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入IP")
// 校验IP格式ipFrom
if !utils.IsIPv6(params.IpFrom) {
this.Fail("请输入正确的IPv6地址")
}
case "all":
params.IpFrom = "0.0.0.0"
}
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
IpListId: params.ListId,
IpFrom: params.IpFrom,
IpTo: params.IpTo,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,
EventLevel: params.EventLevel,
})
if err != nil {
this.ErrorPage(err)
return
}
itemId := createResp.IpItemId
// 日志
defer this.CreateLog(oplogs.LevelInfo, "在WAF策略 %d 名单中添加IP %d", params.FirewallPolicyId, itemId)
this.Success()
}

View File

@@ -4,8 +4,13 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net"
"strings"
)
type CreateIPPopupAction struct {
@@ -21,13 +26,34 @@ func (this *CreateIPPopupAction) RunGet(params struct {
}) {
this.Data["listId"] = params.ListId
listResp, err := this.RPC().IPListRPC().FindEnabledIPList(this.AdminContext(), &pb.FindEnabledIPListRequest{
IpListId: params.ListId,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipList = listResp.IpList
if ipList == nil {
this.NotFound("ipList", params.ListId)
return
}
this.Data["list"] = maps.Map{
"type": ipList.Type,
}
this.Show()
}
func (this *CreateIPPopupAction) RunPost(params struct {
ListId int64
IpFrom string
IpTo string
ListId int64
Method string
IpFrom string
IpTo string
IpData string
ExpiredAt int64
Reason string
Type string
@@ -46,58 +72,207 @@ func (this *CreateIPPopupAction) RunPost(params struct {
this.Fail("IP名单不存在")
}
type ipData struct {
ipFrom string
ipTo string
}
var batchIPs = []*ipData{}
switch params.Type {
case "ipv4":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入开始IP")
if params.Method == "single" {
// 校验IP格式ipFrom/ipTo
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入开始IP")
// 校验IP格式ipFrom/ipTo
var ipFromLong uint64
if !utils.IsIPv4(params.IpFrom) {
this.Fail("请输入正确的开始IP")
}
ipFromLong = utils.IP2Long(params.IpFrom)
var ipFromLong uint64
if !utils.IsIPv4(params.IpFrom) {
this.Fail("请输入正确的开始IP")
}
ipFromLong = utils.IP2Long(params.IpFrom)
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
}
} else if params.Method == "batch" {
if len(params.IpData) == 0 {
this.FailField("ipData", "请输入IP")
}
var lines = strings.Split(params.IpData, "\n")
for index, line := range lines {
line = strings.TrimSpace(line)
if strings.Contains(line, "/") { // CIDR
if strings.Contains(line, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
ipFrom, ipTo, err := configutils.ParseCIDR(line)
if err != nil {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
ipTo: ipTo,
})
} else if strings.Contains(line, "-") { // IP Range
var pieces = strings.Split(line, "-")
var ipFrom = strings.TrimSpace(pieces[0])
var ipTo = strings.TrimSpace(pieces[1])
if net.ParseIP(ipFrom) == nil || net.ParseIP(ipTo) == nil || strings.Contains(ipFrom, ":") || strings.Contains(ipTo, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
if utils.IP2Long(ipFrom) > utils.IP2Long(ipTo) {
ipFrom, ipTo = ipTo, ipFrom
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
ipTo: ipTo,
})
} else if strings.Contains(line, ",") { // IP Range
var pieces = strings.Split(line, ",")
var ipFrom = strings.TrimSpace(pieces[0])
var ipTo = strings.TrimSpace(pieces[1])
if net.ParseIP(ipFrom) == nil || net.ParseIP(ipTo) == nil || strings.Contains(ipFrom, ":") || strings.Contains(ipTo, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
if utils.IP2Long(ipFrom) > utils.IP2Long(ipTo) {
ipFrom, ipTo = ipTo, ipFrom
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
ipTo: ipTo,
})
} else if len(line) > 0 {
var ipFrom = line
if net.ParseIP(ipFrom) == nil || strings.Contains(ipFrom, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
})
}
}
}
case "ipv6":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入IP")
if params.Method == "single" {
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入IP")
// 校验IP格式ipFrom
if !utils.IsIPv6(params.IpFrom) {
this.Fail("请输入正确的IPv6地址")
// 校验IP格式ipFrom
if !utils.IsIPv6(params.IpFrom) {
this.Fail("请输入正确的IPv6地址")
}
} else if params.Method == "batch" {
if len(params.IpData) == 0 {
this.FailField("ipData", "请输入IP")
}
var lines = strings.Split(params.IpData, "\n")
for index, line := range lines {
line = strings.TrimSpace(line)
if strings.Contains(line, "/") { // CIDR
if !strings.Contains(line, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
ipFrom, ipTo, err := configutils.ParseCIDR(line)
if err != nil {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
ipTo: ipTo,
})
} else if strings.Contains(line, "-") { // IP Range
var pieces = strings.Split(line, "-")
var ipFrom = strings.TrimSpace(pieces[0])
var ipTo = strings.TrimSpace(pieces[1])
if net.ParseIP(ipFrom) == nil || net.ParseIP(ipTo) == nil || !strings.Contains(ipFrom, ":") || !strings.Contains(ipTo, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
if utils.IP2Long(ipFrom) > utils.IP2Long(ipTo) {
ipFrom, ipTo = ipTo, ipFrom
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
ipTo: ipTo,
})
} else if strings.Contains(line, ",") { // IP Range
var pieces = strings.Split(line, ",")
var ipFrom = strings.TrimSpace(pieces[0])
var ipTo = strings.TrimSpace(pieces[1])
if net.ParseIP(ipFrom) == nil || net.ParseIP(ipTo) == nil || !strings.Contains(ipFrom, ":") || !strings.Contains(ipTo, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
if utils.IP2Long(ipFrom) > utils.IP2Long(ipTo) {
ipFrom, ipTo = ipTo, ipFrom
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
ipTo: ipTo,
})
} else if len(line) > 0 {
var ipFrom = line
if net.ParseIP(ipFrom) == nil || !strings.Contains(ipFrom, ":") {
this.FailField("ipData", "第"+types.String(index+1)+"行IP格式错误"+line)
}
batchIPs = append(batchIPs, &ipData{
ipFrom: ipFrom,
})
}
}
}
case "all":
params.IpFrom = "0.0.0.0"
}
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
IpListId: params.ListId,
IpFrom: params.IpFrom,
IpTo: params.IpTo,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,
EventLevel: params.EventLevel,
})
if err != nil {
this.ErrorPage(err)
return
}
itemId := createResp.IpItemId
if len(batchIPs) > 0 {
for _, ip := range batchIPs {
_, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
IpListId: params.ListId,
IpFrom: ip.ipFrom,
IpTo: ip.ipTo,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,
EventLevel: params.EventLevel,
})
if err != nil {
this.ErrorPage(err)
return
}
}
// 日志
defer this.CreateLog(oplogs.LevelInfo, "在IP名单中添加IP %d", itemId)
// 日志
defer this.CreateLog(oplogs.LevelInfo, "在IP名单中批量添加IP %d", params.ListId)
} else {
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
IpListId: params.ListId,
IpFrom: params.IpFrom,
IpTo: params.IpTo,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,
EventLevel: params.EventLevel,
})
if err != nil {
this.ErrorPage(err)
return
}
itemId := createResp.IpItemId
// 日志
defer this.CreateLog(oplogs.LevelInfo, "在IP名单 %d 中添加IP %d", params.ListId, itemId)
}
this.Success()
}

View File

@@ -7,6 +7,7 @@ import (
"encoding/csv"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
@@ -201,6 +202,19 @@ func (this *ImportAction) createItemFromValues(values []string, countIgnore *int
item.Reason = values[5]
}
// CIDR
if strings.Contains(item.IpFrom, "/") {
ipFrom, ipTo, err := configutils.ParseCIDR(item.IpFrom)
if err == nil {
item.IpFrom = ipFrom
item.IpTo = ipTo
}
}
if len(item.EventLevel) == 0 {
item.EventLevel = "critical"
}
if net.ParseIP(item.IpFrom) == nil {
*countIgnore++
return nil
@@ -209,5 +223,6 @@ func (this *ImportAction) createItemFromValues(values []string, countIgnore *int
*countIgnore++
return nil
}
return item
}

View File

@@ -25,6 +25,7 @@ func (this *IndexAction) RunGet(params struct {
Ip string
Domain string
HasError int
HasWAF int
RequestId string
ServerId int64
@@ -41,6 +42,7 @@ func (this *IndexAction) RunGet(params struct {
this.Data["domain"] = params.Domain
this.Data["accessLogs"] = []interface{}{}
this.Data["hasError"] = params.HasError
this.Data["hasWAF"] = params.HasWAF
day := params.Day
ipList := []string{}
@@ -52,14 +54,15 @@ func (this *IndexAction) RunGet(params struct {
this.Data["hasError"] = params.HasError
resp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
HasFirewallPolicy: params.HasWAF > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
})
if err != nil {
this.ErrorPage(err)
@@ -87,15 +90,16 @@ func (this *IndexAction) RunGet(params struct {
if len(params.RequestId) > 0 {
this.Data["hasPrev"] = true
prevResp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
Reverse: true,
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
HasFirewallPolicy: params.HasWAF > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
Reverse: true,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -41,6 +41,12 @@ func (this *IndexAction) RunGet(params struct {
"name": server.Name,
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
}) {
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatBoard(this.AdminContext(), &pb.ComposeServerStatBoardRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
@@ -151,6 +157,5 @@ func (this *IndexAction) RunGet(params struct {
}
this.Data["metricCharts"] = chartMaps
}
this.Show()
this.Success()
}

View File

@@ -13,7 +13,7 @@ func init() {
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/boards").
Get("", new(IndexAction)).
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -24,6 +24,7 @@ func (this *HistoryAction) RunGet(params struct {
Keyword string
Ip string
Domain string
HasWAF int
RequestId string
HasError int
@@ -39,6 +40,7 @@ func (this *HistoryAction) RunGet(params struct {
this.Data["domain"] = params.Domain
this.Data["accessLogs"] = []interface{}{}
this.Data["hasError"] = params.HasError
this.Data["hasWAF"] = params.HasWAF
day := params.Day
ipList := []string{}
@@ -50,14 +52,15 @@ func (this *HistoryAction) RunGet(params struct {
this.Data["hasError"] = params.HasError
resp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
HasFirewallPolicy: params.HasWAF > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
})
if err != nil {
this.ErrorPage(err)
@@ -85,15 +88,16 @@ func (this *HistoryAction) RunGet(params struct {
if len(params.RequestId) > 0 {
this.Data["hasPrev"] = true
prevResp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
Reverse: true,
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
HasFirewallPolicy: params.HasWAF > 0,
Day: day,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
Reverse: true,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -20,6 +20,7 @@ func (this *TodayAction) RunGet(params struct {
RequestId string
ServerId int64
HasError int
HasWAF int
Keyword string
Ip string
Domain string
@@ -31,16 +32,18 @@ func (this *TodayAction) RunGet(params struct {
this.Data["keyword"] = params.Keyword
this.Data["ip"] = params.Ip
this.Data["domain"] = params.Domain
this.Data["hasWAF"] = params.HasWAF
resp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
Day: timeutil.Format("Ymd"),
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
HasFirewallPolicy: params.HasWAF > 0,
Day: timeutil.Format("Ymd"),
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
})
if err != nil {
this.ErrorPage(err)
@@ -69,15 +72,16 @@ func (this *TodayAction) RunGet(params struct {
if len(params.RequestId) > 0 {
this.Data["hasPrev"] = true
prevResp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.AdminContext(), &pb.ListHTTPAccessLogsRequest{
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
Day: timeutil.Format("Ymd"),
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
Reverse: true,
RequestId: params.RequestId,
ServerId: params.ServerId,
HasError: params.HasError > 0,
HasFirewallPolicy: params.HasWAF > 0,
Day: timeutil.Format("Ymd"),
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
Size: size,
Reverse: true,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -38,6 +38,10 @@ func (this *CreatePopupAction) RunPost(params struct {
this.Fail("请输入缓存Key")
}
if cacheRef.Conds == nil || len(cacheRef.Conds.Groups) == 0 {
this.Fail("请填写匹配条件分组")
}
err = cacheRef.Init()
if err != nil {
this.ErrorPage(err)

View File

@@ -15,6 +15,8 @@ func init() {
Prefix("/servers/server/settings/cache").
GetPost("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/purge", new(PurgeAction)).
GetPost("/preheat", new(PreheatAction)).
EndAll()
})
}

View File

@@ -0,0 +1,143 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type PreheatAction struct {
actionutils.ParentAction
}
func (this *PreheatAction) Init() {
this.Nav("", "setting", "preheat")
this.SecondMenu("cache")
}
func (this *PreheatAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["webConfig"] = webConfig
this.Show()
}
func (this *PreheatAction) RunPost(params struct {
ServerId int64
WebId int64
Keys string
Must *actions.Must
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "预热服务 %d 缓存", params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.AdminContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if webConfig == nil {
this.NotFound("httpWeb", params.WebId)
return
}
var cache = webConfig.Cache
if cache == nil || !cache.IsOn {
this.Fail("当前没有开启缓存")
}
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server == nil || server.NodeCluster == nil {
this.NotFound("server", params.ServerId)
return
}
var clusterId = server.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NodeCluster
if cluster == nil {
this.NotFound("nodeCluster", clusterId)
return
}
var cachePolicyId = cluster.HttpCachePolicyId
if cachePolicyId == 0 {
this.Fail("当前集群没有设置缓存策略")
}
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: cachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
if len(params.Keys) == 0 {
this.Fail("请输入要预热的Key列表")
}
realKeys := []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
// 发送命令
msg := &messageconfigs.PreheatCacheMessage{
CachePolicyJSON: cachePolicyJSON,
Keys: realKeys,
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), clusterId, messageconfigs.MessageCodePreheatCache, msg, 300)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
this.Success()
}

View File

@@ -0,0 +1,149 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type PurgeAction struct {
actionutils.ParentAction
}
func (this *PurgeAction) Init() {
this.Nav("", "setting", "purge")
this.SecondMenu("cache")
}
func (this *PurgeAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["webConfig"] = webConfig
this.Show()
}
func (this *PurgeAction) RunPost(params struct {
ServerId int64
WebId int64
Type string
Keys string
Must *actions.Must
}) {
// 创建日志
defer this.CreateLog(oplogs.LevelInfo, "删除服务 %d 缓存", params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.AdminContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if webConfig == nil {
this.NotFound("httpWeb", params.WebId)
return
}
var cache = webConfig.Cache
if cache == nil || !cache.IsOn {
this.Fail("当前没有开启缓存")
}
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server == nil || server.NodeCluster == nil {
this.NotFound("server", params.ServerId)
return
}
var clusterId = server.NodeCluster.Id
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
if err != nil {
this.ErrorPage(err)
return
}
var cluster = clusterResp.NodeCluster
if cluster == nil {
this.NotFound("nodeCluster", clusterId)
return
}
var cachePolicyId = cluster.HttpCachePolicyId
if cachePolicyId == 0 {
this.Fail("当前集群没有设置缓存策略")
}
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: cachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
if len(params.Keys) == 0 {
this.Fail("请输入要删除的Key列表")
}
realKeys := []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
// 发送命令
msg := &messageconfigs.PurgeCacheMessage{
CachePolicyJSON: cachePolicyJSON,
Keys: realKeys,
}
if params.Type == "prefix" {
msg.Type = messageconfigs.PurgeCacheMessageTypeDir
} else {
msg.Type = messageconfigs.PurgeCacheMessageTypeFile
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), clusterId, messageconfigs.MessageCodePurgeCache, msg, 10)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
this.Success()
}

View File

@@ -19,7 +19,6 @@ func init() {
Get("/ipadmin/denyList", new(ipadmin.DenyListAction)).
GetPost("/ipadmin/countries", new(ipadmin.CountriesAction)).
GetPost("/ipadmin/provinces", new(ipadmin.ProvincesAction)).
GetPost("/ipadmin/createIPPopup", new(ipadmin.CreateIPPopupAction)).
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
GetPost("/ipadmin/test", new(ipadmin.TestAction)).

View File

@@ -1,95 +0,0 @@
package ipadmin
import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreateIPPopupAction struct {
actionutils.ParentAction
}
func (this *CreateIPPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateIPPopupAction) RunGet(params struct {
ListId int64
Type string
}) {
this.Data["listType"] = params.Type
this.Data["listId"] = params.ListId
this.Show()
}
func (this *CreateIPPopupAction) RunPost(params struct {
ListId int64
IpFrom string
IpTo string
ExpiredAt int64
Reason string
Type string
EventLevel string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
switch params.Type {
case "ipv4":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入开始IP")
// 校验IP格式ipFrom/ipTo
var ipFromLong uint64
if !utils.IsIPv4(params.IpFrom) {
this.Fail("请输入正确的开始IP")
}
ipFromLong = utils.IP2Long(params.IpFrom)
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
ipToLong = utils.IP2Long(params.IpTo)
this.Fail("请输入正确的结束IP")
}
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
}
case "ipv6":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入IP")
// 校验IP格式ipFrom
if !utils.IsIPv6(params.IpFrom) {
this.Fail("请输入正确的IPv6地址")
}
case "all":
params.IpFrom = "0.0.0.0"
}
createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
IpListId: params.ListId,
IpFrom: params.IpFrom,
IpTo: params.IpTo,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,
EventLevel: params.EventLevel,
})
if err != nil {
this.ErrorPage(err)
return
}
itemId := createResp.IpItemId
// 日志
defer this.CreateLog(oplogs.LevelInfo, "在WAF策略 %d 名单中添加IP %d", params.ListId, itemId)
this.Success()
}

View File

@@ -42,6 +42,7 @@ func (this *IndexAction) RunGet(params struct{}) {
"macAddresses": keyResp.AuthorityKey.MacAddresses,
"hostname": keyResp.AuthorityKey.Hostname,
"company": keyResp.AuthorityKey.Company,
"nodes": keyResp.AuthorityKey.Nodes,
"isExpired": !isActive,
"updatedTime": timeutil.FormatTime("Y-m-d H:i:s", keyResp.AuthorityKey.UpdatedAt),
}

View File

@@ -168,7 +168,7 @@ func (this *InstallAction) RunPost(params struct {
nodes.SharedAdminNode.AddSubPID(cmd.Process.Pid)
// 等待API节点初始化完成
time.Sleep(2 * time.Second)
time.Sleep(5 * time.Second)
}
// 写入API节点配置完成安装

View File

@@ -91,16 +91,21 @@ func (this *ValidateDbAction) RunPost(params struct {
}
// 检查权限
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `edgeTest` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")
// edgeTest表名需要根据表结构的变更而变更防止升级时冲突
var testTable = "edgeTest1"
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `" + testTable + "` (\n `id` int(11) NOT NULL AUTO_INCREMENT,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")
if err != nil {
this.Fail("当前连接的用户无法创建新表请检查CREATE权限设置" + err.Error())
}
_, err = db.Exec("ALTER TABLE `edgeTest` CHANGE `id` `id` int(11) NOT NULL AUTO_INCREMENT")
_, err = db.Exec("ALTER TABLE `" + testTable + "` CHANGE `id` `id` int(11) NOT NULL AUTO_INCREMENT")
if err != nil {
this.Fail("当前连接的用户无法修改表结构请检查ALTER权限设置" + err.Error())
}
// 删除edgeTest忽略可能的错误因为我们不需要DROP权限
_, _ = db.Exec("DROP TABLE `" + testTable + "`")
// 检查数据库版本
one, err := db.FindOne("SELECT VERSION() AS v")
if err != nil {

View File

@@ -2,7 +2,7 @@ package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/accessKeys"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/accesskeys"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)

View File

@@ -180,21 +180,16 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
"subtitle": "服务列表",
"icon": "clone outsize",
"subItems": []maps.Map{
{
"name": "证书管理",
"url": "/servers/certs",
"code": "cert",
},
{
"name": "访问日志",
"url": "/servers/logs",
"code": "log",
},
{
"name": "通用设置",
"url": "/servers/components",
"code": "global",
},
{
"name": "服务分组",
"url": "/servers/components/groups",
"code": "group",
},
{
"name": "缓存策略",
"url": "/servers/components/cache",
@@ -216,16 +211,21 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
"url": "/servers/iplists",
"code": "iplist",
},
{
"name": "证书管理",
"url": "/servers/certs",
"code": "cert",
},
{
"name": "统计指标",
"url": "/servers/metrics",
"code": "metric",
},
{
"name": "服务分组",
"url": "/servers/components/groups",
"code": "group",
},
{
"name": "通用设置",
"url": "/servers/components",
"code": "global",
},
},
},
{
@@ -240,6 +240,18 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map {
"url": "/clusters/logs",
"code": "log",
},
{
"name": "IP地址",
"url": "/clusters/ip-addrs",
"code": "ipAddr",
"isOn": teaconst.IsPlus,
},
/**{
"name": "区域监控",
"url": "/clusters/monitors",
"code": "monitor",
"isOn": teaconst.IsPlus,
},**/
{
"name": "SSH认证",
"url": "/clusters/grants",

View File

@@ -6,11 +6,6 @@ import (
// 系统用户
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins/recipients"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins/recipients/groups"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins/recipients/instances"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins/recipients/logs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins/recipients/tasks"
// API节点
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/api"
@@ -41,22 +36,8 @@ import (
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ui"
// 域名服务
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/accessLogs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster/settings"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster/settings/accessLog"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/logs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/routes"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/settings/accesslogs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/test"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/users"
// 服务相关
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/accesslogs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache"
@@ -67,7 +48,6 @@ import (
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/metrics"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/metrics/charts"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/boards"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/delete"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/log"
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings"

View File

@@ -1,5 +1,6 @@
// 单个集群选择
Vue.component("cluster-selector", {
props: ["v-cluster-id"],
mounted: function () {
let that = this
@@ -9,7 +10,6 @@ Vue.component("cluster-selector", {
that.clusters = resp.data.clusters
})
},
props: ["v-cluster-id"],
data: function () {
let clusterId = this.vClusterId
if (clusterId == null) {
@@ -21,7 +21,7 @@ Vue.component("cluster-selector", {
}
},
template: `<div>
<select class="ui dropdown auto-width" name="clusterId" v-model="clusterId">
<select class="ui dropdown" style="max-width: 10em" name="clusterId" v-model="clusterId">
<option value="0">[选择集群]</option>
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
</select>

View File

@@ -12,9 +12,6 @@ Vue.component("node-clusters-labels", {
if (labelSize == null) {
labelSize = "small"
}
if (labelSize == "tiny") {
labelSize += " olive"
}
return {
cluster: cluster,
secondaryClusters: secondaryClusters,
@@ -22,7 +19,7 @@ Vue.component("node-clusters-labels", {
}
},
template: `<div>
<a v-if="cluster != null" :href="'/clusters/cluster?clusterId=' + cluster.id" class="ui label basic" :class="labelSize" title="主集群" style="margin-bottom: 0.3em;">{{cluster.name}}</a>
<a v-for="c in secondaryClusters" :href="'/clusters/cluster?clusterId=' + c.id" class="ui label basic" :class="labelSize" title="从集群" style="margin-bottom: 0.3em;"><span class="grey" style="text-decoration: none">{{c.name}}</span></a>
<a v-if="cluster != null" :href="'/clusters/cluster?clusterId=' + cluster.id" class="ui label basic grey" :class="labelSize" title="主集群" style="margin-bottom: 0.3em;">{{cluster.name}}</a>
<a v-for="c in secondaryClusters" :href="'/clusters/cluster?clusterId=' + c.id" class="ui label basic grey" :class="labelSize" title="从集群" style="margin-bottom: 0.3em;"><span class="grey" style="text-decoration: none">{{c.name}}</span></a>
</div>`
})

View File

@@ -24,7 +24,7 @@ Vue.component("node-clusters-selector", {
let that = this
let selectedClusterIds = [this.primaryClusterId].concat(this.secondaryClusterIds)
teaweb.popup("/clusters/selectPopup?selectedClusterIds=" + selectedClusterIds.join(",") + "&mode=single", {
height: "38em",
height: "30em",
width: "50em",
callback: function (resp) {
if (resp.data.cluster != null) {
@@ -44,7 +44,7 @@ Vue.component("node-clusters-selector", {
let that = this
let selectedClusterIds = [this.primaryClusterId].concat(this.secondaryClusterIds)
teaweb.popup("/clusters/selectPopup?selectedClusterIds=" + selectedClusterIds.join(",") + "&mode=multiple", {
height: "38em",
height: "30em",
width: "50em",
callback: function (resp) {
if (resp.data.cluster != null) {

View File

@@ -1,150 +1,150 @@
Vue.component("health-check-config-box", {
props: ["v-health-check-config"],
data: function () {
let healthCheckConfig = this.vHealthCheckConfig
let urlProtocol = "http"
let urlPort = ""
let urlRequestURI = "/"
let urlHost = ""
props: ["v-health-check-config"],
data: function () {
let healthCheckConfig = this.vHealthCheckConfig
let urlProtocol = "http"
let urlPort = ""
let urlRequestURI = "/"
let urlHost = ""
if (healthCheckConfig == null) {
healthCheckConfig = {
isOn: false,
url: "",
interval: {count: 60, unit: "second"},
statusCodes: [200],
timeout: {count: 10, unit: "second"},
countTries: 3,
tryDelay: {count: 100, unit: "ms"},
autoDown: true,
countUp: 1,
countDown: 1
}
let that = this
setTimeout(function () {
that.changeURL()
}, 500)
} else {
try {
let url = new URL(healthCheckConfig.url)
urlProtocol = url.protocol.substring(0, url.protocol.length - 1)
if (healthCheckConfig == null) {
healthCheckConfig = {
isOn: false,
url: "",
interval: {count: 60, unit: "second"},
statusCodes: [200],
timeout: {count: 10, unit: "second"},
countTries: 3,
tryDelay: {count: 100, unit: "ms"},
autoDown: true,
countUp: 1,
countDown: 3
}
let that = this
setTimeout(function () {
that.changeURL()
}, 500)
} else {
try {
let url = new URL(healthCheckConfig.url)
urlProtocol = url.protocol.substring(0, url.protocol.length - 1)
// 域名
urlHost = url.host
if (urlHost == "%24%7Bhost%7D") {
urlHost = "${host}"
}
let colonIndex = urlHost.indexOf(":")
if (colonIndex > 0) {
urlHost = urlHost.substring(0, colonIndex)
}
// 域名
urlHost = url.host
if (urlHost == "%24%7Bhost%7D") {
urlHost = "${host}"
}
let colonIndex = urlHost.indexOf(":")
if (colonIndex > 0) {
urlHost = urlHost.substring(0, colonIndex)
}
urlPort = url.port
urlRequestURI = url.pathname
if (url.search.length > 0) {
urlRequestURI += url.search
}
} catch (e) {
}
urlPort = url.port
urlRequestURI = url.pathname
if (url.search.length > 0) {
urlRequestURI += url.search
}
} catch (e) {
}
if (healthCheckConfig.statusCodes == null) {
healthCheckConfig.statusCodes = [200]
}
if (healthCheckConfig.interval == null) {
healthCheckConfig.interval = {count: 60, unit: "second"}
}
if (healthCheckConfig.timeout == null) {
healthCheckConfig.timeout = {count: 10, unit: "second"}
}
if (healthCheckConfig.tryDelay == null) {
healthCheckConfig.tryDelay = {count: 100, unit: "ms"}
}
if (healthCheckConfig.countUp == null || healthCheckConfig.countUp < 1) {
healthCheckConfig.countUp = 1
}
if (healthCheckConfig.countDown == null || healthCheckConfig.countDown < 1) {
healthCheckConfig.countDown = 1
}
}
return {
healthCheck: healthCheckConfig,
advancedVisible: false,
urlProtocol: urlProtocol,
urlHost: urlHost,
urlPort: urlPort,
urlRequestURI: urlRequestURI
}
},
watch: {
urlRequestURI: function () {
if (this.urlRequestURI.length > 0 && this.urlRequestURI[0] != "/") {
this.urlRequestURI = "/" + this.urlRequestURI
}
this.changeURL()
},
urlPort: function (v) {
let port = parseInt(v)
if (!isNaN(port)) {
this.urlPort = port.toString()
} else {
this.urlPort = ""
}
this.changeURL()
},
urlProtocol: function () {
this.changeURL()
},
urlHost: function () {
this.changeURL()
},
"healthCheck.countTries": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countTries = count
} else {
this.healthCheck.countTries = 0
}
},
"healthCheck.countUp": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countUp = count
} else {
this.healthCheck.countUp = 0
}
},
"healthCheck.countDown": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countDown = count
} else {
this.healthCheck.countDown = 0
}
}
},
methods: {
showAdvanced: function () {
this.advancedVisible = !this.advancedVisible
},
changeURL: function () {
let urlHost = this.urlHost
if (urlHost.length == 0) {
urlHost = "${host}"
}
this.healthCheck.url = this.urlProtocol + "://" + urlHost + ((this.urlPort.length > 0) ? ":" + this.urlPort : "") + this.urlRequestURI
},
changeStatus: function (values) {
this.healthCheck.statusCodes = values.$map(function (k, v) {
let status = parseInt(v)
if (isNaN(status)) {
return 0
} else {
return status
}
})
}
},
template: `<div>
if (healthCheckConfig.statusCodes == null) {
healthCheckConfig.statusCodes = [200]
}
if (healthCheckConfig.interval == null) {
healthCheckConfig.interval = {count: 60, unit: "second"}
}
if (healthCheckConfig.timeout == null) {
healthCheckConfig.timeout = {count: 10, unit: "second"}
}
if (healthCheckConfig.tryDelay == null) {
healthCheckConfig.tryDelay = {count: 100, unit: "ms"}
}
if (healthCheckConfig.countUp == null || healthCheckConfig.countUp < 1) {
healthCheckConfig.countUp = 1
}
if (healthCheckConfig.countDown == null || healthCheckConfig.countDown < 1) {
healthCheckConfig.countDown = 3
}
}
return {
healthCheck: healthCheckConfig,
advancedVisible: false,
urlProtocol: urlProtocol,
urlHost: urlHost,
urlPort: urlPort,
urlRequestURI: urlRequestURI
}
},
watch: {
urlRequestURI: function () {
if (this.urlRequestURI.length > 0 && this.urlRequestURI[0] != "/") {
this.urlRequestURI = "/" + this.urlRequestURI
}
this.changeURL()
},
urlPort: function (v) {
let port = parseInt(v)
if (!isNaN(port)) {
this.urlPort = port.toString()
} else {
this.urlPort = ""
}
this.changeURL()
},
urlProtocol: function () {
this.changeURL()
},
urlHost: function () {
this.changeURL()
},
"healthCheck.countTries": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countTries = count
} else {
this.healthCheck.countTries = 0
}
},
"healthCheck.countUp": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countUp = count
} else {
this.healthCheck.countUp = 0
}
},
"healthCheck.countDown": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countDown = count
} else {
this.healthCheck.countDown = 0
}
}
},
methods: {
showAdvanced: function () {
this.advancedVisible = !this.advancedVisible
},
changeURL: function () {
let urlHost = this.urlHost
if (urlHost.length == 0) {
urlHost = "${host}"
}
this.healthCheck.url = this.urlProtocol + "://" + urlHost + ((this.urlPort.length > 0) ? ":" + this.urlPort : "") + this.urlRequestURI
},
changeStatus: function (values) {
this.healthCheck.statusCodes = values.$map(function (k, v) {
let status = parseInt(v)
if (isNaN(status)) {
return 0
} else {
return status
}
})
}
},
template: `<div>
<input type="hidden" name="healthCheckJSON" :value="JSON.stringify(healthCheck)"/>
<table class="ui table definition selectable">
<tbody>
@@ -227,7 +227,7 @@ Vue.component("health-check-config-box", {
<p class="comment">连续N次检查失败后自动下线。</p>
</td>
</tr>
</tbody>
</tbody>
<tbody v-show="healthCheck.isOn">
<tr>
<td colspan="2"><more-options-angle @change="showAdvanced"></more-options-angle></td>

View File

@@ -22,3 +22,9 @@ Vue.component("tiny-basic-label", {
Vue.component("micro-basic-label", {
template: `<span class="ui label tiny basic" style="margin-bottom: 0.5em; font-size: 0.7em; padding: 4px"><slot></slot></span>`
})
// 灰色的Label
Vue.component("grey-label", {
template: `<span class="grey small"><slot></slot></span>`
})

View File

@@ -4,7 +4,7 @@
Vue.component("menu-item", {
props: ["href", "active", "code"],
data: function () {
var active = this.active
let active = this.active
if (typeof (active) == "undefined") {
var itemCode = ""
if (typeof (window.TEA.ACTION.data.firstMenuItem) != "undefined") {
@@ -18,8 +18,19 @@ Vue.component("menu-item", {
}
}
}
let href = (this.href == null) ? "" : this.href
if (typeof (href) == "string" && href.length > 0 && href.startsWith(".")) {
let qIndex = href.indexOf("?")
if (qIndex >= 0) {
href = Tea.url(href.substring(0, qIndex)) + href.substring(qIndex)
} else {
href = Tea.url(href)
}
}
return {
vHref: (this.href == null) ? "" : this.href,
vHref: href,
vActive: active
}
},

View File

@@ -7,7 +7,6 @@ Vue.component("source-code-box", {
if (typeof readOnly != "boolean") {
readOnly = true
}
console.log("readonly:", readOnly) // TODO
let box = document.getElementById("source-code-box-" + this.index)
let valueBox = document.getElementById(this.valueBoxId)
let value = ""

View File

@@ -21,12 +21,14 @@ Vue.component("dns-domain-selector", {
callback: function (resp) {
that.domainId = resp.data.domainId
that.domainName = resp.data.domainName
that.change()
}
})
},
remove: function() {
this.domainId = 0
this.domainName = ""
this.change()
},
update: function () {
let that = this
@@ -34,8 +36,15 @@ Vue.component("dns-domain-selector", {
callback: function (resp) {
that.domainId = resp.data.domainId
that.domainName = resp.data.domainName
that.change()
}
})
},
change: function () {
this.$emit("change", {
id: this.domainId,
name: this.domainName
})
}
},
template: `<div>

View File

@@ -11,12 +11,21 @@ Vue.component("dns-route-selector", {
return v.code + "@" + v.domainId
}),
isAdding: false,
routeCode: ""
routeCode: "",
keyword: "",
searchingRoutes: this.vAllRoutes.$copy()
}
},
methods: {
add: function () {
this.isAdding = true
this.keyword = ""
this.routeCode = ""
let that = this
setTimeout(function () {
that.$refs.keywordRef.focus()
}, 200)
},
cancel: function () {
this.isAdding = false
@@ -50,6 +59,23 @@ Vue.component("dns-route-selector", {
})
}
},
watch: {
keyword: function (keyword) {
if (keyword.length == 0) {
this.searchingRoutes = this.vAllRoutes.$copy()
this.routeCode = ""
return
}
this.searchingRoutes = this.vAllRoutes.filter(function (route) {
return teaweb.match(route.name, keyword)
})
if (this.searchingRoutes.length > 0) {
this.routeCode = this.searchingRoutes[0].code + "@" + this.searchingRoutes[0].domainId
} else {
this.routeCode = ""
}
}
},
template: `<div>
<input type="hidden" name="dnsRoutesJSON" :value="JSON.stringify(routeCodes)"/>
<div v-if="routes.length > 0">
@@ -62,16 +88,20 @@ Vue.component("dns-route-selector", {
<div v-if="isAdding">
<div class="ui fields inline">
<div class="ui field">
<select class="ui dropdown auto-width" v-model="routeCode">
<option value="">[请选择]</option>
<option v-for="route in vAllRoutes" :value="route.code + '@' + route.domainId">{{route.name}}{{route.domainName}}</option>
<select class="ui dropdown" style="width: 18em" v-model="routeCode">
<option value="" v-if="keyword.length == 0">[请选择]</option>
<option v-for="route in searchingRoutes" :value="route.code + '@' + route.domainId">{{route.name}}{{route.domainName}}</option>
</select>
</div>
<div class="ui field">
<input type="text" placeholder="搜索..." size="10" v-model="keyword" ref="keywordRef" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
</div>
<div class="ui field">
<button class="ui button tiny" type="button" @click.prevent="confirm">确定</button>
</div>
<div class="ui field">
<a href="" @click.prevent="cancel()"><i class="icon remove"></i></a>
<a href="" @click.prevent="cancel()"><i class="icon remove small"></i></a>
</div>
</div>
</div>

View File

@@ -53,7 +53,7 @@ Vue.component("ip-list-bind-box", {
}
},
template: `<div>
<a href="" @click.prevent="bind()">绑定+</a> &nbsp; <span v-if="lists.length > 0"><span class="disabled small">|&nbsp;</span> 已绑定:</span>
<a href="" @click.prevent="bind()" style="color: rgba(0,0,0,.6)">绑定+</a> &nbsp; <span v-if="lists.length > 0"><span class="disabled small">|&nbsp;</span> 已绑定:</span>
<div class="ui label basic small" v-for="(list, index) in lists">
<a :href="'/servers/iplists/list?listId=' + list.id" title="点击查看详情" style="opacity: 1">{{list.name}}</a>
<a href="" title="删除" @click.prevent="remove(index, list.id)"><i class="icon remove small"></i></a>

View File

@@ -0,0 +1,213 @@
// 节点IP阈值
Vue.component("node-ip-address-thresholds-box", {
props: ["v-thresholds"],
data: function () {
let thresholds = this.vThresholds
if (thresholds == null) {
thresholds = []
}
let avgRequests = {
duration: "",
operator: "lte",
value: ""
}
let avgTrafficOut = {
duration: "",
operator: "lte",
value: ""
}
let avgTrafficIn = {
duration: "",
operator: "lte",
value: ""
}
thresholds.forEach(function (v) {
switch (v.item) {
case "avgRequests":
avgRequests.duration = v.duration
avgRequests.operator = v.operator
avgRequests.value = v.value.toString()
break
case "avgTrafficOut":
avgTrafficOut.duration = v.duration
avgTrafficOut.operator = v.operator
avgTrafficOut.value = v.value.toString()
break
case "avgTrafficIn":
avgTrafficIn.duration = v.duration
avgTrafficIn.operator = v.operator
avgTrafficIn.value = v.value.toString()
break
}
})
return {
thresholds: thresholds,
avgRequests: avgRequests,
avgTrafficOut: avgTrafficOut,
avgTrafficIn: avgTrafficIn
}
},
watch: {
"avgRequests.duration": function () {
this.compose()
},
"avgRequests.operator": function () {
this.compose()
},
"avgRequests.value": function () {
this.compose()
},
"avgTrafficOut.duration": function () {
this.compose()
},
"avgTrafficOut.operator": function () {
this.compose()
},
"avgTrafficOut.value": function () {
this.compose()
},
"avgTrafficIn.duration": function () {
this.compose()
},
"avgTrafficIn.operator": function () {
this.compose()
},
"avgTrafficIn.value": function () {
this.compose()
}
},
methods: {
compose: function () {
let thresholds = []
// avg requests
{
let duration = parseInt(this.avgRequests.duration)
let value = parseInt(this.avgRequests.value)
if (!isNaN(duration) && duration > 0 && !isNaN(value) && value > 0) {
thresholds.push({
item: "avgRequests",
operator: this.avgRequests.operator,
duration: duration,
durationUnit: "minute",
value: value
})
}
}
// avg traffic out
{
let duration = parseInt(this.avgTrafficOut.duration)
let value = parseInt(this.avgTrafficOut.value)
if (!isNaN(duration) && duration > 0 && !isNaN(value) && value > 0) {
thresholds.push({
item: "avgTrafficOut",
operator: this.avgTrafficOut.operator,
duration: duration,
durationUnit: "minute",
value: value
})
}
}
// avg requests
{
let duration = parseInt(this.avgTrafficIn.duration)
let value = parseInt(this.avgTrafficIn.value)
if (!isNaN(duration) && duration > 0 && !isNaN(value) && value > 0) {
thresholds.push({
item: "avgTrafficIn",
operator: this.avgTrafficIn.operator,
duration: duration,
durationUnit: "minute",
value: value
})
}
}
this.thresholds = thresholds
}
},
template: `<div>
<input type="hidden" name="thresholdsJSON" :value="JSON.stringify(thresholds)"/>
<table class="ui table celled">
<thead>
<tr>
<td>统计项目</td>
<th>统计周期</th>
<th>操作符</th>
<th>对比值</th>
</tr>
</thead>
<tr>
<td>平均请求数/秒</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 5em" v-model="avgRequests.duration"/>
<span class="ui label">分钟</span>
</div>
</td>
<td>
<select class="ui dropdown auto-width" v-model="avgRequests.operator">
<option value="lte">小于等于</option>
<option value="gt">大于</option>
</select>
</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 6em" v-model="avgRequests.value"/>
<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: 5em" v-model="avgTrafficOut.duration"/>
<span class="ui label">分钟</span>
</div>
</td>
<td>
<select class="ui dropdown auto-width" v-model="avgTrafficOut.operator">
<option value="lte">小于等于</option>
<option value="gt">大于</option>
</select>
</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 6em" v-model="avgTrafficOut.value"/>
<span class="ui label">MB</span>
</div>
</td>
</tr>
<tr>
<td>平均上行流量/秒</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 5em" v-model="avgTrafficIn.duration"/>
<span class="ui label">分钟</span>
</div>
</td>
<td>
<select class="ui dropdown auto-width" v-model="avgTrafficIn.operator">
<option value="lte">小于等于</option>
<option value="gt">大于</option>
</select>
</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 6em" v-model="avgTrafficIn.value"/>
<span class="ui label">MB</span>
</div>
</td>
</tr>
</table>
<p class="comment">满足所有阈值条件时IP才会上线否则下线。统计周期和对比值设置为0表示没有限制。各个输入项只支持整数数字。</p>
</div>`
})

View File

@@ -1,8 +1,10 @@
// 节点IP地址管理标签形式
Vue.component("node-ip-addresses-box", {
props: ["vIpAddresses"],
props: ["v-ip-addresses", "role"],
data: function () {
return {
ipAddresses: (this.vIpAddresses == null) ? [] : this.vIpAddresses
ipAddresses: (this.vIpAddresses == null) ? [] : this.vIpAddresses,
supportThresholds: this.role != "ns"
}
},
methods: {
@@ -11,10 +13,12 @@ Vue.component("node-ip-addresses-box", {
window.UPDATING_NODE_IP_ADDRESS = null
let that = this;
teaweb.popup("/nodes/ipAddresses/createPopup", {
teaweb.popup("/nodes/ipAddresses/createPopup?supportThresholds=" + (this.supportThresholds ? 1 : 0), {
callback: function (resp) {
that.ipAddresses.push(resp.data.ipAddress);
}
},
height: "24em",
width: "44em"
})
},
@@ -23,10 +27,12 @@ Vue.component("node-ip-addresses-box", {
window.UPDATING_NODE_IP_ADDRESS = address
let that = this;
teaweb.popup("/nodes/ipAddresses/updatePopup", {
teaweb.popup("/nodes/ipAddresses/updatePopup?supportThresholds=" + (this.supportThresholds ? 1 : 0), {
callback: function (resp) {
Vue.set(that.ipAddresses, index, resp.data.ipAddress);
}
},
height: "24em",
width: "44em"
})
},
@@ -48,6 +54,10 @@ Vue.component("node-ip-addresses-box", {
<span v-if="isIPv6(address.ip)" class="grey">[IPv6]</span> {{address.ip}}
<span class="small grey" v-if="address.name.length > 0">{{address.name}}<span v-if="!address.canAccess">,不可访问</span></span>
<span class="small grey" v-if="address.name.length == 0 && !address.canAccess">(不可访问)</span>
<span class="small red" v-if="!address.isOn" title="未启用">[off]</span>
<span class="small red" v-if="!address.isUp" title="已下线">[down]</span>
<span class="small" v-if="address.thresholds != null && address.thresholds.length > 0">[阈值]</span>
&nbsp;
<a href="" title="修改" @click.prevent="updateIPAddress(index, address)"><i class="icon pencil small"></i></a>
<a href="" title="删除" @click.prevent="removeIPAddress(index)"><i class="icon remove"></i></a>
</div>

View File

@@ -32,9 +32,13 @@ Vue.component("ns-access-log-box", {
this.$refs.box.parentNode.style.cssText = ""
}
},
template: `<div class="access-log-row" :style="{'color': (accessLog.nsRecordId == null || accessLog.nsRecordId == 0) ? '#dc143c' : ''}" ref="box">
template: `<div class="access-log-row" :style="{'color': (!accessLog.isRecursive && (accessLog.nsRecordId == null || accessLog.nsRecordId == 0) || (accessLog.isRecursive && accessLog.recordValue != null && accessLog.recordValue.length == 0)) ? '#dc143c' : ''}" ref="box">
<span v-if="accessLog.region != null && accessLog.region.length > 0" class="grey">[{{accessLog.region}}]</span> <keyword :v-word="vKeyword">{{accessLog.remoteAddr}}</keyword> [{{accessLog.timeLocal}}] [{{accessLog.networking}}] <em>{{accessLog.questionType}} <keyword :v-word="vKeyword">{{accessLog.questionName}}</keyword></em> -&gt; <em>{{accessLog.recordType}} <keyword :v-word="vKeyword">{{accessLog.recordValue}}</keyword></em><!-- &nbsp; <a href="" @click.prevent="showLog" title="查看详情"><i class="icon expand"></i></a>-->
<div v-if="accessLog.error != null && accessLog.error.length > 0">
<div v-if="(accessLog.nsRoutes != null && accessLog.nsRoutes.length > 0) || accessLog.isRecursive" style="margin-top: 0.3em">
<span class="ui label tiny basic grey" v-for="route in accessLog.nsRoutes">线路: {{route.name}}</span>
<span class="ui label tiny basic grey" v-if="accessLog.isRecursive">递归DNS</span>
</div>
<div v-if="accessLog.error != null && accessLog.error.length > 0" style="color:#dc143c">
<i class="icon warning circle"></i>错误:[{{accessLog.error}}]
</div>
</div>`

Some files were not shown because too many files have changed in this diff Show More