Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package logs
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"regexp"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
RequestId string
Keyword string
Day string
ClusterId int64
NodeId int64
RecordType string
}) {
day := strings.ReplaceAll(params.Day, "-", "")
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
day = timeutil.Format("Ymd")
}
this.Data["keyword"] = params.Keyword
this.Data["day"] = day[:4] + "-" + day[4:6] + "-" + day[6:]
this.Data["path"] = this.Request.URL.Path
this.Data["clusterId"] = params.ClusterId
this.Data["nodeId"] = params.NodeId
this.Data["recordType"] = params.RecordType
var size = int64(10)
resp, err := this.RPC().NSAccessLogRPC().ListNSAccessLogs(this.UserContext(), &pb.ListNSAccessLogsRequest{
RequestId: params.RequestId,
NsClusterId: params.ClusterId,
NsNodeId: params.NodeId,
NsDomainId: 0,
NsRecordId: 0,
RecordType: params.RecordType,
Size: size,
Day: day,
Keyword: params.Keyword,
Reverse: false,
})
if err != nil {
this.ErrorPage(err)
return
}
var ipList = []string{}
var nodeIds = []int64{}
var domainIds = []int64{}
if len(resp.NsAccessLogs) == 0 {
this.Data["accessLogs"] = []interface{}{}
} else {
this.Data["accessLogs"] = resp.NsAccessLogs
for _, accessLog := range resp.NsAccessLogs {
// IP
if len(accessLog.RemoteAddr) > 0 {
// 去掉端口
ip, _, err := net.SplitHostPort(accessLog.RemoteAddr)
if err == nil {
accessLog.RemoteAddr = ip
if !lists.ContainsString(ipList, ip) {
ipList = append(ipList, ip)
}
}
}
// 节点
if !lists.ContainsInt64(nodeIds, accessLog.NsNodeId) {
nodeIds = append(nodeIds, accessLog.NsNodeId)
}
// 域名
if !lists.ContainsInt64(domainIds, accessLog.NsDomainId) {
domainIds = append(domainIds, accessLog.NsDomainId)
}
}
}
this.Data["hasMore"] = resp.HasMore
this.Data["nextRequestId"] = resp.RequestId
// 上一个requestId
this.Data["hasPrev"] = false
this.Data["lastRequestId"] = ""
if len(params.RequestId) > 0 {
this.Data["hasPrev"] = true
prevResp, err := this.RPC().NSAccessLogRPC().ListNSAccessLogs(this.UserContext(), &pb.ListNSAccessLogsRequest{
RequestId: params.RequestId,
NsClusterId: params.ClusterId,
NsNodeId: params.NodeId,
NsDomainId: 0,
NsRecordId: 0,
RecordType: params.RecordType,
Day: day,
Keyword: params.Keyword,
Size: size,
Reverse: true,
})
if err != nil {
this.ErrorPage(err)
return
}
if int64(len(prevResp.NsAccessLogs)) == size {
this.Data["lastRequestId"] = prevResp.RequestId
}
}
// 根据IP查询区域
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
// 节点信息
var nodeMap = map[int64]interface{}{} // node id => { ... }
for _, nodeId := range nodeIds {
nodeResp, err := this.RPC().NSNodeRPC().FindNSNode(this.UserContext(), &pb.FindNSNodeRequest{NsNodeId: nodeId})
if err != nil {
this.ErrorPage(err)
return
}
var node = nodeResp.NsNode
if node != nil {
nodeMap[node.Id] = maps.Map{
"id": node.Id,
"name": node.Name,
"cluster": maps.Map{
"id": node.NsCluster.Id,
"name": node.NsCluster.Name,
},
}
}
}
this.Data["nodes"] = nodeMap
// 域名信息
var domainMap = map[int64]interface{}{} // domain id => { ... }
for _, domainId := range domainIds {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
this.ErrorPage(err)
return
}
domain := domainResp.NsDomain
if domain != nil {
domainMap[domain.Id] = maps.Map{
"id": domain.Id,
"name": domain.Name,
}
}
}
this.Data["domains"] = domainMap
// 所有记录类型
this.Data["recordTypes"] = dnsconfigs.FindAllRecordTypeDefinitions()
this.Show()
}

View File

@@ -0,0 +1,16 @@
package logs
import (
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Data("teaMenu", "ns").
Data("teaSubMenu", "accessLog").
Prefix("/ns/clusters/accessLogs").
Get("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,195 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"strings"
)
type CreateRecordsAction struct {
actionutils.ParentAction
}
func (this *CreateRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("createRecords")
}
func (this *CreateRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
defaultTTL, err := nsutils.FindDefaultTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
this.Data["defaultTTL"] = defaultTTL
this.Show()
}
func (this *CreateRecordsAction) RunPost(params struct {
Names string
RecordsJSON []byte
RemoveOld bool
RemoveAll bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogCreateNSRecordsBatch)
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
// 检查记录
type record struct {
Name string `json:"name"`
Type string `json:"type"`
Value string `json:"value"`
RouteCodes []string `json:"routeCodes"`
TTL any `json:"ttl"`
}
var records = []*record{}
err = json.Unmarshal(params.RecordsJSON, &records)
if err != nil {
this.Fail("记录参数格式错误:" + err.Error())
return
}
if len(records) == 0 {
this.Fail("请添加至少一个记录")
}
var recordCountsMap = map[string]int32{} // record name_type = count
for _, record := range records {
recordCountsMap[record.Name+"_____"+record.Type]++
}
// 检查限额
for _, domainName := range domainNames {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomainWithName(this.UserContext(), &pb.FindNSDomainWithNameRequest{Name: domainName})
if err != nil {
this.ErrorPage(err)
return
}
if domainResp.NsDomain == nil {
continue
}
_, canCreate, err := nsutils.CheckRecordsQuota(this.UserContext(), domainResp.NsDomain.Id, int32(len(records)))
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("域名 '" + domainName + "' 记录数已超出限额,请购买套餐后重试")
}
// 检查负载均衡限额
if !params.RemoveOld && !params.RemoveAll {
for recordString, recordCount := range recordCountsMap {
var recordPieces = strings.Split(recordString, "_____")
var recordName = recordPieces[0]
var recordType = recordPieces[1]
_, canCreate, err = nsutils.CheckLoadBalanceRecordsQuota(this.UserContext(), domainResp.NsDomain.Id, recordName, recordType, recordCount)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("域名 '" + domainName + "' 记录 '" + recordName + "' 负载均衡数已超出限额,请购买套餐后重试")
}
}
}
}
// 最小TTL
minTTL, err := nsutils.FindMinTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
for _, record := range records {
message, ok := domainutils.ValidateRecordValue(record.Type, record.Value)
if !ok {
this.Fail("记录 '" + record.Name + "' 值校验失败:" + message)
}
// ttl
var recordInt = types.Int32(record.TTL)
if recordInt <= 0 {
recordInt = 600
}
record.TTL = recordInt
// 检查最小TTL
if recordInt < minTTL {
this.Fail("TTL不能小于" + types.String(minTTL) + "秒")
}
}
recordsJSON, err := json.Marshal(records)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSRecordRPC().CreateNSRecordsWithDomainNames(this.UserContext(), &pb.CreateNSRecordsWithDomainNamesRequest{
NsDomainNames: domainNames,
RecordsJSON: recordsJSON,
RemoveOld: params.RemoveOld,
RemoveAll: params.RemoveAll,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,83 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type DeleteDomainsAction struct {
actionutils.ParentAction
}
func (this *DeleteDomainsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("deleteDomains")
}
func (this *DeleteDomainsAction) RunGet(params struct{}) {
this.Show()
}
func (this *DeleteDomainsAction) RunPost(params struct {
Names string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogDeleteNSDomainsBatch, this.UserId())
if len(params.Names) == 0 {
this.FailField("names", "请输入要删除的域名")
}
var domainNames = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
name = strings.ToLower(name)
domainNames = append(domainNames, name)
}
if len(domainNames) == 0 {
this.FailField("names", "请输入要删除的域名")
}
// 检查域名是否存在
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
// 开始删除
_, err = this.RPC().NSDomainRPC().DeleteNSDomains(this.UserContext(), &pb.DeleteNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,92 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type DeleteRecordsAction struct {
actionutils.ParentAction
}
func (this *DeleteRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("deleteRecords")
}
func (this *DeleteRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *DeleteRecordsAction) RunPost(params struct {
Names string
SearchName string
SearchValue string
SearchType string
SearchRouteCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogDeleteNSRecordsBatch)
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
_, err = this.RPC().NSRecordRPC().DeleteNSRecordsWithDomainNames(this.UserContext(), &pb.DeleteNSRecordsWithDomainNamesRequest{
NsDomainNames: domainNames,
SearchName: params.SearchName,
SearchValue: params.SearchValue,
SearchType: params.SearchType,
SearchNSRouteCodes: params.SearchRouteCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,99 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type EnableRecordsAction struct {
actionutils.ParentAction
}
func (this *EnableRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("enableRecords")
}
func (this *EnableRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *EnableRecordsAction) RunPost(params struct {
Names string
SearchName string
SearchValue string
SearchType string
SearchRouteCodes []string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if params.IsOn {
defer this.CreateLogInfo(codes.NS_LogEnableNSRecordsBatch)
} else {
defer this.CreateLogInfo(codes.NS_LogDisableNSRecordsBatch)
}
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecordsIsOnWithDomainNames(this.UserContext(), &pb.UpdateNSRecordsIsOnWithDomainNamesRequest{
NsDomainNames: domainNames,
SearchName: params.SearchName,
SearchValue: params.SearchValue,
SearchType: params.SearchType,
SearchNSRouteCodes: params.SearchRouteCodes,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,219 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
type ImportRecordsAction struct {
actionutils.ParentAction
}
func (this *ImportRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("importRecords")
}
func (this *ImportRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *ImportRecordsAction) RunPost(params struct {
Records string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogImportRecordsBatch)
var allRecordTypes = []string{}
for _, t := range dnsconfigs.FindAllUserRecordTypeDefinitions() {
allRecordTypes = append(allRecordTypes, t.Type)
}
var pbRecords = []*pb.ImportNSRecordsRequest_Record{}
var reg = regexp.MustCompile(`\s+`)
var domainNames = []string{}
minTTL, err := nsutils.FindMinTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
for _, line := range strings.Split(params.Records, "\n") {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
// 域名 记录名 记录类型 记录值 TTL
var pieces = reg.Split(line, -1)
if len(pieces) < 4 {
this.Fail("'" + line + "' 格式错误,缺少必需项")
}
var domainName = strings.ToLower(strings.TrimSpace(pieces[0]))
var recordName = strings.ToLower(strings.TrimSpace(pieces[1]))
var recordType = strings.ToUpper(pieces[2])
var recordValue = strings.TrimSpace(pieces[3])
var recordTTLString = ""
if len(pieces) > 4 {
recordTTLString = pieces[4]
}
// validate
if len(domainName) == 0 {
this.Fail("'" + line + "' 缺少域名")
}
if !domainutils.ValidateDomainFormat(domainName) {
this.Fail("'" + line + "' 域名格式错误")
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
if len(recordName) > 0 && !domainutils.ValidateRecordName(recordName) {
this.Fail("'" + line + "' 记录名格式错误")
}
if len(recordType) == 0 {
this.Fail("'" + line + "' 缺少记录类型")
}
if !lists.ContainsString(allRecordTypes, recordType) {
this.Fail("'" + line + "' 不支持的记录类型 '" + recordType + "'")
}
message, ok := domainutils.ValidateRecordValue(recordType, recordValue)
if !ok {
this.Fail("'" + line + "' 记录值 '" + recordValue + "' 校验失败:" + message)
}
var recordTTL int32 = 600
if len(recordTTLString) > 0 {
if !regexp.MustCompile(`^\d+$`).MatchString(recordTTLString) {
this.Fail("'" + line + "' 错误的TTL '" + recordTTLString + "'")
}
recordTTL = types.Int32(recordTTLString)
if recordTTL > 10*365*86400 {
recordTTL = 10 * 365 * 86400
}
if recordTTL < minTTL {
this.Fail("TTL不能小于" + types.String(minTTL) + "秒")
}
}
pbRecords = append(pbRecords, &pb.ImportNSRecordsRequest_Record{
NsDomainName: domainName,
Name: recordName,
Type: recordType,
Value: recordValue,
Ttl: recordTTL,
})
}
if len(pbRecords) == 0 {
this.FailField("records", "请输入要导入的记录")
}
// 检查域名是否存在
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
// 检查限额
for _, domainName := range domainNames {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomainWithName(this.UserContext(), &pb.FindNSDomainWithNameRequest{Name: domainName})
if err != nil {
this.ErrorPage(err)
return
}
if domainResp.NsDomain == nil {
continue
}
var countRecords int32 = 0
for _, record := range pbRecords {
if record.NsDomainName == domainName {
countRecords++
}
}
if countRecords == 0 {
continue
}
_, canCreate, err := nsutils.CheckRecordsQuota(this.UserContext(), domainResp.NsDomain.Id, countRecords)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("域名 '" + domainName + "' 记录数已超出限额,请购买套餐后重试")
}
// 检查负载均衡限额
var recordCountsMap = map[string]int32{} // record name_type = count
for _, record := range pbRecords {
if record.NsDomainName == domainName {
recordCountsMap[record.Name+"_____"+record.Type]++
}
}
for recordString, recordCount := range recordCountsMap {
var recordPieces = strings.Split(recordString, "_____")
var recordName = recordPieces[0]
var recordType = recordPieces[1]
_, canCreate, err = nsutils.CheckLoadBalanceRecordsQuota(this.UserContext(), domainResp.NsDomain.Id, recordName, recordType, recordCount)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("域名 '" + domainName + "' 记录 '" + recordName + "' 负载均衡数已超出限额,请购买套餐后重试")
}
}
}
_, err = this.RPC().NSRecordRPC().ImportNSRecords(this.UserContext(), &pb.ImportNSRecordsRequest{
NsRecords: pbRecords,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,102 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("index")
}
func (this *IndexAction) RunGet(params struct{}) {
this.Show()
}
func (this *IndexAction) RunPost(params struct {
Names string // 如果批量的话
GroupId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var groupIds = []int64{}
if params.GroupId > 0 {
groupIds = append(groupIds, params.GroupId)
}
defer this.CreateLogInfo(codes.NS_LogCreateNSRecordsBatch)
params.Must.
Field("names", params.Names).
Require("请输入域名")
var names = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
if !domainutils.ValidateDomainFormat(name) {
this.Fail("域名 '" + name + "' 格式不正确")
}
if !lists.ContainsString(names, name) {
names = append(names, name)
}
}
if len(names) == 0 {
this.FailField("names", "请输入域名")
return
}
// 检查限额
_, canCreate, err := nsutils.CheckDomainsQuote(this.UserContext(), int32(len(names)))
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("域名数已超出限额,请购买套餐后重试")
}
// 检查域名是否已经存在
{
existResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: names,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经存在,无法重复添加")
return
}
}
_, err = this.RPC().NSDomainRPC().CreateNSDomains(this.UserContext(), &pb.CreateNSDomainsRequest{
Names: names,
NsDomainGroupIds: groupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,109 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package batch
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type UpdateRecordsAction struct {
actionutils.ParentAction
}
func (this *UpdateRecordsAction) Init() {
this.Nav("", "", "batch")
this.SecondMenu("updateRecords")
}
func (this *UpdateRecordsAction) RunGet(params struct{}) {
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}
func (this *UpdateRecordsAction) RunPost(params struct {
Names string
SearchName string
SearchValue string
SearchType string
SearchRouteCodes []string
NewName string
NewValue string
NewType string
NewRouteCodes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NS_LogUpdateNSRecordsBatch)
// 检查域名是否已经创建
var domainNames = []string{}
for _, domainName := range strings.Split(params.Names, "\n") {
domainName = strings.ToLower(strings.TrimSpace(domainName))
if len(domainName) == 0 {
continue
}
if !lists.ContainsString(domainNames, domainName) {
domainNames = append(domainNames, domainName)
}
}
if len(domainNames) == 0 {
this.FailField("names", "请输入域名")
}
duplicateResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: domainNames,
})
if err != nil {
this.ErrorPage(err)
return
}
var existingDomainNames = duplicateResp.ExistingNames
var notExistingDomainNames = []string{}
for _, domainName := range domainNames {
if !lists.ContainsString(existingDomainNames, domainName) {
notExistingDomainNames = append(notExistingDomainNames, domainName)
}
}
if len(notExistingDomainNames) > 0 {
this.FailField("names", "有以下域名不存在:"+strings.Join(notExistingDomainNames, "")+"。请检查域名是否已删除或用户是否已选择正确。")
}
if len(params.NewValue) > 0 {
message, ok := domainutils.ValidateRecordValue(params.NewType, params.NewValue)
if !ok {
this.Fail("新的记录值 '" + params.NewValue + "' 填写错误:" + message)
}
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecordsWithDomainNames(this.UserContext(), &pb.UpdateNSRecordsWithDomainNamesRequest{
NsDomainNames: domainNames,
SearchName: params.SearchName,
SearchValue: params.SearchValue,
SearchType: params.SearchType,
SearchNSRouteCodes: params.SearchRouteCodes,
NewName: params.NewName,
NewValue: params.NewValue,
NewType: params.NewType,
NewNSRouteCodes: params.NewRouteCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,187 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package domains
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"strings"
)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
this.Nav("", "", "create")
}
func (this *CreateAction) RunGet(params struct{}) {
userConfigResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.UserContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeNSUserConfig})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["configIsValid"] = false
if len(userConfigResp.ValueJSON) > 0 {
var config = &dnsconfigs.NSUserConfig{}
err = json.Unmarshal(userConfigResp.ValueJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
if config.DefaultClusterId > 0 {
this.Data["configIsValid"] = true
}
}
// 套餐限制
maxDomains, canCreate, err := nsutils.CheckDomainsQuote(this.UserContext(), 1)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quotaMaxDomains"] = maxDomains
this.Data["quotaCanCreate"] = canCreate
this.Show()
}
func (this *CreateAction) RunPost(params struct {
AddingType string
Name string
Names string // 如果批量的话
UserId int64
GroupId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 检查套餐限制
_, canCreate, err := nsutils.CheckDomainsQuote(this.UserContext(), 1)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("域名个数已超出最大限额")
}
var groupIds = []int64{}
if params.GroupId > 0 {
groupIds = append(groupIds, params.GroupId)
}
if params.AddingType == "batch" { // 批量添加
defer this.CreateLogInfo(codes.NS_LogCreateNSDomainsBatch)
params.Must.
Field("names", params.Names).
Require("请输入域名")
var names = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
if !domainutils.ValidateDomainFormat(name) {
this.Fail("域名 '" + name + "' 格式不正确")
}
if !lists.ContainsString(names, name) {
names = append(names, name)
}
}
if len(names) == 0 {
this.FailField("names", "请输入域名")
return
}
// 检查域名是否已经存在
{
existResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: names,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经存在,无法重复添加")
return
}
}
_, err := this.RPC().NSDomainRPC().CreateNSDomains(this.UserContext(), &pb.CreateNSDomainsRequest{
UserId: params.UserId,
Names: names,
NsDomainGroupIds: groupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
} else { // 单个添加
var domainId int64
defer func() {
this.CreateLogInfo(codes.NSDomain_LogCreateNSDomain, domainId)
}()
params.Name = strings.ToLower(params.Name)
params.Must.
Field("name", params.Name).
Require("请输入域名").
Expect(func() (message string, success bool) {
success = domainutils.ValidateDomainFormat(params.Name)
if !success {
message = "请输入正确的域名"
}
return
})
// 检查域名是否已经存在
{
existResp, err := this.RPC().NSDomainRPC().ExistNSDomains(this.UserContext(), &pb.ExistNSDomainsRequest{
Names: []string{params.Name},
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(existResp.ExistingNames) > 0 {
this.Fail("域名 " + strings.Join(existResp.ExistingNames, ", ") + " 已经存在,无法重复添加")
return
}
}
createResp, err := this.RPC().NSDomainRPC().CreateNSDomain(this.UserContext(), &pb.CreateNSDomainRequest{
UserId: params.UserId,
Name: params.Name,
NsDomainGroupIds: groupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
domainId = createResp.NsDomainId
this.Success()
}
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package domains
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
DomainId int64
}) {
defer this.CreateLogInfo(codes.NSDomain_LogDeleteNSDomain, params.DomainId)
_, err := this.RPC().NSDomainRPC().DeleteNSDomain(this.UserContext(), &pb.DeleteNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domains
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeletePageAction struct {
actionutils.ParentAction
}
func (this *DeletePageAction) RunPost(params struct {
DomainIds []int64
}) {
defer this.CreateLogInfo(codes.NS_LogDeleteNSDomainsBatch, this.UserId())
if len(params.DomainIds) == 0 {
this.Success()
return
}
for _, domainId := range params.DomainIds {
_, err := this.RPC().NSDomainRPC().DeleteNSDomain(this.UserContext(), &pb.DeleteNSDomainRequest{
NsDomainId: domainId,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,22 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domain
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
DomainId int64
}) {
this.RedirectURL("/ns/domains/records?domainId=" + types.String(params.DomainId))
}

View File

@@ -0,0 +1,66 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domain
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"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 UpdateStatusPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateStatusPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateStatusPopupAction) RunGet(params struct {
DomainId int64
}) {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("NSDomain", params.DomainId)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"status": domain.Status,
}
this.Data["statusList"] = dnsconfigs.FindAllNSDomainStatusList()
this.Show()
}
func (this *UpdateStatusPopupAction) RunPost(params struct {
DomainId int64
Status string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomainStatus, params.DomainId, params.Status)
_, err := this.RPC().NSDomainRPC().UpdateNSDomainStatus(this.UserContext(), &pb.UpdateNSDomainStatusRequest{
NsDomainId: params.DomainId,
Status: params.Status,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,139 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domain
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"strings"
)
type VerifyPopupAction struct {
actionutils.ParentAction
}
func (this *VerifyPopupAction) Init() {
this.Nav("", "", "")
}
func (this *VerifyPopupAction) RunGet(params struct {
DomainId int64
}) {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"status": domain.Status,
}
// 当前用户可以使用的DNS Hosts
if domain.NsCluster == nil || domain.NsCluster.Id <= 0 {
this.WriteString("当前域名(" + domain.Name + ")所在集群已被删除,请删除当前域名后重新添加")
return
}
var hosts []string
hostsResp, err := this.RPC().NSClusterRPC().FindNSClusterHosts(this.UserContext(), &pb.FindNSClusterHostsRequest{
NsClusterId: domain.NsCluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
hosts = hostsResp.Hosts
if hosts == nil {
hosts = []string{}
}
this.Data["hosts"] = hosts
// TXT记录
txtResp, err := this.RPC().NSDomainRPC().FindNSDomainVerifyingInfo(this.UserContext(), &pb.FindNSDomainVerifyingInfoRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["requireTXT"] = txtResp.RequireTXT
this.Data["txt"] = txtResp.Txt
this.Data["txtExpiresTime"] = timeutil.FormatTime("Y-m-d H:i:s", txtResp.ExpiresAt)
this.Show()
}
func (this *VerifyPopupAction) RunPost(params struct {
DomainId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["isOk"] = false
this.Data["message"] = ""
this.Data["rawErrorMessage"] = ""
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
if domain.Status == dnsconfigs.NSDomainStatusVerified {
this.Data["message"] = "已验证"
this.Success()
return
}
if domain.Status != dnsconfigs.NSDomainStatusNone {
this.Data["message"] = "当前状态为:" + dnsconfigs.NSDomainStatusName(domain.Status) + ",不允许验证"
this.Success()
return
}
// 提交验证
verifyResp, err := this.RPC().NSDomainRPC().VerifyNSDomain(this.UserContext(), &pb.VerifyNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if !verifyResp.IsOk {
var message = domainutils.VerifyMessageWithCode(verifyResp.ErrorCode)
if len(verifyResp.CurrentTXTValues) > 0 {
message += "当前TXT解析结果为" + strings.Join(verifyResp.CurrentTXTValues, ", ")
} else if len(verifyResp.CurrentNSValues) > 0 {
message += "当前NS解析结果为" + strings.Join(verifyResp.CurrentNSValues, ", ")
}
message += "。"
this.Data["message"] = message
this.Data["rawErrorMessage"] = verifyResp.ErrorMessage
this.Success()
return
}
this.Data["isOk"] = true
this.Success()
}

View File

@@ -0,0 +1,161 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package domainutils
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeUser/internal/rpc"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
// InitDomain 初始化域名信息
func InitDomain(parent *actionutils.ParentAction, domainId int64) error {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
domainResp, err := rpcClient.NSDomainRPC().FindNSDomain(parent.UserContext(), &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
return err
}
var domain = domainResp.NsDomain
if domain == nil {
return errors.New("InitDomain: can not find domain with id '" + types.String(domainId) + "'")
}
// 记录数量
countRecordsResp, err := rpcClient.NSRecordRPC().CountAllNSRecords(parent.UserContext(), &pb.CountAllNSRecordsRequest{
NsDomainId: domainId,
})
if err != nil {
return err
}
var countRecords = countRecordsResp.Count
// Key数量
countKeysResp, err := rpcClient.NSKeyRPC().CountAllNSKeys(parent.UserContext(), &pb.CountAllNSKeysRequest{
NsDomainId: domainId,
})
if err != nil {
return err
}
var countKeys = countKeysResp.Count
// 健康检查
var healthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(domain.RecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(domain.RecordsHealthCheckJSON, healthCheckConfig)
if err != nil {
return err
}
}
parent.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"countRecords": countRecords,
"countKeys": countKeys,
"enableHealthCheck": healthCheckConfig.IsOn,
}
return nil
}
// VerifyMessageWithCode 通过代号获取验证消息
func VerifyMessageWithCode(code string) string {
var message = ""
switch code {
case "DomainNotFound":
message = "无法找到域名"
case "InvalidStatus":
message = "在当前域名状态下无法提交验证"
case "InvalidDNSHosts":
message = "没有设置正确的DNS主机地址或者NS记录尚未生效"
case "TXTNotFound":
message = "没有找到TXT记录或者TXT记录尚未生效"
case "InvalidTXT":
message = "没有设置正确的TXT记录或者TXT记录尚未生效"
case "TXTExpired":
message = "验证信息已失效,请刷新后重试"
}
return message
}
// CheckHealthCheckPermission 检查是否能够修改健康检查
func CheckHealthCheckPermission(rpcClient *rpc.RPCClient, ctx context.Context, domainId int64) (canUpdate bool, err error) {
if domainId <= 0 {
return false, nil
}
domainResp, err := rpcClient.NSDomainRPC().FindNSDomain(ctx, &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
return false, err
}
var domain = domainResp.NsDomain
if domain == nil {
return false, errors.New("the domain is not found")
}
if domain.UserId > 0 {
// 检查套餐
userPlanResp, err := rpcClient.NSUserPlanRPC().FindNSUserPlan(ctx, &pb.FindNSUserPlanRequest{
UserId: domain.UserId,
NsUserPlanId: 0,
})
if err != nil {
return false, err
}
var userPlan = userPlanResp.NsUserPlan
if userPlan == nil || userPlan.NsPlanId == 0 || userPlan.DayTo < timeutil.Format("Ymd") {
return checkHealthCheckInUserSetting(rpcClient, ctx)
} else {
planResp, err := rpcClient.NSPlanRPC().FindNSPlan(ctx, &pb.FindNSPlanRequest{NsPlanId: userPlan.NsPlanId})
if err != nil {
return false, err
}
if planResp.NsPlan == nil || len(planResp.NsPlan.ConfigJSON) == 0 {
return checkHealthCheckInUserSetting(rpcClient, ctx)
}
var planConfig = dnsconfigs.DefaultNSPlanConfig()
err = json.Unmarshal(planResp.NsPlan.ConfigJSON, planConfig)
if err != nil {
return false, err
}
return planConfig.SupportHealthCheck, nil
}
} else {
return checkHealthCheckInUserSetting(rpcClient, ctx)
}
}
// 检查用户基础设置
func checkHealthCheckInUserSetting(rpcClient *rpc.RPCClient, ctx context.Context) (bool, error) {
resp, err := rpcClient.SysSettingRPC().ReadSysSetting(ctx, &pb.ReadSysSettingRequest{
Code: systemconfigs.SettingCodeNSUserConfig,
})
if err != nil {
return false, err
}
var config = dnsconfigs.NewNSUserConfig()
if len(resp.ValueJSON) > 0 {
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
return false, err
}
if config.DefaultPlanConfig == nil {
config.DefaultPlanConfig = dnsconfigs.DefaultNSUserPlanConfig()
}
return config.DefaultPlanConfig.SupportHealthCheck, nil
}
return false, nil
}

View File

@@ -0,0 +1,51 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package groups
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var groupId int64 = 0
defer func() {
this.CreateLogInfo(codes.NSDomainGroup_LogCreateNSDomainGroup, groupId)
}()
params.Must.
Field("name", params.Name).
Require("请输入分组名称")
createResp, err := this.RPC().NSDomainGroupRPC().CreateNSDomainGroup(this.UserContext(), &pb.CreateNSDomainGroupRequest{
Name: params.Name,
})
if err != nil {
this.ErrorPage(err)
return
}
groupId = createResp.NsDomainGroupId
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package group
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
GroupId int64
}) {
defer this.CreateLogInfo(codes.NSDomainGroup_LogDeleteNSDomainGroup, params.GroupId)
_, err := this.RPC().NSDomainGroupRPC().DeleteNSDomainGroup(this.UserContext(), &pb.DeleteNSDomainGroupRequest{NsDomainGroupId: params.GroupId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,40 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package group
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
GroupId int64
}) {
groupResp, err := this.RPC().NSDomainGroupRPC().FindNSDomainGroup(this.UserContext(), &pb.FindNSDomainGroupRequest{NsDomainGroupId: params.GroupId})
if err != nil {
this.ErrorPage(err)
return
}
var group = groupResp.NsDomainGroup
if group == nil {
this.NotFound("nsDomainGroup", params.GroupId)
return
}
this.Data["group"] = maps.Map{
"id": group.Id,
"name": group.Name,
"isOn": group.IsOn,
}
this.Show()
}

View File

@@ -0,0 +1,69 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package group
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"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 UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
GroupId int64
}) {
groupResp, err := this.RPC().NSDomainGroupRPC().FindNSDomainGroup(this.UserContext(), &pb.FindNSDomainGroupRequest{NsDomainGroupId: params.GroupId})
if err != nil {
this.ErrorPage(err)
return
}
var group = groupResp.NsDomainGroup
if group == nil {
this.NotFound("nsDomainGroup", params.GroupId)
return
}
this.Data["group"] = maps.Map{
"id": group.Id,
"name": group.Name,
"isOn": group.IsOn,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
GroupId int64
Name string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomainGroup_LogUpdateNSDomainGroup, params.GroupId)
params.Must.
Field("name", params.Name).
Require("请输入分组名称")
_, err := this.RPC().NSDomainGroupRPC().UpdateNSDomainGroup(this.UserContext(), &pb.UpdateNSDomainGroupRequest{
NsDomainGroupId: params.GroupId,
Name: params.Name,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,36 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package groups
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
groupsResp, err := this.RPC().NSDomainGroupRPC().FindAllNSDomainGroups(this.UserContext(), &pb.FindAllNSDomainGroupsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var groupMaps = []maps.Map{}
for _, group := range groupsResp.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
"isOn": group.IsOn,
})
}
this.Data["groups"] = groupMaps
this.Show()
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package groups
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
type OptionsAction struct {
actionutils.ParentAction
}
func (this *OptionsAction) RunPost(params struct {
UserId int64
}) {
groupsResp, err := this.RPC().NSDomainGroupRPC().FindAllAvailableNSDomainGroups(this.UserContext(), &pb.FindAllAvailableNSDomainGroupsRequest{
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var groupMaps = []maps.Map{}
for _, group := range groupsResp.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
})
}
this.Data["groups"] = groupMaps
this.Success()
}

View File

@@ -0,0 +1,103 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domains
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
)
type HealthCheckAction struct {
actionutils.ParentAction
}
func (this *HealthCheckAction) Init() {
this.Nav("", "", "healthCheck")
}
func (this *HealthCheckAction) RunGet(params struct {
DomainId int64
}) {
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["domainId"] = params.DomainId
// 检查当前用户权限
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.UserContext(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["canUpdate"] = canUpdate
// 健康检查配置
healthCheckResp, err := this.RPC().NSDomainRPC().FindNSDomainRecordsHealthCheck(this.UserContext(), &pb.FindNSDomainRecordsHealthCheckRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var healthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(healthCheckResp.NsDomainRecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(healthCheckResp.NsDomainRecordsHealthCheckJSON, healthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = healthCheckConfig
this.Show()
}
func (this *HealthCheckAction) RunPost(params struct {
DomainId int64
RecordsHealthCheckJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomainHealthCheck, params.DomainId)
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.UserContext(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
if !canUpdate {
this.Fail("当前用户没有权限修改健康检查设置")
return
}
var config = dnsconfigs.NewNSRecordsHealthCheckConfig()
err = json.Unmarshal(params.RecordsHealthCheckJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
err = config.Init()
if err != nil {
this.Fail("校验配置失败:" + err.Error())
return
}
_, err = this.RPC().NSDomainRPC().UpdateNSDomainRecordsHealthCheck(this.UserContext(), &pb.UpdateNSDomainRecordsHealthCheckRequest{
NsDomainId: params.DomainId,
NsDomainRecordsHealthCheckJSON: params.RecordsHealthCheckJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,72 @@
package domains
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct {
GroupId int64
Keyword string
}) {
this.Data["groupId"] = params.GroupId
this.Data["keyword"] = params.Keyword
// 分页
countResp, err := this.RPC().NSDomainRPC().CountAllNSDomains(this.UserContext(), &pb.CountAllNSDomainsRequest{
NsDomainGroupId: params.GroupId,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// 列表
domainsResp, err := this.RPC().NSDomainRPC().ListNSDomains(this.UserContext(), &pb.ListNSDomainsRequest{
Keyword: params.Keyword,
NsDomainGroupId: params.GroupId,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var domainMaps = []maps.Map{}
for _, domain := range domainsResp.NsDomains {
// 分组信息
var groupMaps = []maps.Map{}
for _, group := range domain.NsDomainGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
"userId": group.UserId,
})
}
domainMaps = append(domainMaps, maps.Map{
"id": domain.Id,
"name": domain.Name,
"status": domain.Status,
"statusName": dnsconfigs.NSDomainStatusName(domain.Status),
"isOn": domain.IsOn,
"groups": groupMaps,
})
}
this.Data["domains"] = domainMaps
this.Show()
}

View File

@@ -0,0 +1,87 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package keys
import (
"encoding/base64"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"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 CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
DomainId int64
}) {
this.Data["domainId"] = params.DomainId
// 所有算法
var algorithmMaps = []maps.Map{}
for _, algo := range dnsconfigs.FindAllKeyAlgorithmTypes() {
algorithmMaps = append(algorithmMaps, maps.Map{
"name": algo.Name,
"code": algo.Code,
})
}
this.Data["algorithms"] = algorithmMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
DomainId int64
Name string
Algo string
Secret string
SecretType string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var keyId int64 = 0
defer func() {
this.CreateLogInfo(codes.NSKey_LogCreateNSKey, keyId)
}()
params.Must.
Field("name", params.Name).
Require("请输入密钥名称").
Field("algo", params.Algo).
Require("请选择算法").
Field("secret", params.Secret).
Require("请输入密码")
// 校验密码
if params.SecretType == dnsconfigs.NSKeySecretTypeBase64 {
_, err := base64.StdEncoding.DecodeString(params.Secret)
if err != nil {
this.FailField("secret", "请输入BASE64格式的密码或者选择明文")
}
}
createResp, err := this.RPC().NSKeyRPC().CreateNSKey(this.UserContext(), &pb.CreateNSKeyRequest{
NsDomainId: params.DomainId,
NsZoneId: 0,
Name: params.Name,
Algo: params.Algo,
Secret: params.Secret,
SecretType: params.SecretType,
})
if err != nil {
this.ErrorPage(err)
return
}
keyId = createResp.NsKeyId
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package keys
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
KeyId int64
}) {
defer this.CreateLogInfo(codes.NSKey_LogDeleteNSKey, params.KeyId)
_, err := this.RPC().NSKeyRPC().DeleteNSKey(this.UserContext(), &pb.DeleteNSKeyRequest{NsKeyId: params.KeyId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,29 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package keys
import (
"encoding/base64"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/rands"
)
type GenerateSecretAction struct {
actionutils.ParentAction
}
func (this *GenerateSecretAction) RunPost(params struct {
SecretType string
}) {
switch params.SecretType {
case dnsconfigs.NSKeySecretTypeClear:
this.Data["secret"] = rands.HexString(128)
case dnsconfigs.NSKeySecretTypeBase64:
this.Data["secret"] = base64.StdEncoding.EncodeToString([]byte(rands.HexString(128)))
default:
this.Data["secret"] = rands.HexString(128)
}
this.Success()
}

View File

@@ -0,0 +1,68 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package keys
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "key")
}
func (this *IndexAction) RunGet(params struct {
DomainId int64
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
// 数量
countResp, err := this.RPC().NSKeyRPC().CountAllNSKeys(this.UserContext(), &pb.CountAllNSKeysRequest{
NsDomainId: params.DomainId,
NsZoneId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// 列表
keysResp, err := this.RPC().NSKeyRPC().ListNSKeys(this.UserContext(), &pb.ListNSKeysRequest{
NsDomainId: params.DomainId,
NsZoneId: 0,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var keyMaps = []maps.Map{}
for _, key := range keysResp.NsKeys {
keyMaps = append(keyMaps, maps.Map{
"id": key.Id,
"name": key.Name,
"secret": key.Secret,
"secretTypeName": dnsconfigs.FindKeySecretTypeName(key.SecretType),
"algoName": dnsconfigs.FindKeyAlgorithmTypeName(key.Algo),
"isOn": key.IsOn,
})
}
this.Data["keys"] = keyMaps
this.Show()
}

View File

@@ -0,0 +1,101 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package keys
import (
"encoding/base64"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"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 UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
KeyId int64
}) {
keyResp, err := this.RPC().NSKeyRPC().FindNSKey(this.UserContext(), &pb.FindNSKeyRequest{NsKeyId: params.KeyId})
if err != nil {
this.ErrorPage(err)
return
}
var key = keyResp.NsKey
if key == nil {
return
}
this.Data["key"] = maps.Map{
"id": key.Id,
"name": key.Name,
"algo": key.Algo,
"secret": key.Secret,
"secretType": key.SecretType,
"isOn": key.IsOn,
}
// 所有算法
var algorithmMaps = []maps.Map{}
for _, algo := range dnsconfigs.FindAllKeyAlgorithmTypes() {
algorithmMaps = append(algorithmMaps, maps.Map{
"name": algo.Name,
"code": algo.Code,
})
}
this.Data["algorithms"] = algorithmMaps
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
KeyId int64
Name string
Algo string
Secret string
SecretType string
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
this.CreateLogInfo(codes.NSKey_LogUpdateNSKey, params.KeyId)
params.Must.
Field("name", params.Name).
Require("请输入密钥名称").
Field("algo", params.Algo).
Require("请选择算法").
Field("secret", params.Secret).
Require("请输入密码")
// 校验密码
if params.SecretType == dnsconfigs.NSKeySecretTypeBase64 {
_, err := base64.StdEncoding.DecodeString(params.Secret)
if err != nil {
this.FailField("secret", "请输入BASE64格式的密码或者选择明文")
}
}
_, err := this.RPC().NSKeyRPC().UpdateNSKey(this.UserContext(), &pb.UpdateNSKeyRequest{
NsKeyId: params.KeyId,
Name: params.Name,
Algo: params.Algo,
Secret: params.Secret,
SecretType: params.SecretType,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,246 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package records
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strings"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
DomainId int64
}) {
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
}
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
// 检查限额
maxRecords, canCreate, err := nsutils.CheckRecordsQuota(this.UserContext(), params.DomainId, 1)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quotaMaxRecords"] = maxRecords
this.Data["quotaCanCreate"] = canCreate
defaultTTL, err := nsutils.FindDefaultTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
this.Data["defaultTTL"] = defaultTTL
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
AddingType string
DomainId int64
Name string
Names string
Type string
Value string
MxPriority int32
SrvPriority int32
SrvWeight int32
SrvPort int32
CaaFlag int32
CaaTag string
Ttl int32
Description string
RouteCodes []string
Weight int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 检查限额
maxRecords, canCreate, err := nsutils.CheckRecordsQuota(this.UserContext(), params.DomainId, 1)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("当前域名记录数已超出最大限额(" + types.String(maxRecords) + "个)")
}
maxLoadBalanceRecords, canCreate, err := nsutils.CheckLoadBalanceRecordsQuota(this.UserContext(), params.DomainId, params.Name, params.Type, 1)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("当前记录 '" + params.Name + "' 负载均衡数已超出最大限额(" + types.String(maxLoadBalanceRecords) + "个)")
}
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.Fail("找不到要修改的域名")
}
var domainName = domain.Name
if params.AddingType == "batch" { // 批量添加
defer this.CreateLogInfo(codes.NSRecord_LogCreateNSRecordsBatch)
if len(params.Names) == 0 {
this.FailField("names", "请输入记录名")
}
var names = []string{}
for _, name := range strings.Split(params.Names, "\n") {
name = strings.TrimSpace(name)
if len(name) == 0 {
continue
}
if !domainutils.ValidateRecordName(name) {
this.FailField("names", "记录名 '"+name+"' 格式不正确,请修改后提交")
}
// 去掉主域名
name = strings.TrimSuffix(name, "."+domainName)
names = append(names, name)
}
// 校验记录值
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
if !ok {
this.FailField("value", "记录值错误:"+message)
}
// SRV
if params.Type == dnsconfigs.RecordTypeSRV {
if params.SrvPort < 0 || params.SrvPort > 65535 {
this.FailField("srvPort", "请输入有效的端口号")
}
}
// 检查TTL
minTTL, err := nsutils.FindMinTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
if params.Ttl < minTTL {
this.Fail("TTL不能小于" + types.String(minTTL) + "秒")
}
_, err = this.RPC().NSRecordRPC().CreateNSRecords(this.UserContext(), &pb.CreateNSRecordsRequest{
NsDomainId: params.DomainId,
Description: params.Description,
Names: names,
Type: params.Type,
Value: params.Value,
MxPriority: params.MxPriority,
SrvPriority: params.SrvPriority,
SrvWeight: params.SrvWeight,
SrvPort: params.SrvPort,
CaaFlag: params.CaaFlag,
CaaTag: params.CaaTag,
Ttl: params.Ttl,
NsRouteCodes: params.RouteCodes,
Weight: params.Weight,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
} else { // 单个添加
var recordId int64
defer func() {
this.CreateLogInfo(codes.NSRecord_LogCreateNSRecord, recordId)
}()
// 校验记录名
if !domainutils.ValidateRecordName(params.Name) {
this.FailField("name", "请输入正确的记录名")
}
// 校验记录值
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
if !ok {
this.FailField("value", "记录值错误:"+message)
}
// 检查TTL
minTTL, err := nsutils.FindMinTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
if params.Ttl < minTTL {
this.Fail("TTL不能小于" + types.String(minTTL) + "秒")
}
createResp, err := this.RPC().NSRecordRPC().CreateNSRecord(this.UserContext(), &pb.CreateNSRecordRequest{
NsDomainId: params.DomainId,
Description: params.Description,
Name: params.Name,
Type: params.Type,
Value: params.Value,
MxPriority: params.MxPriority,
SrvPriority: params.SrvPriority,
SrvWeight: params.SrvWeight,
SrvPort: params.SrvPort,
CaaFlag: params.CaaFlag,
CaaTag: params.CaaTag,
Ttl: params.Ttl,
NsRouteCodes: params.RouteCodes,
Weight: params.Weight,
})
if err != nil {
this.ErrorPage(err)
return
}
recordId = createResp.NsRecordId
this.Success()
}
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package records
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
RecordId int64
}) {
defer this.CreateLogInfo(codes.NSRecord_LogDeleteNSRecord, params.RecordId)
_, err := this.RPC().NSRecordRPC().DeleteNSRecord(this.UserContext(), &pb.DeleteNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,154 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package records
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type HealthCheckPopupAction struct {
actionutils.ParentAction
}
func (this *HealthCheckPopupAction) Init() {
this.Nav("", "", "")
}
func (this *HealthCheckPopupAction) RunGet(params struct {
RecordId int64
}) {
// 记录信息
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.UserContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil || record.NsDomain == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
// 记录健康检查
var recordHealthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
if len(record.HealthCheckJSON) > 0 {
err = json.Unmarshal(record.HealthCheckJSON, recordHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["record"] = maps.Map{
"id": record.Id,
"name": record.Name,
"type": record.Type,
"value": record.Value,
"healthCheckConfig": recordHealthCheckConfig,
}
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: record.NsDomain.Id})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", record.NsDomain.Id)
return
}
// 域名健康检查
var domainHealthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
if len(domain.RecordsHealthCheckJSON) > 0 {
err = json.Unmarshal(domain.RecordsHealthCheckJSON, domainHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["domain"] = maps.Map{
"name": domain.Name,
"healthCheckConfig": domainHealthCheckConfig,
}
// 检查权限
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.UserContext(), record.NsDomain.Id)
if err != nil {
this.ErrorPage(err)
return
}
if !canUpdate {
this.Fail("permission denied")
return
}
this.Show()
}
func (this *HealthCheckPopupAction) RunPost(params struct {
RecordId int64
RecordHealthCheckJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSRecord_LogUpdateNSRecordHealthCheck, params.RecordId)
// 检查权限
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.UserContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil || record.NsDomain == nil {
this.Fail("找不到要修改的记录")
return
}
canUpdate, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.UserContext(), record.NsDomain.Id)
if err != nil {
this.ErrorPage(err)
return
}
if !canUpdate {
this.Fail("permission denied")
return
}
var healthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
err = json.Unmarshal(params.RecordHealthCheckJSON, healthCheckConfig)
if err != nil {
this.Fail("解析配置失败:" + err.Error())
return
}
err = healthCheckConfig.Init()
if err != nil {
this.Fail("校验配置失败:" + err.Error())
return
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecordHealthCheck(this.UserContext(), &pb.UpdateNSRecordHealthCheckRequest{
NsRecordId: params.RecordId,
NsRecordHealthCheckJSON: params.RecordHealthCheckJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,199 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package records
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "record")
}
func (this *IndexAction) RunGet(params struct {
DomainId int64
Type string
Keyword string
RouteCode string
NameOrder string
TypeOrder string
TtlOrder string
UpOrder string
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["type"] = params.Type
this.Data["keyword"] = params.Keyword
this.Data["routeCode"] = params.RouteCode
// 域名的健康检查
domainHealthCheckIsEnabled, err := domainutils.CheckHealthCheckPermission(this.RPC(), this.UserContext(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
var domainHealthCheckIsOn = false
if domainHealthCheckIsEnabled {
domainHealthCheckResp, err := this.RPC().NSDomainRPC().FindNSDomainRecordsHealthCheck(this.UserContext(), &pb.FindNSDomainRecordsHealthCheckRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
if len(domainHealthCheckResp.NsDomainRecordsHealthCheckJSON) > 0 {
var domainHealthCheckConfig = dnsconfigs.NewNSRecordsHealthCheckConfig()
err = json.Unmarshal(domainHealthCheckResp.NsDomainRecordsHealthCheckJSON, domainHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
domainHealthCheckIsOn = domainHealthCheckConfig.IsOn
}
}
this.Data["domainHealthCheckIsOn"] = domainHealthCheckIsOn
// 记录
countResp, err := this.RPC().NSRecordRPC().CountAllNSRecords(this.UserContext(), &pb.CountAllNSRecordsRequest{
NsDomainId: params.DomainId,
Type: params.Type,
NsRouteCode: params.RouteCode,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
recordsResp, err := this.RPC().NSRecordRPC().ListNSRecords(this.UserContext(), &pb.ListNSRecordsRequest{
NsDomainId: params.DomainId,
Type: params.Type,
NsRouteCode: params.RouteCode,
Keyword: params.Keyword,
NameAsc: params.NameOrder == "asc",
NameDesc: params.NameOrder == "desc",
TypeAsc: params.TypeOrder == "asc",
TypeDesc: params.TypeOrder == "desc",
TtlAsc: params.TtlOrder == "asc",
TtlDesc: params.TtlOrder == "desc",
UpAsc: params.UpOrder == "asc",
UpDesc: params.UpOrder == "desc",
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var recordMaps = []maps.Map{}
var recordIds = []int64{}
for _, record := range recordsResp.NsRecords {
recordIds = append(recordIds, record.Id)
}
// 统计
var statMap = map[int64]maps.Map{} // recordId => Map
if len(recordIds) > 0 {
statsResp, err := this.RPC().NSRecordHourlyStatRPC().FindNSRecordHourlyStatWithRecordIds(this.UserContext(), &pb.FindNSRecordHourlyStatWithRecordIdsRequest{NsRecordIds: recordIds})
if err != nil {
this.ErrorPage(err)
return
}
for _, stat := range statsResp.NsRecordHourlyStats {
statMap[stat.NsRecordId] = maps.Map{
"bytes": stat.Bytes,
"countRequests": stat.CountRequests,
"countRequestsFormat": numberutils.FormatCount(stat.CountRequests),
}
}
}
// 列表
for _, record := range recordsResp.NsRecords {
var routeMaps = []maps.Map{}
for _, route := range record.NsRoutes {
routeMaps = append(routeMaps, maps.Map{
"id": route.Id,
"name": route.Name,
})
}
// 统计
recordStatMap, ok := statMap[record.Id]
if !ok {
recordStatMap = maps.Map{
"bytes": 0,
"countRequests": 0,
"countRequestsFormat": "0",
}
}
// 健康检查
var recordHealthCheckConfig = dnsconfigs.NewNSRecordHealthCheckConfig()
if domainHealthCheckIsOn && len(record.HealthCheckJSON) > 0 {
err = json.Unmarshal(record.HealthCheckJSON, recordHealthCheckConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
recordMaps = append(recordMaps, maps.Map{
"id": record.Id,
"name": record.Name,
"type": record.Type,
"value": record.Value,
"mxPriority": record.MxPriority,
"ttl": record.Ttl,
"weight": record.Weight,
"description": record.Description,
"isOn": record.IsOn,
"routes": routeMaps,
"stat": recordStatMap,
"healthCheck": recordHealthCheckConfig,
"isUp": record.IsUp,
})
}
this.Data["records"] = recordMaps
// 是否提示可以精准搜索
this.Data["enableNameSearch"] = false
this.Data["enableValueSearch"] = false
this.Data["searchingKeyword"] = params.Keyword
if countResp.Count > 0 && len(params.Keyword) > 0 {
for _, record := range recordsResp.NsRecords {
if record.Name == params.Keyword {
this.Data["enableNameSearch"] = true
break
}
if record.Value == params.Keyword {
this.Data["enableValueSearch"] = true
break
}
}
}
// 所有记录类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
this.Show()
}

View File

@@ -0,0 +1,60 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package records
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
type StatPopupAction struct {
actionutils.ParentAction
}
func (this *StatPopupAction) Init() {
this.Nav("", "", "")
}
func (this *StatPopupAction) RunGet(params struct {
RecordId int64
}) {
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.UserContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
var routeNames = []string{}
for _, route := range record.NsRoutes {
routeNames = append(routeNames, route.Name)
}
this.Data["record"] = maps.Map{
"id": record.Id,
"name": record.Name,
"routeNames": routeNames,
}
statsResp, err := this.RPC().NSRecordHourlyStatRPC().FindLatestNSRecordsHourlyStats(this.UserContext(), &pb.FindLatestNSRecordsHourlyStatsRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var statMaps = []maps.Map{}
for _, stat := range statsResp.NsRecordHourlyStats {
statMaps = append(statMaps, maps.Map{
"bytes": stat.Bytes,
"countRequests": stat.CountRequests,
"hour": stat.Hour,
})
}
this.Data["stats"] = statMaps
this.Show()
}

View File

@@ -0,0 +1,187 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package records
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
RecordId int64
}) {
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.UserContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
this.Data["record"] = maps.Map{
"id": record.Id,
"name": record.Name,
"type": record.Type,
"value": record.Value,
"mxPriority": record.MxPriority,
"srvPriority": record.SrvPriority,
"srvWeight": record.SrvWeight,
"srvPort": record.SrvPort,
"caaFlag": record.CaaFlag,
"caaTag": record.CaaTag,
"ttl": record.Ttl,
"weight": record.Weight,
"description": record.Description,
"isOn": record.IsOn,
"routes": record.NsRoutes,
}
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: record.NsDomain.Id})
if err != nil {
this.ErrorPage(err)
return
}
domain := domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", record.NsDomain.Id)
return
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
}
// 类型
this.Data["types"] = dnsconfigs.FindAllUserRecordTypeDefinitions()
// TTL
defaultTTL, err := nsutils.FindDefaultTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
this.Data["defaultTTL"] = defaultTTL
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
RecordId int64
Name string
Type string
Value string
MxPriority int32
SrvPriority int32
SrvWeight int32
SrvPort int32
CaaFlag int32
CaaTag string
Ttl int32
Description string
IsOn bool
RouteCodes []string
Weight int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
this.CreateLogInfo(codes.NSRecord_LogUpdateNSRecordHealthCheck, params.RecordId)
// 校验记录名
if !domainutils.ValidateRecordName(params.Name) {
this.FailField("name", "请输入正确的记录名")
}
// 名称是否有改变
recordResp, err := this.RPC().NSRecordRPC().FindNSRecord(this.UserContext(), &pb.FindNSRecordRequest{NsRecordId: params.RecordId})
if err != nil {
this.ErrorPage(err)
return
}
var record = recordResp.NsRecord
if record == nil || record.NsDomain == nil {
this.NotFound("nsRecord", params.RecordId)
return
}
if record.Name != params.Name {
// 检查限额
maxLoadBalanceRecords, canCreate, err := nsutils.CheckLoadBalanceRecordsQuota(this.UserContext(), record.NsDomain.Id, params.Name, params.Type, 1)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("当前记录 '" + params.Name + "' 负载均衡数已超出最大限额(" + types.String(maxLoadBalanceRecords) + "个)")
}
}
// 校验记录值
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
if !ok {
this.FailField("value", "记录值错误:"+message)
}
// SRV
if params.Type == dnsconfigs.RecordTypeSRV {
if params.SrvPort < 0 || params.SrvPort > 65535 {
this.FailField("srvPort", "请输入有效的端口号")
}
}
// 检查TTL
minTTL, err := nsutils.FindMinTTL(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
if params.Ttl < minTTL {
this.Fail("TTL不能小于" + types.String(minTTL) + "秒")
}
_, err = this.RPC().NSRecordRPC().UpdateNSRecord(this.UserContext(), &pb.UpdateNSRecordRequest{
NsRecordId: params.RecordId,
Description: params.Description,
Name: params.Name,
Type: params.Type,
Value: params.Value,
MxPriority: params.MxPriority,
SrvPriority: params.SrvPriority,
SrvWeight: params.SrvWeight,
SrvPort: params.SrvPort,
CaaFlag: params.CaaFlag,
CaaTag: params.CaaTag,
Ttl: params.Ttl,
IsOn: params.IsOn,
NsRouteCodes: params.RouteCodes,
Weight: params.Weight,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,30 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package records
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type UpdateUpAction struct {
actionutils.ParentAction
}
func (this *UpdateUpAction) RunPost(params struct {
RecordId int64
}) {
defer this.CreateLogInfo(codes.NSRecord_LogUpNSRecord, params.RecordId)
_, err := this.RPC().NSRecordRPC().UpdateNSRecordIsUp(this.UserContext(), &pb.UpdateNSRecordIsUpRequest{
NsRecordId: params.RecordId,
IsUp: true,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,81 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package domains
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
)
type TsigAction struct {
actionutils.ParentAction
}
func (this *TsigAction) Init() {
this.Nav("", "", "tsig")
}
func (this *TsigAction) RunGet(params struct {
DomainId int64
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
// TSIG信息
tsigResp, err := this.RPC().NSDomainRPC().FindNSDomainTSIG(this.UserContext(), &pb.FindNSDomainTSIGRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
var tsigJSON = tsigResp.TsigJSON
var tsigConfig = &dnsconfigs.NSTSIGConfig{}
if len(tsigJSON) > 0 {
err = json.Unmarshal(tsigJSON, tsigConfig)
if err != nil {
// 只是提示错误,仍然允许用户修改
logs.Error(err)
}
}
this.Data["tsig"] = tsigConfig
this.Show()
}
func (this *TsigAction) RunPost(params struct {
DomainId int64
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomainTSIG, params.DomainId)
var tsigConfig = &dnsconfigs.NSTSIGConfig{
IsOn: params.IsOn,
}
tsigJSON, err := json.Marshal(tsigConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NSDomainRPC().UpdateNSDomainTSIG(this.UserContext(), &pb.UpdateNSDomainTSIGRequest{
NsDomainId: params.DomainId,
TsigJSON: tsigJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,122 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package domains
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "", "update")
}
func (this *UpdateAction) RunGet(params struct {
DomainId int64
}) {
// 初始化域名信息
err := domainutils.InitDomain(this.Parent(), params.DomainId)
if err != nil {
this.ErrorPage(err)
return
}
var countRecords = this.Data.GetMap("domain").GetInt64("countRecords")
var countKeys = this.Data.GetMap("domain").GetInt64("countKeys")
// 域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: params.DomainId})
if err != nil {
this.ErrorPage(err)
return
}
domain := domainResp.NsDomain
if domain == nil {
this.NotFound("nsDomain", params.DomainId)
return
}
var clusterId = int64(0)
if domain.NsCluster != nil {
clusterId = domain.NsCluster.Id
}
// 用户信息
var userId = int64(0)
if domain.User != nil {
userId = domain.User.Id
}
// 分组信息
var groupId int64
if len(domain.NsDomainGroups) > 0 {
groupId = domain.NsDomainGroups[0].Id
}
this.Data["domain"] = maps.Map{
"id": domain.Id,
"name": domain.Name,
"isOn": domain.IsOn,
"clusterId": clusterId,
"userId": userId,
"countRecords": countRecords,
"countKeys": countKeys,
"groupId": groupId,
}
// DNS服务器
if domain.NsCluster == nil || domain.NsCluster.Id <= 0 {
this.WriteString("当前域名(" + domain.Name + ")所在集群已被删除,请删除当前域名后重新添加")
return
}
hostsResp, err := this.RPC().NSClusterRPC().FindNSClusterHosts(this.UserContext(), &pb.FindNSClusterHostsRequest{
NsClusterId: domain.NsCluster.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var hosts = hostsResp.Hosts
if hosts == nil {
hosts = []string{}
}
this.Data["dnsHosts"] = hosts
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
DomainId int64
GroupId int64
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSDomain_LogUpdateNSDomain, params.DomainId)
var groupIds = []int64{}
if params.GroupId > 0 {
groupIds = append(groupIds, params.GroupId)
}
_, err := this.RPC().NSDomainRPC().UpdateNSDomain(this.UserContext(), &pb.UpdateNSDomainRequest{
NsDomainId: params.DomainId,
NsDomainGroupIds: groupIds,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,56 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package domains
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domainutils"
)
type VerifyPageAction struct {
actionutils.ParentAction
}
func (this *VerifyPageAction) RunPost(params struct {
DomainIds []int64
}) {
defer this.CreateLogInfo(codes.NSDomain_LogValidateNSDomains)
if len(params.DomainIds) == 0 {
this.Success()
return
}
for _, domainId := range params.DomainIds {
// 检查域名信息
domainResp, err := this.RPC().NSDomainRPC().FindNSDomain(this.UserContext(), &pb.FindNSDomainRequest{NsDomainId: domainId})
if err != nil {
this.ErrorPage(err)
return
}
var domain = domainResp.NsDomain
if domain == nil || domain.Status != dnsconfigs.NSDomainStatusNone {
continue
}
verifyResp, err := this.RPC().NSDomainRPC().VerifyNSDomain(this.UserContext(), &pb.VerifyNSDomainRequest{NsDomainId: domainId})
if err != nil {
this.ErrorPage(err)
return
}
if !verifyResp.IsOk {
var message = domainutils.VerifyMessageWithCode(verifyResp.ErrorCode)
if len(message) > 0 {
this.Fail("域名 '" + domain.Name + "' 验证失败:" + message)
return
}
this.Fail("域名 '" + domain.Name + "' 验证失败")
return
}
}
this.Success()
}

View File

@@ -0,0 +1,147 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package ns
import (
"encoding/json"
"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"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "ns")
}
func (this *IndexAction) RunGet(params struct{}) {
// 跳转到域名
this.RedirectURL("/ns/domains")
return
resp, err := this.RPC().NSRPC().ComposeNSBoard(this.UserContext(), &pb.ComposeNSBoardRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["board"] = maps.Map{
"countDomains": resp.CountNSDomains,
"countRecords": resp.CountNSRecords,
"countClusters": resp.CountNSClusters,
"countNodes": resp.CountNSNodes,
"countOfflineNodes": resp.CountOfflineNSNodes,
}
// 流量排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.HourlyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
"hour": stat.Hour[8:],
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["hourlyStats"] = statMaps
}
{
var statMaps = []maps.Map{}
for _, stat := range resp.DailyTrafficStats {
statMaps = append(statMaps, maps.Map{
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["dailyStats"] = statMaps
}
// 域名排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSDomainStats {
statMaps = append(statMaps, maps.Map{
"domainId": stat.NsDomainId,
"domainName": stat.NsDomainName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topDomainStats"] = statMaps
}
// 节点排行
{
var statMaps = []maps.Map{}
for _, stat := range resp.TopNSNodeStats {
statMaps = append(statMaps, maps.Map{
"clusterId": stat.NsClusterId,
"nodeId": stat.NsNodeId,
"nodeName": stat.NsNodeName,
"countRequests": stat.CountRequests,
"bytes": stat.Bytes,
})
}
this.Data["topNodeStats"] = statMaps
}
// CPU
{
var statMaps = []maps.Map{}
for _, stat := range resp.CpuNodeValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": valueMap.GetFloat32("usage"),
})
}
this.Data["cpuValues"] = statMaps
}
// Memory
{
var statMaps = []maps.Map{}
for _, stat := range resp.MemoryNodeValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": valueMap.GetFloat32("usage"),
})
}
this.Data["memoryValues"] = statMaps
}
// Load
{
var statMaps = []maps.Map{}
for _, stat := range resp.LoadNodeValues {
var valueMap = maps.Map{}
err = json.Unmarshal(stat.ValueJSON, &valueMap)
if err != nil {
continue
}
statMaps = append(statMaps, maps.Map{
"time": timeutil.FormatTime("H:i", stat.CreatedAt),
"value": valueMap.GetFloat32("load1m"),
})
}
this.Data["loadValues"] = statMaps
}
this.Show()
}

View File

@@ -0,0 +1,98 @@
//go:build plus
package ns
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/batch"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/domain"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/groups"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/groups/group"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/keys"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/domains/records"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/plans"
"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", "ns").
Prefix("/ns").
Get("", new(IndexAction)).
// 域名列表
Prefix("/ns/domains").
Data("teaSubMenu", "domain").
Get("", new(domains.IndexAction)).
GetPost("/create", new(domains.CreateAction)).
Post("/delete", new(domains.DeleteAction)).
Post("/deletePage", new(domains.DeletePageAction)).
Post("/verifyPage", new(domains.VerifyPageAction)).
GetPost("/update", new(domains.UpdateAction)).
GetPost("/tsig", new(domains.TsigAction)).
GetPost("/healthCheck", new(domains.HealthCheckAction)).
// 单个域名
Prefix("/ns/domains/domain").
Data("teaSubMenu", "domain").
Get("", new(domain.IndexAction)).
GetPost("/updateStatusPopup", new(domain.UpdateStatusPopupAction)).
GetPost("/verifyPopup", new(domain.VerifyPopupAction)).
// 域名批量操作
Prefix("/ns/domains/batch").
Data("teaSubMenu", "domainBatch").
GetPost("", new(batch.IndexAction)).
GetPost("/createRecords", new(batch.CreateRecordsAction)).
GetPost("/deleteDomains", new(batch.DeleteDomainsAction)).
GetPost("/updateRecords", new(batch.UpdateRecordsAction)).
GetPost("/enableRecords", new(batch.EnableRecordsAction)).
GetPost("/importRecords", new(batch.ImportRecordsAction)).
GetPost("/deleteRecords", new(batch.DeleteRecordsAction)).
// 域名密钥
Prefix("/ns/domains/keys").
Data("teaSubMenu", "domain").
Get("", new(keys.IndexAction)).
GetPost("/createPopup", new(keys.CreatePopupAction)).
GetPost("/updatePopup", new(keys.UpdatePopupAction)).
Post("/delete", new(keys.DeleteAction)).
Post("/generateSecret", new(keys.GenerateSecretAction)).
// 记录相关
Prefix("/ns/domains/records").
Get("", new(records.IndexAction)).
GetPost("/createPopup", new(records.CreatePopupAction)).
GetPost("/updatePopup", new(records.UpdatePopupAction)).
Post("/delete", new(records.DeleteAction)).
GetPost("/statPopup", new(records.StatPopupAction)).
GetPost("/healthCheckPopup", new(records.HealthCheckPopupAction)).
Post("/updateUp", new(records.UpdateUpAction)).
// 域名分组列表
Prefix("/ns/domains/groups").
Data("teaSubMenu", "domainGroup").
Get("", new(groups.IndexAction)).
GetPost("/createPopup", new(groups.CreatePopupAction)).
Post("/options", new(groups.OptionsAction)).
// 域名分组
Prefix("/ns/domains/groups/group").
Data("teaSubMenu", "domainGroup").
Get("", new(group.IndexAction)).
GetPost("/updatePopup", new(group.UpdatePopupAction)).
Post("/delete", new(group.DeleteAction)).
// 套餐
Prefix("/ns/plans").
Data("teaSubMenu", "plan").
Get("", new(plans.IndexAction)).
GetPost("/buy", new(plans.BuyAction)).
//
EndAll()
})
}

View File

@@ -0,0 +1,7 @@
//go:build !plus
// +build !plus
package ns
// 此文件用于在非 Plus 版本中保持包的完整性
// Plus 版本的功能在 init.go 中实现(需要 //go:build plus 标签)

View File

@@ -0,0 +1,235 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package nsutils
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeUser/internal/rpc"
timeutil "github.com/iwind/TeaGo/utils/time"
"strings"
)
// FindUserPlanConfig 用户配置
// 需要保证非 error 的情况下,一定会返回一个不为空的 NSPlanConfig
func FindUserPlanConfig(ctx context.Context) (*dnsconfigs.NSPlanConfig, error) {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return nil, err
}
// 从套餐中读取
userPlanResp, err := rpcClient.NSUserPlanRPC().FindNSUserPlan(ctx, &pb.FindNSUserPlanRequest{})
if err != nil {
return nil, err
}
if userPlanResp.NsUserPlan != nil &&
len(userPlanResp.NsUserPlan.DayTo) > 0 &&
userPlanResp.NsUserPlan.DayTo >= timeutil.Format("Ymd") /** 在有效期内 **/ {
var planId = userPlanResp.NsUserPlan.NsPlanId
if planId > 0 {
planResp, err := rpcClient.NSPlanRPC().FindNSPlan(ctx, &pb.FindNSPlanRequest{NsPlanId: planId})
if err != nil {
return nil, err
}
if planResp.NsPlan != nil &&
len(planResp.NsPlan.ConfigJSON) > 0 &&
planResp.NsPlan.IsOn {
var config = dnsconfigs.DefaultNSUserPlanConfig()
err = json.Unmarshal(planResp.NsPlan.ConfigJSON, config)
if err != nil {
return nil, errors.New("decode plan config failed '" + err.Error() + "'")
}
return config, nil
}
}
}
// 从用户设置中读取
userConfigResp, err := rpcClient.SysSettingRPC().ReadSysSetting(ctx, &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeNSUserConfig})
if err != nil {
return nil, err
}
if len(userConfigResp.ValueJSON) > 0 {
var config = dnsconfigs.NewNSUserConfig()
err = json.Unmarshal(userConfigResp.ValueJSON, config)
if err != nil {
return nil, err
}
if config.DefaultPlanConfig != nil {
return config.DefaultPlanConfig, nil
}
}
return dnsconfigs.DefaultNSUserPlanConfig(), nil
}
// FindBasicPlan 查找基础套餐
func FindBasicPlan(ctx context.Context) (*dnsconfigs.NSPlanConfig, error) {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return nil, err
}
// 从用户设置中读取
userConfigResp, err := rpcClient.SysSettingRPC().ReadSysSetting(ctx, &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeNSUserConfig})
if err != nil {
return nil, err
}
if len(userConfigResp.ValueJSON) > 0 {
var config = dnsconfigs.NewNSUserConfig()
err = json.Unmarshal(userConfigResp.ValueJSON, config)
if err != nil {
return nil, err
}
if config.DefaultPlanConfig != nil {
return config.DefaultPlanConfig, nil
}
}
return dnsconfigs.DefaultNSUserPlanConfig(), nil
}
// CheckDomainsQuote 检查域名限额
func CheckDomainsQuote(ctx context.Context, countNew int32) (maxDomains int32, ok bool, err error) {
config, err := FindUserPlanConfig(ctx)
if err != nil {
return 0, false, err
}
maxDomains = config.MaxDomains
if maxDomains <= 0 {
return 0, true, nil
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return maxDomains, false, err
}
countDomainsResp, err := rpcClient.NSDomainRPC().CountAllNSDomains(ctx, &pb.CountAllNSDomainsRequest{})
if err != nil {
return maxDomains, false, err
}
if countDomainsResp.Count+int64(countNew) > int64(maxDomains) {
return maxDomains, false, nil
}
return maxDomains, true, nil
}
// CheckRecordsQuota 检查记录限额
func CheckRecordsQuota(ctx context.Context, domainId int64, countNew int32) (maxRecords int32, ok bool, err error) {
config, err := FindUserPlanConfig(ctx)
if err != nil {
return 0, false, err
}
maxRecords = config.MaxRecordsPerDomain
if maxRecords <= 0 {
return 0, true, nil
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return maxRecords, false, err
}
countRecordsResp, err := rpcClient.NSRecordRPC().CountAllNSRecords(ctx, &pb.CountAllNSRecordsRequest{
NsDomainId: domainId,
})
if err != nil {
return maxRecords, false, err
}
if countRecordsResp.Count+int64(countNew) > int64(maxRecords) {
return maxRecords, false, nil
}
return maxRecords, true, nil
}
// CheckLoadBalanceRecordsQuota 检查负载均衡限额
func CheckLoadBalanceRecordsQuota(ctx context.Context, domainId int64, recordName string, recordType string, countNew int32) (maxRecords int32, ok bool, err error) {
recordName = strings.ToLower(recordName)
recordType = strings.ToUpper(recordType)
config, err := FindUserPlanConfig(ctx)
if err != nil {
return 0, false, err
}
maxRecords = config.MaxLoadBalanceRecordsPerRecord
if maxRecords <= 0 {
return 0, true, nil
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return maxRecords, false, err
}
countRecordsResp, err := rpcClient.NSRecordRPC().CountAllNSRecordsWithName(ctx, &pb.CountAllNSRecordsWithNameRequest{
NsDomainId: domainId,
Name: recordName,
Type: recordType,
})
if err != nil {
return maxRecords, false, err
}
if countRecordsResp.Count+int64(countNew) > int64(maxRecords) {
return maxRecords, false, nil
}
return maxRecords, true, nil
}
// FindMinTTL 获取最小的TTL
func FindMinTTL(ctx context.Context) (int32, error) {
config, err := FindUserPlanConfig(ctx)
if err != nil {
return 0, err
}
return config.MinTTL, nil
}
func FindDefaultTTL(ctx context.Context) (int32, error) {
config, err := FindUserPlanConfig(ctx)
if err != nil {
return 0, err
}
var minTTL = config.MinTTL
if minTTL > 600 {
return minTTL, nil
}
return 600, nil
}
// CheckRoutesQuota 检查线路限额
func CheckRoutesQuota(ctx context.Context, countNew int32) (maxCustomRoutes int32, ok bool, err error) {
config, err := FindUserPlanConfig(ctx)
if err != nil {
return 0, false, err
}
maxCustomRoutes = config.MaxCustomRoutes
if maxCustomRoutes <= 0 {
return 0, true, nil
}
rpcClient, err := rpc.SharedRPC()
if err != nil {
return maxCustomRoutes, false, err
}
countRoutesResp, err := rpcClient.NSRouteRPC().CountAllNSRoutes(ctx, &pb.CountAllNSRoutesRequest{})
if err != nil {
return maxCustomRoutes, false, err
}
if countRoutesResp.Count+int64(countNew) > int64(maxCustomRoutes) {
return maxCustomRoutes, false, nil
}
return maxCustomRoutes, true, nil
}

View File

@@ -0,0 +1,158 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package plans
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/finance/financeutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type BuyAction struct {
actionutils.ParentAction
}
func (this *BuyAction) Init() {
this.Nav("", "", "")
}
func (this *BuyAction) RunGet(params struct {
PlanId int64
}) {
planResp, err := this.RPC().NSPlanRPC().FindNSPlan(this.UserContext(), &pb.FindNSPlanRequest{NsPlanId: params.PlanId})
if err != nil {
this.ErrorPage(err)
return
}
var plan = planResp.NsPlan
if plan == nil || !plan.IsOn {
this.NotFound("nsPlan", params.PlanId)
return
}
this.Data["plan"] = maps.Map{
"id": plan.Id,
"name": plan.Name,
"monthlyPrice": fmt.Sprintf("%.2f", plan.MonthlyPrice),
"yearlyPrice": fmt.Sprintf("%.2f", plan.YearlyPrice),
"monthFrom": timeutil.Format("Y-m-d"),
"monthTo": timeutil.Format("Y-m-d", time.Now().AddDate(0, 1, 0)),
"yearFrom": timeutil.Format("Y-m-d"),
"yearTo": timeutil.Format("Y-m-d", time.Now().AddDate(1, 0, 0)),
}
this.Show()
}
func (this *BuyAction) RunPost(params struct {
PlanId int64
Period string
MethodCode string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
planResp, err := this.RPC().NSPlanRPC().FindNSPlan(this.UserContext(), &pb.FindNSPlanRequest{NsPlanId: params.PlanId})
if err != nil {
this.ErrorPage(err)
return
}
var plan = planResp.NsPlan
if plan == nil || !plan.IsOn {
this.NotFound("nsPlan", params.PlanId)
return
}
var price float64
var dayFrom = timeutil.Format("Ymd")
var dayTo = dayFrom
switch params.Period {
case "yearly":
price = float64(plan.YearlyPrice)
dayTo = timeutil.Format("Ymd", time.Now().AddDate(1, 0, 0))
case "monthly":
price = float64(plan.MonthlyPrice)
dayTo = timeutil.Format("Ymd", time.Now().AddDate(0, 1, 0))
default:
this.Fail("请输入正确的付费周期")
return
}
// 用户是否已经购买了套餐
userPlanResp, err := this.RPC().NSUserPlanRPC().FindNSUserPlan(this.UserContext(), &pb.FindNSUserPlanRequest{})
if err != nil {
this.ErrorPage(err)
return
}
if userPlanResp.NsUserPlan != nil && userPlanResp.NsUserPlan.DayTo >= timeutil.Format("Ymd") {
this.Fail("当前已经有一个生效的套餐,无法重复购买")
}
// 使用余额购买
this.Data["success"] = false
this.Data["orderCode"] = ""
if params.MethodCode == "@balance" {
balance, err := financeutils.FindUserBalance(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
if price > balance {
this.Fail("当前余额不足,需要:" + fmt.Sprintf("%.2f元", price) + ",现有:" + fmt.Sprintf("%.2f元", balance) + " ,请充值后再试")
return
}
// 直接购买
_, err = this.RPC().NSUserPlanRPC().BuyNSUserPlan(this.UserContext(), &pb.BuyNSUserPlanRequest{
UserId: this.UserId(),
PlanId: plan.Id,
Period: params.Period,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["success"] = true
this.Success()
return
}
// 生成订单
var orderParams = &userconfigs.OrderTypeBuyNSPlanParams{
PlanId: plan.Id,
DayFrom: dayFrom,
DayTo: dayTo,
Period: params.Period,
}
orderParamsJSON, err := json.Marshal(orderParams)
if err != nil {
this.ErrorPage(err)
return
}
resp, err := this.RPC().UserOrderRPC().CreateUserOrder(this.UserContext(), &pb.CreateUserOrderRequest{
Type: userconfigs.OrderTypeBuyNSPlan,
OrderMethodCode: params.MethodCode,
Amount: price,
ParamsJSON: orderParamsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["orderCode"] = resp.Code
this.Success()
}

View File

@@ -0,0 +1,92 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package plans
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/remotelogs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct{}) {
// 当前我的套餐
userPlanResp, err := this.RPC().NSUserPlanRPC().FindNSUserPlan(this.UserContext(), &pb.FindNSUserPlanRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var userPlan = userPlanResp.NsUserPlan
this.Data["userPlan"] = nil
if userPlan != nil && userPlan.NsPlan != nil {
if len(userPlan.DayTo) < 8 {
userPlan.DayTo = timeutil.Format("Ymd")
}
this.Data["userPlan"] = maps.Map{
"dayTo": userPlan.DayTo[:4] + "-" + userPlan.DayTo[4:6] + "-" + userPlan.DayTo[6:],
"isExpired": userPlan.DayTo < timeutil.Format("Ymd"),
"plan": maps.Map{
"id": userPlan.NsPlan.Id,
"name": userPlan.NsPlan.Name,
},
}
}
var planMaps = []maps.Map{}
// 基础套餐
basicPlanConfig, err := nsutils.FindBasicPlan(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
planMaps = append(planMaps, maps.Map{
"id": 0,
"name": "注册用户",
"monthlyPrice": 0,
"yearlyPrice": 0,
"config": basicPlanConfig,
})
// 所有套餐
plansResp, err := this.RPC().NSPlanRPC().FindAllEnabledNSPlans(this.UserContext(), &pb.FindAllEnabledNSPlansRequest{})
if err != nil {
this.ErrorPage(err)
return
}
for _, plan := range plansResp.NsPlans {
var config = &dnsconfigs.NSPlanConfig{}
if len(plan.ConfigJSON) == 0 {
continue
}
err = json.Unmarshal(plan.ConfigJSON, config)
if err != nil {
remotelogs.Error("NSPlan", "decode plan config failed: plan: "+types.String(plan.Id)+": "+err.Error())
continue
}
planMaps = append(planMaps, maps.Map{
"id": plan.Id,
"name": plan.Name,
"monthlyPrice": plan.MonthlyPrice,
"yearlyPrice": plan.YearlyPrice,
"config": config,
})
}
this.Data["plans"] = planMaps
this.Show()
}

View File

@@ -0,0 +1,98 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
}
func (this *CreatePopupAction) RunGet(params struct {
ClusterId int64
DomainId int64
UserId int64
}) {
this.Data["clusterId"] = params.ClusterId
this.Data["domainId"] = params.DomainId
this.Data["userId"] = params.UserId
this.Data["rangeTypes"] = dnsconfigs.AllNSRouteRangeTypes()
// 线路限额
maxCustomRoutes, canCreate, err := nsutils.CheckRoutesQuota(this.UserContext(), 1)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["quotaMaxCustomRoutes"] = maxCustomRoutes
this.Data["quotaCanCreate"] = canCreate
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
ClusterId int64
DomainId int64
UserId int64
Name string
RangesJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var routeId = int64(0)
defer func() {
this.CreateLogInfo(codes.NSRoute_LogCreateNSRoute, routeId)
}()
// 线路限额
maxCustomRoutes, canCreate, err := nsutils.CheckRoutesQuota(this.UserContext(), 1)
if err != nil {
this.ErrorPage(err)
return
}
if !canCreate {
this.Fail("超出线路最大限额(" + types.String(maxCustomRoutes) + "个),请升级套餐后再试")
return
}
params.Must.Field("name", params.Name).
Require("请输入线路名称")
ranges, err := dnsconfigs.InitNSRangesFromJSON(params.RangesJSON)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
if len(ranges) == 0 {
this.Fail("请添加线路范围")
}
createResp, err := this.RPC().NSRouteRPC().CreateNSRoute(this.UserContext(), &pb.CreateNSRouteRequest{
NsClusterId: params.ClusterId,
NsDomainId: params.DomainId,
UserId: params.UserId,
Name: params.Name,
RangesJSON: params.RangesJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
routeId = createResp.NsRouteId
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
RouteId int64
}) {
defer this.CreateLogInfo(codes.NSRoute_LogDeleteNSRoute, params.RouteId)
_, err := this.RPC().NSRouteRPC().DeleteNSRoute(this.UserContext(), &pb.DeleteNSRouteRequest{NsRouteId: params.RouteId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,41 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
routesResp, err := this.RPC().NSRouteRPC().FindAllNSRoutes(this.UserContext(), &pb.FindAllNSRoutesRequest{
NsClusterId: 0,
NsDomainId: 0,
UserId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
var routeMaps = []maps.Map{}
for _, route := range routesResp.NsRoutes {
routeMaps = append(routeMaps, maps.Map{
"id": route.Id,
"name": route.Name,
"isOn": route.IsOn,
})
}
this.Data["routes"] = routeMaps
this.Show()
}

View File

@@ -0,0 +1,25 @@
package clusters
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", "ns").
Data("teaSubMenu", "route").
Prefix("/ns/routes").
Get("", new(IndexAction)).
Get("/route", new(RouteAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
Post("/sort", new(SortAction)).
Post("/options", new(OptionsAction)).
Get("/internal", new(InternalAction)).
EndAll()
})
}

View File

@@ -0,0 +1,89 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package clusters
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
type InternalAction struct {
actionutils.ParentAction
}
func (this *InternalAction) Init() {
this.Nav("", "", "internal")
}
func (this *InternalAction) RunGet(params struct{}) {
this.Data["ispRoutes"] = dnsconfigs.AllDefaultISPRoutes
this.Data["chinaProvinceRoutes"] = dnsconfigs.AllDefaultChinaProvinceRoutes
this.Data["worldRegionRoutes"] = dnsconfigs.AllDefaultWorldRegionRoutes
// Agent相关线路
agentsResp, err := this.RPC().NSRouteRPC().FindAllAgentNSRoutes(this.UserContext(), &pb.FindAllAgentNSRoutesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var agentRouteMaps = []maps.Map{}
for _, route := range agentsResp.NsRoutes {
agentRouteMaps = append(agentRouteMaps, maps.Map{
"name": route.Name,
"code": route.Code,
})
}
this.Data["agentRoutes"] = agentRouteMaps
// 系统内置公用线路
publicRoutesResp, err := this.RPC().NSRouteRPC().FindAllPublicNSRoutes(this.UserContext(), &pb.FindAllPublicRoutesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var publicCategoryMaps = []maps.Map{}
var publicCategoryIds = []int64{}
var publicRouteMaps = []maps.Map{}
for _, route := range publicRoutesResp.NsRoutes {
var categoryId int64 = 0
if route.NsRouteCategory != nil {
categoryId = route.NsRouteCategory.Id
}
publicRouteMaps = append(publicRouteMaps, maps.Map{
"id": route.Id,
"name": route.Name,
"code": route.Code,
"categoryId": categoryId,
})
// 未分类
if route.NsRouteCategory == nil {
if !lists.ContainsInt64(publicCategoryIds, 0) {
publicCategoryIds = append(publicCategoryIds, 0)
publicCategoryMaps = append(publicCategoryMaps, maps.Map{
"id": 0,
"name": "官方线路",
})
}
continue
}
// 有分类
if !lists.ContainsInt64(publicCategoryIds, route.NsRouteCategory.Id) {
publicCategoryIds = append(publicCategoryIds, route.NsRouteCategory.Id)
publicCategoryMaps = append(publicCategoryMaps, maps.Map{
"id": route.NsRouteCategory.Id,
"name": route.NsRouteCategory.Name,
})
}
}
this.Data["publicCategories"] = publicCategoryMaps
this.Data["publicRoutes"] = publicRouteMaps
this.Show()
}

View File

@@ -0,0 +1,203 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/ns/nsutils"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type OptionsAction struct {
actionutils.ParentAction
}
func (this *OptionsAction) RunPost(params struct {
ClusterId int64
DomainId int64
UserId int64
}) {
var routeMaps = []maps.Map{}
planConfig, err := nsutils.FindUserPlanConfig(this.UserContext())
if err != nil {
this.ErrorPage(err)
return
}
// 默认线路
for _, route := range dnsconfigs.AllDefaultRoutes {
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "default",
})
}
// 自定义
routesResp, err := this.RPC().NSRouteRPC().FindAllNSRoutes(this.UserContext(), &pb.FindAllNSRoutesRequest{
NsClusterId: params.ClusterId,
NsDomainId: params.DomainId,
UserId: params.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
for _, route := range routesResp.NsRoutes {
if len(route.Code) == 0 {
route.Code = "id:" + types.String(route.Id)
}
if !route.IsOn {
continue
}
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "user",
})
}
// 运营商
this.Data["supportISPRoutes"] = false
if planConfig == nil || planConfig.SupportISPRoutes {
this.Data["supportISPRoutes"] = true
for _, route := range dnsconfigs.AllDefaultISPRoutes {
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "isp",
})
}
}
// 中国省份
this.Data["supportChinaProvinceRoutes"] = false
if planConfig == nil || planConfig.SupportChinaProvinceRoutes {
this.Data["supportChinaProvinceRoutes"] = true
for _, route := range dnsconfigs.AllDefaultChinaProvinceRoutes {
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "china",
})
}
}
// 全球
this.Data["supportWorldRegionRoutes"] = false
if planConfig == nil || planConfig.SupportCountryRoutes {
this.Data["supportWorldRegionRoutes"] = true
for _, route := range dnsconfigs.AllDefaultWorldRegionRoutes {
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "world",
})
}
}
// 省份
provincesResp, err := this.RPC().RegionProvinceRPC().FindAllRegionProvinces(this.UserContext(), &pb.FindAllRegionProvincesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var provinceMaps = []maps.Map{}
for _, province := range provincesResp.RegionProvinces {
if province.RegionCountry == nil || len(province.RegionCountry.RouteCode) == 0 {
continue
}
provinceMaps = append(provinceMaps, maps.Map{
"id": province.Id,
"name": province.Name,
"code": "region:province:" + types.String(province.Id),
"type": "province",
"countryCode": province.RegionCountry.RouteCode,
})
}
this.Data["provinces"] = provinceMaps
// 搜索引擎
this.Data["supportAgentRoutes"] = false
if planConfig == nil || planConfig.SupportAgentRoutes {
this.Data["supportAgentRoutes"] = true
agentsResp, err := this.RPC().NSRouteRPC().FindAllAgentNSRoutes(this.UserContext(), &pb.FindAllAgentNSRoutesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
for _, route := range agentsResp.NsRoutes {
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "agent",
})
}
}
// 公共线路
this.Data["publicCategories"] = []maps.Map{}
this.Data["supportPublicRoutes"] = false
publicRoutesResp, err := this.RPC().NSRouteRPC().FindAllPublicNSRoutes(this.UserContext(), &pb.FindAllPublicRoutesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
if len(publicRoutesResp.NsRoutes) == 0 {
this.Data["supportPublicRoutes"] = true
} else if planConfig == nil || planConfig.SupportPublicRoutes {
this.Data["supportPublicRoutes"] = true
var publicCategoryMaps = []maps.Map{}
var publicCategoryIds = []int64{}
for _, route := range publicRoutesResp.NsRoutes {
var categoryId int64 = 0
if route.NsRouteCategory != nil {
categoryId = route.NsRouteCategory.Id
}
routeMaps = append(routeMaps, maps.Map{
"name": route.Name,
"code": route.Code,
"type": "public:category:" + types.String(categoryId),
})
// 未分类
if route.NsRouteCategory == nil {
if !lists.ContainsInt64(publicCategoryIds, 0) {
publicCategoryIds = append(publicCategoryIds, 0)
publicCategoryMaps = append(publicCategoryMaps, maps.Map{
"id": 0,
"name": "官方线路",
"type": "public:category:" + types.String(0),
})
}
continue
}
// 有分类
if !lists.ContainsInt64(publicCategoryIds, categoryId) {
publicCategoryIds = append(publicCategoryIds, route.NsRouteCategory.Id)
publicCategoryMaps = append(publicCategoryMaps, maps.Map{
"id": route.NsRouteCategory.Id,
"name": route.NsRouteCategory.Name,
"type": "public:category:" + types.String(categoryId),
})
}
}
this.Data["publicCategories"] = publicCategoryMaps
}
this.Data["routes"] = routeMaps
this.Success()
}

View File

@@ -0,0 +1,17 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
type RouteAction struct {
actionutils.ParentAction
}
func (this *RouteAction) Init() {
this.Nav("", "", "")
}
func (this *RouteAction) RunGet(params struct{}) {
this.Show()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package clusters
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type SortAction struct {
actionutils.ParentAction
}
func (this *SortAction) RunPost(params struct {
RouteIds []int64
}) {
defer this.CreateLogInfo(codes.NSRoute_LogSortNSRoutes)
_, err := this.RPC().NSRouteRPC().UpdateNSRouteOrders(this.UserContext(), &pb.UpdateNSRouteOrdersRequest{NsRouteIds: params.RouteIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,95 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package clusters
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"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 UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
RouteId int64
}) {
routeResp, err := this.RPC().NSRouteRPC().FindNSRoute(this.UserContext(), &pb.FindNSRouteRequest{NsRouteId: params.RouteId})
if err != nil {
this.ErrorPage(err)
return
}
var route = routeResp.NsRoute
if route == nil {
this.NotFound("nsRoute", params.RouteId)
return
}
var rangeMaps = []maps.Map{}
if len(route.RangesJSON) > 0 {
err = json.Unmarshal(route.RangesJSON, &rangeMaps)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["route"] = maps.Map{
"id": route.Id,
"name": route.Name,
"isOn": route.IsOn,
"ranges": rangeMaps,
}
// 范围类型
this.Data["rangeTypes"] = dnsconfigs.AllNSRouteRangeTypes()
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
RouteId int64
Name string
RangesJSON []byte
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.NSRoute_LogUpdateNSRoute, params.RouteId)
params.Must.Field("name", params.Name).
Require("请输入线路名称")
ranges, err := dnsconfigs.InitNSRangesFromJSON(params.RangesJSON)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
if len(ranges) == 0 {
this.Fail("请添加线路范围")
}
_, err = this.RPC().NSRouteRPC().UpdateNSRoute(this.UserContext(), &pb.UpdateNSRouteRequest{
NsRouteId: params.RouteId,
Name: params.Name,
RangesJSON: params.RangesJSON,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}