前端页面
This commit is contained in:
123
EdgeAdmin/internal/web/actions/default/httpdns/addPortPopup.go
Normal file
123
EdgeAdmin/internal/web/actions/default/httpdns/addPortPopup.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package httpdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddPortPopupAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AddPortPopupAction) Init() {
|
||||||
|
this.Nav("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AddPortPopupAction) RunGet(params struct {
|
||||||
|
Protocol string
|
||||||
|
From string
|
||||||
|
SupportRange bool
|
||||||
|
}) {
|
||||||
|
this.Data["from"] = params.From
|
||||||
|
|
||||||
|
var protocols = serverconfigs.FindAllServerProtocols()
|
||||||
|
if len(params.Protocol) > 0 {
|
||||||
|
result := []maps.Map{}
|
||||||
|
for _, p := range protocols {
|
||||||
|
if p.GetString("code") == params.Protocol {
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protocols = result
|
||||||
|
}
|
||||||
|
this.Data["protocols"] = protocols
|
||||||
|
|
||||||
|
this.Data["supportRange"] = params.SupportRange
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AddPortPopupAction) RunPost(params struct {
|
||||||
|
SupportRange bool
|
||||||
|
|
||||||
|
Protocol string
|
||||||
|
Address string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
// 校验地址
|
||||||
|
var addr = maps.Map{
|
||||||
|
"protocol": params.Protocol,
|
||||||
|
"host": "",
|
||||||
|
"portRange": "",
|
||||||
|
"minPort": 0,
|
||||||
|
"maxPort": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
var portRegexp = regexp.MustCompile(`^\d+$`)
|
||||||
|
if portRegexp.MatchString(params.Address) { // 单个端口
|
||||||
|
addr["portRange"] = this.checkPort(params.Address)
|
||||||
|
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(params.Address) { // Port1-Port2
|
||||||
|
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(params.Address)
|
||||||
|
} else if strings.Contains(params.Address, ":") { // IP:Port
|
||||||
|
index := strings.LastIndex(params.Address, ":")
|
||||||
|
addr["host"] = strings.TrimSpace(params.Address[:index])
|
||||||
|
port := strings.TrimSpace(params.Address[index+1:])
|
||||||
|
if portRegexp.MatchString(port) {
|
||||||
|
addr["portRange"] = this.checkPort(port)
|
||||||
|
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(port) { // Port1-Port2
|
||||||
|
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(port)
|
||||||
|
} else {
|
||||||
|
this.FailField("address", "请输入正确的端口或者网络地址")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FailField("address", "请输入正确的端口或者网络地址")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["address"] = addr
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AddPortPopupAction) checkPort(port string) (portRange string) {
|
||||||
|
var intPort = types.Int(port)
|
||||||
|
if intPort < 1 {
|
||||||
|
this.FailField("address", "端口号不能小于1")
|
||||||
|
}
|
||||||
|
if intPort > 65535 {
|
||||||
|
this.FailField("address", "端口号不能大于65535")
|
||||||
|
}
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AddPortPopupAction) checkPortRange(port string) (portRange string, minPort int, maxPort int) {
|
||||||
|
var pieces = strings.Split(port, "-")
|
||||||
|
var piece1 = strings.TrimSpace(pieces[0])
|
||||||
|
var piece2 = strings.TrimSpace(pieces[1])
|
||||||
|
var port1 = types.Int(piece1)
|
||||||
|
var port2 = types.Int(piece2)
|
||||||
|
|
||||||
|
if port1 < 1 {
|
||||||
|
this.FailField("address", "端口号不能小于1")
|
||||||
|
}
|
||||||
|
if port1 > 65535 {
|
||||||
|
this.FailField("address", "端口号不能大于65535")
|
||||||
|
}
|
||||||
|
|
||||||
|
if port2 < 1 {
|
||||||
|
this.FailField("address", "端口号不能小于1")
|
||||||
|
}
|
||||||
|
if port2 > 65535 {
|
||||||
|
this.FailField("address", "端口号不能大于65535")
|
||||||
|
}
|
||||||
|
|
||||||
|
if port1 > port2 {
|
||||||
|
port1, port2 = port2, port1
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.String(port1) + "-" + types.String(port2), port1, port2
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
package apps
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/policies"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,15 +14,41 @@ type AppSettingsAction struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *AppSettingsAction) Init() {
|
func (this *AppSettingsAction) Init() {
|
||||||
this.Nav("httpdns", "app", "")
|
this.Nav("httpdns", "app", "settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *AppSettingsAction) RunGet(params struct {
|
func (this *AppSettingsAction) RunGet(params struct {
|
||||||
AppId int64
|
AppId int64
|
||||||
|
Section string
|
||||||
}) {
|
}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
app := pickApp(params.AppId)
|
app := pickApp(params.AppId)
|
||||||
|
|
||||||
|
// 顶部 tabbar
|
||||||
|
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), params.AppId, "settings")
|
||||||
|
|
||||||
|
// 当前选中的 section
|
||||||
|
section := params.Section
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = "basic"
|
||||||
|
}
|
||||||
|
this.Data["activeSection"] = section
|
||||||
|
appIdStr := strconv.FormatInt(params.AppId, 10)
|
||||||
|
this.Data["leftMenuItems"] = []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"name": "基础配置",
|
||||||
|
"url": "/httpdns/apps/app/settings?appId=" + appIdStr + "§ion=basic",
|
||||||
|
"isActive": section == "basic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "认证与密钥",
|
||||||
|
"url": "/httpdns/apps/app/settings?appId=" + appIdStr + "§ion=auth",
|
||||||
|
"isActive": section == "auth",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
settings := loadAppSettings(app)
|
settings := loadAppSettings(app)
|
||||||
|
this.Data["clusters"] = policies.LoadAvailableDeployClusters()
|
||||||
this.Data["app"] = app
|
this.Data["app"] = app
|
||||||
this.Data["settings"] = settings
|
this.Data["settings"] = settings
|
||||||
this.Show()
|
this.Show()
|
||||||
@@ -29,15 +58,23 @@ func (this *AppSettingsAction) RunPost(params struct {
|
|||||||
AppId int64
|
AppId int64
|
||||||
|
|
||||||
AppStatus bool
|
AppStatus bool
|
||||||
|
PrimaryClusterId int64
|
||||||
|
BackupClusterId int64
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
params.Must.Field("appId", params.AppId).Gt(0, "please select app")
|
params.Must.Field("appId", params.AppId).Gt(0, "please select app")
|
||||||
|
params.Must.Field("primaryClusterId", params.PrimaryClusterId).Gt(0, "please select primary cluster")
|
||||||
|
if params.BackupClusterId > 0 && params.BackupClusterId == params.PrimaryClusterId {
|
||||||
|
this.FailField("backupClusterId", "backup cluster must be different from primary cluster")
|
||||||
|
}
|
||||||
|
|
||||||
app := pickApp(params.AppId)
|
app := pickApp(params.AppId)
|
||||||
settings := loadAppSettings(app)
|
settings := loadAppSettings(app)
|
||||||
settings["appStatus"] = params.AppStatus
|
settings["appStatus"] = params.AppStatus
|
||||||
|
settings["primaryClusterId"] = params.PrimaryClusterId
|
||||||
|
settings["backupClusterId"] = params.BackupClusterId
|
||||||
|
|
||||||
// SNI strategy is fixed to level2 empty.
|
// SNI strategy is fixed to level2 empty.
|
||||||
settings["sniPolicy"] = "level2"
|
settings["sniPolicy"] = "level2"
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ func defaultAppSettings(app maps.Map) maps.Map {
|
|||||||
aesSecretPlain := randomPlainSecret("as")
|
aesSecretPlain := randomPlainSecret("as")
|
||||||
return maps.Map{
|
return maps.Map{
|
||||||
"appId": app.GetString("appId"),
|
"appId": app.GetString("appId"),
|
||||||
|
"primaryClusterId": app.GetInt64("clusterId"),
|
||||||
|
"backupClusterId": int64(0),
|
||||||
"signSecretPlain": signSecretPlain,
|
"signSecretPlain": signSecretPlain,
|
||||||
"signSecretMasked": maskSecret(signSecretPlain),
|
"signSecretMasked": maskSecret(signSecretPlain),
|
||||||
"signSecretUpdatedAt": "2026-02-20 12:30:00",
|
"signSecretUpdatedAt": "2026-02-20 12:30:00",
|
||||||
@@ -45,6 +47,8 @@ func defaultAppSettings(app maps.Map) maps.Map {
|
|||||||
func cloneSettings(settings maps.Map) maps.Map {
|
func cloneSettings(settings maps.Map) maps.Map {
|
||||||
return maps.Map{
|
return maps.Map{
|
||||||
"appId": settings.GetString("appId"),
|
"appId": settings.GetString("appId"),
|
||||||
|
"primaryClusterId": settings.GetInt64("primaryClusterId"),
|
||||||
|
"backupClusterId": settings.GetInt64("backupClusterId"),
|
||||||
"signSecretPlain": settings.GetString("signSecretPlain"),
|
"signSecretPlain": settings.GetString("signSecretPlain"),
|
||||||
"signSecretMasked": settings.GetString("signSecretMasked"),
|
"signSecretMasked": settings.GetString("signSecretMasked"),
|
||||||
"signSecretUpdatedAt": settings.GetString("signSecretUpdatedAt"),
|
"signSecretUpdatedAt": settings.GetString("signSecretUpdatedAt"),
|
||||||
@@ -90,6 +94,12 @@ func saveAppSettings(appId int64, settings maps.Map) {
|
|||||||
appSettingsStore.Unlock()
|
appSettingsStore.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deleteAppSettings(appId int64) {
|
||||||
|
appSettingsStore.Lock()
|
||||||
|
delete(appSettingsStore.data, appId)
|
||||||
|
appSettingsStore.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func resetSignSecret(app maps.Map) maps.Map {
|
func resetSignSecret(app maps.Map) maps.Map {
|
||||||
settings := loadAppSettings(app)
|
settings := loadAppSettings(app)
|
||||||
signSecretPlain := randomPlainSecret("ss")
|
signSecretPlain := randomPlainSecret("ss")
|
||||||
@@ -144,6 +154,19 @@ func maskSecret(secret string) string {
|
|||||||
func ensureSettingsFields(settings maps.Map) bool {
|
func ensureSettingsFields(settings maps.Map) bool {
|
||||||
changed := false
|
changed := false
|
||||||
|
|
||||||
|
if settings.GetInt64("primaryClusterId") <= 0 {
|
||||||
|
settings["primaryClusterId"] = int64(1)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if settings.GetInt64("backupClusterId") < 0 {
|
||||||
|
settings["backupClusterId"] = int64(0)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if settings.GetInt64("backupClusterId") > 0 && settings.GetInt64("backupClusterId") == settings.GetInt64("primaryClusterId") {
|
||||||
|
settings["backupClusterId"] = int64(0)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
signSecretPlain := settings.GetString("signSecretPlain")
|
signSecretPlain := settings.GetString("signSecretPlain")
|
||||||
if len(signSecretPlain) == 0 {
|
if len(signSecretPlain) == 0 {
|
||||||
signSecretPlain = randomPlainSecret("ss")
|
signSecretPlain = randomPlainSecret("ss")
|
||||||
|
|||||||
@@ -19,17 +19,27 @@ func (this *CreatePopupAction) RunGet(params struct{}) {
|
|||||||
clusters := policies.LoadAvailableDeployClusters()
|
clusters := policies.LoadAvailableDeployClusters()
|
||||||
this.Data["clusters"] = clusters
|
this.Data["clusters"] = clusters
|
||||||
|
|
||||||
defaultClusterID := policies.LoadDefaultClusterID()
|
defaultPrimaryClusterId := policies.LoadDefaultClusterID()
|
||||||
if defaultClusterID <= 0 && len(clusters) > 0 {
|
if defaultPrimaryClusterId <= 0 && len(clusters) > 0 {
|
||||||
defaultClusterID = clusters[0].GetInt64("id")
|
defaultPrimaryClusterId = clusters[0].GetInt64("id")
|
||||||
}
|
}
|
||||||
this.Data["defaultClusterId"] = defaultClusterID
|
this.Data["defaultPrimaryClusterId"] = defaultPrimaryClusterId
|
||||||
|
|
||||||
|
defaultBackupClusterId := int64(0)
|
||||||
|
for _, cluster := range clusters {
|
||||||
|
clusterId := cluster.GetInt64("id")
|
||||||
|
if clusterId > 0 && clusterId != defaultPrimaryClusterId {
|
||||||
|
defaultBackupClusterId = clusterId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Data["defaultBackupClusterId"] = defaultBackupClusterId
|
||||||
|
|
||||||
// Mock users for dropdown
|
// Mock users for dropdown
|
||||||
this.Data["users"] = []maps.Map{
|
this.Data["users"] = []maps.Map{
|
||||||
{"id": int64(1), "name": "张三", "username": "zhangsan"},
|
{"id": int64(1), "name": "User A", "username": "zhangsan"},
|
||||||
{"id": int64(2), "name": "李四", "username": "lisi"},
|
{"id": int64(2), "name": "User B", "username": "lisi"},
|
||||||
{"id": int64(3), "name": "王五", "username": "wangwu"},
|
{"id": int64(3), "name": "User C", "username": "wangwu"},
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
@@ -37,13 +47,17 @@ func (this *CreatePopupAction) RunGet(params struct{}) {
|
|||||||
|
|
||||||
func (this *CreatePopupAction) RunPost(params struct {
|
func (this *CreatePopupAction) RunPost(params struct {
|
||||||
Name string
|
Name string
|
||||||
ClusterId int64
|
PrimaryClusterId int64
|
||||||
|
BackupClusterId int64
|
||||||
UserId int64
|
UserId int64
|
||||||
|
|
||||||
Must *actions.Must
|
Must *actions.Must
|
||||||
CSRF *actionutils.CSRF
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
params.Must.Field("name", params.Name).Require("请输入应用名称")
|
params.Must.Field("name", params.Name).Require("please input app name")
|
||||||
params.Must.Field("clusterId", params.ClusterId).Gt(0, "请选择所属集群")
|
params.Must.Field("primaryClusterId", params.PrimaryClusterId).Gt(0, "please select primary cluster")
|
||||||
|
if params.BackupClusterId > 0 && params.BackupClusterId == params.PrimaryClusterId {
|
||||||
|
this.FailField("backupClusterId", "backup cluster must be different from primary cluster")
|
||||||
|
}
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ func (this *CustomRecordsAction) RunGet(params struct {
|
|||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
|
|
||||||
app := pickApp(params.AppId)
|
app := pickApp(params.AppId)
|
||||||
|
// 自定义解析属于域名管理子页,顶部沿用应用 tabbar(高亮域名列表)
|
||||||
|
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), params.AppId, "domains")
|
||||||
|
|
||||||
domains := mockDomains(app.GetInt64("id"))
|
domains := mockDomains(app.GetInt64("id"))
|
||||||
domain := pickDomainFromDomains(domains, params.DomainId)
|
domain := pickDomainFromDomains(domains, params.DomainId)
|
||||||
domainName := domain.GetString("name")
|
domainName := domain.GetString("name")
|
||||||
@@ -35,7 +38,6 @@ func (this *CustomRecordsAction) RunGet(params struct {
|
|||||||
|
|
||||||
for _, record := range records {
|
for _, record := range records {
|
||||||
record["lineText"] = buildLineText(record)
|
record["lineText"] = buildLineText(record)
|
||||||
record["paramsText"] = buildParamsText(record)
|
|
||||||
record["recordValueText"] = buildRecordValueText(record)
|
record["recordValueText"] = buildRecordValueText(record)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ func (this *CustomRecordsCreatePopupAction) RunGet(params struct {
|
|||||||
"weightEnabled": false,
|
"weightEnabled": false,
|
||||||
"ttl": 30,
|
"ttl": 30,
|
||||||
"isOn": true,
|
"isOn": true,
|
||||||
"sdnsParamsJson": "[]",
|
|
||||||
"recordItemsJson": `[{"type":"A","value":"","weight":100}]`,
|
"recordItemsJson": `[{"type":"A","value":"","weight":100}]`,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +64,6 @@ func (this *CustomRecordsCreatePopupAction) RunGet(params struct {
|
|||||||
record["ttl"] = existing.GetInt("ttl")
|
record["ttl"] = existing.GetInt("ttl")
|
||||||
record["isOn"] = existing.GetBool("isOn")
|
record["isOn"] = existing.GetBool("isOn")
|
||||||
|
|
||||||
sdnsParams, _ := existing["sdnsParams"].([]maps.Map)
|
|
||||||
record["sdnsParamsJson"] = marshalJSON(sdnsParams, "[]")
|
|
||||||
|
|
||||||
recordItems := make([]maps.Map, 0)
|
recordItems := make([]maps.Map, 0)
|
||||||
recordType := strings.ToUpper(strings.TrimSpace(existing.GetString("recordType")))
|
recordType := strings.ToUpper(strings.TrimSpace(existing.GetString("recordType")))
|
||||||
values, _ := existing["recordValues"].([]maps.Map)
|
values, _ := existing["recordValues"].([]maps.Map)
|
||||||
@@ -140,7 +136,6 @@ func (this *CustomRecordsCreatePopupAction) RunPost(params struct {
|
|||||||
LineCountry string
|
LineCountry string
|
||||||
|
|
||||||
RuleName string
|
RuleName string
|
||||||
SDNSParamsJSON string
|
|
||||||
RecordItemsJSON string
|
RecordItemsJSON string
|
||||||
WeightEnabled bool
|
WeightEnabled bool
|
||||||
TTL int
|
TTL int
|
||||||
@@ -154,7 +149,6 @@ func (this *CustomRecordsCreatePopupAction) RunPost(params struct {
|
|||||||
params.Domain = strings.TrimSpace(params.Domain)
|
params.Domain = strings.TrimSpace(params.Domain)
|
||||||
params.LineScope = strings.ToLower(strings.TrimSpace(params.LineScope))
|
params.LineScope = strings.ToLower(strings.TrimSpace(params.LineScope))
|
||||||
params.RuleName = strings.TrimSpace(params.RuleName)
|
params.RuleName = strings.TrimSpace(params.RuleName)
|
||||||
params.SDNSParamsJSON = strings.TrimSpace(params.SDNSParamsJSON)
|
|
||||||
params.RecordItemsJSON = strings.TrimSpace(params.RecordItemsJSON)
|
params.RecordItemsJSON = strings.TrimSpace(params.RecordItemsJSON)
|
||||||
|
|
||||||
domain := maps.Map{}
|
domain := maps.Map{}
|
||||||
@@ -181,16 +175,6 @@ func (this *CustomRecordsCreatePopupAction) RunPost(params struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sdnsParams, err := parseSDNSParamsJSON(params.SDNSParamsJSON)
|
|
||||||
if err != nil {
|
|
||||||
this.Fail(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(sdnsParams) > 10 {
|
|
||||||
this.Fail("sdns params should be <= 10")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
recordValues, err := parseRecordItemsJSON(params.RecordItemsJSON, params.WeightEnabled)
|
recordValues, err := parseRecordItemsJSON(params.RecordItemsJSON, params.WeightEnabled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Fail(err.Error())
|
this.Fail(err.Error())
|
||||||
@@ -250,7 +234,7 @@ func (this *CustomRecordsCreatePopupAction) RunPost(params struct {
|
|||||||
"lineContinent": lineContinent,
|
"lineContinent": lineContinent,
|
||||||
"lineCountry": lineCountry,
|
"lineCountry": lineCountry,
|
||||||
"ruleName": params.RuleName,
|
"ruleName": params.RuleName,
|
||||||
"sdnsParams": sdnsParams,
|
"sdnsParams": []maps.Map{},
|
||||||
"recordType": recordType,
|
"recordType": recordType,
|
||||||
"recordValues": recordValues,
|
"recordValues": recordValues,
|
||||||
"weightEnabled": params.WeightEnabled,
|
"weightEnabled": params.WeightEnabled,
|
||||||
@@ -261,38 +245,6 @@ func (this *CustomRecordsCreatePopupAction) RunPost(params struct {
|
|||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSDNSParamsJSON(raw string) ([]maps.Map, error) {
|
|
||||||
if len(raw) == 0 {
|
|
||||||
return []maps.Map{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
list := []maps.Map{}
|
|
||||||
if err := json.Unmarshal([]byte(raw), &list); err != nil {
|
|
||||||
return nil, fmt.Errorf("sdns params json is invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]maps.Map, 0, len(list))
|
|
||||||
for _, item := range list {
|
|
||||||
name := strings.TrimSpace(item.GetString("name"))
|
|
||||||
value := strings.TrimSpace(item.GetString("value"))
|
|
||||||
if len(name) == 0 && len(value) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(name) < 2 || len(name) > 64 {
|
|
||||||
return nil, fmt.Errorf("sdns param name length should be in 2-64")
|
|
||||||
}
|
|
||||||
if len(value) < 1 || len(value) > 64 {
|
|
||||||
return nil, fmt.Errorf("sdns param value length should be in 1-64")
|
|
||||||
}
|
|
||||||
result = append(result, maps.Map{
|
|
||||||
"name": name,
|
|
||||||
"value": value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRecordItemsJSON(raw string, weightEnabled bool) ([]maps.Map, error) {
|
func parseRecordItemsJSON(raw string, weightEnabled bool) ([]maps.Map, error) {
|
||||||
if len(raw) == 0 {
|
if len(raw) == 0 {
|
||||||
return []maps.Map{}, nil
|
return []maps.Map{}, nil
|
||||||
|
|||||||
@@ -25,12 +25,7 @@ var customRecordStore = struct {
|
|||||||
"lineRegion": "华东",
|
"lineRegion": "华东",
|
||||||
"lineProvince": "上海",
|
"lineProvince": "上海",
|
||||||
"ruleName": "上海电信灰度-v2",
|
"ruleName": "上海电信灰度-v2",
|
||||||
"sdnsParams": []maps.Map{
|
"sdnsParams": []maps.Map{},
|
||||||
{
|
|
||||||
"name": "app_ver",
|
|
||||||
"value": "2.3.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"recordType": "A",
|
"recordType": "A",
|
||||||
"recordValues": []maps.Map{{"type": "A", "value": "1.1.1.10", "weight": 100}},
|
"recordValues": []maps.Map{{"type": "A", "value": "1.1.1.10", "weight": 100}},
|
||||||
"weightEnabled": false,
|
"weightEnabled": false,
|
||||||
@@ -134,6 +129,12 @@ func deleteCustomRecord(appID int64, recordID int64) {
|
|||||||
customRecordStore.data[appID] = filtered
|
customRecordStore.data[appID] = filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deleteCustomRecordsByApp(appID int64) {
|
||||||
|
customRecordStore.Lock()
|
||||||
|
defer customRecordStore.Unlock()
|
||||||
|
delete(customRecordStore.data, appID)
|
||||||
|
}
|
||||||
|
|
||||||
func toggleCustomRecord(appID int64, recordID int64, isOn bool) {
|
func toggleCustomRecord(appID int64, recordID int64, isOn bool) {
|
||||||
customRecordStore.Lock()
|
customRecordStore.Lock()
|
||||||
defer customRecordStore.Unlock()
|
defer customRecordStore.Unlock()
|
||||||
@@ -207,27 +208,6 @@ func buildLineText(record maps.Map) string {
|
|||||||
return strings.Join(finalParts, " / ")
|
return strings.Join(finalParts, " / ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildParamsText(record maps.Map) string {
|
|
||||||
params, ok := record["sdnsParams"].([]maps.Map)
|
|
||||||
if !ok || len(params) == 0 {
|
|
||||||
return "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := make([]string, 0, len(params))
|
|
||||||
for _, param := range params {
|
|
||||||
name := strings.TrimSpace(param.GetString("name"))
|
|
||||||
value := strings.TrimSpace(param.GetString("value"))
|
|
||||||
if len(name) == 0 || len(value) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parts = append(parts, name+"="+value)
|
|
||||||
}
|
|
||||||
if len(parts) == 0 {
|
|
||||||
return "-"
|
|
||||||
}
|
|
||||||
return strings.Join(parts, "; ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildRecordValueText(record maps.Map) string {
|
func buildRecordValueText(record maps.Map) string {
|
||||||
values, ok := record["recordValues"].([]maps.Map)
|
values, ok := record["recordValues"].([]maps.Map)
|
||||||
if !ok || len(values) == 0 {
|
if !ok || len(values) == 0 {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteAction) Init() {
|
||||||
|
this.Nav("httpdns", "app", "delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteAction) RunGet(params struct {
|
||||||
|
AppId int64
|
||||||
|
}) {
|
||||||
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
|
app := pickApp(params.AppId)
|
||||||
|
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), app.GetInt64("id"), "delete")
|
||||||
|
this.Data["app"] = app
|
||||||
|
this.Data["domainCount"] = len(mockDomains(app.GetInt64("id")))
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteAction) RunPost(params struct {
|
||||||
|
AppId int64
|
||||||
|
}) {
|
||||||
|
if params.AppId > 0 {
|
||||||
|
if deleteApp(params.AppId) {
|
||||||
|
deleteAppSettings(params.AppId)
|
||||||
|
deleteCustomRecordsByApp(params.AppId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ type DomainsAction struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *DomainsAction) Init() {
|
func (this *DomainsAction) Init() {
|
||||||
this.Nav("httpdns", "app", "")
|
this.Nav("httpdns", "app", "domains")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *DomainsAction) RunGet(params struct {
|
func (this *DomainsAction) RunGet(params struct {
|
||||||
@@ -19,6 +19,9 @@ func (this *DomainsAction) RunGet(params struct {
|
|||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
app := pickApp(params.AppId)
|
app := pickApp(params.AppId)
|
||||||
|
|
||||||
|
// 构建顶部 tabbar
|
||||||
|
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), params.AppId, "domains")
|
||||||
|
|
||||||
domains := mockDomains(app.GetInt64("id"))
|
domains := mockDomains(app.GetInt64("id"))
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
domainName := domain.GetString("name")
|
domainName := domain.GetString("name")
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ func init() {
|
|||||||
Prefix("/httpdns/apps").
|
Prefix("/httpdns/apps").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
Get("/app", new(AppAction)).
|
Get("/app", new(AppAction)).
|
||||||
|
Get("/sdk", new(SDKAction)).
|
||||||
GetPost("/app/settings", new(AppSettingsAction)).
|
GetPost("/app/settings", new(AppSettingsAction)).
|
||||||
Post("/app/settings/toggleSignEnabled", new(AppSettingsToggleSignEnabledAction)).
|
Post("/app/settings/toggleSignEnabled", new(AppSettingsToggleSignEnabledAction)).
|
||||||
Post("/app/settings/resetSignSecret", new(AppSettingsResetSignSecretAction)).
|
Post("/app/settings/resetSignSecret", new(AppSettingsResetSignSecretAction)).
|
||||||
@@ -22,6 +23,7 @@ func init() {
|
|||||||
Get("/domains", new(DomainsAction)).
|
Get("/domains", new(DomainsAction)).
|
||||||
Get("/customRecords", new(CustomRecordsAction)).
|
Get("/customRecords", new(CustomRecordsAction)).
|
||||||
GetPost("/createPopup", new(CreatePopupAction)).
|
GetPost("/createPopup", new(CreatePopupAction)).
|
||||||
|
GetPost("/delete", new(DeleteAction)).
|
||||||
GetPost("/domains/createPopup", new(DomainsCreatePopupAction)).
|
GetPost("/domains/createPopup", new(DomainsCreatePopupAction)).
|
||||||
Post("/domains/delete", new(DomainsDeleteAction)).
|
Post("/domains/delete", new(DomainsDeleteAction)).
|
||||||
GetPost("/customRecords/createPopup", new(CustomRecordsCreatePopupAction)).
|
GetPost("/customRecords/createPopup", new(CustomRecordsCreatePopupAction)).
|
||||||
|
|||||||
@@ -2,15 +2,23 @@ package apps
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockApps() []maps.Map {
|
var appStore = struct {
|
||||||
|
sync.RWMutex
|
||||||
|
data []maps.Map
|
||||||
|
}{
|
||||||
|
data: defaultMockApps(),
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultMockApps() []maps.Map {
|
||||||
return []maps.Map{
|
return []maps.Map{
|
||||||
{
|
{
|
||||||
"id": int64(1),
|
"id": int64(1),
|
||||||
"name": "主站移动业务",
|
"name": "\u4e3b\u7ad9\u79fb\u52a8\u4e1a\u52a1",
|
||||||
"appId": "ab12xc34s2",
|
"appId": "ab12xc34s2",
|
||||||
"clusterId": int64(1),
|
"clusterId": int64(1),
|
||||||
"domainCount": 3,
|
"domainCount": 3,
|
||||||
@@ -20,12 +28,12 @@ func mockApps() []maps.Map {
|
|||||||
"pinningMode": "report",
|
"pinningMode": "report",
|
||||||
"sanMode": "strict",
|
"sanMode": "strict",
|
||||||
"riskLevel": "medium",
|
"riskLevel": "medium",
|
||||||
"riskSummary": "Pinning 处于观察模式",
|
"riskSummary": "Pinning \u5904\u4e8e\u89c2\u5bdf\u6a21\u5f0f",
|
||||||
"secretVersion": "v2026.02.20",
|
"secretVersion": "v2026.02.20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": int64(2),
|
"id": int64(2),
|
||||||
"name": "视频网关业务",
|
"name": "\u89c6\u9891\u7f51\u5173\u4e1a\u52a1",
|
||||||
"appId": "vd8992ksm1",
|
"appId": "vd8992ksm1",
|
||||||
"clusterId": int64(2),
|
"clusterId": int64(2),
|
||||||
"domainCount": 1,
|
"domainCount": 1,
|
||||||
@@ -35,12 +43,12 @@ func mockApps() []maps.Map {
|
|||||||
"pinningMode": "enforce",
|
"pinningMode": "enforce",
|
||||||
"sanMode": "strict",
|
"sanMode": "strict",
|
||||||
"riskLevel": "low",
|
"riskLevel": "low",
|
||||||
"riskSummary": "已启用强校验",
|
"riskSummary": "\u5df2\u542f\u7528\u5f3a\u6821\u9a8c",
|
||||||
"secretVersion": "v2026.02.18",
|
"secretVersion": "v2026.02.18",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": int64(3),
|
"id": int64(3),
|
||||||
"name": "海外灰度测试",
|
"name": "\u6d77\u5916\u7070\u5ea6\u6d4b\u8bd5",
|
||||||
"appId": "ov7711hkq9",
|
"appId": "ov7711hkq9",
|
||||||
"clusterId": int64(1),
|
"clusterId": int64(1),
|
||||||
"domainCount": 2,
|
"domainCount": 2,
|
||||||
@@ -50,12 +58,57 @@ func mockApps() []maps.Map {
|
|||||||
"pinningMode": "off",
|
"pinningMode": "off",
|
||||||
"sanMode": "report",
|
"sanMode": "report",
|
||||||
"riskLevel": "high",
|
"riskLevel": "high",
|
||||||
"riskSummary": "应用关闭且证书策略偏弱",
|
"riskSummary": "\u5e94\u7528\u5173\u95ed\u4e14\u8bc1\u4e66\u7b56\u7565\u504f\u5f31",
|
||||||
"secretVersion": "v2026.01.30",
|
"secretVersion": "v2026.01.30",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cloneMap(src maps.Map) maps.Map {
|
||||||
|
dst := maps.Map{}
|
||||||
|
for k, v := range src {
|
||||||
|
dst[k] = v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneApps(apps []maps.Map) []maps.Map {
|
||||||
|
result := make([]maps.Map, 0, len(apps))
|
||||||
|
for _, app := range apps {
|
||||||
|
result = append(result, cloneMap(app))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockApps() []maps.Map {
|
||||||
|
appStore.RLock()
|
||||||
|
defer appStore.RUnlock()
|
||||||
|
return cloneApps(appStore.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteApp(appID int64) bool {
|
||||||
|
if appID <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
appStore.Lock()
|
||||||
|
defer appStore.Unlock()
|
||||||
|
|
||||||
|
found := false
|
||||||
|
filtered := make([]maps.Map, 0, len(appStore.data))
|
||||||
|
for _, app := range appStore.data {
|
||||||
|
if app.GetInt64("id") == appID {
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filtered = append(filtered, app)
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
appStore.data = filtered
|
||||||
|
}
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
func filterApps(keyword string, riskLevel string, ecsMode string, pinningMode string) []maps.Map {
|
func filterApps(keyword string, riskLevel string, ecsMode string, pinningMode string) []maps.Map {
|
||||||
all := mockApps()
|
all := mockApps()
|
||||||
if len(keyword) == 0 && len(riskLevel) == 0 && len(ecsMode) == 0 && len(pinningMode) == 0 {
|
if len(keyword) == 0 && len(riskLevel) == 0 && len(ecsMode) == 0 && len(pinningMode) == 0 {
|
||||||
@@ -89,6 +142,15 @@ func filterApps(keyword string, riskLevel string, ecsMode string, pinningMode st
|
|||||||
|
|
||||||
func pickApp(appID int64) maps.Map {
|
func pickApp(appID int64) maps.Map {
|
||||||
apps := mockApps()
|
apps := mockApps()
|
||||||
|
if len(apps) == 0 {
|
||||||
|
return maps.Map{
|
||||||
|
"id": int64(0),
|
||||||
|
"name": "",
|
||||||
|
"appId": "",
|
||||||
|
"clusterId": int64(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if appID <= 0 {
|
if appID <= 0 {
|
||||||
return apps[0]
|
return apps[0]
|
||||||
}
|
}
|
||||||
|
|||||||
27
EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk.go
Normal file
27
EdgeAdmin/internal/web/actions/default/httpdns/apps/sdk.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SDKAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SDKAction) Init() {
|
||||||
|
this.Nav("httpdns", "app", "sdk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SDKAction) RunGet(params struct {
|
||||||
|
AppId int64
|
||||||
|
}) {
|
||||||
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
|
app := pickApp(params.AppId)
|
||||||
|
|
||||||
|
// 构建顶部 tabbar
|
||||||
|
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), params.AppId, "sdk")
|
||||||
|
|
||||||
|
this.Data["app"] = app
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
@@ -21,6 +21,10 @@ func (this *ClusterAction) RunGet(params struct {
|
|||||||
}) {
|
}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
cluster := pickCluster(params.ClusterId)
|
cluster := pickCluster(params.ClusterId)
|
||||||
|
|
||||||
|
// 构建顶部 tabbar
|
||||||
|
httpdnsutils.AddClusterTabbar(this.Parent(), cluster.GetString("name"), params.ClusterId, "node")
|
||||||
|
|
||||||
this.Data["clusterId"] = params.ClusterId
|
this.Data["clusterId"] = params.ClusterId
|
||||||
this.Data["cluster"] = cluster
|
this.Data["cluster"] = cluster
|
||||||
this.Data["installState"] = params.InstalledState
|
this.Data["installState"] = params.InstalledState
|
||||||
|
|||||||
@@ -1,8 +1,17 @@
|
|||||||
package clusters
|
package clusters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/policies"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClusterSettingsAction struct {
|
type ClusterSettingsAction struct {
|
||||||
@@ -15,35 +24,184 @@ func (this *ClusterSettingsAction) Init() {
|
|||||||
|
|
||||||
func (this *ClusterSettingsAction) RunGet(params struct {
|
func (this *ClusterSettingsAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
|
Section string
|
||||||
}) {
|
}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
cluster := pickCluster(params.ClusterId)
|
cluster := pickCluster(params.ClusterId)
|
||||||
installDir := cluster.GetString("installDir")
|
settings := loadClusterSettings(cluster)
|
||||||
if len(installDir) == 0 {
|
cluster["name"] = settings.GetString("name")
|
||||||
installDir = "/opt/edge-httpdns"
|
|
||||||
|
// 构建顶部 tabbar
|
||||||
|
httpdnsutils.AddClusterTabbar(this.Parent(), cluster.GetString("name"), params.ClusterId, "setting")
|
||||||
|
|
||||||
|
// 当前选中的 section
|
||||||
|
section := params.Section
|
||||||
|
if len(section) == 0 {
|
||||||
|
section = "basic"
|
||||||
}
|
}
|
||||||
|
this.Data["activeSection"] = section
|
||||||
|
|
||||||
|
// 左侧菜单
|
||||||
|
cid := strconv.FormatInt(params.ClusterId, 10)
|
||||||
|
this.Data["leftMenuItems"] = []map[string]interface{}{
|
||||||
|
{"name": "基础设置", "url": "/httpdns/clusters/cluster/settings?clusterId=" + cid + "§ion=basic", "isActive": section == "basic"},
|
||||||
|
{"name": "TLS", "url": "/httpdns/clusters/cluster/settings?clusterId=" + cid + "§ion=tls", "isActive": section == "tls"},
|
||||||
|
}
|
||||||
|
|
||||||
|
settings["isDefaultCluster"] = (policies.LoadDefaultClusterID() == cluster.GetInt64("id"))
|
||||||
|
|
||||||
this.Data["cluster"] = cluster
|
this.Data["cluster"] = cluster
|
||||||
this.Data["settings"] = map[string]interface{}{
|
// 构造前端需要的 tlsConfig 格式
|
||||||
"region": cluster.GetString("region"),
|
var listenAddresses []*serverconfigs.NetworkAddressConfig
|
||||||
"gatewayDomain": cluster.GetString("gatewayDomain"),
|
listenAddrsRaw := settings.GetString("listenAddrsJSON")
|
||||||
"cacheTtl": cluster.GetInt("cacheTtl"),
|
if len(listenAddrsRaw) > 0 {
|
||||||
"fallbackTimeout": cluster.GetInt("fallbackTimeout"),
|
_ = json.Unmarshal([]byte(listenAddrsRaw), &listenAddresses)
|
||||||
"installDir": installDir,
|
} else {
|
||||||
"isOn": cluster.GetBool("isOn"),
|
// 默认 443 端口
|
||||||
|
listenAddresses = []*serverconfigs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: serverconfigs.ProtocolHTTPS,
|
||||||
|
Host: "",
|
||||||
|
PortRange: "443",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造前端需要的 SSLPolicy
|
||||||
|
var sslPolicy *sslconfigs.SSLPolicy
|
||||||
|
originCertPem := settings.GetString("originCertPem")
|
||||||
|
originKeyPem := settings.GetString("originKeyPem")
|
||||||
|
if len(originCertPem) > 0 && len(originKeyPem) > 0 {
|
||||||
|
sslPolicy = &sslconfigs.SSLPolicy{
|
||||||
|
IsOn: true,
|
||||||
|
MinVersion: settings.GetString("tlsMinVersion"),
|
||||||
|
CipherSuitesIsOn: settings.GetBool("tlsCipherSuitesOn"),
|
||||||
|
OCSPIsOn: settings.GetBool("tlsOcspOn"),
|
||||||
|
ClientAuthType: int(settings.GetInt32("tlsClientAuthType")),
|
||||||
|
Certs: []*sslconfigs.SSLCertConfig{
|
||||||
|
{
|
||||||
|
IsOn: true,
|
||||||
|
CertData: []byte(originCertPem),
|
||||||
|
KeyData: []byte(originKeyPem),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sslPolicy = &sslconfigs.SSLPolicy{
|
||||||
|
IsOn: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["tlsConfig"] = maps.Map{
|
||||||
|
"isOn": true,
|
||||||
|
"listen": listenAddresses,
|
||||||
|
"sslPolicy": sslPolicy,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["cluster"] = cluster
|
||||||
|
this.Data["settings"] = settings
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClusterSettingsAction) RunPost(params struct {
|
func (this *ClusterSettingsAction) RunPost(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
Name string
|
Name string
|
||||||
Region string
|
|
||||||
GatewayDomain string
|
GatewayDomain string
|
||||||
CacheTtl int32
|
CacheTtl int32
|
||||||
FallbackTimeout int32
|
FallbackTimeout int32
|
||||||
InstallDir string
|
InstallDir string
|
||||||
IsOn bool
|
IsOn bool
|
||||||
|
IsDefaultCluster bool
|
||||||
|
|
||||||
|
Addresses []byte
|
||||||
|
SslPolicyJSON []byte
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
CSRF *actionutils.CSRF
|
||||||
}) {
|
}) {
|
||||||
// Mock successful save
|
params.Must.Field("clusterId", params.ClusterId).Gt(0, "please select cluster")
|
||||||
|
params.Must.Field("name", params.Name).Require("please input cluster name")
|
||||||
|
params.Must.Field("gatewayDomain", params.GatewayDomain).Require("please input service domain")
|
||||||
|
params.Must.Field("cacheTtl", params.CacheTtl).Gt(0, "cache ttl should be greater than 0")
|
||||||
|
params.Must.Field("fallbackTimeout", params.FallbackTimeout).Gt(0, "fallback timeout should be greater than 0")
|
||||||
|
params.Must.Field("installDir", params.InstallDir).Require("please input install dir")
|
||||||
|
|
||||||
|
|
||||||
|
if params.IsDefaultCluster && !params.IsOn {
|
||||||
|
this.Fail("默认集群必须保持启用状态")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster := pickCluster(params.ClusterId)
|
||||||
|
settings := loadClusterSettings(cluster)
|
||||||
|
settings["name"] = strings.TrimSpace(params.Name)
|
||||||
|
settings["gatewayDomain"] = strings.TrimSpace(params.GatewayDomain)
|
||||||
|
settings["cacheTtl"] = int(params.CacheTtl)
|
||||||
|
settings["fallbackTimeout"] = int(params.FallbackTimeout)
|
||||||
|
settings["installDir"] = strings.TrimSpace(params.InstallDir)
|
||||||
|
settings["isOn"] = params.IsOn
|
||||||
|
// 处理地址
|
||||||
|
var addresses = []*serverconfigs.NetworkAddressConfig{}
|
||||||
|
if len(params.Addresses) > 0 {
|
||||||
|
err := json.Unmarshal(params.Addresses, &addresses)
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("端口地址解析失败:" + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
addressesJSON, _ := json.Marshal(addresses)
|
||||||
|
settings["listenAddrsJSON"] = string(addressesJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 SSL 配置
|
||||||
|
var originCertPem = ""
|
||||||
|
var originKeyPem = ""
|
||||||
|
var tlsMinVersion = "TLS 1.1"
|
||||||
|
var tlsCipherSuitesOn = false
|
||||||
|
var tlsOcspOn = false
|
||||||
|
var tlsClientAuthType = sslconfigs.SSLClientAuthType(0)
|
||||||
|
|
||||||
|
if len(params.SslPolicyJSON) > 0 {
|
||||||
|
sslPolicy := &sslconfigs.SSLPolicy{}
|
||||||
|
err := json.Unmarshal(params.SslPolicyJSON, sslPolicy)
|
||||||
|
if err == nil {
|
||||||
|
tlsMinVersion = sslPolicy.MinVersion
|
||||||
|
tlsCipherSuitesOn = sslPolicy.CipherSuitesIsOn
|
||||||
|
tlsOcspOn = sslPolicy.OCSPIsOn
|
||||||
|
tlsClientAuthType = sslconfigs.SSLClientAuthType(sslPolicy.ClientAuthType)
|
||||||
|
|
||||||
|
if len(sslPolicy.Certs) > 0 {
|
||||||
|
cert := sslPolicy.Certs[0]
|
||||||
|
originCertPem = string(cert.CertData)
|
||||||
|
originKeyPem = string(cert.KeyData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(originCertPem) == 0 || len(originKeyPem) == 0 {
|
||||||
|
this.Fail("请上传或选择证书")
|
||||||
|
}
|
||||||
|
|
||||||
|
settings["originHttps"] = true
|
||||||
|
settings["originCertPem"] = originCertPem
|
||||||
|
settings["originKeyPem"] = originKeyPem
|
||||||
|
if len(tlsMinVersion) == 0 {
|
||||||
|
tlsMinVersion = "TLS 1.1"
|
||||||
|
}
|
||||||
|
settings["tlsMinVersion"] = tlsMinVersion
|
||||||
|
settings["tlsCipherSuitesOn"] = tlsCipherSuitesOn
|
||||||
|
settings["tlsOcspOn"] = tlsOcspOn
|
||||||
|
settings["tlsClientAuthType"] = int(tlsClientAuthType)
|
||||||
|
settings["lastModifiedAt"] = nowDateTime()
|
||||||
|
settings["certUpdatedAt"] = nowDateTime()
|
||||||
|
|
||||||
|
saveClusterSettings(params.ClusterId, settings)
|
||||||
|
|
||||||
|
currentDefaultClusterId := policies.LoadDefaultClusterID()
|
||||||
|
if params.IsDefaultCluster {
|
||||||
|
policies.SaveDefaultClusterID(params.ClusterId)
|
||||||
|
} else if currentDefaultClusterId == params.ClusterId {
|
||||||
|
policies.SaveDefaultClusterID(0)
|
||||||
|
}
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package clusters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
var clusterSettingsStore = struct {
|
||||||
|
sync.RWMutex
|
||||||
|
data map[int64]maps.Map
|
||||||
|
}{
|
||||||
|
data: map[int64]maps.Map{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultClusterSettings(cluster maps.Map) maps.Map {
|
||||||
|
installDir := strings.TrimSpace(cluster.GetString("installDir"))
|
||||||
|
if len(installDir) == 0 {
|
||||||
|
installDir = "/opt/edge-httpdns"
|
||||||
|
}
|
||||||
|
|
||||||
|
return maps.Map{
|
||||||
|
"name": cluster.GetString("name"),
|
||||||
|
"gatewayDomain": strings.TrimSpace(cluster.GetString("gatewayDomain")),
|
||||||
|
"cacheTtl": cluster.GetInt("cacheTtl"),
|
||||||
|
"fallbackTimeout": cluster.GetInt("fallbackTimeout"),
|
||||||
|
"installDir": installDir,
|
||||||
|
"isOn": cluster.GetBool("isOn"),
|
||||||
|
"originHttps": true,
|
||||||
|
"originCertPem": "",
|
||||||
|
"originKeyPem": "",
|
||||||
|
"tlsMinVersion": "TLS 1.1",
|
||||||
|
"tlsCipherSuitesOn": false,
|
||||||
|
"tlsOcspOn": false,
|
||||||
|
"tlsClientAuthType": 0,
|
||||||
|
"certUpdatedAt": "",
|
||||||
|
"lastModifiedAt": "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneClusterSettings(settings maps.Map) maps.Map {
|
||||||
|
return maps.Map{
|
||||||
|
"name": settings.GetString("name"),
|
||||||
|
"gatewayDomain": settings.GetString("gatewayDomain"),
|
||||||
|
"cacheTtl": settings.GetInt("cacheTtl"),
|
||||||
|
"fallbackTimeout": settings.GetInt("fallbackTimeout"),
|
||||||
|
"installDir": settings.GetString("installDir"),
|
||||||
|
"isOn": settings.GetBool("isOn"),
|
||||||
|
"originHttps": true,
|
||||||
|
"originCertPem": settings.GetString("originCertPem"),
|
||||||
|
"originKeyPem": settings.GetString("originKeyPem"),
|
||||||
|
"tlsMinVersion": settings.GetString("tlsMinVersion"),
|
||||||
|
"tlsCipherSuitesOn": settings.GetBool("tlsCipherSuitesOn"),
|
||||||
|
"tlsOcspOn": settings.GetBool("tlsOcspOn"),
|
||||||
|
"tlsClientAuthType": settings.GetInt("tlsClientAuthType"),
|
||||||
|
"certUpdatedAt": settings.GetString("certUpdatedAt"),
|
||||||
|
"lastModifiedAt": settings.GetString("lastModifiedAt"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadClusterSettings(cluster maps.Map) maps.Map {
|
||||||
|
clusterId := cluster.GetInt64("id")
|
||||||
|
|
||||||
|
clusterSettingsStore.RLock()
|
||||||
|
settings, ok := clusterSettingsStore.data[clusterId]
|
||||||
|
clusterSettingsStore.RUnlock()
|
||||||
|
if ok {
|
||||||
|
if len(settings.GetString("tlsMinVersion")) == 0 {
|
||||||
|
settings["tlsMinVersion"] = "TLS 1.1"
|
||||||
|
}
|
||||||
|
return cloneClusterSettings(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = defaultClusterSettings(cluster)
|
||||||
|
saveClusterSettings(clusterId, settings)
|
||||||
|
return cloneClusterSettings(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveClusterSettings(clusterId int64, settings maps.Map) {
|
||||||
|
clusterSettingsStore.Lock()
|
||||||
|
clusterSettingsStore.data[clusterId] = cloneClusterSettings(settings)
|
||||||
|
clusterSettingsStore.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyClusterSettingsOverrides(cluster maps.Map) {
|
||||||
|
clusterId := cluster.GetInt64("id")
|
||||||
|
if clusterId <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterSettingsStore.RLock()
|
||||||
|
settings, ok := clusterSettingsStore.data[clusterId]
|
||||||
|
clusterSettingsStore.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster["name"] = settings.GetString("name")
|
||||||
|
cluster["gatewayDomain"] = settings.GetString("gatewayDomain")
|
||||||
|
cluster["cacheTtl"] = settings.GetInt("cacheTtl")
|
||||||
|
cluster["fallbackTimeout"] = settings.GetInt("fallbackTimeout")
|
||||||
|
cluster["installDir"] = settings.GetString("installDir")
|
||||||
|
cluster["isOn"] = settings.GetBool("isOn")
|
||||||
|
}
|
||||||
|
|
||||||
|
func nowDateTime() string {
|
||||||
|
return time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package clusters
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateNodeAction struct {
|
type CreateNodeAction struct {
|
||||||
@@ -11,14 +10,18 @@ type CreateNodeAction struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreateNodeAction) Init() {
|
func (this *CreateNodeAction) Init() {
|
||||||
this.Nav("", "node", "createNode")
|
this.Nav("httpdns", "cluster", "createNode")
|
||||||
this.SecondMenu("nodes")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *CreateNodeAction) RunGet(params struct{ ClusterId int64 }) {
|
func (this *CreateNodeAction) RunGet(params struct{ ClusterId int64 }) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
|
cluster := pickCluster(params.ClusterId)
|
||||||
|
|
||||||
|
// 构建顶部 tabbar
|
||||||
|
httpdnsutils.AddClusterTabbar(this.Parent(), cluster.GetString("name"), params.ClusterId, "node")
|
||||||
|
|
||||||
this.Data["clusterId"] = params.ClusterId
|
this.Data["clusterId"] = params.ClusterId
|
||||||
this.Data["cluster"] = maps.Map{"id": params.ClusterId, "name": "Mock Cluster"}
|
this.Data["cluster"] = cluster
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ func (this *DeleteAction) RunGet(params struct {
|
|||||||
}) {
|
}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
cluster := pickCluster(params.ClusterId)
|
cluster := pickCluster(params.ClusterId)
|
||||||
|
|
||||||
|
// 构建顶部 tabbar
|
||||||
|
httpdnsutils.AddClusterTabbar(this.Parent(), cluster.GetString("name"), params.ClusterId, "delete")
|
||||||
|
|
||||||
this.Data["cluster"] = cluster
|
this.Data["cluster"] = cluster
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package clusters
|
|||||||
import "github.com/iwind/TeaGo/maps"
|
import "github.com/iwind/TeaGo/maps"
|
||||||
|
|
||||||
func mockClusters() []maps.Map {
|
func mockClusters() []maps.Map {
|
||||||
return []maps.Map{
|
clusters := []maps.Map{
|
||||||
{
|
{
|
||||||
"id": int64(1),
|
"id": int64(1),
|
||||||
"name": "gateway-cn-hz",
|
"name": "gateway-cn-hz",
|
||||||
@@ -31,6 +31,12 @@ func mockClusters() []maps.Map {
|
|||||||
"isOn": true,
|
"isOn": true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, cluster := range clusters {
|
||||||
|
applyClusterSettingsOverrides(cluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clusters
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickCluster(clusterId int64) maps.Map {
|
func pickCluster(clusterId int64) maps.Map {
|
||||||
|
|||||||
@@ -1,66 +1,15 @@
|
|||||||
package guide
|
package guide
|
||||||
|
|
||||||
import (
|
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
httpdnsApps "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/apps"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
|
||||||
httpdnsPolicies "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/policies"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
func (this *IndexAction) Init() {
|
||||||
this.Nav("httpdns", "guide", "")
|
this.Nav("httpdns", "app", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
func (this *IndexAction) RunGet(params struct{}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
this.RedirectURL("/httpdns/apps")
|
||||||
|
|
||||||
apps := []maps.Map{
|
|
||||||
{
|
|
||||||
"id": int64(1),
|
|
||||||
"name": "主站移动业务",
|
|
||||||
"appId": "ab12xc34s2",
|
|
||||||
"clusterId": int64(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": int64(2),
|
|
||||||
"name": "视频网关业务",
|
|
||||||
"appId": "vd8992ksm1",
|
|
||||||
"clusterId": int64(2),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": int64(3),
|
|
||||||
"name": "海外灰度测试",
|
|
||||||
"appId": "ov7711hkq9",
|
|
||||||
"clusterId": int64(1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, app := range apps {
|
|
||||||
clusterID := app.GetInt64("clusterId")
|
|
||||||
app["gatewayDomain"] = httpdnsPolicies.LoadClusterGatewayByID(clusterID)
|
|
||||||
|
|
||||||
settings := httpdnsApps.LoadAppSettingsByAppID(app.GetString("appId"))
|
|
||||||
if settings == nil {
|
|
||||||
app["signSecret"] = ""
|
|
||||||
app["signSecretMasked"] = ""
|
|
||||||
app["aesSecret"] = ""
|
|
||||||
app["aesSecretMasked"] = ""
|
|
||||||
app["signEnabled"] = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
app["signSecret"] = settings.GetString("signSecretPlain")
|
|
||||||
app["signSecretMasked"] = settings.GetString("signSecretMasked")
|
|
||||||
app["aesSecret"] = settings.GetString("aesSecretPlain")
|
|
||||||
app["aesSecretMasked"] = settings.GetString("aesSecretMasked")
|
|
||||||
app["signEnabled"] = settings.GetBool("signEnabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Data["apps"] = apps
|
|
||||||
this.Show()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package httpdnsutils
|
package httpdnsutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
@@ -11,39 +13,55 @@ func AddLeftMenu(action *actionutils.ParentAction) {
|
|||||||
action.Data["teaSubMenu"] = tab
|
action.Data["teaSubMenu"] = tab
|
||||||
action.Data["leftMenuItems"] = []maps.Map{
|
action.Data["leftMenuItems"] = []maps.Map{
|
||||||
{
|
{
|
||||||
"name": "集群管理",
|
"name": "\u96c6\u7fa4\u7ba1\u7406",
|
||||||
"url": "/httpdns/clusters",
|
"url": "/httpdns/clusters",
|
||||||
"isActive": tab == "cluster",
|
"isActive": tab == "cluster",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "全局配置",
|
"name": "\u5e94\u7528\u7ba1\u7406",
|
||||||
"url": "/httpdns/policies",
|
|
||||||
"isActive": tab == "policy",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "应用管理",
|
|
||||||
"url": "/httpdns/apps",
|
"url": "/httpdns/apps",
|
||||||
"isActive": tab == "app",
|
"isActive": tab == "app",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SDK接入引导",
|
"name": "\u8bbf\u95ee\u65e5\u5fd7",
|
||||||
"url": "/httpdns/guide",
|
|
||||||
"isActive": tab == "guide",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "解析日志",
|
|
||||||
"url": "/httpdns/resolveLogs",
|
"url": "/httpdns/resolveLogs",
|
||||||
"isActive": tab == "resolveLogs",
|
"isActive": tab == "resolveLogs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "运行日志",
|
"name": "\u8fd0\u884c\u65e5\u5fd7",
|
||||||
"url": "/httpdns/runtimeLogs",
|
"url": "/httpdns/runtimeLogs",
|
||||||
"isActive": tab == "runtimeLogs",
|
"isActive": tab == "runtimeLogs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "解析测试",
|
"name": "\u89e3\u6790\u6d4b\u8bd5",
|
||||||
"url": "/httpdns/sandbox",
|
"url": "/httpdns/sandbox",
|
||||||
"isActive": tab == "sandbox",
|
"isActive": tab == "sandbox",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddClusterTabbar builds the top tabbar on cluster pages.
|
||||||
|
func AddClusterTabbar(action *actionutils.ParentAction, clusterName string, clusterId int64, selectedTab string) {
|
||||||
|
cid := strconv.FormatInt(clusterId, 10)
|
||||||
|
tabbar := actionutils.NewTabbar()
|
||||||
|
tabbar.Add("", "", "/httpdns/clusters", "arrow left", false)
|
||||||
|
titleItem := tabbar.Add(clusterName, "", "/httpdns/clusters/cluster?clusterId="+cid, "angle right", true)
|
||||||
|
titleItem.IsTitle = true
|
||||||
|
tabbar.Add("\u8282\u70b9\u5217\u8868", "", "/httpdns/clusters/cluster?clusterId="+cid, "server", selectedTab == "node")
|
||||||
|
tabbar.Add("\u96c6\u7fa4\u8bbe\u7f6e", "", "/httpdns/clusters/cluster/settings?clusterId="+cid, "setting", selectedTab == "setting")
|
||||||
|
tabbar.Add("\u5220\u9664\u96c6\u7fa4", "", "/httpdns/clusters/delete?clusterId="+cid, "trash", selectedTab == "delete")
|
||||||
|
actionutils.SetTabbar(action, tabbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAppTabbar builds the top tabbar on app pages.
|
||||||
|
func AddAppTabbar(action *actionutils.ParentAction, appName string, appId int64, selectedTab string) {
|
||||||
|
aid := strconv.FormatInt(appId, 10)
|
||||||
|
tabbar := actionutils.NewTabbar()
|
||||||
|
tabbar.Add("", "", "/httpdns/apps", "arrow left", false)
|
||||||
|
titleItem := tabbar.Add(appName, "", "/httpdns/apps/domains?appId="+aid, "angle right", true)
|
||||||
|
titleItem.IsTitle = true
|
||||||
|
tabbar.Add("\u57df\u540d\u5217\u8868", "", "/httpdns/apps/domains?appId="+aid, "list", selectedTab == "domains")
|
||||||
|
tabbar.Add("\u5e94\u7528\u8bbe\u7f6e", "", "/httpdns/apps/app/settings?appId="+aid, "setting", selectedTab == "settings")
|
||||||
|
tabbar.Add("\u5220\u9664\u5e94\u7528", "", "/httpdns/apps/delete?appId="+aid, "trash", selectedTab == "delete")
|
||||||
|
actionutils.SetTabbar(action, tabbar)
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func init() {
|
|||||||
Data("teaSubMenu", "cluster").
|
Data("teaSubMenu", "cluster").
|
||||||
Prefix("/httpdns").
|
Prefix("/httpdns").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
|
GetPost("/addPortPopup", new(AddPortPopupAction)).
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ func (this *IndexAction) RunGet(params struct{}) {
|
|||||||
|
|
||||||
func (this *IndexAction) RunPost(params struct {
|
func (this *IndexAction) RunPost(params struct {
|
||||||
DefaultClusterId int64
|
DefaultClusterId int64
|
||||||
EnableUserDomainVerify bool
|
|
||||||
DefaultTTL int
|
DefaultTTL int
|
||||||
DefaultFallbackMs int
|
DefaultFallbackMs int
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ func (this *IndexAction) RunPost(params struct {
|
|||||||
|
|
||||||
saveGlobalPolicies(maps.Map{
|
saveGlobalPolicies(maps.Map{
|
||||||
"defaultClusterId": params.DefaultClusterId,
|
"defaultClusterId": params.DefaultClusterId,
|
||||||
"enableUserDomainVerify": params.EnableUserDomainVerify,
|
"enableUserDomainVerify": true,
|
||||||
"defaultTTL": params.DefaultTTL,
|
"defaultTTL": params.DefaultTTL,
|
||||||
"defaultFallbackMs": params.DefaultFallbackMs,
|
"defaultFallbackMs": params.DefaultFallbackMs,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func loadGlobalPolicies() maps.Map {
|
|||||||
|
|
||||||
return maps.Map{
|
return maps.Map{
|
||||||
"defaultClusterId": globalPoliciesStore.data.GetInt64("defaultClusterId"),
|
"defaultClusterId": globalPoliciesStore.data.GetInt64("defaultClusterId"),
|
||||||
"enableUserDomainVerify": globalPoliciesStore.data.GetBool("enableUserDomainVerify"),
|
"enableUserDomainVerify": true,
|
||||||
"defaultTTL": globalPoliciesStore.data.GetInt("defaultTTL"),
|
"defaultTTL": globalPoliciesStore.data.GetInt("defaultTTL"),
|
||||||
"defaultFallbackMs": globalPoliciesStore.data.GetInt("defaultFallbackMs"),
|
"defaultFallbackMs": globalPoliciesStore.data.GetInt("defaultFallbackMs"),
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ func saveGlobalPolicies(policies maps.Map) {
|
|||||||
globalPoliciesStore.Lock()
|
globalPoliciesStore.Lock()
|
||||||
globalPoliciesStore.data = maps.Map{
|
globalPoliciesStore.data = maps.Map{
|
||||||
"defaultClusterId": policies.GetInt64("defaultClusterId"),
|
"defaultClusterId": policies.GetInt64("defaultClusterId"),
|
||||||
"enableUserDomainVerify": policies.GetBool("enableUserDomainVerify"),
|
"enableUserDomainVerify": true,
|
||||||
"defaultTTL": policies.GetInt("defaultTTL"),
|
"defaultTTL": policies.GetInt("defaultTTL"),
|
||||||
"defaultFallbackMs": policies.GetInt("defaultFallbackMs"),
|
"defaultFallbackMs": policies.GetInt("defaultFallbackMs"),
|
||||||
}
|
}
|
||||||
@@ -110,6 +110,14 @@ func LoadDefaultClusterID() int64 {
|
|||||||
return globalPoliciesStore.data.GetInt64("defaultClusterId")
|
return globalPoliciesStore.data.GetInt64("defaultClusterId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveDefaultClusterID updates default deploy cluster id.
|
||||||
|
// Pass 0 to clear and let caller fallback to first available cluster.
|
||||||
|
func SaveDefaultClusterID(clusterID int64) {
|
||||||
|
globalPoliciesStore.Lock()
|
||||||
|
globalPoliciesStore.data["defaultClusterId"] = clusterID
|
||||||
|
globalPoliciesStore.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// LoadClusterGatewayByID returns gateway domain for the selected cluster.
|
// LoadClusterGatewayByID returns gateway domain for the selected cluster.
|
||||||
func LoadClusterGatewayByID(clusterID int64) string {
|
func LoadClusterGatewayByID(clusterID int64) string {
|
||||||
clusters := loadAvailableDeployClusters()
|
clusters := loadAvailableDeployClusters()
|
||||||
|
|||||||
@@ -25,8 +25,16 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}) {
|
}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
|
|
||||||
|
if params.ClusterId > 0 {
|
||||||
this.Data["clusterId"] = params.ClusterId
|
this.Data["clusterId"] = params.ClusterId
|
||||||
|
} else {
|
||||||
|
this.Data["clusterId"] = ""
|
||||||
|
}
|
||||||
|
if params.NodeId > 0 {
|
||||||
this.Data["nodeId"] = params.NodeId
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
} else {
|
||||||
|
this.Data["nodeId"] = ""
|
||||||
|
}
|
||||||
this.Data["appId"] = params.AppId
|
this.Data["appId"] = params.AppId
|
||||||
this.Data["domain"] = params.Domain
|
this.Data["domain"] = params.Domain
|
||||||
this.Data["status"] = params.Status
|
this.Data["status"] = params.Status
|
||||||
|
|||||||
@@ -25,8 +25,16 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}) {
|
}) {
|
||||||
httpdnsutils.AddLeftMenu(this.Parent())
|
httpdnsutils.AddLeftMenu(this.Parent())
|
||||||
|
|
||||||
|
if params.ClusterId > 0 {
|
||||||
this.Data["clusterId"] = params.ClusterId
|
this.Data["clusterId"] = params.ClusterId
|
||||||
|
} else {
|
||||||
|
this.Data["clusterId"] = ""
|
||||||
|
}
|
||||||
|
if params.NodeId > 0 {
|
||||||
this.Data["nodeId"] = params.NodeId
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
} else {
|
||||||
|
this.Data["nodeId"] = ""
|
||||||
|
}
|
||||||
this.Data["dayFrom"] = params.DayFrom
|
this.Data["dayFrom"] = params.DayFrom
|
||||||
this.Data["dayTo"] = params.DayTo
|
this.Data["dayTo"] = params.DayTo
|
||||||
this.Data["level"] = params.Level
|
this.Data["level"] = params.Level
|
||||||
@@ -123,7 +131,6 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
if len(keyword) > 0 {
|
if len(keyword) > 0 {
|
||||||
if !strings.Contains(strings.ToLower(log["tag"].(string)), keyword) &&
|
if !strings.Contains(strings.ToLower(log["tag"].(string)), keyword) &&
|
||||||
!strings.Contains(strings.ToLower(log["module"].(string)), keyword) &&
|
|
||||||
!strings.Contains(strings.ToLower(log["description"].(string)), keyword) &&
|
!strings.Contains(strings.ToLower(log["description"].(string)), keyword) &&
|
||||||
!strings.Contains(strings.ToLower(log["nodeName"].(string)), keyword) {
|
!strings.Contains(strings.ToLower(log["nodeName"].(string)), keyword) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package sandbox
|
package sandbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -21,18 +18,10 @@ func (this *TestAction) RunPost(params struct {
|
|||||||
Domain string
|
Domain string
|
||||||
ClientIp string
|
ClientIp string
|
||||||
Qtype string
|
Qtype string
|
||||||
SDNSParamsJSON string
|
|
||||||
}) {
|
}) {
|
||||||
if len(params.ClientIp) == 0 {
|
if len(params.ClientIp) == 0 {
|
||||||
params.ClientIp = "203.0.113.100"
|
params.ClientIp = "203.0.113.100"
|
||||||
}
|
}
|
||||||
params.SDNSParamsJSON = strings.TrimSpace(params.SDNSParamsJSON)
|
|
||||||
|
|
||||||
sdnsParams, err := parseSDNSParamsJSON(params.SDNSParamsJSON)
|
|
||||||
if err != nil {
|
|
||||||
this.Fail(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
clientSubnet := this.maskSubnet(params.ClientIp, 24, 56)
|
clientSubnet := this.maskSubnet(params.ClientIp, 24, 56)
|
||||||
if len(clientSubnet) == 0 {
|
if len(clientSubnet) == 0 {
|
||||||
@@ -51,9 +40,6 @@ func (this *TestAction) RunPost(params struct {
|
|||||||
query.Set("dn", params.Domain)
|
query.Set("dn", params.Domain)
|
||||||
query.Set("cip", params.ClientIp)
|
query.Set("cip", params.ClientIp)
|
||||||
query.Set("qtype", params.Qtype)
|
query.Set("qtype", params.Qtype)
|
||||||
for _, item := range sdnsParams {
|
|
||||||
query.Add("sdns_"+item.GetString("name"), item.GetString("value"))
|
|
||||||
}
|
|
||||||
requestURL := "https://api.httpdns.example.com/resolve?" + query.Encode()
|
requestURL := "https://api.httpdns.example.com/resolve?" + query.Encode()
|
||||||
|
|
||||||
this.Data["result"] = maps.Map{
|
this.Data["result"] = maps.Map{
|
||||||
@@ -65,7 +51,6 @@ func (this *TestAction) RunPost(params struct {
|
|||||||
"client_ip": params.ClientIp,
|
"client_ip": params.ClientIp,
|
||||||
"client_region": "中国, 上海, 上海",
|
"client_region": "中国, 上海, 上海",
|
||||||
"line_name": "默认线路",
|
"line_name": "默认线路",
|
||||||
"sdns_params": sdnsParams,
|
|
||||||
"ips": []string{"203.0.113.10", "203.0.113.11"},
|
"ips": []string{"203.0.113.10", "203.0.113.11"},
|
||||||
"records": []maps.Map{
|
"records": []maps.Map{
|
||||||
{
|
{
|
||||||
@@ -108,44 +93,6 @@ func (this *TestAction) RunPost(params struct {
|
|||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSDNSParamsJSON(raw string) ([]maps.Map, error) {
|
|
||||||
if len(raw) == 0 {
|
|
||||||
return []maps.Map{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
list := []maps.Map{}
|
|
||||||
if err := json.Unmarshal([]byte(raw), &list); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(list) > 10 {
|
|
||||||
return nil, fmt.Errorf("sdns params should be <= 10")
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]maps.Map, 0, len(list))
|
|
||||||
for _, item := range list {
|
|
||||||
name := strings.TrimSpace(item.GetString("name"))
|
|
||||||
value := strings.TrimSpace(item.GetString("value"))
|
|
||||||
if len(name) == 0 && len(value) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(name) == 0 || len(name) > 64 {
|
|
||||||
return nil, fmt.Errorf("sdns param name should be in 1-64")
|
|
||||||
}
|
|
||||||
if len(value) == 0 || len(value) > 64 {
|
|
||||||
return nil, fmt.Errorf("sdns param value should be in 1-64")
|
|
||||||
}
|
|
||||||
result = append(result, maps.Map{
|
|
||||||
"name": name,
|
|
||||||
"value": value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(result) > 10 {
|
|
||||||
return nil, fmt.Errorf("sdns params should be <= 10")
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *TestAction) maskSubnet(ip string, ipv4Prefix int, ipv6Prefix int) string {
|
func (this *TestAction) maskSubnet(ip string, ipv4Prefix int, ipv6Prefix int) string {
|
||||||
parsed := net.ParseIP(ip)
|
parsed := net.ParseIP(ip)
|
||||||
if parsed == nil {
|
if parsed == nil {
|
||||||
|
|||||||
@@ -113,50 +113,6 @@ func FindAllMenuMaps(langCode string, nodeLogsType string, countUnreadNodeLogs i
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"code": "httpdns",
|
|
||||||
"module": configloaders.AdminModuleCodeHttpDNS,
|
|
||||||
"name": "HTTPDNS",
|
|
||||||
"subtitle": "",
|
|
||||||
"icon": "shield alternate",
|
|
||||||
"subItems": []maps.Map{
|
|
||||||
{
|
|
||||||
"name": "集群管理",
|
|
||||||
"url": "/httpdns/clusters",
|
|
||||||
"code": "cluster",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "全局配置",
|
|
||||||
"url": "/httpdns/policies",
|
|
||||||
"code": "policy",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "应用管理",
|
|
||||||
"url": "/httpdns/apps",
|
|
||||||
"code": "app",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "SDK接入引导",
|
|
||||||
"url": "/httpdns/guide",
|
|
||||||
"code": "guide",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "解析日志",
|
|
||||||
"url": "/httpdns/resolveLogs",
|
|
||||||
"code": "resolveLogs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "运行日志",
|
|
||||||
"url": "/httpdns/runtimeLogs",
|
|
||||||
"code": "runtimeLogs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "解析测试",
|
|
||||||
"url": "/httpdns/sandbox",
|
|
||||||
"code": "sandbox",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"code": "dns",
|
"code": "dns",
|
||||||
"module": configloaders.AdminModuleCodeDNS,
|
"module": configloaders.AdminModuleCodeDNS,
|
||||||
@@ -181,6 +137,40 @@ func FindAllMenuMaps(langCode string, nodeLogsType string, countUnreadNodeLogs i
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"code": "httpdns",
|
||||||
|
"module": configloaders.AdminModuleCodeHttpDNS,
|
||||||
|
"name": "HTTPDNS",
|
||||||
|
"subtitle": "",
|
||||||
|
"icon": "shield alternate",
|
||||||
|
"subItems": []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "集群管理",
|
||||||
|
"url": "/httpdns/clusters",
|
||||||
|
"code": "cluster",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "应用管理",
|
||||||
|
"url": "/httpdns/apps",
|
||||||
|
"code": "app",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "访问日志",
|
||||||
|
"url": "/httpdns/resolveLogs",
|
||||||
|
"code": "resolveLogs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "运行日志",
|
||||||
|
"url": "/httpdns/runtimeLogs",
|
||||||
|
"code": "runtimeLogs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "解析测试",
|
||||||
|
"url": "/httpdns/sandbox",
|
||||||
|
"code": "sandbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"code": "users",
|
"code": "users",
|
||||||
"module": configloaders.AdminModuleCodeUser,
|
"module": configloaders.AdminModuleCodeUser,
|
||||||
|
|||||||
@@ -178,50 +178,6 @@ func FindAllMenuMaps(langCode string, nodeLogsType string, countUnreadNodeLogs i
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"code": "httpdns",
|
|
||||||
"module": configloaders.AdminModuleCodeHttpDNS,
|
|
||||||
"name": "HTTPDNS",
|
|
||||||
"subtitle": "",
|
|
||||||
"icon": "shield alternate",
|
|
||||||
"subItems": []maps.Map{
|
|
||||||
{
|
|
||||||
"name": "集群管理",
|
|
||||||
"url": "/httpdns/clusters",
|
|
||||||
"code": "cluster",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "全局配置",
|
|
||||||
"url": "/httpdns/policies",
|
|
||||||
"code": "policy",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "应用管理",
|
|
||||||
"url": "/httpdns/apps",
|
|
||||||
"code": "app",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "SDK接入引导",
|
|
||||||
"url": "/httpdns/guide",
|
|
||||||
"code": "guide",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "解析日志",
|
|
||||||
"url": "/httpdns/resolveLogs",
|
|
||||||
"code": "resolveLogs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "运行日志",
|
|
||||||
"url": "/httpdns/runtimeLogs",
|
|
||||||
"code": "runtimeLogs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "解析测试",
|
|
||||||
"url": "/httpdns/sandbox",
|
|
||||||
"code": "sandbox",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"code": "dns",
|
"code": "dns",
|
||||||
"module": configloaders.AdminModuleCodeDNS,
|
"module": configloaders.AdminModuleCodeDNS,
|
||||||
@@ -319,6 +275,40 @@ func FindAllMenuMaps(langCode string, nodeLogsType string, countUnreadNodeLogs i
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"code": "httpdns",
|
||||||
|
"module": configloaders.AdminModuleCodeHttpDNS,
|
||||||
|
"name": "HTTPDNS",
|
||||||
|
"subtitle": "",
|
||||||
|
"icon": "shield alternate",
|
||||||
|
"subItems": []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "集群管理",
|
||||||
|
"url": "/httpdns/clusters",
|
||||||
|
"code": "cluster",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "应用管理",
|
||||||
|
"url": "/httpdns/apps",
|
||||||
|
"code": "app",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "访问日志",
|
||||||
|
"url": "/httpdns/resolveLogs",
|
||||||
|
"code": "resolveLogs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "运行日志",
|
||||||
|
"url": "/httpdns/runtimeLogs",
|
||||||
|
"code": "runtimeLogs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "解析测试",
|
||||||
|
"url": "/httpdns/sandbox",
|
||||||
|
"code": "sandbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"code": "users",
|
"code": "users",
|
||||||
"module": configloaders.AdminModuleCodeUser,
|
"module": configloaders.AdminModuleCodeUser,
|
||||||
@@ -477,3 +467,4 @@ func FindAllMenuMaps(langCode string, nodeLogsType string, countUnreadNodeLogs i
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
{$template "menu"}
|
||||||
|
{$template "/left_menu_with_menu"}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.httpdns-settings-grid {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
|
||||||
.httpdns-settings-grid .left-nav-column {
|
|
||||||
max-width: 220px;
|
|
||||||
}
|
|
||||||
.httpdns-settings-grid .right-form-column {
|
|
||||||
padding-left: .3em !important;
|
|
||||||
}
|
|
||||||
.httpdns-side-menu .item {
|
|
||||||
padding-top: .8em !important;
|
|
||||||
padding-bottom: .8em !important;
|
|
||||||
}
|
|
||||||
.httpdns-mini-action {
|
.httpdns-mini-action {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -22,39 +10,61 @@
|
|||||||
margin-left: .55em;
|
margin-left: .55em;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.httpdns-mini-action:hover {
|
.httpdns-mini-action:hover {
|
||||||
color: #1e70bf;
|
color: #1e70bf;
|
||||||
}
|
}
|
||||||
|
|
||||||
.httpdns-mini-action .icon {
|
.httpdns-mini-action .icon {
|
||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
font-size: .92em !important;
|
font-size: .92em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.httpdns-note.comment {
|
.httpdns-note.comment {
|
||||||
color: #8f9aa6 !important;
|
color: #8f9aa6 !important;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: .45em !important;
|
margin-top: .45em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.httpdns-auth-table td.title {
|
||||||
|
width: 260px !important;
|
||||||
|
min-width: 260px !important;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-break: keep-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.httpdns-secret-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.httpdns-mini-icon {
|
||||||
|
color: #8c96a3 !important;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.httpdns-mini-icon:hover {
|
||||||
|
color: #5e6c7b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.httpdns-state-on {
|
||||||
|
color: #21ba45;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.httpdns-state-off {
|
||||||
|
color: #8f9aa6;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div>
|
<div class="right-box with-menu">
|
||||||
<div class="ui menu text blue">
|
|
||||||
<div class="item"><strong>{{app.name}}</strong> (<code>{{settings.appId}}</code>)</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
|
|
||||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
<csrf-token></csrf-token>
|
<csrf-token></csrf-token>
|
||||||
<input type="hidden" name="appId" :value="app.id" />
|
<input type="hidden" name="appId" :value="app.id" />
|
||||||
|
|
||||||
<div class="ui stackable grid httpdns-settings-grid">
|
|
||||||
<div class="three wide computer four wide tablet sixteen wide mobile column left-nav-column">
|
|
||||||
<div class="ui fluid vertical pointing menu httpdns-side-menu">
|
|
||||||
<a href="" class="item" :class="{active: activeSection == 'basic'}" @click.prevent="activeSection='basic'">基础配置</a>
|
|
||||||
<a href="" class="item" :class="{active: activeSection == 'auth'}" @click.prevent="activeSection='auth'">认证与密钥</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="thirteen wide computer twelve wide tablet sixteen wide mobile column right-form-column">
|
|
||||||
<table class="ui table selectable definition" v-show="activeSection == 'basic'">
|
<table class="ui table selectable definition" v-show="activeSection == 'basic'">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">App ID</td>
|
<td class="title">App ID</td>
|
||||||
@@ -62,9 +72,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">应用启用</td>
|
<td class="title">应用启用</td>
|
||||||
<td><checkbox name="appStatus" value="1" v-model="settings.appStatus"></checkbox></td>
|
<td>
|
||||||
|
<checkbox name="appStatus" value="1" v-model="settings.appStatus"></checkbox>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">SNI 防护配置</td>
|
<td class="title">SNI 防护配置</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -74,35 +85,51 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<table class="ui table selectable definition" v-show="activeSection == 'auth'">
|
<table class="ui table selectable definition httpdns-auth-table" v-show="activeSection == 'auth'">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">请求验签</td>
|
<td class="title">请求验签</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="ui label tiny green" v-if="settings.signEnabled">已开启</span>
|
<span
|
||||||
<span class="ui label tiny basic" v-else>已关闭</span>
|
:class="settings.signEnabled ? 'httpdns-state-on' : 'httpdns-state-off'">{{settings.signEnabled
|
||||||
<a href="" class="ui mini button" :class="settings.signEnabled ? 'basic' : 'primary'" style="margin-left: .8em;" @click.prevent="toggleSignEnabled">{{settings.signEnabled ? "关闭请求验签" : "开启请求验签"}}</a>
|
? "已开启" : "已关闭"}}</span>
|
||||||
|
<a href="" class="ui mini button basic" style="margin-left: .8em;"
|
||||||
|
@click.prevent="toggleSignEnabled">{{settings.signEnabled ? "关闭请求验签" : "开启请求验签"}}</a>
|
||||||
<p class="comment httpdns-note">打开后,服务端会对请求进行签名校验。</p>
|
<p class="comment httpdns-note">打开后,服务端会对请求进行签名校验。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">加签 Secret</td>
|
<td class="title">加签 Secret</td>
|
||||||
<td>
|
<td>
|
||||||
|
<div class="httpdns-secret-line">
|
||||||
<code>{{signSecretVisible ? settings.signSecretPlain : settings.signSecretMasked}}</code>
|
<code>{{signSecretVisible ? settings.signSecretPlain : settings.signSecretMasked}}</code>
|
||||||
<a href="" class="httpdns-mini-action" @click.prevent="signSecretVisible = !signSecretVisible" :title="signSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="signSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
<a href="" class="httpdns-mini-icon" @click.prevent="signSecretVisible = !signSecretVisible"
|
||||||
<a href="" class="httpdns-mini-action" title="复制加签 Secret" @click.prevent="copySecret(settings.signSecretPlain, '加签 Secret')"><i class="copy outline icon"></i></a>
|
:title="signSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon"
|
||||||
<a href="" class="httpdns-mini-action" @click.prevent="resetSignSecret">[重置]</a>
|
:class="signSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||||
|
<a href="" class="httpdns-mini-icon" title="复制加签 Secret"
|
||||||
|
@click.prevent="copySecret(settings.signSecretPlain, '加签 Secret')"><i
|
||||||
|
class="copy outline icon"></i></a>
|
||||||
|
<a href="" class="httpdns-mini-icon" title="重置加签 Secret" @click.prevent="resetSignSecret"><i
|
||||||
|
class="redo icon"></i></a>
|
||||||
|
</div>
|
||||||
<p class="comment httpdns-note">最近更新:{{settings.signSecretUpdatedAt}}</p>
|
<p class="comment httpdns-note">最近更新:{{settings.signSecretUpdatedAt}}</p>
|
||||||
<p class="comment httpdns-note" v-if="!settings.signEnabled">请求验签已关闭,当前不使用加签 Secret。</p>
|
<p class="comment httpdns-note" v-if="!settings.signEnabled">请求验签已关闭,当前不使用加签 Secret。</p>
|
||||||
<p class="comment httpdns-note">用于生成鉴权接口的安全密钥。</p>
|
<p class="comment httpdns-note">用于生成鉴权接口的安全密钥。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">AES 数据加密 Secret</td>
|
<td class="title">数据加密 Secret</td>
|
||||||
<td>
|
<td>
|
||||||
|
<div class="httpdns-secret-line">
|
||||||
<code>{{aesSecretVisible ? settings.aesSecretPlain : settings.aesSecretMasked}}</code>
|
<code>{{aesSecretVisible ? settings.aesSecretPlain : settings.aesSecretMasked}}</code>
|
||||||
<a href="" class="httpdns-mini-action" @click.prevent="aesSecretVisible = !aesSecretVisible" :title="aesSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="aesSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
<a href="" class="httpdns-mini-icon" @click.prevent="aesSecretVisible = !aesSecretVisible"
|
||||||
<a href="" class="httpdns-mini-action" title="复制 AES 数据加密 Secret" @click.prevent="copySecret(settings.aesSecretPlain, 'AES Secret')"><i class="copy outline icon"></i></a>
|
:title="aesSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon"
|
||||||
<a href="" class="httpdns-mini-action" @click.prevent="resetAESSecret">[重置]</a>
|
:class="aesSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||||
|
<a href="" class="httpdns-mini-icon" title="复制数据加密 Secret"
|
||||||
|
@click.prevent="copySecret(settings.aesSecretPlain, 'AES Secret')"><i
|
||||||
|
class="copy outline icon"></i></a>
|
||||||
|
<a href="" class="httpdns-mini-icon" title="重置数据加密 Secret" @click.prevent="resetAESSecret"><i
|
||||||
|
class="redo icon"></i></a>
|
||||||
|
</div>
|
||||||
<p class="comment httpdns-note">最近更新:{{settings.aesSecretUpdatedAt}}</p>
|
<p class="comment httpdns-note">最近更新:{{settings.aesSecretUpdatedAt}}</p>
|
||||||
<p class="comment httpdns-note">用于解析接口数据加密的密钥。</p>
|
<p class="comment httpdns-note">用于解析接口数据加密的密钥。</p>
|
||||||
</td>
|
</td>
|
||||||
@@ -110,7 +137,5 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.activeSection = "basic";
|
this.activeSection = this.activeSection || "basic";
|
||||||
this.success = NotifyReloadSuccess("保存成功");
|
this.success = NotifyReloadSuccess("保存成功");
|
||||||
this.signSecretVisible = false;
|
this.signSecretVisible = false;
|
||||||
this.aesSecretVisible = false;
|
this.aesSecretVisible = false;
|
||||||
@@ -9,7 +9,7 @@ Tea.context(function () {
|
|||||||
let targetIsOn = !this.settings.signEnabled;
|
let targetIsOn = !this.settings.signEnabled;
|
||||||
|
|
||||||
if (targetIsOn) {
|
if (targetIsOn) {
|
||||||
teaweb.confirm("html:开启后,服务端将会对解析请求进行验签鉴权,<span class='red'>未签名、签名无效或过期的请求都解析失败</span>,确认开启吗?", function () {
|
teaweb.confirm("html:开启后,服务端会对解析请求进行签名鉴权,<span class='red'>未签名、签名无效或过期的请求都解析失败</span>,确认开启吗?", function () {
|
||||||
that.$post("/httpdns/apps/app/settings/toggleSignEnabled")
|
that.$post("/httpdns/apps/app/settings/toggleSignEnabled")
|
||||||
.params({
|
.params({
|
||||||
appId: that.app.id,
|
appId: that.app.id,
|
||||||
@@ -23,7 +23,7 @@ Tea.context(function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
teaweb.confirm("html:关闭后,服务端将不会对解析请求进行验签鉴权,可能<span class='red'>存在被刷风险</span>,确认关闭吗?", function () {
|
teaweb.confirm("html:关闭后,服务端不会对解析请求进行签名鉴权,可能<span class='red'>存在被刷风险</span>,确认关闭吗?", function () {
|
||||||
that.$post("/httpdns/apps/app/settings/toggleSignEnabled")
|
that.$post("/httpdns/apps/app/settings/toggleSignEnabled")
|
||||||
.params({
|
.params({
|
||||||
appId: that.app.id,
|
appId: that.app.id,
|
||||||
@@ -109,3 +109,4 @@ Tea.context(function () {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,23 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>所属集群 *</td>
|
<td>主集群 *</td>
|
||||||
<td>
|
<td>
|
||||||
<select class="ui dropdown" name="clusterId" v-model="defaultClusterId">
|
<select class="ui dropdown" name="primaryClusterId" v-model="defaultPrimaryClusterId">
|
||||||
<option value="">[请选择集群]</option>
|
<option value="">[请选择集群]</option>
|
||||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="comment">应用的解析请求将由所选集群下的网关节点处理。默认值来自“HTTPDNS 用户设置”。</p>
|
<p class="comment">主集群用于优先处理应用解析请求。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>备集群</td>
|
||||||
|
<td>
|
||||||
|
<select class="ui dropdown" name="backupClusterId" v-model="defaultBackupClusterId">
|
||||||
|
<option :value="0">[不设置]</option>
|
||||||
|
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||||
|
</select>
|
||||||
|
<p class="comment">当主集群不可用时,可切换到备集群。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -1,38 +1,31 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.httpdns-col-ttl {
|
.httpdns-col-ttl {
|
||||||
width: 72px;
|
width: 72px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.httpdns-col-actions {
|
.httpdns-col-actions {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div>
|
<second-menu>
|
||||||
<div class="ui menu text blue">
|
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
|
||||||
<div class="item"><strong>{{app.name}}</strong> (<code>{{app.appId}}</code>)</div>
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
</div>
|
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">域名列表</a>
|
||||||
<div class="ui divider"></div>
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>{{domain.name}}</strong></div>
|
||||||
|
<a href="" class="item" @click.prevent="createRecord" :class="{disabled: !domain || !domain.id}">创建规则</a>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
<div class="ui small message">
|
<table class="ui table selectable celled" v-if="records.length > 0" style="margin-top: .8em;">
|
||||||
当前域名:<strong>{{domain.name}}</strong>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="" @click.prevent="createRecord" class="ui button primary small" :class="{disabled: !domain || !domain.id}">
|
|
||||||
<i class="icon plus"></i> 新增自定义解析规则
|
|
||||||
</a>
|
|
||||||
<a :href="'/httpdns/apps/domains?appId=' + app.id" class="ui button small">返回域名管理</a>
|
|
||||||
|
|
||||||
<table class="ui table selectable celled" v-if="records.length > 0" style="margin-top: 1em;">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>线路</th>
|
<th>线路</th>
|
||||||
<th>规则名称</th>
|
<th>规则名称</th>
|
||||||
<th>SDNS 参数</th>
|
|
||||||
<th>解析记录</th>
|
<th>解析记录</th>
|
||||||
<th class="httpdns-col-ttl">TTL</th>
|
<th class="httpdns-col-ttl">TTL</th>
|
||||||
<th class="width10">状态</th>
|
<th class="width10">状态</th>
|
||||||
@@ -43,10 +36,7 @@
|
|||||||
<tr v-for="record in records">
|
<tr v-for="record in records">
|
||||||
<td>{{record.lineText}}</td>
|
<td>{{record.lineText}}</td>
|
||||||
<td>{{record.ruleName}}</td>
|
<td>{{record.ruleName}}</td>
|
||||||
<td>{{record.paramsText}}</td>
|
<td>{{record.recordValueText}}</td>
|
||||||
<td>
|
|
||||||
{{record.recordValueText}}
|
|
||||||
</td>
|
|
||||||
<td class="httpdns-col-ttl">{{record.ttl}}s</td>
|
<td class="httpdns-col-ttl">{{record.ttl}}s</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="green" v-if="record.isOn">已启用</span>
|
<span class="green" v-if="record.isOn">已启用</span>
|
||||||
@@ -61,6 +51,5 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p class="ui warning message" v-if="!domain || !domain.id">当前应用暂无可用域名,请先到域名管理中添加域名。</p>
|
<not-found-box v-if="!domain || !domain.id">当前应用暂无可用域名,请先到域名管理中添加域名。</not-found-box>
|
||||||
<p class="ui message" v-else-if="records.length == 0">当前域名还没有自定义解析规则。</p>
|
<not-found-box v-else-if="records.length == 0">当前域名还没有自定义解析规则。</not-found-box>
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
if (typeof this.records == "undefined") {
|
if (typeof this.records == "undefined") {
|
||||||
this.records = [];
|
this.records = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
<input type="hidden" name="domainId" :value="domain.id" />
|
<input type="hidden" name="domainId" :value="domain.id" />
|
||||||
<input type="hidden" name="domain" :value="record.domain" />
|
<input type="hidden" name="domain" :value="record.domain" />
|
||||||
<input type="hidden" name="recordId" :value="record.id" />
|
<input type="hidden" name="recordId" :value="record.id" />
|
||||||
<input type="hidden" name="sdnsParamsJSON" :value="JSON.stringify(sdnsParams)" />
|
|
||||||
<input type="hidden" name="recordItemsJSON" :value="JSON.stringify(recordItems)" />
|
<input type="hidden" name="recordItemsJSON" :value="JSON.stringify(recordItems)" />
|
||||||
|
|
||||||
<table class="ui table definition selectable">
|
<table class="ui table definition selectable">
|
||||||
@@ -81,26 +80,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td class="title">SDNS 参数配置</td>
|
|
||||||
<td>
|
|
||||||
<div class="httpdns-row" v-for="(param, index) in sdnsParams">
|
|
||||||
<div class="field flex">
|
|
||||||
<input type="text" maxlength="64" placeholder="参数名称" v-model="param.name" />
|
|
||||||
</div>
|
|
||||||
<div class="field flex">
|
|
||||||
<input type="text" maxlength="64" placeholder="参数值" v-model="param.value" />
|
|
||||||
</div>
|
|
||||||
<a href="" @click.prevent="removeSDNSParam(index)" title="删除"><i class="icon trash alternate outline"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="httpdns-inline-actions">
|
|
||||||
<a href="" @click.prevent="addSDNSParam" :class="{disabled: sdnsParams.length >= 10}">
|
|
||||||
<i class="icon plus circle"></i>添加参数
|
|
||||||
</a>
|
|
||||||
<span class="count">{{sdnsParams.length}}/10</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">解析记录值 *</td>
|
<td class="title">解析记录值 *</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -75,11 +75,6 @@ Tea.context(function () {
|
|||||||
vm.record.weightEnabled = vm.normalizeBoolean(vm.record.weightEnabled, false);
|
vm.record.weightEnabled = vm.normalizeBoolean(vm.record.weightEnabled, false);
|
||||||
vm.record.isOn = vm.normalizeBoolean(vm.record.isOn, true);
|
vm.record.isOn = vm.normalizeBoolean(vm.record.isOn, true);
|
||||||
|
|
||||||
vm.sdnsParams = vm.parseJSONList(vm.record.sdnsParamsJson);
|
|
||||||
if (vm.sdnsParams.length == 0) {
|
|
||||||
vm.sdnsParams.push({name: "", value: ""});
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.recordItems = vm.parseJSONList(vm.record.recordItemsJson);
|
vm.recordItems = vm.parseJSONList(vm.record.recordItemsJson);
|
||||||
if (vm.recordItems.length == 0) {
|
if (vm.recordItems.length == 0) {
|
||||||
vm.recordItems.push({type: "A", value: "", weight: 100});
|
vm.recordItems.push({type: "A", value: "", weight: 100});
|
||||||
@@ -139,23 +134,6 @@ Tea.context(function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.addSDNSParam = function () {
|
|
||||||
if (vm.sdnsParams.length >= 10) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
vm.sdnsParams.push({name: "", value: ""});
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.removeSDNSParam = function (index) {
|
|
||||||
if (index < 0 || index >= vm.sdnsParams.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
vm.sdnsParams.splice(index, 1);
|
|
||||||
if (vm.sdnsParams.length == 0) {
|
|
||||||
vm.sdnsParams.push({name: "", value: ""});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.addRecordItem = function () {
|
vm.addRecordItem = function () {
|
||||||
if (vm.recordItems.length >= 10) {
|
if (vm.recordItems.length >= 10) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
12
EdgeAdmin/web/views/@default/httpdns/apps/delete.html
Normal file
12
EdgeAdmin/web/views/@default/httpdns/apps/delete.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{$layout}
|
||||||
|
|
||||||
|
<second-menu>
|
||||||
|
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
|
||||||
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>删除应用</strong></div>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
|
<div class="buttons-box">
|
||||||
|
<button class="ui button red" type="button" @click.prevent="deleteApp(app.id)">删除当前应用</button>
|
||||||
|
<p class="comment" style="margin-top: .8em;">包含{{domainCount}}域名</p>
|
||||||
|
</div>
|
||||||
16
EdgeAdmin/web/views/@default/httpdns/apps/delete.js
Normal file
16
EdgeAdmin/web/views/@default/httpdns/apps/delete.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.deleteApp = function (appId) {
|
||||||
|
let that = this;
|
||||||
|
teaweb.confirm("确定要删除此应用吗?", function () {
|
||||||
|
that.$post("/httpdns/apps/delete")
|
||||||
|
.params({
|
||||||
|
appId: appId
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
teaweb.success("删除成功", function () {
|
||||||
|
window.location = "/httpdns/apps";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,35 +1,62 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
|
||||||
|
|
||||||
<div>
|
<div class="margin"></div>
|
||||||
<div class="ui menu text blue">
|
|
||||||
<div class="item"><strong>{{app.name}}</strong> (<code>{{app.appId}}</code>)</div>
|
<style>
|
||||||
|
.httpdns-domains-table .httpdns-domains-op {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<second-menu>
|
||||||
|
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
|
||||||
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>域名列表</strong></div>
|
||||||
|
<a href="" class="item" @click.prevent="bindDomain">创建域名</a>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
|
<form class="ui form small" @submit.prevent="doSearch">
|
||||||
|
<div class="ui fields inline">
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" v-model.trim="keywordInput" placeholder="按域名筛选..." />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui field">
|
||||||
|
<button type="submit" class="ui button small">搜索</button>
|
||||||
|
|
||||||
|
<a href="" v-if="keyword.length > 0" @click.prevent="clearSearch">[清除条件]</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<a href="" @click.prevent="bindDomain" class="ui button primary small"><i class="icon plus"></i>添加域名</a>
|
<table class="ui table selectable celled httpdns-domains-table" v-if="filteredDomains().length > 0">
|
||||||
|
<colgroup>
|
||||||
<table class="ui table selectable celled" v-if="domains.length > 0" style="margin-top: 1em;">
|
<col style="width:60%;" />
|
||||||
|
<col style="width:20%;" />
|
||||||
|
<col style="width:20%;" />
|
||||||
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>域名列表</th>
|
<th>服务域名</th>
|
||||||
<th class="two wide">规则策略</th>
|
<th class="width10">规则策略</th>
|
||||||
<th class="two op">操作</th>
|
<th class="httpdns-domains-op">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="domain in domains">
|
<tr v-for="domain in filteredDomains()">
|
||||||
<td><strong>{{domain.name}}</strong></td>
|
|
||||||
<td>
|
<td>
|
||||||
<a :href="'/httpdns/apps/customRecords?appId=' + app.id + '&domainId=' + domain.id">{{domain.customRecordCount}}</a>
|
<div><strong>{{domain.name}}</strong></div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a :href="'/httpdns/apps/customRecords?appId=' + app.id + '&domainId=' + domain.id">自定义解析</a> |
|
<a
|
||||||
|
:href="'/httpdns/apps/customRecords?appId=' + app.id + '&domainId=' + domain.id">{{domain.customRecordCount}}</a>
|
||||||
|
</td>
|
||||||
|
<td class="httpdns-domains-op">
|
||||||
|
<a :href="'/httpdns/apps/customRecords?appId=' + app.id + '&domainId=' + domain.id">自定义解析</a>
|
||||||
|
|
|
||||||
<a href="" @click.prevent="deleteDomain(domain.id)">解绑</a>
|
<a href="" @click.prevent="deleteDomain(domain.id)">解绑</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p class="ui message" v-if="domains.length == 0">该应用尚未绑定域名。</p>
|
<not-found-box v-if="domains.length == 0">当前应用尚未绑定域名。</not-found-box>
|
||||||
</div>
|
<not-found-box v-if="domains.length > 0 && filteredDomains().length == 0">没有匹配的域名。</not-found-box>
|
||||||
@@ -1,8 +1,37 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
|
if (typeof this.keywordInput == "undefined") {
|
||||||
|
this.keywordInput = "";
|
||||||
|
}
|
||||||
|
if (typeof this.keyword == "undefined") {
|
||||||
|
this.keyword = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof this.domains == "undefined") {
|
if (typeof this.domains == "undefined") {
|
||||||
this.domains = [];
|
this.domains = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.keywordInput = this.keyword;
|
||||||
|
|
||||||
|
this.doSearch = function () {
|
||||||
|
this.keyword = this.keywordInput.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearSearch = function () {
|
||||||
|
this.keywordInput = "";
|
||||||
|
this.keyword = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
this.filteredDomains = function () {
|
||||||
|
let keyword = this.keyword.trim().toLowerCase();
|
||||||
|
if (keyword.length == 0) {
|
||||||
|
return this.domains;
|
||||||
|
}
|
||||||
|
return this.domains.filter(function (domain) {
|
||||||
|
let name = (domain.name || "").toLowerCase();
|
||||||
|
return name.indexOf(keyword) >= 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.bindDomain = function () {
|
this.bindDomain = function () {
|
||||||
teaweb.popup("/httpdns/apps/domains/createPopup?appId=" + this.app.id, {
|
teaweb.popup("/httpdns/apps/domains/createPopup?appId=" + this.app.id, {
|
||||||
height: "24em",
|
height: "24em",
|
||||||
@@ -11,6 +40,19 @@ Tea.context(function () {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.deleteApp = function () {
|
||||||
|
let that = this;
|
||||||
|
teaweb.confirm("确定要删除当前应用吗?", function () {
|
||||||
|
that.$post("/httpdns/apps/delete")
|
||||||
|
.params({
|
||||||
|
appId: that.app.id
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
window.location = "/httpdns/apps";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.deleteDomain = function (domainId) {
|
this.deleteDomain = function (domainId) {
|
||||||
let that = this;
|
let that = this;
|
||||||
teaweb.confirm("确定要解绑这个域名吗?", function () {
|
teaweb.confirm("确定要解绑这个域名吗?", function () {
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
{$template "menu"}
|
||||||
|
|
||||||
|
<div class="ui margin"></div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.httpdns-apps-table .httpdns-apps-op {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<form method="get" class="ui form" action="/httpdns/apps">
|
<form method="get" class="ui form" action="/httpdns/apps">
|
||||||
<div class="ui fields inline">
|
<div class="ui fields inline">
|
||||||
@@ -16,21 +24,30 @@
|
|||||||
|
|
||||||
<p class="ui message" v-if="apps.length == 0">暂时没有符合条件的 HTTPDNS 应用。</p>
|
<p class="ui message" v-if="apps.length == 0">暂时没有符合条件的 HTTPDNS 应用。</p>
|
||||||
|
|
||||||
<table class="ui table selectable celled" v-if="apps.length > 0">
|
<table class="ui table selectable celled httpdns-apps-table" v-if="apps.length > 0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:34%;" />
|
||||||
|
<col style="width:28%;" />
|
||||||
|
<col style="width:12%;" />
|
||||||
|
<col style="width:10%;" />
|
||||||
|
<col style="width:16%;" />
|
||||||
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>应用名称</th>
|
<th>应用名称</th>
|
||||||
<th>AppID</th>
|
<th>AppID</th>
|
||||||
<th>绑定域名数</th>
|
<th class="center">绑定域名数</th>
|
||||||
<th class="one wide center">状态</th>
|
<th class="center">状态</th>
|
||||||
<th class="tz op">操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="app in apps">
|
<tr v-for="app in apps">
|
||||||
<td>
|
<td>
|
||||||
<a :href="'/httpdns/apps/app?appId=' + app.id">
|
<a :href="'/httpdns/apps/app?appId=' + app.id">
|
||||||
<strong><keyword :v-word="keyword">{{app.name}}</keyword></strong>
|
<strong>
|
||||||
|
<keyword :v-word="keyword">{{app.name}}</keyword>
|
||||||
|
</strong>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -41,9 +58,10 @@
|
|||||||
<td class="center">
|
<td class="center">
|
||||||
<label-on :v-is-on="app.isOn"></label-on>
|
<label-on :v-is-on="app.isOn"></label-on>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="httpdns-apps-op">
|
||||||
<a :href="'/httpdns/apps/app?appId=' + app.id">域名管理</a> |
|
<a :href="'/httpdns/apps/app?appId=' + app.id">域名管理</a> |
|
||||||
<a :href="'/httpdns/apps/app/settings?appId=' + app.id">应用设置</a>
|
<a :href="'/httpdns/apps/app/settings?appId=' + app.id">应用设置</a> |
|
||||||
|
<a :href="'/httpdns/apps/sdk?appId=' + app.id">SDK集成</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
79
EdgeAdmin/web/views/@default/httpdns/apps/sdk.html
Normal file
79
EdgeAdmin/web/views/@default/httpdns/apps/sdk.html
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
{$layout}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.httpdns-sdk-cards {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
.httpdns-sdk-cards > .card {
|
||||||
|
min-height: 168px;
|
||||||
|
}
|
||||||
|
.httpdns-sdk-desc {
|
||||||
|
margin-top: .6em;
|
||||||
|
color: #5e6c7b;
|
||||||
|
}
|
||||||
|
.httpdns-sdk-meta {
|
||||||
|
margin-top: .35em;
|
||||||
|
color: #9aa6b2;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.httpdns-sdk-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .6em;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.httpdns-sdk-actions .button {
|
||||||
|
padding-left: .8em !important;
|
||||||
|
padding-right: .8em !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<second-menu>
|
||||||
|
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
|
||||||
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>SDK 集成</strong></div>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
|
<div class="ui three stackable cards httpdns-sdk-cards">
|
||||||
|
<div class="card">
|
||||||
|
<div class="content">
|
||||||
|
<div class="header"><i class="icon android green"></i> Android SDK</div>
|
||||||
|
<div class="httpdns-sdk-meta">Java / Kotlin</div>
|
||||||
|
<div class="description httpdns-sdk-desc">适用于 Android 客户端接入。</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra content">
|
||||||
|
<div class="httpdns-sdk-actions">
|
||||||
|
<a class="ui button compact mini basic" href="javascript:;"><i class="icon download"></i> 下载 SDK</a>
|
||||||
|
<a class="ui button compact mini basic" href="javascript:;"><i class="icon book"></i> 集成文档</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="content">
|
||||||
|
<div class="header"><i class="icon apple grey"></i> iOS SDK</div>
|
||||||
|
<div class="httpdns-sdk-meta">Objective-C / Swift</div>
|
||||||
|
<div class="description httpdns-sdk-desc">适用于 iOS 客户端接入。</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra content">
|
||||||
|
<div class="httpdns-sdk-actions">
|
||||||
|
<a class="ui button compact mini basic" href="javascript:;"><i class="icon download"></i> 下载 SDK</a>
|
||||||
|
<a class="ui button compact mini basic" href="javascript:;"><i class="icon book"></i> 集成文档</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="content">
|
||||||
|
<div class="header"><i class="icon mobile alternate blue"></i> Flutter SDK</div>
|
||||||
|
<div class="httpdns-sdk-meta">Dart / Plugin</div>
|
||||||
|
<div class="description httpdns-sdk-desc">适用于 Flutter 跨平台接入。</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra content">
|
||||||
|
<div class="httpdns-sdk-actions">
|
||||||
|
<a class="ui button compact mini basic" href="javascript:;"><i class="icon download"></i> 下载 SDK</a>
|
||||||
|
<a class="ui button compact mini basic" href="javascript:;"><i class="icon book"></i> 集成文档</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
<second-menu>
|
<second-menu>
|
||||||
<menu-item :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</menu-item>
|
<a class="item" :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</a>
|
||||||
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
<menu-item :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id" code="index">节点列表</menu-item>
|
|
||||||
<menu-item :href="'/httpdns/clusters/createNode?clusterId=' + cluster.id" code="createNode">创建节点</menu-item>
|
|
||||||
<menu-item :href="'/httpdns/clusters/cluster/settings?clusterId=' + cluster.id" code="settings">集群设置</menu-item>
|
|
||||||
<menu-item :href="'/httpdns/clusters/delete?clusterId=' + cluster.id" code="delete">删除集群</menu-item>
|
|
||||||
</second-menu>
|
</second-menu>
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "cluster_menu"}
|
|
||||||
<div class="ui margin"></div>
|
|
||||||
|
|
||||||
<div class="right-menu">
|
<second-menu>
|
||||||
<a :href="'/httpdns/clusters/createNode?clusterId=' + clusterId" class="item">[创建节点]</a>
|
<a class="item" :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</a>
|
||||||
</div>
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>节点列表</strong></div>
|
||||||
|
<a class="item" :href="'/httpdns/clusters/createNode?clusterId=' + clusterId">创建节点</a>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
<form class="ui form" action="/httpdns/clusters/cluster">
|
<form class="ui form" action="/httpdns/clusters/cluster">
|
||||||
<input type="hidden" name="clusterId" :value="clusterId" />
|
<input type="hidden" name="clusterId" :value="clusterId" />
|
||||||
@@ -43,6 +44,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>节点名称</th>
|
<th>节点名称</th>
|
||||||
<th>IP地址</th>
|
<th>IP地址</th>
|
||||||
|
<th class="center">CPU</th>
|
||||||
|
<th class="center">内存</th>
|
||||||
|
<th class="center">负载</th>
|
||||||
<th class="width-5 center">状态</th>
|
<th class="width-5 center">状态</th>
|
||||||
<th class="two op">操作</th>
|
<th class="two op">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -64,11 +68,21 @@
|
|||||||
<div class="ui label tiny basic">{{addr.ip}}
|
<div class="ui label tiny basic">{{addr.ip}}
|
||||||
<span class="small" v-if="addr.name.length > 0">({{addr.name}})</span>
|
<span class="small" v-if="addr.name.length > 0">({{addr.name}})</span>
|
||||||
<span class="small red" v-if="!addr.canAccess" title="不公开访问">[内]</span>
|
<span class="small red" v-if="!addr.canAccess" title="不公开访问">[内]</span>
|
||||||
<span class="small red" v-if="!addr.isOn">[下线]</span>
|
|
||||||
<span class="small red" v-if="!addr.isUp" title="健康检查失败">[宕机]</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<span v-if="node.status != null && node.status.isActive">{{node.status.cpuUsageText}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<span v-if="node.status != null && node.status.isActive">{{node.status.memUsageText}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<span v-if="node.status != null && node.status.isActive && node.status.load1m > 0">{{node.status.load1m}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<div v-if="!node.isUp">
|
<div v-if="!node.isUp">
|
||||||
<span class="red">宕机</span>
|
<span class="red">宕机</span>
|
||||||
|
|||||||
@@ -1,31 +1,57 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "cluster_menu"}
|
|
||||||
<div class="ui margin"></div>
|
|
||||||
|
|
||||||
<h3 class="ui header">集群设置</h3>
|
{$var "header"}
|
||||||
<p><strong>{{cluster.name}}</strong></p>
|
<script src="/servers/certs/datajs" type="text/javascript"></script>
|
||||||
|
<script src="/js/sortable.min.js" type="text/javascript"></script>
|
||||||
|
{$end}
|
||||||
|
|
||||||
|
<second-menu>
|
||||||
|
<a class="item" :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</a>
|
||||||
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>集群设置</strong></div>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
|
|
||||||
|
{$template "/left_menu_with_menu"}
|
||||||
|
|
||||||
|
<div class="right-box with-menu">
|
||||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
<input type="hidden" name="clusterId" :value="cluster.id" />
|
<input type="hidden" name="clusterId" :value="cluster.id" />
|
||||||
<table class="ui table definition selectable">
|
|
||||||
|
<table class="ui table definition selectable" v-show="activeSection == 'basic'">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">集群名称 *</td>
|
<td class="title">集群名称 *</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="cluster.name" />
|
<input type="text" name="name" maxlength="50" ref="focus" v-model="cluster.name" />
|
||||||
<p class="comment">用于区分不同环境的解析节点池。</p>
|
<p class="comment">用于在系统内部标识该 HTTPDNS 集群。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>集群服务域名 *</td>
|
<td>服务域名 *</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="gatewayDomain" maxlength="255" v-model="settings.gatewayDomain" placeholder="例如 gw-hz.httpdns.example.com" />
|
<input type="text" name="gatewayDomain" maxlength="255" v-model="settings.gatewayDomain"
|
||||||
<p class="comment">该集群下应用用于 SDK 接入的 HTTPDNS 服务域名。</p>
|
placeholder="例如 gw-hz.httpdns.example.com" />
|
||||||
|
<p class="comment">当前集群对外提供 HTTPDNS 服务的接入域名。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
<td>节点安装根目录</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="installDir" maxlength="100" v-model="settings.installDir" />
|
||||||
|
<p class="comment">边缘节点安装 HTTPDNS 服务的默认所在目录,此目录将被用于下发配置。通常保持默认即可。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>默认解析 TTL</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui input right labeled">
|
||||||
|
<input type="text" name="cacheTtl" maxlength="5" v-model="settings.cacheTtl"
|
||||||
|
style="width: 6em" />
|
||||||
|
<span class="ui label">秒</span>
|
||||||
|
</div>
|
||||||
|
<p class="comment">SDK 向 HTTPDNS 请求域名解析时,返回的默认缓存有效期 (TTL)。SDK 超时后将重新发起请求。</p>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tbody v-show="moreOptionsVisible">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>降级超时容忍度</td>
|
<td>降级超时容忍度</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -34,38 +60,46 @@
|
|||||||
style="width: 6em" />
|
style="width: 6em" />
|
||||||
<span class="ui label">毫秒</span>
|
<span class="ui label">毫秒</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">HTTPDNS 网关请求源站超时多长时间后,强制降级返回缓存兜底IP(保障 P99 响应延迟)。</p>
|
<p class="comment">HTTPDNS 节点在回源查询其它 DNS 时的最大等待时间。超出此时间将触发服务降级逻辑(返回上一有效缓存或错误)。</p>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>本地内存缓存</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui input right labeled">
|
|
||||||
<input type="text" name="cacheTtl" maxlength="5" v-model="settings.cacheTtl"
|
|
||||||
style="width: 6em" />
|
|
||||||
<span class="ui label">秒</span>
|
|
||||||
</div>
|
|
||||||
<p class="comment">在网关节点内存中缓存解析结果的时长,缓解峰值查询压力。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>节点安装根目录</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="installDir" maxlength="100" v-model="settings.installDir" />
|
|
||||||
<p class="comment">此集群下新加网关节点的主程序默认部署路径。</p>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>启用当前集群</td>
|
<td>启用当前集群</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="isOn" value="1" v-model="settings.isOn" />
|
<input type="checkbox" name="isOn" value="1" v-model="settings.isOn"
|
||||||
|
@change="syncDefaultCluster" />
|
||||||
<label></label>
|
<label></label>
|
||||||
</div>
|
</div>
|
||||||
<p class="comment">如果取消启用,此集群下的所有 HTTPDNS 网关节点将停止处理解析请求。</p>
|
<p class="comment">如果取消启用,整个集群的 HTTPDNS 解析服务将停止。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>默认集群</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" name="isDefaultCluster" value="1" v-model="settings.isDefaultCluster" />
|
||||||
|
<label>设置为默认部署集群</label>
|
||||||
|
</div>
|
||||||
|
<p class="comment">全局设置。如果应用未单独指定集群,将默认分配和部署到该集群中。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<table class="ui table selectable definition" v-show="activeSection == 'tls'">
|
||||||
|
<tr>
|
||||||
|
<td class="title">绑定端口 *</td>
|
||||||
|
<td>
|
||||||
|
<network-addresses-box :v-url="'/httpdns/addPortPopup'" :v-addresses="tlsConfig.listen"
|
||||||
|
:v-protocol="'tls'" :v-support-range="true"></network-addresses-box>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- SSL配置 -->
|
||||||
|
<ssl-config-box v-show="activeSection == 'tls'" :v-ssl-policy="tlsConfig.sslPolicy"
|
||||||
|
:v-protocol="'tls'"></ssl-config-box>
|
||||||
|
|
||||||
<submit-btn></submit-btn>
|
<submit-btn></submit-btn>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
Tea.context(function () {
|
Tea.context(function () {
|
||||||
this.success = NotifyReloadSuccess("保存成功");
|
this.success = NotifyReloadSuccess("保存成功");
|
||||||
|
|
||||||
|
this.activeSection = this.activeSection || "basic";
|
||||||
|
this.tlsAdvancedVisible = false;
|
||||||
|
|
||||||
|
if (!this.settings) {
|
||||||
|
this.settings = {};
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "cluster_menu"}
|
|
||||||
|
<second-menu>
|
||||||
|
<a class="item" :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</a>
|
||||||
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>创建节点</strong></div>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
<input type="hidden" name="clusterId" :value="clusterId" />
|
<input type="hidden" name="clusterId" :value="clusterId" />
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "cluster_menu"}
|
|
||||||
<div class="ui margin"></div>
|
<second-menu>
|
||||||
|
<a class="item" :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</a>
|
||||||
|
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||||
|
<div class="item"><strong>删除集群</strong></div>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
<div class="buttons-box">
|
<div class="buttons-box">
|
||||||
<button class="ui button red" type="button" @click.prevent="deleteCluster(cluster.id)">删除当前集群</button>
|
<button class="ui button red" type="button" @click.prevent="deleteCluster(cluster.id)">删除当前集群</button>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
{$template "menu"}
|
||||||
|
|
||||||
|
<div class="ui margin"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div v-if="hasErrorLogs" class="ui icon message small error">
|
<div v-if="hasErrorLogs" class="ui icon message small error">
|
||||||
<i class="icon warning circle"></i>
|
<i class="icon warning circle"></i>
|
||||||
@@ -66,3 +68,4 @@
|
|||||||
|
|
||||||
<div class="page" v-html="page"></div>
|
<div class="page" v-html="page"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -74,8 +74,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>集群服务地址</td>
|
<td>集群服务地址</td>
|
||||||
<td>
|
<td>
|
||||||
<code>{{selectedApp.gatewayDomain}}</code>
|
<code>{{selectedApp.gatewayDomainDisplay}}</code>
|
||||||
<a href="" class="httpdns-mini-action" title="复制服务地址" @click.prevent="copyText(selectedApp.gatewayDomain, '服务地址')"><i class="copy outline icon"></i></a>
|
<a href="" class="httpdns-mini-action" title="复制服务地址" @click.prevent="copyText(selectedApp.gatewayDomainDisplay, '服务地址')"><i class="copy outline icon"></i></a>
|
||||||
|
<p class="comment" v-if="selectedApp.gatewayDomains && selectedApp.gatewayDomains.length > 1">已启用主备:第一个为主集群,后续为备集群。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
if (!this.selectedApp.gatewayDomain || this.selectedApp.gatewayDomain.length == 0) {
|
if (!this.selectedApp.gatewayDomain || this.selectedApp.gatewayDomain.length == 0) {
|
||||||
this.selectedApp.gatewayDomain = "gw.httpdns.example.com"
|
this.selectedApp.gatewayDomain = "gw.httpdns.example.com"
|
||||||
}
|
}
|
||||||
|
if (!this.selectedApp.gatewayDomainDisplay || this.selectedApp.gatewayDomainDisplay.length == 0) {
|
||||||
|
this.selectedApp.gatewayDomainDisplay = this.selectedApp.gatewayDomain
|
||||||
|
}
|
||||||
|
|
||||||
this.signSecretVisible = false
|
this.signSecretVisible = false
|
||||||
this.aesSecretVisible = false
|
this.aesSecretVisible = false
|
||||||
|
|||||||
@@ -72,13 +72,6 @@
|
|||||||
<p class="comment httpdns-policy-note">用户新建应用时默认落到此集群。</p>
|
<p class="comment httpdns-policy-note">用户新建应用时默认落到此集群。</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td class="title">启用用户域名校验</td>
|
|
||||||
<td>
|
|
||||||
<checkbox name="enableUserDomainVerify" value="1" v-model="policies.enableUserDomainVerify"></checkbox>
|
|
||||||
<p class="comment httpdns-policy-note">开启后,用户添加域名需要通过归属校验。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<table class="ui table definition selectable" v-show="activeSection == 'basic'">
|
<table class="ui table definition selectable" v-show="activeSection == 'basic'">
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ Tea.context(function () {
|
|||||||
this.policies.defaultClusterId = this.availableClusters[0].id;
|
this.policies.defaultClusterId = this.availableClusters[0].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof this.policies.enableUserDomainVerify == "undefined") {
|
|
||||||
this.policies.enableUserDomainVerify = true;
|
|
||||||
}
|
|
||||||
if (typeof this.policies.defaultTTL == "undefined" || this.policies.defaultTTL <= 0) {
|
if (typeof this.policies.defaultTTL == "undefined" || this.policies.defaultTTL <= 0) {
|
||||||
this.policies.defaultTTL = 30;
|
this.policies.defaultTTL = 30;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<first-menu>
|
<first-menu>
|
||||||
<menu-item href="/httpdns/resolveLogs" code="index">解析日志</menu-item>
|
<menu-item href="/httpdns/resolveLogs" code="index">访问日志</menu-item>
|
||||||
</first-menu>
|
</first-menu>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
|
||||||
|
<div class="margin"></div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.httpdns-log-summary {
|
.httpdns-log-summary {
|
||||||
@@ -14,7 +15,8 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<form method="get" action="/httpdns/resolveLogs" class="ui form" autocomplete="off">
|
<div>
|
||||||
|
<form method="get" action="/httpdns/resolveLogs" class="ui form small" autocomplete="off">
|
||||||
<div class="ui fields inline">
|
<div class="ui fields inline">
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
||||||
@@ -25,14 +27,15 @@
|
|||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<select class="ui dropdown" name="nodeId" v-model="nodeId">
|
<select class="ui dropdown" name="nodeId" v-model="nodeId">
|
||||||
<option value="">[节点]</option>
|
<option value="">[节点]</option>
|
||||||
<option v-for="node in nodes" :value="node.id" v-if="clusterId == '' || clusterId == node.clusterId">{{node.name}}</option>
|
<option v-for="node in nodes" :value="node.id"
|
||||||
|
v-if="clusterId == '' || clusterId == node.clusterId">{{node.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" name="appId" v-model="appId" placeholder="AppID" style="width:10em" />
|
<input type="text" name="appId" v-model="appId" placeholder="AppID" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" name="domain" v-model="domain" placeholder="域名" style="width:13em" />
|
<input type="text" name="domain" v-model="domain" placeholder="域名" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<select class="ui dropdown" name="status" v-model="status">
|
<select class="ui dropdown" name="status" v-model="status">
|
||||||
@@ -42,20 +45,25 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" name="keyword" v-model="keyword" placeholder="应用/域名/IP/结果IP" style="width:14em" />
|
<input type="text" name="keyword" v-model="keyword" placeholder="应用/域名/IP/结果IP" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<button type="submit" class="ui button">查询</button>
|
<button type="submit" class="ui button small">查询</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field" v-if="clusterId.toString().length > 0 || nodeId.toString().length > 0 || appId.length > 0 || domain.length > 0 || status.length > 0 || keyword.length > 0">
|
<div class="ui field"
|
||||||
|
v-if="clusterId.toString().length > 0 || nodeId.toString().length > 0 || appId.length > 0 || domain.length > 0 || status.length > 0 || keyword.length > 0">
|
||||||
<a href="/httpdns/resolveLogs">[清除条件]</a>
|
<a href="/httpdns/resolveLogs">[清除条件]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="comment" v-if="resolveLogs.length == 0">暂时还没有解析日志。</p>
|
<div class="margin"></div>
|
||||||
|
|
||||||
<div style="overflow-x:auto;" v-if="resolveLogs.length > 0">
|
<not-found-box v-if="resolveLogs.length == 0">暂时还没有访问日志。</not-found-box>
|
||||||
|
|
||||||
|
<div v-if="resolveLogs.length > 0">
|
||||||
|
<div style="overflow-x:auto;">
|
||||||
<table class="ui table selectable celled">
|
<table class="ui table selectable celled">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -75,7 +83,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<div class="httpdns-log-summary">
|
<div class="httpdns-log-summary">
|
||||||
{{log.time}}
|
{{log.time}}
|
||||||
| {{log.appName}}(<code>{{log.appId}}</code>)
|
| {{log.appName}} (<code>{{log.appId}}</code>)
|
||||||
| <code>{{log.clientIp}}</code>
|
| <code>{{log.clientIp}}</code>
|
||||||
| {{log.os}}/{{log.sdkVersion}}
|
| {{log.os}}/{{log.sdkVersion}}
|
||||||
| {{log.query}} {{log.domain}} ->
|
| {{log.query}} {{log.domain}} ->
|
||||||
@@ -84,7 +92,7 @@
|
|||||||
|
|
|
|
||||||
<span class="green" v-if="log.status == 'success'"><strong>成功</strong></span>
|
<span class="green" v-if="log.status == 'success'"><strong>成功</strong></span>
|
||||||
<span class="red" v-else><strong>失败</strong></span>
|
<span class="red" v-else><strong>失败</strong></span>
|
||||||
<span class="grey" v-if="log.errorCode != 'none'">({{log.errorCode}})</span>
|
<span class="grey" v-if="log.errorCode != 'none'">({{log.errorCode}})</span>
|
||||||
| {{log.costMs}}ms
|
| {{log.costMs}}ms
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -92,3 +100,4 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
{$template "menu"}
|
||||||
|
|
||||||
|
<div class="margin"></div>
|
||||||
{$template "/datepicker"}
|
{$template "/datepicker"}
|
||||||
|
|
||||||
<form method="get" action="/httpdns/runtimeLogs" class="ui form" autocomplete="off">
|
<style>
|
||||||
|
.httpdns-runtime-level {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<form method="get" action="/httpdns/runtimeLogs" class="ui form small" autocomplete="off">
|
||||||
<div class="ui fields inline">
|
<div class="ui fields inline">
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
||||||
@@ -13,14 +21,16 @@
|
|||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<select class="ui dropdown" name="nodeId" v-model="nodeId">
|
<select class="ui dropdown" name="nodeId" v-model="nodeId">
|
||||||
<option value="">[节点]</option>
|
<option value="">[节点]</option>
|
||||||
<option v-for="node in nodes" :value="node.id" v-if="clusterId == '' || clusterId == node.clusterId">{{node.name}}</option>
|
<option v-for="node in nodes" :value="node.id" v-if="clusterId == '' || clusterId == node.clusterId">
|
||||||
|
{{node.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" style="width:8em" id="day-from-picker"/>
|
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" style="width:7.8em"
|
||||||
|
id="day-from-picker" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" style="width:8em" id="day-to-picker"/>
|
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" style="width:7.8em" id="day-to-picker" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<select class="ui dropdown" name="level" v-model="level">
|
<select class="ui dropdown" name="level" v-model="level">
|
||||||
@@ -32,18 +42,21 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<input type="text" name="keyword" style="width:15em" v-model="keyword" placeholder="类型/模块/详情/节点"/>
|
<input type="text" name="keyword" v-model="keyword" placeholder="类型/详情/节点" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<button type="submit" class="ui button">查询</button>
|
<button type="submit" class="ui button small">查询</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui field" v-if="clusterId.toString().length > 0 || nodeId.toString().length > 0 || dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
<div class="ui field"
|
||||||
|
v-if="clusterId.toString().length > 0 || nodeId.toString().length > 0 || dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
||||||
<a href="/httpdns/runtimeLogs">[清除条件]</a>
|
<a href="/httpdns/runtimeLogs">[清除条件]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p class="comment" v-if="runtimeLogs.length == 0">暂时还没有运行日志。</p>
|
<div class="margin"></div>
|
||||||
|
|
||||||
|
<not-found-box v-if="runtimeLogs.length == 0">暂时还没有运行日志。</not-found-box>
|
||||||
|
|
||||||
<table class="ui table selectable celled" v-if="runtimeLogs.length > 0">
|
<table class="ui table selectable celled" v-if="runtimeLogs.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -53,10 +66,8 @@
|
|||||||
<th>节点</th>
|
<th>节点</th>
|
||||||
<th>级别</th>
|
<th>级别</th>
|
||||||
<th>类型</th>
|
<th>类型</th>
|
||||||
<th>模块</th>
|
|
||||||
<th>详情</th>
|
<th>详情</th>
|
||||||
<th>次数</th>
|
<th>次数</th>
|
||||||
<th>请求ID</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -65,16 +76,13 @@
|
|||||||
<td>{{log.clusterName}}</td>
|
<td>{{log.clusterName}}</td>
|
||||||
<td>{{log.nodeName}}</td>
|
<td>{{log.nodeName}}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="ui label tiny red" v-if="log.level == 'error'">error</span>
|
<span
|
||||||
<span class="ui label tiny orange" v-else-if="log.level == 'warning'">warning</span>
|
class="httpdns-runtime-level"
|
||||||
<span class="ui label tiny blue" v-else-if="log.level == 'info'">info</span>
|
:class="{red:log.level == 'error', orange:log.level == 'warning', green:log.level == 'success'}">{{log.level}}</span>
|
||||||
<span class="ui label tiny green" v-else>success</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td><code>{{log.tag}}</code></td>
|
<td><code>{{log.tag}}</code></td>
|
||||||
<td>{{log.module}}</td>
|
|
||||||
<td>{{log.description}}</td>
|
<td>{{log.description}}</td>
|
||||||
<td>{{log.count}}</td>
|
<td>{{log.count}}</td>
|
||||||
<td><code>{{log.requestId}}</code></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 左侧菜单由 Go Backend 自动生成注入,此处加首行子菜单使其符合标准平台样式 -->
|
<!-- 左侧菜单由 Go Backend 自动生成注入,此处加首行子菜单使其符合标准平台样式 -->
|
||||||
<first-menu>
|
<first-menu>
|
||||||
<menu-item href="/httpdns/sandbox" code="index">API 在线沙盒</menu-item>
|
<menu-item href="/httpdns/sandbox" code="index">解析测试</menu-item>
|
||||||
</first-menu>
|
</first-menu>
|
||||||
@@ -1,24 +1,6 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
{$template "menu"}
|
|
||||||
<style>
|
<div class="ui margin"></div>
|
||||||
.httpdns-sdns-row {
|
|
||||||
display: flex;
|
|
||||||
gap: .5em;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: .45em;
|
|
||||||
}
|
|
||||||
.httpdns-sdns-row .field {
|
|
||||||
margin: 0 !important;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.httpdns-sdns-actions {
|
|
||||||
margin-top: .2em;
|
|
||||||
}
|
|
||||||
.httpdns-sdns-actions .count {
|
|
||||||
color: #8f9aa6;
|
|
||||||
margin-left: .4em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="ui grid stackable">
|
<div class="ui grid stackable">
|
||||||
@@ -52,24 +34,6 @@
|
|||||||
@click.prevent="request.qtype='AAAA'" type="button">AAAA</button>
|
@click.prevent="request.qtype='AAAA'" type="button">AAAA</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label>SDNS 参数</label>
|
|
||||||
<div class="httpdns-sdns-row" v-for="(param, index) in sdnsParams">
|
|
||||||
<div class="field">
|
|
||||||
<input type="text" maxlength="64" placeholder="参数名称" v-model="param.name" />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<input type="text" maxlength="64" placeholder="参数值" v-model="param.value" />
|
|
||||||
</div>
|
|
||||||
<a href="" @click.prevent="removeSDNSParam(index)" title="删除"><i class="icon trash alternate outline"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="httpdns-sdns-actions">
|
|
||||||
<a href="" @click.prevent="addSDNSParam" :class="{disabled: sdnsParams.length >= 10}">
|
|
||||||
<i class="icon plus circle"></i>添加参数
|
|
||||||
</a>
|
|
||||||
<span class="count">{{sdnsParams.length}}/10</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
@@ -123,7 +87,8 @@
|
|||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="ui segment">
|
<div class="ui segment">
|
||||||
<div class="grey">客户端 IP</div>
|
<div class="grey">客户端 IP</div>
|
||||||
<div style="margin-top:.4em;"><code>{{response.data.client_ip || request.clientIp || '-'}}</code></div>
|
<div style="margin-top:.4em;">
|
||||||
|
<code>{{response.data.client_ip || request.clientIp || '-'}}</code></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
@@ -155,7 +120,8 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(row, idx) in response.resultRows">
|
<tr v-for="(row, idx) in response.resultRows">
|
||||||
<td v-if="idx === 0" :rowspan="response.resultRows.length">{{row.domain || request.domain}}</td>
|
<td v-if="idx === 0" :rowspan="response.resultRows.length">{{row.domain ||
|
||||||
|
request.domain}}</td>
|
||||||
<td>{{row.type || request.qtype}}</td>
|
<td>{{row.type || request.qtype}}</td>
|
||||||
<td><code>{{row.ip}}</code></td>
|
<td><code>{{row.ip}}</code></td>
|
||||||
<td>{{row.ttl}}s</td>
|
<td>{{row.ttl}}s</td>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ Tea.context(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.request = this.newRequest()
|
this.request = this.newRequest()
|
||||||
this.sdnsParams = [{name: "", value: ""}]
|
|
||||||
|
|
||||||
this.response = {
|
this.response = {
|
||||||
hasResult: false,
|
hasResult: false,
|
||||||
@@ -26,39 +25,6 @@ Tea.context(function () {
|
|||||||
this.apps = []
|
this.apps = []
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addSDNSParam = function () {
|
|
||||||
if (this.sdnsParams.length >= 10) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.sdnsParams.push({name: "", value: ""})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.removeSDNSParam = function (index) {
|
|
||||||
if (index < 0 || index >= this.sdnsParams.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.sdnsParams.splice(index, 1)
|
|
||||||
if (this.sdnsParams.length == 0) {
|
|
||||||
this.sdnsParams.push({name: "", value: ""})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cleanSDNSParams = function () {
|
|
||||||
let list = []
|
|
||||||
this.sdnsParams.forEach(function (item) {
|
|
||||||
let name = (item.name || "").trim()
|
|
||||||
let value = (item.value || "").trim()
|
|
||||||
if (name.length == 0 && value.length == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list.push({
|
|
||||||
name: name,
|
|
||||||
value: value
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
this.normalizeResultRows = function (data) {
|
this.normalizeResultRows = function (data) {
|
||||||
if (typeof data == "undefined" || data == null) {
|
if (typeof data == "undefined" || data == null) {
|
||||||
return []
|
return []
|
||||||
@@ -106,7 +72,6 @@ Tea.context(function () {
|
|||||||
this.response.hasResult = false
|
this.response.hasResult = false
|
||||||
|
|
||||||
let payload = Object.assign({}, this.request)
|
let payload = Object.assign({}, this.request)
|
||||||
payload.sdnsParamsJSON = JSON.stringify(this.cleanSDNSParams())
|
|
||||||
|
|
||||||
this.$post("/httpdns/sandbox/test")
|
this.$post("/httpdns/sandbox/test")
|
||||||
.params(payload)
|
.params(payload)
|
||||||
@@ -122,7 +87,6 @@ Tea.context(function () {
|
|||||||
|
|
||||||
this.resetForm = function () {
|
this.resetForm = function () {
|
||||||
this.request = this.newRequest()
|
this.request = this.newRequest()
|
||||||
this.sdnsParams = [{name: "", value: ""}]
|
|
||||||
this.response = {
|
this.response = {
|
||||||
hasResult: false,
|
hasResult: false,
|
||||||
code: -1,
|
code: -1,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
- 全局配置
|
- 全局配置
|
||||||
- 应用管理
|
- 应用管理
|
||||||
- SDK接入引导
|
- SDK接入引导
|
||||||
- 解析日志
|
- 访问日志
|
||||||
- 运行日志
|
- 运行日志
|
||||||
- 解析测试
|
- 解析测试
|
||||||
2. 现阶段以管理端页面与 Mock 数据联调为主。
|
2. 现阶段以管理端页面与 Mock 数据联调为主。
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
|
|
||||||
## 6. 日志页面(现状)
|
## 6. 日志页面(现状)
|
||||||
|
|
||||||
### 6.1 解析日志
|
### 6.1 访问日志
|
||||||
1. 主列:
|
1. 主列:
|
||||||
- 集群
|
- 集群
|
||||||
- 节点
|
- 节点
|
||||||
|
|||||||
Reference in New Issue
Block a user