带阿里标识的版本
This commit is contained in:
26
EdgeUser/internal/web/actions/default/httpdns/apps/app.go
Normal file
26
EdgeUser/internal/web/actions/default/httpdns/apps/app.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type AppAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AppAction) Init() {
|
||||
this.Nav("httpdns", "app", "")
|
||||
}
|
||||
|
||||
func (this *AppAction) RunGet(params struct {
|
||||
AppId int64
|
||||
}) {
|
||||
app, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.RedirectURL("/httpdns/apps/domains?appId=" + strconv.FormatInt(app.GetInt64("id"), 10))
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/httpdns/httpdnsutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type AppSettingsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AppSettingsAction) Init() {
|
||||
this.Nav("httpdns", "app", "settings")
|
||||
}
|
||||
|
||||
func (this *AppSettingsAction) RunGet(params struct {
|
||||
AppId int64
|
||||
Section string
|
||||
}) {
|
||||
httpdnsutils.AddLeftMenu(this.Parent())
|
||||
|
||||
app, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), app.GetInt64("id"), "settings")
|
||||
|
||||
section := params.Section
|
||||
if len(section) == 0 {
|
||||
section = "basic"
|
||||
}
|
||||
this.Data["activeSection"] = section
|
||||
|
||||
appIDStr := strconv.FormatInt(app.GetInt64("id"), 10)
|
||||
this.Data["leftMenuItems"] = []maps.Map{
|
||||
{
|
||||
"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",
|
||||
},
|
||||
}
|
||||
|
||||
clusterResp, err := this.RPC().HTTPDNSClusterRPC().FindAllHTTPDNSClusters(this.UserContext(), &pb.FindAllHTTPDNSClustersRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
clusterDomainMap := map[int64]string{}
|
||||
for _, cluster := range clusterResp.GetClusters() {
|
||||
clusterDomainMap[cluster.GetId()] = cluster.GetServiceDomain()
|
||||
}
|
||||
|
||||
primaryClusterId := app.GetInt64("primaryClusterId")
|
||||
backupClusterId := app.GetInt64("backupClusterId")
|
||||
settings := maps.Map{
|
||||
"appId": app.GetString("appId"),
|
||||
"appStatus": app.GetBool("isOn"),
|
||||
"primaryClusterId": primaryClusterId,
|
||||
"backupClusterId": backupClusterId,
|
||||
"primaryServiceDomain": clusterDomainMap[primaryClusterId],
|
||||
"backupServiceDomain": clusterDomainMap[backupClusterId],
|
||||
"signEnabled": app.GetBool("signEnabled"),
|
||||
"signSecretPlain": app.GetString("signSecretPlain"),
|
||||
"signSecretMasked": app.GetString("signSecretMasked"),
|
||||
"signSecretUpdatedAt": app.GetString("signSecretUpdated"),
|
||||
}
|
||||
this.Data["app"] = app
|
||||
this.Data["settings"] = settings
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *AppSettingsAction) RunPost(params struct {
|
||||
AppId int64
|
||||
|
||||
AppStatus bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.Field("appId", params.AppId).Gt(0, "请选择应用")
|
||||
|
||||
appResp, err := this.RPC().HTTPDNSAppRPC().FindHTTPDNSApp(this.UserContext(), &pb.FindHTTPDNSAppRequest{
|
||||
AppDbId: params.AppId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if appResp.GetApp() == nil {
|
||||
this.Fail("找不到对应的应用")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPDNSAppRPC().UpdateHTTPDNSApp(this.UserContext(), &pb.UpdateHTTPDNSAppRequest{
|
||||
AppDbId: params.AppId,
|
||||
Name: appResp.GetApp().GetName(),
|
||||
PrimaryClusterId: appResp.GetApp().GetPrimaryClusterId(),
|
||||
BackupClusterId: appResp.GetApp().GetBackupClusterId(),
|
||||
IsOn: params.AppStatus,
|
||||
UserId: appResp.GetApp().GetUserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type AppSettingsResetSignSecretAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AppSettingsResetSignSecretAction) RunPost(params struct {
|
||||
AppId int64
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.Field("appId", params.AppId).Gt(0, "璇烽€夋嫨搴旂敤")
|
||||
|
||||
_, err := this.RPC().HTTPDNSAppRPC().ResetHTTPDNSAppSignSecret(this.UserContext(), &pb.ResetHTTPDNSAppSignSecretRequest{
|
||||
AppDbId: params.AppId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type AppSettingsToggleSignEnabledAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AppSettingsToggleSignEnabledAction) RunPost(params struct {
|
||||
AppId int64
|
||||
IsOn int
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.Field("appId", params.AppId).Gt(0, "璇烽€夋嫨搴旂敤")
|
||||
|
||||
_, err := this.RPC().HTTPDNSAppRPC().UpdateHTTPDNSAppSignEnabled(this.UserContext(), &pb.UpdateHTTPDNSAppSignEnabledRequest{
|
||||
AppDbId: params.AppId,
|
||||
SignEnabled: params.IsOn == 1,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
47
EdgeUser/internal/web/actions/default/httpdns/apps/create.go
Normal file
47
EdgeUser/internal/web/actions/default/httpdns/apps/create.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type CreateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreateAction) Init() {
|
||||
this.Nav("", "", "create")
|
||||
}
|
||||
|
||||
func (this *CreateAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreateAction) RunPost(params struct {
|
||||
Name string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.Field("name", params.Name).Require("请输入应用名称")
|
||||
|
||||
createResp, err := this.RPC().HTTPDNSAppRPC().CreateHTTPDNSApp(this.UserContext(), &pb.CreateHTTPDNSAppRequest{
|
||||
Name: params.Name,
|
||||
AppId: "app" + strconv.FormatInt(time.Now().UnixNano()%1_000_000_000_000, 36),
|
||||
PrimaryClusterId: 0,
|
||||
BackupClusterId: 0,
|
||||
IsOn: true,
|
||||
SignEnabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["appId"] = createResp.GetAppDbId()
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/httpdns/httpdnsutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CustomRecordsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CustomRecordsAction) Init() {
|
||||
this.Nav("httpdns", "app", "")
|
||||
}
|
||||
|
||||
func (this *CustomRecordsAction) RunGet(params struct {
|
||||
AppId int64
|
||||
DomainId int64
|
||||
}) {
|
||||
httpdnsutils.AddLeftMenu(this.Parent())
|
||||
|
||||
app, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), app.GetInt64("id"), "domains")
|
||||
|
||||
domains, err := listDomainMaps(this.Parent(), app.GetInt64("id"), "")
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
domain := findDomainMap(domains, params.DomainId)
|
||||
|
||||
records := make([]maps.Map, 0)
|
||||
if domain.GetInt64("id") > 0 {
|
||||
records, err = listCustomRuleMaps(this.Parent(), domain.GetInt64("id"))
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, record := range records {
|
||||
record["domain"] = domain.GetString("name")
|
||||
record["lineText"] = buildLineText(record)
|
||||
record["recordValueText"] = buildRecordValueText(record)
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["app"] = app
|
||||
this.Data["domain"] = domain
|
||||
this.Data["records"] = records
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CustomRecordsCreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CustomRecordsCreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CustomRecordsCreatePopupAction) RunGet(params struct {
|
||||
AppId int64
|
||||
DomainId int64
|
||||
RecordId int64
|
||||
}) {
|
||||
app, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
domains, err := listDomainMaps(this.Parent(), app.GetInt64("id"), "")
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
domain := findDomainMap(domains, params.DomainId)
|
||||
|
||||
record := maps.Map{
|
||||
"id": int64(0),
|
||||
"domain": domain.GetString("name"),
|
||||
"lineScope": "china",
|
||||
"lineCarrier": "榛樿",
|
||||
"lineRegion": "榛樿",
|
||||
"lineProvince": "榛樿",
|
||||
"lineContinent": "榛樿",
|
||||
"lineCountry": "榛樿",
|
||||
"ruleName": "",
|
||||
"weightEnabled": false,
|
||||
"ttl": 30,
|
||||
"isOn": true,
|
||||
"recordItemsJson": `[{"type":"A","value":"","weight":100}]`,
|
||||
}
|
||||
|
||||
if params.RecordId > 0 && domain.GetInt64("id") > 0 {
|
||||
rules, err := listCustomRuleMaps(this.Parent(), domain.GetInt64("id"))
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if rule.GetInt64("id") != params.RecordId {
|
||||
continue
|
||||
}
|
||||
record["id"] = rule.GetInt64("id")
|
||||
record["domain"] = domain.GetString("name")
|
||||
record["lineScope"] = rule.GetString("lineScope")
|
||||
record["lineCarrier"] = defaultLineField(rule.GetString("lineCarrier"))
|
||||
record["lineRegion"] = defaultLineField(rule.GetString("lineRegion"))
|
||||
record["lineProvince"] = defaultLineField(rule.GetString("lineProvince"))
|
||||
record["lineContinent"] = defaultLineField(rule.GetString("lineContinent"))
|
||||
record["lineCountry"] = defaultLineField(rule.GetString("lineCountry"))
|
||||
record["ruleName"] = rule.GetString("ruleName")
|
||||
record["weightEnabled"] = rule.GetBool("weightEnabled")
|
||||
record["ttl"] = rule.GetInt("ttl")
|
||||
record["isOn"] = rule.GetBool("isOn")
|
||||
record["recordItemsJson"] = marshalJSON(rule["recordValues"], "[]")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["app"] = app
|
||||
this.Data["domain"] = domain
|
||||
this.Data["record"] = record
|
||||
this.Data["isEditing"] = params.RecordId > 0
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CustomRecordsCreatePopupAction) RunPost(params struct {
|
||||
AppId int64
|
||||
DomainId int64
|
||||
|
||||
RecordId int64
|
||||
Domain string
|
||||
LineScope string
|
||||
|
||||
LineCarrier string
|
||||
LineRegion string
|
||||
LineProvince string
|
||||
LineContinent string
|
||||
LineCountry string
|
||||
|
||||
RuleName string
|
||||
RecordItemsJSON string
|
||||
WeightEnabled bool
|
||||
Ttl int
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.Field("appId", params.AppId).Gt(0, "璇烽€夋嫨搴旂敤")
|
||||
params.Must.Field("domainId", params.DomainId).Gt(0, "请选择所属域名")
|
||||
|
||||
params.LineScope = strings.ToLower(strings.TrimSpace(params.LineScope))
|
||||
if params.LineScope != "china" && params.LineScope != "overseas" {
|
||||
params.LineScope = "china"
|
||||
}
|
||||
params.RuleName = strings.TrimSpace(params.RuleName)
|
||||
if len(params.RuleName) == 0 {
|
||||
this.Fail("请输入规则名称")
|
||||
return
|
||||
}
|
||||
if params.Ttl <= 0 || params.Ttl > 86400 {
|
||||
this.Fail("TTL值必须在 1-86400 范围内")
|
||||
return
|
||||
}
|
||||
|
||||
recordValues, err := parseRecordItemsJSON(params.RecordItemsJSON, params.WeightEnabled)
|
||||
if err != nil {
|
||||
this.Fail(err.Error())
|
||||
return
|
||||
}
|
||||
if len(recordValues) == 0 {
|
||||
this.Fail("请输入解析记录值")
|
||||
return
|
||||
}
|
||||
if len(recordValues) > 10 {
|
||||
this.Fail("单个规则最多只能添加 10 条解析记录")
|
||||
return
|
||||
}
|
||||
|
||||
lineCarrier := strings.TrimSpace(params.LineCarrier)
|
||||
lineRegion := strings.TrimSpace(params.LineRegion)
|
||||
lineProvince := strings.TrimSpace(params.LineProvince)
|
||||
lineContinent := strings.TrimSpace(params.LineContinent)
|
||||
lineCountry := strings.TrimSpace(params.LineCountry)
|
||||
if len(lineCarrier) == 0 {
|
||||
lineCarrier = "榛樿"
|
||||
}
|
||||
if len(lineRegion) == 0 {
|
||||
lineRegion = "榛樿"
|
||||
}
|
||||
if len(lineProvince) == 0 {
|
||||
lineProvince = "榛樿"
|
||||
}
|
||||
if len(lineContinent) == 0 {
|
||||
lineContinent = "榛樿"
|
||||
}
|
||||
if len(lineCountry) == 0 {
|
||||
lineCountry = "榛樿"
|
||||
}
|
||||
if params.LineScope == "overseas" {
|
||||
lineCarrier = ""
|
||||
lineRegion = ""
|
||||
lineProvince = ""
|
||||
} else {
|
||||
lineContinent = ""
|
||||
lineCountry = ""
|
||||
}
|
||||
|
||||
records := make([]*pb.HTTPDNSRuleRecord, 0, len(recordValues))
|
||||
for i, item := range recordValues {
|
||||
records = append(records, &pb.HTTPDNSRuleRecord{
|
||||
Id: 0,
|
||||
RuleId: 0,
|
||||
RecordType: item.GetString("type"),
|
||||
RecordValue: item.GetString("value"),
|
||||
Weight: int32(item.GetInt("weight")),
|
||||
Sort: int32(i + 1),
|
||||
})
|
||||
}
|
||||
|
||||
rule := &pb.HTTPDNSCustomRule{
|
||||
Id: params.RecordId,
|
||||
AppId: params.AppId,
|
||||
DomainId: params.DomainId,
|
||||
RuleName: params.RuleName,
|
||||
LineScope: params.LineScope,
|
||||
LineCarrier: lineCarrier,
|
||||
LineRegion: lineRegion,
|
||||
LineProvince: lineProvince,
|
||||
LineContinent: lineContinent,
|
||||
LineCountry: lineCountry,
|
||||
Ttl: int32(params.Ttl),
|
||||
IsOn: params.IsOn,
|
||||
Priority: 100,
|
||||
Records: records,
|
||||
}
|
||||
|
||||
if params.RecordId > 0 {
|
||||
err = updateCustomRule(this.Parent(), rule)
|
||||
} else {
|
||||
_, err = createCustomRule(this.Parent(), rule)
|
||||
}
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func parseRecordItemsJSON(raw string, weightEnabled bool) ([]maps.Map, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if len(raw) == 0 {
|
||||
return []maps.Map{}, nil
|
||||
}
|
||||
|
||||
list := []maps.Map{}
|
||||
if err := json.Unmarshal([]byte(raw), &list); err != nil {
|
||||
return nil, fmt.Errorf("解析记录格式不正确")
|
||||
}
|
||||
|
||||
result := make([]maps.Map, 0, len(list))
|
||||
for _, item := range list {
|
||||
recordType := strings.ToUpper(strings.TrimSpace(item.GetString("type")))
|
||||
recordValue := strings.TrimSpace(item.GetString("value"))
|
||||
if len(recordType) == 0 && len(recordValue) == 0 {
|
||||
continue
|
||||
}
|
||||
if recordType != "A" && recordType != "AAAA" {
|
||||
return nil, fmt.Errorf("记录类型只能是 A 或 AAAA")
|
||||
}
|
||||
if len(recordValue) == 0 {
|
||||
return nil, fmt.Errorf("记录值不能为空")
|
||||
}
|
||||
|
||||
weight := item.GetInt("weight")
|
||||
if !weightEnabled {
|
||||
weight = 100
|
||||
}
|
||||
if weight < 1 || weight > 100 {
|
||||
return nil, fmt.Errorf("鏉冮噸鍊煎繀椤诲湪 1-100 涔嬮棿")
|
||||
}
|
||||
|
||||
result = append(result, maps.Map{
|
||||
"type": recordType,
|
||||
"value": recordValue,
|
||||
"weight": weight,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func marshalJSON(v interface{}, fallback string) string {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package apps
|
||||
|
||||
import "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
|
||||
type CustomRecordsDeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CustomRecordsDeleteAction) RunPost(params struct {
|
||||
AppId int64
|
||||
RecordId int64
|
||||
}) {
|
||||
if params.RecordId > 0 {
|
||||
err := deleteCustomRule(this.Parent(), params.RecordId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package apps
|
||||
|
||||
import "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
|
||||
type CustomRecordsToggleAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CustomRecordsToggleAction) RunPost(params struct {
|
||||
AppId int64
|
||||
RecordId int64
|
||||
IsOn bool
|
||||
}) {
|
||||
if params.RecordId > 0 {
|
||||
err := toggleCustomRule(this.Parent(), params.RecordId, params.IsOn)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
48
EdgeUser/internal/web/actions/default/httpdns/apps/delete.go
Normal file
48
EdgeUser/internal/web/actions/default/httpdns/apps/delete.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/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, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), app.GetInt64("id"), "delete")
|
||||
this.Data["app"] = app
|
||||
domains, err := listDomainMaps(this.Parent(), app.GetInt64("id"), "")
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["domainCount"] = len(domains)
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
AppId int64
|
||||
}) {
|
||||
if params.AppId > 0 {
|
||||
err := deleteAppByID(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/httpdns/httpdnsutils"
|
||||
)
|
||||
|
||||
type DomainsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DomainsAction) Init() {
|
||||
this.Nav("httpdns", "app", "domains")
|
||||
}
|
||||
|
||||
func (this *DomainsAction) RunGet(params struct {
|
||||
AppId int64
|
||||
}) {
|
||||
httpdnsutils.AddLeftMenu(this.Parent())
|
||||
app, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 鏋勫缓椤堕儴 tabbar
|
||||
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), params.AppId, "domains")
|
||||
|
||||
domains, err := listDomainMaps(this.Parent(), app.GetInt64("id"), "")
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["app"] = app
|
||||
this.Data["domains"] = domains
|
||||
this.Data["keyword"] = ""
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type DomainsCreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DomainsCreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *DomainsCreatePopupAction) RunGet(params struct {
|
||||
AppId int64
|
||||
}) {
|
||||
app, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["app"] = app
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *DomainsCreatePopupAction) RunPost(params struct {
|
||||
AppId int64
|
||||
Domain string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.Field("appId", params.AppId).Gt(0, "璇烽€夋嫨搴旂敤")
|
||||
params.Must.Field("domain", params.Domain).Require("请输入域名")
|
||||
|
||||
err := createDomain(this.Parent(), params.AppId, params.Domain)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package apps
|
||||
|
||||
import "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
|
||||
type DomainsDeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DomainsDeleteAction) RunPost(params struct {
|
||||
DomainId int64
|
||||
}) {
|
||||
if params.DomainId > 0 {
|
||||
err := deleteDomain(this.Parent(), params.DomainId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
func maskSecret(secret string) string {
|
||||
secret = strings.TrimSpace(secret)
|
||||
if len(secret) < 4 {
|
||||
return "******"
|
||||
}
|
||||
|
||||
prefix := ""
|
||||
for i := 0; i < len(secret); i++ {
|
||||
if secret[i] == '_' {
|
||||
prefix = secret[:i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(prefix) == 0 {
|
||||
prefix = secret[:2]
|
||||
}
|
||||
|
||||
if len(secret) <= 8 {
|
||||
return prefix + "xxxx"
|
||||
}
|
||||
return prefix + "xxxxxxxx" + secret[len(secret)-4:]
|
||||
}
|
||||
|
||||
func buildLineText(record maps.Map) string {
|
||||
parts := []string{}
|
||||
if strings.TrimSpace(record.GetString("lineScope")) == "overseas" {
|
||||
parts = append(parts,
|
||||
strings.TrimSpace(record.GetString("lineContinent")),
|
||||
strings.TrimSpace(record.GetString("lineCountry")),
|
||||
)
|
||||
} else {
|
||||
parts = append(parts,
|
||||
strings.TrimSpace(record.GetString("lineCarrier")),
|
||||
strings.TrimSpace(record.GetString("lineRegion")),
|
||||
strings.TrimSpace(record.GetString("lineProvince")),
|
||||
)
|
||||
}
|
||||
|
||||
finalParts := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if len(part) == 0 || part == "榛樿" {
|
||||
continue
|
||||
}
|
||||
finalParts = append(finalParts, part)
|
||||
}
|
||||
if len(finalParts) == 0 {
|
||||
return "榛樿"
|
||||
}
|
||||
return strings.Join(finalParts, " / ")
|
||||
}
|
||||
|
||||
func buildRecordValueText(record maps.Map) string {
|
||||
values, ok := record["recordValues"].([]maps.Map)
|
||||
if !ok || len(values) == 0 {
|
||||
return "-"
|
||||
}
|
||||
|
||||
weightEnabled := record.GetBool("weightEnabled")
|
||||
defaultType := strings.ToUpper(strings.TrimSpace(record.GetString("recordType")))
|
||||
parts := make([]string, 0, len(values))
|
||||
for _, item := range values {
|
||||
value := strings.TrimSpace(item.GetString("value"))
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
recordType := strings.ToUpper(strings.TrimSpace(item.GetString("type")))
|
||||
if len(recordType) == 0 {
|
||||
recordType = defaultType
|
||||
}
|
||||
if recordType != "A" && recordType != "AAAA" {
|
||||
recordType = "A"
|
||||
}
|
||||
part := recordType + " " + value
|
||||
if weightEnabled {
|
||||
part += "(" + strconv.Itoa(item.GetInt("weight")) + ")"
|
||||
}
|
||||
parts = append(parts, part)
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return "-"
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
28
EdgeUser/internal/web/actions/default/httpdns/apps/index.go
Normal file
28
EdgeUser/internal/web/actions/default/httpdns/apps/index.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/httpdns/httpdnsutils"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("httpdns", "app", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Keyword string
|
||||
}) {
|
||||
httpdnsutils.AddLeftMenu(this.Parent())
|
||||
this.Data["keyword"] = params.Keyword
|
||||
apps, err := listAppMaps(this.Parent(), params.Keyword)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["apps"] = apps
|
||||
this.Show()
|
||||
}
|
||||
35
EdgeUser/internal/web/actions/default/httpdns/apps/init.go
Normal file
35
EdgeUser/internal/web/actions/default/httpdns/apps/init.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "httpdns").
|
||||
Data("teaSubMenu", "app").
|
||||
Prefix("/httpdns/apps").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/app", new(AppAction)).
|
||||
Get("/sdk", new(SdkAction)).
|
||||
Get("/sdk/check", new(SdkCheckAction)).
|
||||
Get("/sdk/download", new(SdkDownloadAction)).
|
||||
Get("/sdk/doc", new(SdkDocAction)).
|
||||
GetPost("/app/settings", new(AppSettingsAction)).
|
||||
Post("/app/settings/toggleSignEnabled", new(AppSettingsToggleSignEnabledAction)).
|
||||
Post("/app/settings/resetSignSecret", new(AppSettingsResetSignSecretAction)).
|
||||
Get("/domains", new(DomainsAction)).
|
||||
Get("/customRecords", new(CustomRecordsAction)).
|
||||
GetPost("/create", new(CreateAction)).
|
||||
GetPost("/delete", new(DeleteAction)).
|
||||
GetPost("/domains/createPopup", new(DomainsCreatePopupAction)).
|
||||
Post("/domains/delete", new(DomainsDeleteAction)).
|
||||
GetPost("/customRecords/createPopup", new(CustomRecordsCreatePopupAction)).
|
||||
Post("/customRecords/delete", new(CustomRecordsDeleteAction)).
|
||||
Post("/customRecords/toggle", new(CustomRecordsToggleAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
func listAppMaps(parent *actionutils.ParentAction, keyword string) ([]maps.Map, error) {
|
||||
resp, err := parent.RPC().HTTPDNSAppRPC().ListHTTPDNSApps(parent.UserContext(), &pb.ListHTTPDNSAppsRequest{
|
||||
Offset: 0,
|
||||
Size: 10_000,
|
||||
Keyword: strings.TrimSpace(keyword),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]maps.Map, 0, len(resp.GetApps()))
|
||||
for _, app := range resp.GetApps() {
|
||||
domainResp, err := parent.RPC().HTTPDNSDomainRPC().ListHTTPDNSDomainsWithAppId(parent.UserContext(), &pb.ListHTTPDNSDomainsWithAppIdRequest{
|
||||
AppDbId: app.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, appPBToMap(app, int64(len(domainResp.GetDomains()))))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func findAppMap(parent *actionutils.ParentAction, appDbId int64) (maps.Map, error) {
|
||||
if appDbId > 0 {
|
||||
resp, err := parent.RPC().HTTPDNSAppRPC().FindHTTPDNSApp(parent.UserContext(), &pb.FindHTTPDNSAppRequest{
|
||||
AppDbId: appDbId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.GetApp() != nil {
|
||||
domainResp, err := parent.RPC().HTTPDNSDomainRPC().ListHTTPDNSDomainsWithAppId(parent.UserContext(), &pb.ListHTTPDNSDomainsWithAppIdRequest{
|
||||
AppDbId: appDbId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return appPBToMap(resp.GetApp(), int64(len(domainResp.GetDomains()))), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("应用不存在或无权限访问")
|
||||
}
|
||||
|
||||
apps, err := listAppMaps(parent, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
return maps.Map{
|
||||
"id": int64(0),
|
||||
"name": "",
|
||||
"appId": "",
|
||||
}, nil
|
||||
}
|
||||
return apps[0], nil
|
||||
}
|
||||
|
||||
func createApp(parent *actionutils.ParentAction, name string, primaryClusterId int64, backupClusterId int64) (int64, error) {
|
||||
newAppId := "app" + strconv.FormatInt(time.Now().UnixNano()%1_000_000_000_000, 36)
|
||||
resp, err := parent.RPC().HTTPDNSAppRPC().CreateHTTPDNSApp(parent.UserContext(), &pb.CreateHTTPDNSAppRequest{
|
||||
Name: strings.TrimSpace(name),
|
||||
AppId: newAppId,
|
||||
PrimaryClusterId: primaryClusterId,
|
||||
BackupClusterId: backupClusterId,
|
||||
IsOn: true,
|
||||
SignEnabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp.GetAppDbId(), nil
|
||||
}
|
||||
|
||||
func deleteAppByID(parent *actionutils.ParentAction, appDbId int64) error {
|
||||
_, err := parent.RPC().HTTPDNSAppRPC().DeleteHTTPDNSApp(parent.UserContext(), &pb.DeleteHTTPDNSAppRequest{
|
||||
AppDbId: appDbId,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func updateAppSettings(parent *actionutils.ParentAction, appDbId int64, name string, primaryClusterId int64, backupClusterId int64, isOn bool, userId int64) error {
|
||||
_, err := parent.RPC().HTTPDNSAppRPC().UpdateHTTPDNSApp(parent.UserContext(), &pb.UpdateHTTPDNSAppRequest{
|
||||
AppDbId: appDbId,
|
||||
Name: strings.TrimSpace(name),
|
||||
PrimaryClusterId: primaryClusterId,
|
||||
BackupClusterId: backupClusterId,
|
||||
IsOn: isOn,
|
||||
UserId: userId,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func updateAppSignEnabled(parent *actionutils.ParentAction, appDbId int64, signEnabled bool) error {
|
||||
_, err := parent.RPC().HTTPDNSAppRPC().UpdateHTTPDNSAppSignEnabled(parent.UserContext(), &pb.UpdateHTTPDNSAppSignEnabledRequest{
|
||||
AppDbId: appDbId,
|
||||
SignEnabled: signEnabled,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func resetAppSignSecret(parent *actionutils.ParentAction, appDbId int64) (*pb.ResetHTTPDNSAppSignSecretResponse, error) {
|
||||
return parent.RPC().HTTPDNSAppRPC().ResetHTTPDNSAppSignSecret(parent.UserContext(), &pb.ResetHTTPDNSAppSignSecretRequest{
|
||||
AppDbId: appDbId,
|
||||
})
|
||||
}
|
||||
|
||||
func listDomainMaps(parent *actionutils.ParentAction, appDbId int64, keyword string) ([]maps.Map, error) {
|
||||
resp, err := parent.RPC().HTTPDNSDomainRPC().ListHTTPDNSDomainsWithAppId(parent.UserContext(), &pb.ListHTTPDNSDomainsWithAppIdRequest{
|
||||
AppDbId: appDbId,
|
||||
Keyword: strings.TrimSpace(keyword),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]maps.Map, 0, len(resp.GetDomains()))
|
||||
for _, domain := range resp.GetDomains() {
|
||||
result = append(result, maps.Map{
|
||||
"id": domain.GetId(),
|
||||
"name": domain.GetDomain(),
|
||||
"isOn": domain.GetIsOn(),
|
||||
"customRecordCount": domain.GetRuleCount(),
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func createDomain(parent *actionutils.ParentAction, appDbId int64, domain string) error {
|
||||
_, err := parent.RPC().HTTPDNSDomainRPC().CreateHTTPDNSDomain(parent.UserContext(), &pb.CreateHTTPDNSDomainRequest{
|
||||
AppDbId: appDbId,
|
||||
Domain: strings.TrimSpace(domain),
|
||||
IsOn: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteDomain(parent *actionutils.ParentAction, domainId int64) error {
|
||||
_, err := parent.RPC().HTTPDNSDomainRPC().DeleteHTTPDNSDomain(parent.UserContext(), &pb.DeleteHTTPDNSDomainRequest{
|
||||
DomainId: domainId,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func findDomainMap(domains []maps.Map, domainID int64) maps.Map {
|
||||
if len(domains) == 0 {
|
||||
return maps.Map{}
|
||||
}
|
||||
if domainID <= 0 {
|
||||
return domains[0]
|
||||
}
|
||||
for _, domain := range domains {
|
||||
if domain.GetInt64("id") == domainID {
|
||||
return domain
|
||||
}
|
||||
}
|
||||
return domains[0]
|
||||
}
|
||||
|
||||
func listCustomRuleMaps(parent *actionutils.ParentAction, domainId int64) ([]maps.Map, error) {
|
||||
resp, err := parent.RPC().HTTPDNSRuleRPC().ListHTTPDNSCustomRulesWithDomainId(parent.UserContext(), &pb.ListHTTPDNSCustomRulesWithDomainIdRequest{
|
||||
DomainId: domainId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]maps.Map, 0, len(resp.GetRules()))
|
||||
for _, rule := range resp.GetRules() {
|
||||
recordValues := make([]maps.Map, 0, len(rule.GetRecords()))
|
||||
recordType := "A"
|
||||
weightEnabled := false
|
||||
for _, record := range rule.GetRecords() {
|
||||
if len(recordType) == 0 {
|
||||
recordType = strings.ToUpper(strings.TrimSpace(record.GetRecordType()))
|
||||
}
|
||||
if record.GetWeight() > 0 && record.GetWeight() != 100 {
|
||||
weightEnabled = true
|
||||
}
|
||||
recordValues = append(recordValues, maps.Map{
|
||||
"type": strings.ToUpper(strings.TrimSpace(record.GetRecordType())),
|
||||
"value": record.GetRecordValue(),
|
||||
"weight": record.GetWeight(),
|
||||
})
|
||||
}
|
||||
if len(recordValues) == 0 {
|
||||
recordValues = append(recordValues, maps.Map{
|
||||
"type": "A",
|
||||
"value": "",
|
||||
"weight": 100,
|
||||
})
|
||||
}
|
||||
|
||||
item := maps.Map{
|
||||
"id": rule.GetId(),
|
||||
"lineScope": rule.GetLineScope(),
|
||||
"lineCarrier": defaultLineField(rule.GetLineCarrier()),
|
||||
"lineRegion": defaultLineField(rule.GetLineRegion()),
|
||||
"lineProvince": defaultLineField(rule.GetLineProvince()),
|
||||
"lineContinent": defaultLineField(rule.GetLineContinent()),
|
||||
"lineCountry": defaultLineField(rule.GetLineCountry()),
|
||||
"ruleName": rule.GetRuleName(),
|
||||
"recordType": recordType,
|
||||
"recordValues": recordValues,
|
||||
"weightEnabled": weightEnabled,
|
||||
"ttl": rule.GetTtl(),
|
||||
"isOn": rule.GetIsOn(),
|
||||
"updatedAt": formatDateTime(rule.GetUpdatedAt()),
|
||||
}
|
||||
item["lineText"] = buildLineText(item)
|
||||
item["recordValueText"] = buildRecordValueText(item)
|
||||
result = append(result, item)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func createCustomRule(parent *actionutils.ParentAction, rule *pb.HTTPDNSCustomRule) (int64, error) {
|
||||
resp, err := parent.RPC().HTTPDNSRuleRPC().CreateHTTPDNSCustomRule(parent.UserContext(), &pb.CreateHTTPDNSCustomRuleRequest{
|
||||
Rule: rule,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp.GetRuleId(), nil
|
||||
}
|
||||
|
||||
func updateCustomRule(parent *actionutils.ParentAction, rule *pb.HTTPDNSCustomRule) error {
|
||||
_, err := parent.RPC().HTTPDNSRuleRPC().UpdateHTTPDNSCustomRule(parent.UserContext(), &pb.UpdateHTTPDNSCustomRuleRequest{
|
||||
Rule: rule,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteCustomRule(parent *actionutils.ParentAction, ruleId int64) error {
|
||||
_, err := parent.RPC().HTTPDNSRuleRPC().DeleteHTTPDNSCustomRule(parent.UserContext(), &pb.DeleteHTTPDNSCustomRuleRequest{
|
||||
RuleId: ruleId,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func toggleCustomRule(parent *actionutils.ParentAction, ruleId int64, isOn bool) error {
|
||||
_, err := parent.RPC().HTTPDNSRuleRPC().UpdateHTTPDNSCustomRuleStatus(parent.UserContext(), &pb.UpdateHTTPDNSCustomRuleStatusRequest{
|
||||
RuleId: ruleId,
|
||||
IsOn: isOn,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func appPBToMap(app *pb.HTTPDNSApp, domainCount int64) maps.Map {
|
||||
signSecret := app.GetSignSecret()
|
||||
return maps.Map{
|
||||
"id": app.GetId(),
|
||||
"name": app.GetName(),
|
||||
"appId": app.GetAppId(),
|
||||
"clusterId": app.GetPrimaryClusterId(),
|
||||
"primaryClusterId": app.GetPrimaryClusterId(),
|
||||
"backupClusterId": app.GetBackupClusterId(),
|
||||
"userId": app.GetUserId(),
|
||||
"isOn": app.GetIsOn(),
|
||||
"domainCount": domainCount,
|
||||
"sniPolicyText": "闅愬尶 SNI",
|
||||
"signEnabled": app.GetSignEnabled(),
|
||||
"signSecretPlain": signSecret,
|
||||
"signSecretMasked": maskSecret(signSecret),
|
||||
"signSecretUpdated": formatDateTime(app.GetSignUpdatedAt()),
|
||||
}
|
||||
}
|
||||
|
||||
func defaultLineField(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) == 0 {
|
||||
return "榛樿"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func formatDateTime(ts int64) string {
|
||||
if ts <= 0 {
|
||||
return ""
|
||||
}
|
||||
return timeutil.FormatTime("Y-m-d H:i:s", ts)
|
||||
}
|
||||
30
EdgeUser/internal/web/actions/default/httpdns/apps/sdk.go
Normal file
30
EdgeUser/internal/web/actions/default/httpdns/apps/sdk.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/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, err := findAppMap(this.Parent(), params.AppId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
httpdnsutils.AddAppTabbar(this.Parent(), app.GetString("name"), params.AppId, "sdk")
|
||||
this.Data["app"] = app
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type SdkCheckAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SdkCheckAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SdkCheckAction) RunGet(params struct {
|
||||
Platform string
|
||||
Version string
|
||||
Type string
|
||||
}) {
|
||||
platform, _, _, filename, err := resolveSDKPlatform(params.Platform)
|
||||
if err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = err.Error()
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
t := strings.ToLower(strings.TrimSpace(params.Type))
|
||||
if t == "doc" {
|
||||
docPath := findUploadedSDKDocPath(platform, params.Version)
|
||||
if len(docPath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "Documentation is unavailable, please contact admin"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadURL := "/httpdns/apps/sdk/doc?platform=" + url.QueryEscape(platform)
|
||||
if len(strings.TrimSpace(params.Version)) > 0 {
|
||||
downloadURL += "&version=" + url.QueryEscape(strings.TrimSpace(params.Version))
|
||||
}
|
||||
this.Data["exists"] = true
|
||||
this.Data["url"] = downloadURL
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
archivePath := findSDKArchivePath(filename, params.Version)
|
||||
if len(archivePath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "SDK package is unavailable, please contact admin"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadURL := "/httpdns/apps/sdk/download?platform=" + url.QueryEscape(platform)
|
||||
if len(strings.TrimSpace(params.Version)) > 0 {
|
||||
downloadURL += "&version=" + url.QueryEscape(strings.TrimSpace(params.Version))
|
||||
}
|
||||
downloadURL += "&raw=1"
|
||||
this.Data["exists"] = true
|
||||
this.Data["url"] = downloadURL
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type SdkDocAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SdkDocAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SdkDocAction) RunGet(params struct {
|
||||
Platform string
|
||||
Version string
|
||||
}) {
|
||||
platform, _, readmeRelativePath, _, err := resolveSDKPlatform(params.Platform)
|
||||
if err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = err.Error()
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var data []byte
|
||||
uploadedDocPath := findUploadedSDKDocPath(platform, params.Version)
|
||||
if len(uploadedDocPath) > 0 {
|
||||
data, err = os.ReadFile(uploadedDocPath)
|
||||
}
|
||||
|
||||
sdkRoot, sdkRootErr := findSDKRoot()
|
||||
if len(data) == 0 && sdkRootErr == nil {
|
||||
readmePath := filepath.Join(sdkRoot, readmeRelativePath)
|
||||
data, err = os.ReadFile(readmePath)
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
localDocPath := findLocalSDKDocPath(platform)
|
||||
if len(localDocPath) > 0 {
|
||||
data, err = os.ReadFile(localDocPath)
|
||||
}
|
||||
}
|
||||
|
||||
if len(data) == 0 || err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "SDK documentation is unavailable for current platform, please contact admin"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadName := filepath.Base(uploadedDocPath)
|
||||
if len(downloadName) == 0 || downloadName == "." || downloadName == string(filepath.Separator) {
|
||||
downloadName = "httpdns-sdk-" + strings.ToLower(platform) + ".md"
|
||||
}
|
||||
|
||||
this.AddHeader("Content-Type", "text/markdown; charset=utf-8")
|
||||
this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";")
|
||||
_, _ = this.ResponseWriter.Write(data)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type SdkDownloadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SdkDownloadAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SdkDownloadAction) RunGet(params struct {
|
||||
Platform string
|
||||
Version string
|
||||
Raw int
|
||||
}) {
|
||||
_, _, _, filename, err := resolveSDKPlatform(params.Platform)
|
||||
if err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = err.Error()
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
archivePath := findSDKArchivePath(filename, params.Version)
|
||||
if len(archivePath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "SDK archive is unavailable for current platform, please contact admin: " + filename
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
fp, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "failed to open SDK archive: " + err.Error()
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = fp.Close()
|
||||
}()
|
||||
|
||||
downloadName := filepath.Base(archivePath)
|
||||
if len(downloadName) == 0 || downloadName == "." || downloadName == string(filepath.Separator) {
|
||||
downloadName = filename
|
||||
}
|
||||
|
||||
if params.Raw == 1 {
|
||||
this.AddHeader("Content-Type", "application/octet-stream")
|
||||
this.AddHeader("X-SDK-Filename", downloadName)
|
||||
} else {
|
||||
this.AddHeader("Content-Type", "application/zip")
|
||||
this.AddHeader("Content-Disposition", "attachment; filename=\""+downloadName+"\";")
|
||||
}
|
||||
this.AddHeader("X-Accel-Buffering", "no")
|
||||
_, _ = io.Copy(this.ResponseWriter, fp)
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
)
|
||||
|
||||
func sdkUploadDir() string {
|
||||
dirs := sdkUploadDirs()
|
||||
if len(dirs) > 0 {
|
||||
return dirs[0]
|
||||
}
|
||||
return filepath.Clean(Tea.Root + "/data/httpdns/sdk")
|
||||
}
|
||||
|
||||
func sdkUploadDirs() []string {
|
||||
candidates := []string{
|
||||
filepath.Clean(Tea.Root + "/../data/httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/data/httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../edge-admin/data/httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../edge-user/data/httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../../data/httpdns/sdk"),
|
||||
}
|
||||
results := make([]string, 0, len(candidates))
|
||||
seen := map[string]bool{}
|
||||
for _, dir := range candidates {
|
||||
if len(dir) == 0 || seen[dir] {
|
||||
continue
|
||||
}
|
||||
seen[dir] = true
|
||||
results = append(results, dir)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func sdkUploadSearchDirs() []string {
|
||||
dirs := sdkUploadDirs()
|
||||
results := make([]string, 0, len(dirs))
|
||||
for _, dir := range dirs {
|
||||
stat, err := os.Stat(dir)
|
||||
if err == nil && stat.IsDir() {
|
||||
results = append(results, dir)
|
||||
}
|
||||
}
|
||||
if len(results) == 0 {
|
||||
results = append(results, sdkUploadDir())
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func findFirstExistingDir(paths []string) string {
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && stat.IsDir() {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findFirstExistingFile(paths []string) string {
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() && stat.Size() > 0 {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findNewestExistingFile(paths []string) string {
|
||||
type fileInfo struct {
|
||||
path string
|
||||
modTime time.Time
|
||||
}
|
||||
result := fileInfo{}
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil || stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
if stat.Size() <= 0 {
|
||||
continue
|
||||
}
|
||||
if len(result.path) == 0 || stat.ModTime().After(result.modTime) || (stat.ModTime().Equal(result.modTime) && path > result.path) {
|
||||
result.path = path
|
||||
result.modTime = stat.ModTime()
|
||||
}
|
||||
}
|
||||
return result.path
|
||||
}
|
||||
|
||||
func findSDKRoot() (string, error) {
|
||||
candidates := []string{
|
||||
filepath.Clean(Tea.Root + "/EdgeHttpDNS/sdk"),
|
||||
filepath.Clean(Tea.Root + "/edge-httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/edge-httpdns/edge-httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../EdgeHttpDNS/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../../EdgeHttpDNS/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../edge-httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../../edge-httpdns/sdk"),
|
||||
}
|
||||
|
||||
dir := findFirstExistingDir(candidates)
|
||||
if len(dir) > 0 {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
return "", errors.New("SDK files are not found on current server")
|
||||
}
|
||||
|
||||
func resolveSDKPlatform(platform string) (key string, relativeDir string, readmeRelativePath string, downloadFilename string, err error) {
|
||||
switch strings.ToLower(strings.TrimSpace(platform)) {
|
||||
case "android":
|
||||
return "android", "android", "android/README.md", "httpdns-sdk-android.zip", nil
|
||||
case "ios":
|
||||
return "ios", "ios", "ios/README.md", "httpdns-sdk-ios.zip", nil
|
||||
case "flutter":
|
||||
return "flutter", "flutter/aliyun_httpdns", "flutter/aliyun_httpdns/README.md", "httpdns-sdk-flutter.zip", nil
|
||||
default:
|
||||
return "", "", "", "", errors.New("invalid platform, expected one of: android, ios, flutter")
|
||||
}
|
||||
}
|
||||
|
||||
func findSDKArchivePath(downloadFilename string, version string) string {
|
||||
searchDirs := sdkUploadSearchDirs()
|
||||
|
||||
normalizedVersion := strings.TrimSpace(version)
|
||||
base := strings.TrimSuffix(downloadFilename, ".zip")
|
||||
if len(normalizedVersion) > 0 {
|
||||
versionFiles := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
versionFiles = append(versionFiles, filepath.Join(dir, base+"-v"+normalizedVersion+".zip"))
|
||||
}
|
||||
if path := findFirstExistingFile(versionFiles); len(path) > 0 {
|
||||
return path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
patternName := base + "-v*.zip"
|
||||
matches := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
found, _ := filepath.Glob(filepath.Join(dir, patternName))
|
||||
for _, file := range found {
|
||||
stat, err := os.Stat(file)
|
||||
if err == nil && !stat.IsDir() {
|
||||
matches = append(matches, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(matches) > 0 {
|
||||
return findNewestExistingFile(matches)
|
||||
}
|
||||
|
||||
exactFiles := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
exactFiles = append(exactFiles, filepath.Join(dir, downloadFilename))
|
||||
}
|
||||
if path := findFirstExistingFile(exactFiles); len(path) > 0 {
|
||||
return path
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func findUploadedSDKDocPath(platform string, version string) string {
|
||||
platform = strings.ToLower(strings.TrimSpace(platform))
|
||||
if len(platform) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
searchDirs := sdkUploadSearchDirs()
|
||||
normalizedVersion := strings.TrimSpace(version)
|
||||
if len(normalizedVersion) > 0 {
|
||||
exactVersion := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
exactVersion = append(exactVersion, filepath.Join(dir, "httpdns-sdk-"+platform+"-v"+normalizedVersion+".md"))
|
||||
}
|
||||
if file := findFirstExistingFile(exactVersion); len(file) > 0 {
|
||||
return file
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
matches := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
pattern := filepath.Join(dir, "httpdns-sdk-"+platform+"-v*.md")
|
||||
found, _ := filepath.Glob(pattern)
|
||||
matches = append(matches, found...)
|
||||
}
|
||||
if len(matches) > 0 {
|
||||
sort.Strings(matches)
|
||||
return findNewestExistingFile(matches)
|
||||
}
|
||||
|
||||
exact := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
exact = append(exact, filepath.Join(dir, "httpdns-sdk-"+platform+".md"))
|
||||
}
|
||||
return findFirstExistingFile(exact)
|
||||
}
|
||||
|
||||
func findLocalSDKDocPath(platform string) string {
|
||||
filename := strings.ToLower(strings.TrimSpace(platform)) + ".md"
|
||||
candidates := []string{
|
||||
filepath.Clean(Tea.Root + "/edge-admin/web/views/@default/httpdns/apps/docs/" + filename),
|
||||
filepath.Clean(Tea.Root + "/EdgeUser/web/views/@default/httpdns/apps/docs/" + filename),
|
||||
filepath.Clean(Tea.Root + "/EdgeAdmin/web/views/@default/httpdns/apps/docs/" + filename),
|
||||
}
|
||||
return findFirstExistingFile(candidates)
|
||||
}
|
||||
Reference in New Issue
Block a user