This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/cmd"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Data["types"] = serverconfigs.FindAllAccessLogStorageTypes()
this.Data["syslogPriorities"] = serverconfigs.AccessLogSyslogStoragePriorities
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
Type string
// file
FilePath string
FileAutoCreate bool
// es
EsEndpoint string
EsIndex string
EsIsDataStream bool
EsMappingType string
EsUsername string
EsPassword string
// mysql
MysqlHost string
MysqlPort int
MysqlUsername string
MysqlPassword string
MysqlDatabase string
MysqlTable string
MysqlLogField string
// tcp
TcpNetwork string
TcpAddr string
// syslog
SyslogProtocol string
SyslogServerAddr string
SyslogServerPort int
SyslogSocket string
SyslogTag string
SyslogPriority int
// command
CommandCommand string
CommandArgs string
CommandDir string
IsPublic bool
FirewallOnly bool
DisableDefaultDB bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var policyId int64 = 0
defer func() {
this.CreateLogInfo(codes.HTTPAccessLogPolicy_LogCreateHTTPAccessLogPolicy, policyId)
}()
params.Must.
Field("name", params.Name).
Require("请输入日志策略的名称").
Field("type", params.Type).
Require("请选择存储类型")
var options any = nil
switch params.Type {
case serverconfigs.AccessLogStorageTypeFile:
params.Must.
Field("filePath", params.FilePath).
Require("请输入日志文件路径")
var storage = new(serverconfigs.AccessLogFileStorageConfig)
storage.Path = params.FilePath
storage.AutoCreate = params.FileAutoCreate
options = storage
case serverconfigs.AccessLogStorageTypeES:
params.Must.
Field("esEndpoint", params.EsEndpoint).
Require("请输入Endpoint").
Field("esIndex", params.EsIndex).
Require("请输入Index名称")
if !params.EsIsDataStream {
params.Must.
Field("esMappingType", params.EsMappingType).
Require("请输入Mapping名称")
}
var storage = new(serverconfigs.AccessLogESStorageConfig)
storage.Endpoint = params.EsEndpoint
storage.Index = params.EsIndex
storage.IsDataStream = params.EsIsDataStream
storage.MappingType = params.EsMappingType
storage.Username = params.EsUsername
storage.Password = params.EsPassword
options = storage
case serverconfigs.AccessLogStorageTypeTCP:
params.Must.
Field("tcpNetwork", params.TcpNetwork).
Require("请选择网络协议").
Field("tcpAddr", params.TcpAddr).
Require("请输入网络地址")
var storage = new(serverconfigs.AccessLogTCPStorageConfig)
storage.Network = params.TcpNetwork
storage.Addr = params.TcpAddr
options = storage
case serverconfigs.AccessLogStorageTypeSyslog:
switch params.SyslogProtocol {
case serverconfigs.AccessLogSyslogStorageProtocolTCP, serverconfigs.AccessLogSyslogStorageProtocolUDP:
params.Must.
Field("syslogServerAddr", params.SyslogServerAddr).
Require("请输入网络地址")
case serverconfigs.AccessLogSyslogStorageProtocolSocket:
params.Must.
Field("syslogSocket", params.SyslogSocket).
Require("请输入Socket路径")
}
var storage = new(serverconfigs.AccessLogSyslogStorageConfig)
storage.Protocol = params.SyslogProtocol
storage.ServerAddr = params.SyslogServerAddr
storage.ServerPort = params.SyslogServerPort
storage.Socket = params.SyslogSocket
storage.Tag = params.SyslogTag
storage.Priority = params.SyslogPriority
options = storage
case serverconfigs.AccessLogStorageTypeCommand:
params.Must.
Field("commandCommand", params.CommandCommand).
Require("请输入可执行命令")
var storage = new(serverconfigs.AccessLogCommandStorageConfig)
storage.Command = params.CommandCommand
storage.Args = cmd.ParseArgs(params.CommandArgs)
storage.Dir = params.CommandDir
options = storage
}
if options == nil {
this.Fail("找不到选择的存储类型")
}
optionsJSON, err := json.Marshal(options)
if err != nil {
this.ErrorPage(err)
return
}
createResp, err := this.RPC().HTTPAccessLogPolicyRPC().CreateHTTPAccessLogPolicy(this.AdminContext(), &pb.CreateHTTPAccessLogPolicyRequest{
Name: params.Name,
Type: params.Type,
OptionsJSON: optionsJSON,
CondsJSON: nil, // TODO
IsPublic: params.IsPublic,
FirewallOnly: params.FirewallOnly,
DisableDefaultDB: params.DisableDefaultDB,
})
if err != nil {
this.ErrorPage(err)
return
}
policyId = createResp.HttpAccessLogPolicyId
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
PolicyId int64
}) {
defer this.CreateLogInfo(codes.HTTPAccessLogPolicy_LogDeleteHTTPAccessLogPolicy, params.PolicyId)
_, err := this.RPC().HTTPAccessLogPolicyRPC().DeleteHTTPAccessLogPolicy(this.AdminContext(), &pb.DeleteHTTPAccessLogPolicyRequest{HttpAccessLogPolicyId: params.PolicyId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,64 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().HTTPAccessLogPolicyRPC().CountAllHTTPAccessLogPolicies(this.AdminContext(), &pb.CountAllHTTPAccessLogPoliciesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
policiesResp, err := this.RPC().HTTPAccessLogPolicyRPC().ListHTTPAccessLogPolicies(this.AdminContext(), &pb.ListHTTPAccessLogPoliciesRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var policyMaps = []maps.Map{}
for _, policy := range policiesResp.HttpAccessLogPolicies {
var optionsMap = maps.Map{}
if len(policy.OptionsJSON) > 0 {
err = json.Unmarshal(policy.OptionsJSON, &optionsMap)
if err != nil {
this.ErrorPage(err)
return
}
}
policyMaps = append(policyMaps, maps.Map{
"id": policy.Id,
"name": policy.Name,
"type": policy.Type,
"typeName": serverconfigs.FindAccessLogStorageTypeName(policy.Type),
"isOn": policy.IsOn,
"isPublic": policy.IsPublic,
"firewallOnly": policy.FirewallOnly,
"disableDefaultDB": policy.DisableDefaultDB,
"options": optionsMap,
})
}
this.Data["policies"] = policyMaps
this.Show()
}

View File

@@ -0,0 +1,29 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package accesslogs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(plus.NewHelper(plus.ComponentCodeLog)).
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Prefix("/servers/accesslogs").
Data("teaMenu", "servers").
Data("teaSubMenu", "accesslog").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
Get("/policy", new(PolicyAction)).
GetPost("/test", new(TestAction)).
GetPost("/update", new(UpdateAction)).
Post("/delete", new(DeleteAction)).
EndAll()
})
}

View File

@@ -0,0 +1,36 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/accesslogs/policyutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
)
type PolicyAction struct {
actionutils.ParentAction
}
func (this *PolicyAction) Init() {
this.Nav("", "", "policy")
}
func (this *PolicyAction) RunGet(params struct {
PolicyId int64
}) {
err := policyutils.InitPolicy(this.Parent(), params.PolicyId)
if err != nil {
this.ErrorPage(err)
return
}
var policyMap = this.Data.GetMap("policy")
if policyMap.GetString("type") == serverconfigs.AccessLogStorageTypeSyslog {
this.Data["syslogPriorityName"] = serverconfigs.FindAccessLogSyslogStoragePriorityName(policyMap.GetMap("options").GetInt("priority"))
} else {
this.Data["syslogPriorityName"] = ""
}
this.Show()
}

View File

@@ -0,0 +1,51 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package policyutils
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
// InitPolicy 初始化访问日志策略
func InitPolicy(parent *actionutils.ParentAction, policyId int64) error {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
policyResp, err := rpcClient.HTTPAccessLogPolicyRPC().FindHTTPAccessLogPolicy(parent.AdminContext(), &pb.FindHTTPAccessLogPolicyRequest{HttpAccessLogPolicyId: policyId})
if err != nil {
return err
}
var policy = policyResp.HttpAccessLogPolicy
if policy == nil {
return errors.New("can not find policy '" + types.String(policyId) + "'")
}
var options = maps.Map{}
if len(policy.OptionsJSON) > 0 {
err = json.Unmarshal(policy.OptionsJSON, &options)
if err != nil {
return err
}
}
parent.Data["policy"] = maps.Map{
"id": policy.Id,
"name": policy.Name,
"type": policy.Type,
"typeName": serverconfigs.FindAccessLogStorageTypeName(policy.Type),
"isOn": policy.IsOn,
"isPublic": policy.IsPublic,
"firewallOnly": policy.FirewallOnly,
"disableDefaultDB": policy.DisableDefaultDB,
"options": options,
}
return nil
}

View File

@@ -0,0 +1,81 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/accesslogs/policyutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"strconv"
"time"
)
type TestAction struct {
actionutils.ParentAction
}
func (this *TestAction) Init() {
this.Nav("", "", "test")
}
func (this *TestAction) RunGet(params struct {
PolicyId int64
}) {
err := policyutils.InitPolicy(this.Parent(), params.PolicyId)
if err != nil {
this.ErrorPage(err)
return
}
var now = time.Now()
var uri = "/doc.html"
testJSON, err := json.MarshalIndent(&pb.HTTPAccessLog{
RequestId: types.String(time.Now().UnixMilli()) + "1" + strconv.Itoa(1),
UserAgent: this.Request.UserAgent(),
Request: "GET " + uri + " HTTP/1.1",
RequestPath: uri,
Referer: "https://example.com/",
RemoteAddr: "8.8.8.8",
Timestamp: now.Unix(),
TimeISO8601: now.Format("2006-01-02T15:04:05.000Z07:00"),
TimeLocal: now.Format("2/Jan/2006:15:04:05 -0700"),
}, "", " ")
if err != nil {
this.ErrorPage(err)
return
}
this.Data["testJSON"] = string(testJSON)
this.Show()
}
func (this *TestAction) RunPost(params struct {
PolicyId int64
BodyJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.HTTPAccessLogPolicy_LogTestHTTPAccessLogPolicy, params.PolicyId)
var accessLog = &pb.HTTPAccessLog{}
err := json.Unmarshal(params.BodyJSON, accessLog)
if err != nil {
this.Fail("发送内容不是有效的JSON" + err.Error())
}
_, err = this.RPC().HTTPAccessLogPolicyRPC().WriteHTTPAccessLogPolicy(this.AdminContext(), &pb.WriteHTTPAccessLogPolicyRequest{
HttpAccessLogPolicyId: params.PolicyId,
HttpAccessLog: accessLog,
})
if err != nil {
this.Fail("发送失败:" + err.Error())
return
}
this.Success()
}

View File

@@ -0,0 +1,206 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesslogs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/accesslogs/policyutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/cmd"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "", "update")
}
func (this *UpdateAction) RunGet(params struct {
PolicyId int64
}) {
err := policyutils.InitPolicy(this.Parent(), params.PolicyId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["types"] = serverconfigs.FindAllAccessLogStorageTypes()
this.Data["syslogPriorities"] = serverconfigs.AccessLogSyslogStoragePriorities
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
PolicyId int64
Name string
// file
FilePath string
FileAutoCreate bool
// es
EsEndpoint string
EsIndex string
EsIsDataStream bool
EsMappingType string
EsUsername string
EsPassword string
// mysql
MysqlHost string
MysqlPort int
MysqlUsername string
MysqlPassword string
MysqlDatabase string
MysqlTable string
MysqlLogField string
// tcp
TcpNetwork string
TcpAddr string
// syslog
SyslogProtocol string
SyslogServerAddr string
SyslogServerPort int
SyslogSocket string
SyslogTag string
SyslogPriority int
// command
CommandCommand string
CommandArgs string
CommandDir string
IsOn bool
IsPublic bool
FirewallOnly bool
DisableDefaultDB bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.HTTPAccessLogPolicy_LogUpdateHTTPAccessLogPolicy, params.PolicyId)
policyResp, err := this.RPC().HTTPAccessLogPolicyRPC().FindHTTPAccessLogPolicy(this.AdminContext(), &pb.FindHTTPAccessLogPolicyRequest{HttpAccessLogPolicyId: params.PolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policy = policyResp.HttpAccessLogPolicy
if policy == nil {
this.Fail("找不到要修改的策略")
return
}
params.Must.
Field("name", params.Name).
Require("请输入日志策略的名称")
var options interface{} = nil
switch policy.Type {
case serverconfigs.AccessLogStorageTypeFile:
params.Must.
Field("filePath", params.FilePath).
Require("请输入日志文件路径")
var storage = new(serverconfigs.AccessLogFileStorageConfig)
storage.Path = params.FilePath
storage.AutoCreate = params.FileAutoCreate
options = storage
case serverconfigs.AccessLogStorageTypeES:
params.Must.
Field("esEndpoint", params.EsEndpoint).
Require("请输入Endpoint").
Field("esIndex", params.EsIndex).
Require("请输入Index名称")
if !params.EsIsDataStream {
params.Must.
Field("esMappingType", params.EsMappingType).
Require("请输入Mapping名称")
}
var storage = new(serverconfigs.AccessLogESStorageConfig)
storage.Endpoint = params.EsEndpoint
storage.Index = params.EsIndex
storage.IsDataStream = params.EsIsDataStream
storage.MappingType = params.EsMappingType
storage.Username = params.EsUsername
storage.Password = params.EsPassword
options = storage
case serverconfigs.AccessLogStorageTypeTCP:
params.Must.
Field("tcpNetwork", params.TcpNetwork).
Require("请选择网络协议").
Field("tcpAddr", params.TcpAddr).
Require("请输入网络地址")
var storage = new(serverconfigs.AccessLogTCPStorageConfig)
storage.Network = params.TcpNetwork
storage.Addr = params.TcpAddr
options = storage
case serverconfigs.AccessLogStorageTypeSyslog:
switch params.SyslogProtocol {
case serverconfigs.AccessLogSyslogStorageProtocolTCP, serverconfigs.AccessLogSyslogStorageProtocolUDP:
params.Must.
Field("syslogServerAddr", params.SyslogServerAddr).
Require("请输入网络地址")
case serverconfigs.AccessLogSyslogStorageProtocolSocket:
params.Must.
Field("syslogSocket", params.SyslogSocket).
Require("请输入Socket路径")
}
var storage = new(serverconfigs.AccessLogSyslogStorageConfig)
storage.Protocol = params.SyslogProtocol
storage.ServerAddr = params.SyslogServerAddr
storage.ServerPort = params.SyslogServerPort
storage.Socket = params.SyslogSocket
storage.Tag = params.SyslogTag
storage.Priority = params.SyslogPriority
options = storage
case serverconfigs.AccessLogStorageTypeCommand:
params.Must.
Field("commandCommand", params.CommandCommand).
Require("请输入可执行命令")
var storage = new(serverconfigs.AccessLogCommandStorageConfig)
storage.Command = params.CommandCommand
storage.Args = cmd.ParseArgs(params.CommandArgs)
storage.Dir = params.CommandDir
options = storage
}
if options == nil {
this.Fail("找不到选择的存储类型")
}
optionsJSON, err := json.Marshal(options)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPAccessLogPolicyRPC().UpdateHTTPAccessLogPolicy(this.AdminContext(), &pb.UpdateHTTPAccessLogPolicyRequest{
HttpAccessLogPolicyId: params.PolicyId,
Name: params.Name,
OptionsJSON: optionsJSON,
CondsJSON: nil, // TODO
IsOn: params.IsOn,
IsPublic: params.IsPublic,
FirewallOnly: params.FirewallOnly,
DisableDefaultDB: params.DisableDefaultDB,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,187 @@
package servers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/url"
"regexp"
"strings"
)
type AddOriginPopupAction struct {
actionutils.ParentAction
}
func (this *AddOriginPopupAction) Init() {
this.Nav("", "", "")
}
func (this *AddOriginPopupAction) RunGet(params struct {
ServerType string
}) {
this.Data["serverType"] = params.ServerType
this.getOSSHook()
this.Show()
}
func (this *AddOriginPopupAction) RunPost(params struct {
Protocol string
Addr string
DomainsJSON []byte
Host string
FollowPort bool
Http2Enabled bool
Must *actions.Must
}) {
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
if err != nil {
this.ErrorPage(err)
return
}
if !goNext {
return
}
// 初始化
var pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
}
var addrConfig = &serverconfigs.NetworkAddressConfig{
Protocol: serverconfigs.Protocol(params.Protocol),
}
var ossJSON []byte
if ossConfig != nil { // OSS
ossJSON, err = json.Marshal(ossConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = ossConfig.Init()
if err != nil {
this.Fail("校验OSS配置时出错" + err.Error())
return
}
} else { // 普通源站
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
}
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
var portIndex = strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.Fail("地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
// 检查端口号
if port == "0" {
this.Fail("端口号不能为0")
}
if !configutils.HasVariables(port) {
// 必须是整数
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
this.Fail("端口号只能为整数")
}
var portInt = types.Int(port)
if portInt == 0 {
this.Fail("端口号不能为0")
}
if portInt > 65535 {
this.Fail("端口号不能大于65535")
}
}
pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
}
addrConfig = &serverconfigs.NetworkAddressConfig{
Protocol: serverconfigs.Protocol(params.Protocol),
Host: host,
PortRange: port,
}
}
// 专属域名
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err := json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.ErrorPage(err)
return
}
// 去除可能误加的斜杠
for index, domain := range domains {
domains[index] = strings.TrimSuffix(domain, "/")
}
}
resp, err := this.RPC().OriginRPC().CreateOrigin(this.AdminContext(), &pb.CreateOriginRequest{
Name: "",
Addr: pbAddr,
OssJSON: ossJSON,
Description: "",
Weight: 10,
IsOn: true,
Domains: domains,
Host: params.Host,
FollowPort: params.FollowPort,
Http2Enabled: params.Http2Enabled,
})
if err != nil {
this.ErrorPage(err)
return
}
var origin = &serverconfigs.OriginConfig{
Id: resp.OriginId,
IsOn: true,
Addr: addrConfig,
OSS: ossConfig,
}
this.Data["origin"] = maps.Map{
"id": resp.OriginId,
"isOn": true,
"addr": addrConfig,
"addrSummary": origin.AddrSummary(),
}
// 创建日志
defer this.CreateLogInfo(codes.ServerOrigin_LogCreateOrigin, resp.OriginId)
this.Success()
}

View File

@@ -0,0 +1,20 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package servers
import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
"github.com/iwind/TeaGo/maps"
)
func (this *AddOriginPopupAction) getOSSHook() {
this.Data["ossTypes"] = []maps.Map{}
this.Data["ossBucketParams"] = []maps.Map{}
this.Data["ossForm"] = ""
}
func (this *AddOriginPopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
goNext = true
return
}

View File

@@ -0,0 +1,31 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package servers
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/origins/originutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
"github.com/iwind/TeaGo/maps"
"os"
)
func (this *AddOriginPopupAction) getOSSHook() {
if !teaconst.IsPlus {
this.Data["ossTypes"] = []maps.Map{}
this.Data["ossBucketParams"] = []maps.Map{}
} else {
this.Data["ossTypes"] = ossconfigs.FindAllOSSTypes()
this.Data["ossBucketParams"] = ossconfigs.FindAllOSSBucketParamDefinitions()
}
// 表单
var formFile = originutils.OSSTemplateFile("addPopup_oss_plus.html")
formData, _ := os.ReadFile(formFile)
this.Data["ossForm"] = string(formData)
}
func (this *AddOriginPopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
return originutils.ParseOSSFromRequest(protocol, this.Object())
}

View File

@@ -0,0 +1,124 @@
package servers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
type AddPortPopupAction struct {
actionutils.ParentAction
}
func (this *AddPortPopupAction) Init() {
this.Nav("", "", "")
}
func (this *AddPortPopupAction) RunGet(params struct {
ServerType string
Protocol string
From string
SupportRange bool
}) {
this.Data["from"] = params.From
protocols := serverconfigs.FindAllServerProtocolsForType(params.ServerType)
if len(params.Protocol) > 0 {
result := []maps.Map{}
for _, p := range protocols {
if p.GetString("code") == params.Protocol {
result = append(result, p)
}
}
protocols = result
}
this.Data["protocols"] = protocols
this.Data["supportRange"] = params.SupportRange
this.Show()
}
func (this *AddPortPopupAction) RunPost(params struct {
SupportRange bool
Protocol string
Address string
Must *actions.Must
}) {
// 校验地址
addr := maps.Map{
"protocol": params.Protocol,
"host": "",
"portRange": "",
"minPort": 0,
"maxPort": 0,
}
var portRegexp = regexp.MustCompile(`^\d+$`)
if portRegexp.MatchString(params.Address) { // 单个端口
addr["portRange"] = this.checkPort(params.Address)
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(params.Address) { // Port1-Port2
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(params.Address)
} else if strings.Contains(params.Address, ":") { // IP:Port
index := strings.LastIndex(params.Address, ":")
addr["host"] = strings.TrimSpace(params.Address[:index])
port := strings.TrimSpace(params.Address[index+1:])
if portRegexp.MatchString(port) {
addr["portRange"] = this.checkPort(port)
} else if params.SupportRange && regexp.MustCompile(`^\d+\s*-\s*\d+$`).MatchString(port) { // Port1-Port2
addr["portRange"], addr["minPort"], addr["maxPort"] = this.checkPortRange(port)
} else {
this.FailField("address", "请输入正确的端口或者网络地址")
}
} else {
this.FailField("address", "请输入正确的端口或者网络地址")
}
this.Data["address"] = addr
this.Success()
}
func (this *AddPortPopupAction) checkPort(port string) (portRange string) {
var intPort = types.Int(port)
if intPort < 1 {
this.FailField("address", "端口号不能小于1")
}
if intPort > 65535 {
this.FailField("address", "端口号不能大于65535")
}
return port
}
func (this *AddPortPopupAction) checkPortRange(port string) (portRange string, minPort int, maxPort int) {
var pieces = strings.Split(port, "-")
var piece1 = strings.TrimSpace(pieces[0])
var piece2 = strings.TrimSpace(pieces[1])
var port1 = types.Int(piece1)
var port2 = types.Int(piece2)
if port1 < 1 {
this.FailField("address", "端口号不能小于1")
}
if port1 > 65535 {
this.FailField("address", "端口号不能大于65535")
}
if port2 < 1 {
this.FailField("address", "端口号不能小于1")
}
if port2 > 65535 {
this.FailField("address", "端口号不能大于65535")
}
if port1 > port2 {
port1, port2 = port2, port1
}
return types.String(port1) + "-" + types.String(port2), port1, port2
}

View File

@@ -0,0 +1,117 @@
package servers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net"
"net/url"
"regexp"
"strings"
)
type AddServerNamePopupAction struct {
actionutils.ParentAction
}
func (this *AddServerNamePopupAction) Init() {
this.Nav("", "", "")
}
func (this *AddServerNamePopupAction) RunGet(params struct{}) {
this.Show()
}
func (this *AddServerNamePopupAction) RunPost(params struct {
Mode string
ServerName string
ServerNames string
Must *actions.Must
}) {
if params.Mode == "single" {
var serverName = strings.ToLower(params.ServerName)
// 去除空格
serverName = regexp.MustCompile(`\s+`).ReplaceAllString(serverName, "")
// 是否包含了多个域名
var splitReg = regexp.MustCompile(`([,、|,;|])`)
if splitReg.MatchString(serverName) {
params.ServerNames = strings.Join(splitReg.Split(serverName, -1), "\n")
params.Mode = "multiple"
} else {
// 处理URL
if regexp.MustCompile(`^(?i)(http|https|ftp)://`).MatchString(serverName) {
u, err := url.Parse(serverName)
if err == nil && len(u.Host) > 0 {
serverName = u.Host
}
}
// 去除端口
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
host, _, err := net.SplitHostPort(serverName)
if err == nil && len(host) > 0 {
serverName = host
}
}
params.Must.
Field("serverName", serverName).
Require("请输入域名")
this.Data["serverName"] = maps.Map{
"name": serverName,
"type": "full",
}
this.Success()
return
}
}
if params.Mode == "multiple" {
if len(params.ServerNames) == 0 {
this.FailField("serverNames", "请输入至少域名")
}
var serverNames = []string{}
for _, line := range strings.Split(params.ServerNames, "\n") {
var serverName = strings.TrimSpace(line)
serverName = regexp.MustCompile(`\s+`).ReplaceAllString(serverName, "")
if len(serverName) == 0 {
continue
}
// 处理URL
if regexp.MustCompile(`^(?i)(http|https|ftp)://`).MatchString(serverName) {
u, err := url.Parse(serverName)
if err == nil && len(u.Host) > 0 {
serverName = u.Host
}
}
// 去除端口
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
host, _, err := net.SplitHostPort(serverName)
if err == nil && len(host) > 0 {
serverName = host
}
}
// 转成小写
serverName = strings.ToLower(serverName)
serverNames = append(serverNames, serverName)
}
this.Data["serverName"] = maps.Map{
"name": "",
"type": "full",
"subNames": serverNames,
}
}
this.Success()
}

View File

@@ -0,0 +1,101 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"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 {
ProviderCode string
}) {
this.Data["providerCode"] = params.ProviderCode
// 服务商列表
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.AcmeProviders {
providerMaps = append(providerMaps, maps.Map{
"name": provider.Name,
"code": provider.Code,
"description": provider.Description,
"requireEAB": provider.RequireEAB,
"eabDescription": provider.EabDescription,
})
}
this.Data["providers"] = providerMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
ProviderCode string
EabKid string
EabKey string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var accountId int64
defer func() {
this.CreateLogInfo(codes.ACMEProviderAccount_LogCreateACMEProviderAccount, accountId)
}()
params.Must.
Field("name", params.Name).
Require("请输入账号名称").
Field("providerCode", params.ProviderCode).
Require("请选择服务商")
providerResp, err := this.RPC().ACMEProviderRPC().FindACMEProviderWithCode(this.AdminContext(), &pb.FindACMEProviderWithCodeRequest{AcmeProviderCode: params.ProviderCode})
if err != nil {
this.ErrorPage(err)
return
}
var provider = providerResp.AcmeProvider
if provider == nil {
this.Fail("请选择服务商")
}
if provider.RequireEAB {
params.Must.
Field("eabKid", params.EabKid).
Require("请输入EAB Kid").
Field("eabKey", params.EabKey).
Require("请输入EAB HMAC Key")
}
createResp, err := this.RPC().ACMEProviderAccountRPC().CreateACMEProviderAccount(this.AdminContext(), &pb.CreateACMEProviderAccountRequest{
Name: params.Name,
ProviderCode: params.ProviderCode,
EabKid: params.EabKid,
EabKey: params.EabKey,
})
if err != nil {
this.ErrorPage(err)
return
}
accountId = createResp.AcmeProviderAccountId
this.Success()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
AccountId int64
}) {
defer this.CreateLogInfo(codes.ACMEProviderAccount_LogDeleteACMEProviderAccount, params.AccountId)
_, err := this.RPC().ACMEProviderAccountRPC().DeleteACMEProviderAccount(this.AdminContext(), &pb.DeleteACMEProviderAccountRequest{AcmeProviderAccountId: params.AccountId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,61 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "account")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().ACMEProviderAccountRPC().CountAllEnabledACMEProviderAccounts(this.AdminContext(), &pb.CountAllEnabledACMEProviderAccountsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
accountsResp, err := this.RPC().ACMEProviderAccountRPC().ListEnabledACMEProviderAccounts(this.AdminContext(), &pb.ListEnabledACMEProviderAccountsRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var accountMaps = []maps.Map{}
for _, account := range accountsResp.AcmeProviderAccounts {
var providerMap maps.Map
if account.AcmeProvider != nil {
providerMap = maps.Map{
"name": account.AcmeProvider.Name,
"code": account.AcmeProvider.Code,
"requireEAB": account.AcmeProvider.RequireEAB,
}
}
accountMaps = append(accountMaps, maps.Map{
"id": account.Id,
"isOn": account.IsOn,
"name": account.Name,
"eabKid": account.EabKid,
"eabKey": account.EabKey,
"provider": providerMap,
})
}
this.Data["accounts"] = accountMaps
this.Show()
}

View File

@@ -0,0 +1,109 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accounts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
AccountId int64
}) {
// 账号信息
accountResp, err := this.RPC().ACMEProviderAccountRPC().FindEnabledACMEProviderAccount(this.AdminContext(), &pb.FindEnabledACMEProviderAccountRequest{AcmeProviderAccountId: params.AccountId})
if err != nil {
this.ErrorPage(err)
return
}
var account = accountResp.AcmeProviderAccount
if account == nil {
this.NotFound("ACMEProviderAccount", params.AccountId)
return
}
var providerMap maps.Map
if account.AcmeProvider != nil {
providerMap = maps.Map{
"name": account.AcmeProvider.Name,
"code": account.AcmeProvider.Code,
"description": account.AcmeProvider.Description,
"eabDescription": account.AcmeProvider.EabDescription,
"requireEAB": account.AcmeProvider.RequireEAB,
}
}
this.Data["account"] = maps.Map{
"id": account.Id,
"name": account.Name,
"isOn": account.IsOn,
"providerCode": account.ProviderCode,
"eabKid": account.EabKid,
"eabKey": account.EabKey,
"provider": providerMap,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
AccountId int64
Name string
ProviderCode string
EabKid string
EabKey string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.ACMEProviderAccount_LogUpdateACMEProviderAccount, params.AccountId)
params.Must.
Field("name", params.Name).
Require("请输入账号名称").
Field("providerCode", params.ProviderCode).
Require("请选择服务商")
providerResp, err := this.RPC().ACMEProviderRPC().FindACMEProviderWithCode(this.AdminContext(), &pb.FindACMEProviderWithCodeRequest{AcmeProviderCode: params.ProviderCode})
if err != nil {
this.ErrorPage(err)
return
}
var provider = providerResp.AcmeProvider
if provider == nil {
this.Fail("请选择服务商")
}
if provider.RequireEAB {
params.Must.
Field("eabKid", params.EabKid).
Require("请输入EAB Kid").
Field("eabKey", params.EabKey).
Require("请输入EAB HMAC Key")
}
_, err = this.RPC().ACMEProviderAccountRPC().UpdateACMEProviderAccount(this.AdminContext(), &pb.UpdateACMEProviderAccountRequest{
AcmeProviderAccountId: params.AccountId,
Name: params.Name,
EabKid: params.EabKid,
EabKey: params.EabKey,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,150 @@
package acme
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
this.Nav("", "", "create")
}
func (this *CreateAction) RunGet(params struct{}) {
// 证书服务商
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.AcmeProviders {
providerMaps = append(providerMaps, maps.Map{
"name": provider.Name,
"code": provider.Code,
})
}
this.Data["providers"] = providerMaps
// 域名解析服务商
dnsProvidersResp, err := this.RPC().DNSProviderRPC().FindAllEnabledDNSProviders(this.AdminContext(), &pb.FindAllEnabledDNSProvidersRequest{
AdminId: this.AdminId(),
UserId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
dnsProviderMaps := []maps.Map{}
for _, provider := range dnsProvidersResp.DnsProviders {
dnsProviderMaps = append(dnsProviderMaps, maps.Map{
"id": provider.Id,
"name": provider.Name,
"typeName": provider.TypeName,
})
}
this.Data["dnsProviders"] = dnsProviderMaps
this.Show()
}
func (this *CreateAction) RunPost(params struct {
PlatformUserId int64
TaskId int64
AuthType string
AcmeUserId int64
DnsProviderId int64
DnsDomain string
Domains []string
AutoRenew bool
AuthURL string
Must *actions.Must
}) {
if params.AuthType != "dns" && params.AuthType != "http" {
this.Fail("无法识别的认证方式'" + params.AuthType + "'")
}
if params.AcmeUserId <= 0 {
this.Fail("请选择一个申请证书的用户")
}
// 校验DNS相关信息
dnsDomain := strings.ToLower(params.DnsDomain)
if params.AuthType == "dns" {
if params.DnsProviderId <= 0 {
this.Fail("请选择DNS服务商")
}
if len(params.DnsDomain) == 0 {
this.Fail("请输入顶级域名")
}
if !domainutils.ValidateDomainFormat(dnsDomain) {
this.Fail("请输入正确的顶级域名")
}
}
if len(params.Domains) == 0 {
this.Fail("请输入证书域名列表")
}
var realDomains = []string{}
for _, domain := range params.Domains {
domain = strings.ToLower(domain)
if params.AuthType == "dns" { // DNS认证
if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {
this.Fail("证书域名中的" + domain + "和顶级域名不一致")
}
} else if params.AuthType == "http" { // HTTP认证
if strings.Contains(domain, "*") {
this.Fail("在HTTP认证时域名" + domain + "不能包含通配符")
}
}
realDomains = append(realDomains, domain)
}
if params.TaskId == 0 {
createResp, err := this.RPC().ACMETaskRPC().CreateACMETask(this.AdminContext(), &pb.CreateACMETaskRequest{
UserId: params.PlatformUserId,
AuthType: params.AuthType,
AcmeUserId: params.AcmeUserId,
DnsProviderId: params.DnsProviderId,
DnsDomain: dnsDomain,
Domains: realDomains,
AutoRenew: params.AutoRenew,
AuthURL: params.AuthURL,
})
if err != nil {
this.ErrorPage(err)
return
}
params.TaskId = createResp.AcmeTaskId
defer this.CreateLogInfo(codes.ACMETask_LogCreateACMETask, createResp.AcmeTaskId)
} else {
_, err := this.RPC().ACMETaskRPC().UpdateACMETask(this.AdminContext(), &pb.UpdateACMETaskRequest{
AcmeTaskId: params.TaskId,
AcmeUserId: params.AcmeUserId,
DnsProviderId: params.DnsProviderId,
DnsDomain: dnsDomain,
Domains: realDomains,
AutoRenew: params.AutoRenew,
AuthURL: params.AuthURL,
})
if err != nil {
this.ErrorPage(err)
return
}
defer this.CreateLogInfo(codes.ACMETask_LogUpdateACMETask, params.TaskId)
}
this.Data["taskId"] = params.TaskId
this.Success()
}

View File

@@ -0,0 +1,25 @@
package acme
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteTaskAction struct {
actionutils.ParentAction
}
func (this *DeleteTaskAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo(codes.ACMETask_LogDeleteACMETask, params.TaskId)
_, err := this.RPC().ACMETaskRPC().DeleteACMETask(this.AdminContext(), &pb.DeleteACMETaskRequest{AcmeTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,298 @@
package acme
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "task")
this.SecondMenu("list")
}
func (this *IndexAction) RunGet(params struct {
UserId int64
Type string
Keyword string
UserType string
}) {
this.Data["type"] = params.Type
this.Data["keyword"] = params.Keyword
this.Data["userType"] = params.UserType
var userOnly = params.UserId > 0 || params.UserType == "user"
// 当前用户
this.Data["searchingUserId"] = params.UserId
var userMap = maps.Map{
"id": 0,
"username": "",
"fullname": "",
}
if params.UserId > 0 {
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user != nil {
userMap = maps.Map{
"id": user.Id,
"username": user.Username,
"fullname": user.Fullname,
}
}
}
this.Data["user"] = userMap
var countAll int64
var countAvailable int64
var countExpired int64
var count7Days int64
var count30Days int64
// 计算数量
{
// all
resp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countAll = resp.Count
// available
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
IsAvailable: true,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countAvailable = resp.Count
// expired
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
IsExpired: true,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countExpired = resp.Count
// expire in 7 days
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 7,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
count7Days = resp.Count
// expire in 30 days
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.AdminContext(), &pb.CountAllEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 30,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
count30Days = resp.Count
}
this.Data["countAll"] = countAll
this.Data["countAvailable"] = countAvailable
this.Data["countExpired"] = countExpired
this.Data["count7Days"] = count7Days
this.Data["count30Days"] = count30Days
// 分页
var page *actionutils.Page
var tasksResp *pb.ListEnabledACMETasksResponse
var err error
switch params.Type {
case "":
page = this.NewPage(countAll)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "available":
page = this.NewPage(countAvailable)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
IsAvailable: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "expired":
page = this.NewPage(countExpired)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
IsExpired: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "7days":
page = this.NewPage(count7Days)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 7,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "30days":
page = this.NewPage(count30Days)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
ExpiringDays: 30,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
default:
page = this.NewPage(countAll)
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.AdminContext(), &pb.ListEnabledACMETasksRequest{
UserId: params.UserId,
Keyword: params.Keyword,
UserOnly: userOnly,
Offset: page.Offset,
Size: page.Size,
})
}
if err != nil {
this.ErrorPage(err)
return
}
this.Data["page"] = page.AsHTML()
var taskMaps = []maps.Map{}
for _, task := range tasksResp.AcmeTasks {
if task.AcmeUser == nil {
continue
}
// 服务商
var providerMap maps.Map
if task.AcmeUser.AcmeProvider != nil {
providerMap = maps.Map{
"name": task.AcmeUser.AcmeProvider.Name,
"code": task.AcmeUser.AcmeProvider.Code,
}
}
// 账号
var accountMap maps.Map
if task.AcmeUser.AcmeProviderAccount != nil {
accountMap = maps.Map{
"id": task.AcmeUser.AcmeProviderAccount.Id,
"name": task.AcmeUser.AcmeProviderAccount.Name,
}
}
// DNS服务商
dnsProviderMap := maps.Map{}
if task.AuthType == "dns" && task.DnsProvider != nil {
dnsProviderMap = maps.Map{
"id": task.DnsProvider.Id,
"name": task.DnsProvider.Name,
}
}
// 证书
var certMap maps.Map = nil
if task.SslCert != nil {
certMap = maps.Map{
"id": task.SslCert.Id,
"name": task.SslCert.Name,
"beginTime": timeutil.FormatTime("Y-m-d", task.SslCert.TimeBeginAt),
"endTime": timeutil.FormatTime("Y-m-d", task.SslCert.TimeEndAt),
}
}
// 日志
var logMap maps.Map = nil
if task.LatestACMETaskLog != nil {
logMap = maps.Map{
"id": task.LatestACMETaskLog.Id,
"isOk": task.LatestACMETaskLog.IsOk,
"error": task.LatestACMETaskLog.Error,
"createdTime": timeutil.FormatTime("m-d", task.CreatedAt),
}
}
// user
userResp, err := this.RPC().ACMETaskRPC().FindACMETaskUser(this.AdminContext(), &pb.FindACMETaskUserRequest{AcmeTaskId: task.Id})
if err != nil {
this.ErrorPage(err)
return
}
var taskUserMap = maps.Map{
"id": 0,
}
if userResp.User != nil {
taskUserMap = maps.Map{
"id": userResp.User.Id,
"username": userResp.User.Username,
"fullname": userResp.User.Fullname,
}
}
taskMaps = append(taskMaps, maps.Map{
"id": task.Id,
"authType": task.AuthType,
"acmeUser": maps.Map{
"id": task.AcmeUser.Id,
"email": task.AcmeUser.Email,
"provider": providerMap,
"account": accountMap,
},
"dnsProvider": dnsProviderMap,
"dnsDomain": task.DnsDomain,
"domains": task.Domains,
"autoRenew": task.AutoRenew,
"cert": certMap,
"log": logMap,
"user": taskUserMap,
})
}
this.Data["tasks"] = taskMaps
this.Show()
}

View File

@@ -0,0 +1,30 @@
package acme
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type RunAction struct {
actionutils.ParentAction
}
func (this *RunAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo(codes.ACMETask_LogRunACMETask, params.TaskId)
runResp, err := this.RPC().ACMETaskRPC().RunACMETask(this.AdminContext(), &pb.RunACMETaskRequest{AcmeTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)
return
}
if runResp.IsOk {
this.Data["certId"] = runResp.SslCertId
this.Success()
} else {
this.Fail(runResp.Error)
}
}

View File

@@ -0,0 +1,170 @@
package acme
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
type UpdateTaskPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateTaskPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateTaskPopupAction) RunGet(params struct {
TaskId int64
}) {
taskResp, err := this.RPC().ACMETaskRPC().FindEnabledACMETask(this.AdminContext(), &pb.FindEnabledACMETaskRequest{AcmeTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)
return
}
var task = taskResp.AcmeTask
if task == nil {
this.NotFound("acmeTask", params.TaskId)
return
}
var dnsProviderMap maps.Map
if task.DnsProvider != nil {
dnsProviderMap = maps.Map{
"id": task.DnsProvider.Id,
}
} else {
dnsProviderMap = maps.Map{
"id": 0,
}
}
var acmeUserMap maps.Map
if task.AcmeUser != nil {
acmeUserMap = maps.Map{
"id": task.AcmeUser.Id,
}
} else {
acmeUserMap = maps.Map{
"id": 0,
}
}
this.Data["task"] = maps.Map{
"id": task.Id,
"authType": task.AuthType,
"acmeUser": acmeUserMap,
"dnsDomain": task.DnsDomain,
"domains": task.Domains,
"autoRenew": task.AutoRenew,
"isOn": task.IsOn,
"authURL": task.AuthURL,
"dnsProvider": dnsProviderMap,
}
// 域名解析服务商
providersResp, err := this.RPC().DNSProviderRPC().FindAllEnabledDNSProviders(this.AdminContext(), &pb.FindAllEnabledDNSProvidersRequest{
AdminId: this.AdminId(),
UserId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.DnsProviders {
providerMaps = append(providerMaps, maps.Map{
"id": provider.Id,
"name": provider.Name,
"typeName": provider.TypeName,
})
}
this.Data["providers"] = providerMaps
this.Show()
}
func (this *UpdateTaskPopupAction) RunPost(params struct {
TaskId int64
AuthType string
AcmeUserId int64
DnsProviderId int64
DnsDomain string
DomainsJSON []byte
AutoRenew bool
AuthURL string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.ACMETask_LogUpdateACMETask, params.TaskId)
if params.AuthType != "dns" && params.AuthType != "http" {
this.Fail("无法识别的认证方式'" + params.AuthType + "'")
}
if params.AcmeUserId <= 0 {
this.Fail("请选择一个申请证书的用户")
}
dnsDomain := strings.ToLower(params.DnsDomain)
if params.AuthType == "dns" {
if params.DnsProviderId <= 0 {
this.Fail("请选择DNS服务商")
}
if len(params.DnsDomain) == 0 {
this.Fail("请输入顶级域名")
}
if !domainutils.ValidateDomainFormat(dnsDomain) {
this.Fail("请输入正确的顶级域名")
}
}
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err := json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.Fail("解析域名数据失败:" + err.Error())
return
}
}
if len(domains) == 0 {
this.Fail("请输入证书域名列表")
}
var realDomains = []string{}
for _, domain := range domains {
domain = strings.ToLower(domain)
if params.AuthType == "dns" {
if !strings.HasSuffix(domain, "."+dnsDomain) && domain != dnsDomain {
this.Fail("证书域名中的" + domain + "和顶级域名不一致")
}
} else if params.AuthType == "http" { // HTTP认证
if strings.Contains(domain, "*") {
this.Fail("在HTTP认证时域名" + domain + "不能包含通配符")
}
}
realDomains = append(realDomains, domain)
}
_, err := this.RPC().ACMETaskRPC().UpdateACMETask(this.AdminContext(), &pb.UpdateACMETaskRequest{
AcmeTaskId: params.TaskId,
AcmeUserId: params.AcmeUserId,
DnsProviderId: params.DnsProviderId,
DnsDomain: dnsDomain,
Domains: realDomains,
AutoRenew: params.AutoRenew,
AuthURL: params.AuthURL,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,44 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package acme
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type UserOptionsAction struct {
actionutils.ParentAction
}
func (this *UserOptionsAction) RunPost(params struct {
PlatformUserId int64
}) {
// 获取所有可用的用户
usersResp, err := this.RPC().ACMEUserRPC().FindAllACMEUsers(this.AdminContext(), &pb.FindAllACMEUsersRequest{
AdminId: 0,
UserId: params.PlatformUserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var userMaps = []maps.Map{}
for _, user := range usersResp.AcmeUsers {
description := user.Description
if len(description) > 0 {
description = "" + description + ""
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"description": description,
"email": user.Email,
"providerCode": user.AcmeProviderCode,
})
}
this.Data["users"] = userMaps
this.Success()
}

View File

@@ -0,0 +1,33 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type AccountsWithCodeAction struct {
actionutils.ParentAction
}
func (this *AccountsWithCodeAction) RunPost(params struct {
Code string
}) {
accountsResp, err := this.RPC().ACMEProviderAccountRPC().FindAllACMEProviderAccountsWithProviderCode(this.AdminContext(), &pb.FindAllACMEProviderAccountsWithProviderCodeRequest{AcmeProviderCode: params.Code})
if err != nil {
this.ErrorPage(err)
return
}
var accountMaps = []maps.Map{}
for _, account := range accountsResp.AcmeProviderAccounts {
accountMaps = append(accountMaps, maps.Map{
"id": account.Id,
"name": account.Name,
})
}
this.Data["accounts"] = accountMaps
this.Success()
}

View File

@@ -0,0 +1,133 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"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 {
PlatformUserId int64
ProviderCode string
}) {
this.Data["platformUserId"] = params.PlatformUserId
this.Data["providerCode"] = params.ProviderCode
// 平台用户信息
this.Data["platformUser"] = nil
if params.PlatformUserId > 0 {
platformUserResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.PlatformUserId})
if err != nil {
this.ErrorPage(err)
return
}
var platformUser = platformUserResp.User
if platformUser != nil {
this.Data["platformUser"] = maps.Map{
"id": platformUser.Id,
"username": platformUser.Username,
"fullname": platformUser.Fullname,
}
}
}
// 服务商
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.AdminContext(), &pb.FindAllACMEProvidersRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var providerMaps = []maps.Map{}
for _, provider := range providersResp.AcmeProviders {
providerMaps = append(providerMaps, maps.Map{
"code": provider.Code,
"name": provider.Name,
"requireEAB": provider.RequireEAB,
})
}
this.Data["providers"] = providerMaps
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
PlatformUserId int64
Email string
ProviderCode string
AccountId int64
Description string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
params.Must.
Field("email", params.Email).
Require("请输入邮箱").
Email("请输入正确的邮箱格式").
Field("providerCode", params.ProviderCode).
Require("请选择所属服务商")
providerResp, err := this.RPC().ACMEProviderRPC().FindACMEProviderWithCode(this.AdminContext(), &pb.FindACMEProviderWithCodeRequest{
AcmeProviderCode: params.ProviderCode,
})
if err != nil {
this.ErrorPage(err)
return
}
if providerResp.AcmeProvider == nil {
this.Fail("找不到要选择的证书")
}
if providerResp.AcmeProvider.RequireEAB {
if params.AccountId <= 0 {
this.Fail("此服务商要求必须选择或创建服务商账号")
}
// 同一个账号只能有一个用户
countResp, err := this.RPC().ACMEUserRPC().
CountACMEUsers(this.AdminContext(), &pb.CountAcmeUsersRequest{
AcmeProviderAccountId: params.AccountId,
})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此服务商账号已被别的用户使用,请换成别的账号")
}
}
createResp, err := this.RPC().ACMEUserRPC().CreateACMEUser(this.AdminContext(), &pb.CreateACMEUserRequest{
UserId: params.PlatformUserId,
Email: params.Email,
Description: params.Description,
AcmeProviderCode: params.ProviderCode,
AcmeProviderAccountId: params.AccountId,
})
if err != nil {
this.ErrorPage(err)
return
}
// 返回数据
this.Data["acmeUser"] = maps.Map{
"id": createResp.AcmeUserId,
"description": params.Description,
"email": params.Email,
"providerCode": params.ProviderCode,
}
// 日志
defer this.CreateLogInfo(codes.ACMEUser_LogCreateACMEUser, createResp.AcmeUserId)
this.Success()
}

View File

@@ -0,0 +1,34 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
UserId int64
}) {
defer this.CreateLogInfo(codes.ACMEUser_LogDeleteACMEUser, params.UserId)
countResp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasksWithACMEUserId(this.AdminContext(), &pb.CountAllEnabledACMETasksWithACMEUserIdRequest{AcmeUserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("有任务正在和这个用户关联,所以不能删除")
}
_, err = this.RPC().ACMEUserRPC().DeleteACMEUser(this.AdminContext(), &pb.DeleteACMEUserRequest{AcmeUserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,73 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "user")
}
func (this *IndexAction) RunGet(params struct{}) {
countResp, err := this.RPC().ACMEUserRPC().CountACMEUsers(this.AdminContext(), &pb.CountAcmeUsersRequest{
AdminId: this.AdminId(),
UserId: 0,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
this.Data["page"] = page.AsHTML()
usersResp, err := this.RPC().ACMEUserRPC().ListACMEUsers(this.AdminContext(), &pb.ListACMEUsersRequest{
AdminId: this.AdminId(),
UserId: 0,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
userMaps := []maps.Map{}
for _, user := range usersResp.AcmeUsers {
// 服务商
var providerMap maps.Map
if user.AcmeProvider != nil {
providerMap = maps.Map{
"name": user.AcmeProvider.Name,
"code": user.AcmeProvider.Code,
}
}
// 账号
var accountMap maps.Map
if user.AcmeProviderAccount != nil {
accountMap = maps.Map{
"id": user.AcmeProviderAccount.Id,
"name": user.AcmeProviderAccount.Name,
}
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"email": user.Email,
"description": user.Description,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", user.CreatedAt),
"provider": providerMap,
"account": accountMap,
})
}
this.Data["users"] = userMaps
this.Show()
}

View File

@@ -0,0 +1,15 @@
package users
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
type SelectPopupAction struct {
actionutils.ParentAction
}
func (this *SelectPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectPopupAction) RunGet(params struct{}) {
this.Show()
}

View File

@@ -0,0 +1,81 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
UserId int64
}) {
userResp, err := this.RPC().ACMEUserRPC().FindEnabledACMEUser(this.AdminContext(), &pb.FindEnabledACMEUserRequest{AcmeUserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
user := userResp.AcmeUser
if user == nil {
this.NotFound("acmeUser", params.UserId)
return
}
// 服务商
var providerMap maps.Map
if user.AcmeProvider != nil {
providerMap = maps.Map{
"name": user.AcmeProvider.Name,
"code": user.AcmeProvider.Code,
}
}
// 账号
var accountMap maps.Map
if user.AcmeProviderAccount != nil {
accountMap = maps.Map{
"id": user.AcmeProviderAccount.Id,
"name": user.AcmeProviderAccount.Name,
}
}
this.Data["user"] = maps.Map{
"id": user.Id,
"email": user.Email,
"description": user.Description,
"provider": providerMap,
"account": accountMap,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
UserId int64
Description string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.ACMEUser_LogUpdateACMEUser, params.UserId)
_, err := this.RPC().ACMEUserRPC().UpdateACMEUser(this.AdminContext(), &pb.UpdateACMEUserRequest{
AcmeUserId: params.UserId,
Description: params.Description,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,77 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type CertPopupAction struct {
actionutils.ParentAction
}
func (this *CertPopupAction) Init() {
}
func (this *CertPopupAction) RunGet(params struct {
CertId int64
}) {
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
var certConfig = &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
var reverseCommonNames = []string{}
for i := len(certConfig.CommonNames) - 1; i >= 0; i-- {
reverseCommonNames = append(reverseCommonNames, certConfig.CommonNames[i])
}
this.Data["info"] = maps.Map{
"id": certConfig.Id,
"name": certConfig.Name,
"description": certConfig.Description,
"isOn": certConfig.IsOn,
"isAvailable": certConfig.TimeEndAt >= time.Now().Unix(),
"commonNames": reverseCommonNames,
"dnsNames": certConfig.DNSNames,
// TODO 检查是否为7天或30天内过期
"beginTime": timeutil.FormatTime("Y-m-d H:i:s", certConfig.TimeBeginAt),
"endTime": timeutil.FormatTime("Y-m-d H:i:s", certConfig.TimeEndAt),
"isCA": certConfig.IsCA,
"certString": string(certConfig.CertData),
"keyString": string(certConfig.KeyData),
}
// 引入的服务
serversResp, err := this.RPC().ServerRPC().FindAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.FindAllEnabledServersWithSSLCertIdRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
var serverMaps = []maps.Map{}
for _, server := range serversResp.Servers {
serverMaps = append(serverMaps, maps.Map{
"id": server.Id,
"isOn": server.IsOn,
"name": server.Name,
"type": server.Type,
})
}
this.Data["servers"] = serverMaps
this.Show()
}

View File

@@ -0,0 +1,60 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
)
// 所有相关数据
type DatajsAction struct {
actionutils.ParentAction
}
func (this *DatajsAction) Init() {
}
func (this *DatajsAction) RunGet(params struct{}) {
this.AddHeader("Content-Type", "text/javascript; charset=utf-8")
{
cipherSuitesJSON, err := json.Marshal(sslconfigs.AllTLSCipherSuites)
if err != nil {
this.ErrorPage(err)
return
}
this.WriteString("window.SSL_ALL_CIPHER_SUITES = " + string(cipherSuitesJSON) + ";\n")
}
{
modernCipherSuitesJSON, err := json.Marshal(sslconfigs.TLSModernCipherSuites)
if err != nil {
this.ErrorPage(err)
return
}
this.WriteString("window.SSL_MODERN_CIPHER_SUITES = " + string(modernCipherSuitesJSON) + ";\n")
}
{
intermediateCipherSuitesJSON, err := json.Marshal(sslconfigs.TLSIntermediateCipherSuites)
if err != nil {
this.ErrorPage(err)
return
}
this.WriteString("window.SSL_INTERMEDIATE_CIPHER_SUITES = " + string(intermediateCipherSuitesJSON) + ";\n")
}
{
sslVersionsJSON, err := json.Marshal(sslconfigs.AllTlsVersions)
if err != nil {
this.ErrorPage(err)
return
}
this.WriteString("window.SSL_ALL_VERSIONS = " + string(sslVersionsJSON) + ";\n")
}
{
clientAuthTypesJSON, err := json.Marshal(sslconfigs.AllSSLClientAuthTypes())
if err != nil {
this.ErrorPage(err)
return
}
this.WriteString("window.SSL_ALL_CLIENT_AUTH_TYPES = " + string(clientAuthTypesJSON) + ";\n")
}
}

View File

@@ -0,0 +1,52 @@
package certs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
CertId int64
}) {
// 创建日志
defer this.CreateLogInfo(codes.SSLCert_LogDeleteSSLCert, params.CertId)
// 是否正在被服务使用
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此证书正在被某些服务引用,请先修改服务后再删除。")
}
// 是否正在被API节点使用
countResp, err = this.RPC().APINodeRPC().CountAllEnabledAPINodesWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledAPINodesWithSSLCertIdRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此证书正在被某些API节点引用请先修改API节点后再删除")
}
err = this.filterDelete(params.CertId)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().SSLCertRPC().DeleteSSLCert(this.AdminContext(), &pb.DeleteSSLCertRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package certs
func (this *DeleteAction) filterDelete(certId int64) error {
return nil
}

View File

@@ -0,0 +1,35 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package certs
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
func (this *DeleteAction) filterDelete(certId int64) error {
// 是否正在被用户节点使用
if teaconst.IsPlus {
countResp, err := this.RPC().UserNodeRPC().CountAllEnabledUserNodesWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledUserNodesWithSSLCertIdRequest{SslCertId: certId})
if err != nil {
return err
}
if countResp.Count > 0 {
this.Fail("此证书正在被某些用户节点引用,请先修改相关用户节点后再删除")
}
}
// 是否正在被NS集群使用
if teaconst.IsPlus {
countResp, err := this.RPC().NSClusterRPC().CountAllNSClustersWithSSLCertId(this.AdminContext(), &pb.CountAllNSClustersWithSSLCertIdRequest{SslCertId: certId})
if err != nil {
return err
}
if countResp.Count > 0 {
this.Fail("此证书正在被某些DNS集群节点引用请先修改相关DNS集群设置后再删除")
}
}
return nil
}

View File

@@ -0,0 +1,40 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"strconv"
)
type DownloadCertAction struct {
actionutils.ParentAction
}
func (this *DownloadCertAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadCertAction) RunGet(params struct {
CertId int64
}) {
defer this.CreateLogInfo(codes.SSLCert_LogDownloadSSLCert, params.CertId)
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
certConfig := &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\"cert-"+strconv.FormatInt(params.CertId, 10)+".pem\";")
_, _ = this.Write(certConfig.CertData)
}

View File

@@ -0,0 +1,40 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"strconv"
)
type DownloadKeyAction struct {
actionutils.ParentAction
}
func (this *DownloadKeyAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadKeyAction) RunGet(params struct {
CertId int64
}) {
defer this.CreateLogInfo(codes.SSLCert_LogDownloadSSLCertKey, params.CertId)
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
certConfig := &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\"key-"+strconv.FormatInt(params.CertId, 10)+".pem\";")
_, _ = this.Write(certConfig.KeyData)
}

View File

@@ -0,0 +1,83 @@
package certs
import (
"archive/zip"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"strconv"
)
type DownloadZipAction struct {
actionutils.ParentAction
}
func (this *DownloadZipAction) Init() {
this.Nav("", "", "")
}
func (this *DownloadZipAction) RunGet(params struct {
CertId int64
}) {
defer this.CreateLogInfo(codes.SSLCert_LogDownloadSSLCertZip, params.CertId)
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
certConfig := &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
z := zip.NewWriter(this.ResponseWriter)
defer func() {
_ = z.Close()
}()
this.AddHeader("Content-Disposition", "attachment; filename=\"cert-"+strconv.FormatInt(params.CertId, 10)+".zip\";")
// cert
{
w, err := z.Create("cert.pem")
if err != nil {
this.ErrorPage(err)
return
}
_, err = w.Write(certConfig.CertData)
if err != nil {
this.ErrorPage(err)
return
}
err = z.Flush()
if err != nil {
this.ErrorPage(err)
return
}
}
// key
if !certConfig.IsCA {
w, err := z.Create("key.pem")
if err != nil {
this.ErrorPage(err)
return
}
_, err = w.Write(certConfig.KeyData)
if err != nil {
this.ErrorPage(err)
return
}
err = z.Flush()
if err != nil {
this.ErrorPage(err)
return
}
}
}

View File

@@ -0,0 +1,62 @@
package certs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
)
type Helper struct {
helpers.LangHelper
}
func NewHelper() *Helper {
return &Helper{}
}
func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) {
var action = actionPtr.Object()
if action.Request.Method != http.MethodGet {
return
}
action.Data["teaMenu"] = "servers"
var countOCSP int64 = 0
parentAction, ok := actionPtr.(actionutils.ActionInterface)
if ok {
countOCSPResp, err := parentAction.RPC().SSLCertRPC().CountAllSSLCertsWithOCSPError(parentAction.AdminContext(), &pb.CountAllSSLCertsWithOCSPErrorRequest{})
if err == nil {
countOCSP = countOCSPResp.Count
}
}
var ocspMenuName = this.Lang(actionPtr, codes.SSLCert_MenuOCSP)
if countOCSP > 0 {
ocspMenuName += "(" + types.String(countOCSP) + ")"
}
var menu = []maps.Map{
{
"name": this.Lang(actionPtr, codes.SSLCert_MenuCerts),
"url": "/servers/certs",
"isActive": action.Data.GetString("leftMenuItem") == "cert",
},
{
"name": this.Lang(actionPtr, codes.SSLCert_MenuApply),
"url": "/servers/certs/acme",
"isActive": action.Data.GetString("leftMenuItem") == "acme",
},
{
"name": ocspMenuName,
"url": "/servers/certs/ocsp",
"isActive": action.Data.GetString("leftMenuItem") == "ocsp",
},
}
action.Data["leftMenuItems"] = menu
}

View File

@@ -0,0 +1,285 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.FirstMenu("index")
}
func (this *IndexAction) RunGet(params struct {
UserId int64
Type string // [empty] | ca | 7days | ...
Keyword string
UserType string
}) {
this.Data["type"] = params.Type
this.Data["keyword"] = params.Keyword
if params.UserId > 0 {
params.UserType = "user"
}
this.Data["userType"] = params.UserType
// 当前用户
this.Data["searchingUserId"] = params.UserId
var userMap = maps.Map{
"id": 0,
"username": "",
"fullname": "",
}
if params.UserId > 0 {
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user != nil {
userMap = maps.Map{
"id": user.Id,
"username": user.Username,
"fullname": user.Fullname,
}
}
}
this.Data["user"] = userMap
var countAll int64
var countCA int64
var countAvailable int64
var countExpired int64
var count7Days int64
var count30Days int64
var userOnly = params.UserType == "user" || params.UserId > 0
// 计算数量
{
// all
resp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countAll = resp.Count
// CA
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
IsCA: true,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countCA = resp.Count
// available
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
IsAvailable: true,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countAvailable = resp.Count
// expired
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
IsExpired: true,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
countExpired = resp.Count
// expire in 7 days
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
ExpiringDays: 7,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
count7Days = resp.Count
// expire in 30 days
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
ExpiringDays: 30,
Keyword: params.Keyword,
UserOnly: userOnly,
})
if err != nil {
this.ErrorPage(err)
return
}
count30Days = resp.Count
}
this.Data["countAll"] = countAll
this.Data["countCA"] = countCA
this.Data["countAvailable"] = countAvailable
this.Data["countExpired"] = countExpired
this.Data["count7Days"] = count7Days
this.Data["count30Days"] = count30Days
// 分页
var page *actionutils.Page
var listResp *pb.ListSSLCertsResponse
var err error
switch params.Type {
case "":
page = this.NewPage(countAll)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "ca":
page = this.NewPage(countCA)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
IsCA: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "available":
page = this.NewPage(countAvailable)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
IsAvailable: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "expired":
page = this.NewPage(countExpired)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
IsExpired: true,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
case "7days":
page = this.NewPage(count7Days)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
ExpiringDays: 7,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
})
case "30days":
page = this.NewPage(count30Days)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
ExpiringDays: 30,
Offset: page.Offset,
Size: page.Size,
Keyword: params.Keyword,
UserOnly: userOnly,
})
default:
page = this.NewPage(countAll)
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Keyword: params.Keyword,
UserOnly: userOnly,
Offset: page.Offset,
Size: page.Size,
})
}
if err != nil {
this.ErrorPage(err)
return
}
var certConfigs = []*sslconfigs.SSLCertConfig{}
err = json.Unmarshal(listResp.SslCertsJSON, &certConfigs)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["certs"] = certConfigs
var certMaps = []maps.Map{}
var nowTime = time.Now().Unix()
for _, certConfig := range certConfigs {
// count servers
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{
SslCertId: certConfig.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
// user
userResp, err := this.RPC().SSLCertRPC().FindSSLCertUser(this.AdminContext(), &pb.FindSSLCertUserRequest{SslCertId: certConfig.Id})
if err != nil {
this.ErrorPage(err)
return
}
var certUserMap = maps.Map{
"id": 0,
}
if userResp.User != nil {
certUserMap = maps.Map{
"id": userResp.User.Id,
"username": userResp.User.Username,
"fullname": userResp.User.Fullname,
}
}
certMaps = append(certMaps, maps.Map{
"isOn": certConfig.IsOn,
"beginDay": timeutil.FormatTime("Y-m-d", certConfig.TimeBeginAt),
"endDay": timeutil.FormatTime("Y-m-d", certConfig.TimeEndAt),
"isExpired": nowTime > certConfig.TimeEndAt,
"isAvailable": nowTime <= certConfig.TimeEndAt,
"countServers": countServersResp.Count,
"user": certUserMap,
})
}
this.Data["certInfos"] = certMaps
this.Data["page"] = page.AsHTML()
this.Show()
}

View File

@@ -0,0 +1,73 @@
package certs
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/acme"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/acme/accounts"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/acme/users"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/certs/ocsp"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(NewHelper()).
Data("teaMenu", "servers").
Data("teaSubMenu", "cert").
Prefix("/servers/certs").
Data("leftMenuItem", "cert").
Get("", new(IndexAction)).
GetPost("/uploadPopup", new(UploadPopupAction)).
GetPost("/uploadBatchPopup", new(UploadBatchPopupAction)).
Post("/delete", new(DeleteAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Get("/certPopup", new(CertPopupAction)).
Get("/viewKey", new(ViewKeyAction)).
Get("/viewCert", new(ViewCertAction)).
Get("/downloadKey", new(DownloadKeyAction)).
Get("/downloadCert", new(DownloadCertAction)).
Get("/downloadZip", new(DownloadZipAction)).
Get("/selectPopup", new(SelectPopupAction)).
Get("/datajs", new(DatajsAction)).
// ACME任务
Prefix("/servers/certs/acme").
Data("leftMenuItem", "acme").
Get("", new(acme.IndexAction)).
GetPost("/create", new(acme.CreateAction)).
Post("/run", new(acme.RunAction)).
GetPost("/updateTaskPopup", new(acme.UpdateTaskPopupAction)).
Post("/deleteTask", new(acme.DeleteTaskAction)).
Post("/userOptions", new(acme.UserOptionsAction)).
// ACME用户
Prefix("/servers/certs/acme/users").
Get("", new(users.IndexAction)).
GetPost("/createPopup", new(users.CreatePopupAction)).
GetPost("/updatePopup", new(users.UpdatePopupAction)).
Post("/delete", new(users.DeleteAction)).
GetPost("/selectPopup", new(users.SelectPopupAction)).
Post("/accountsWithCode", new(users.AccountsWithCodeAction)).
// ACME账号
Prefix("/servers/certs/acme/accounts").
Get("", new(accounts.IndexAction)).
GetPost("/createPopup", new(accounts.CreatePopupAction)).
GetPost("/updatePopup", new(accounts.UpdatePopupAction)).
Post("/delete", new(accounts.DeleteAction)).
// OCSP
Prefix("/servers/certs/ocsp").
Data("leftMenuItem", "ocsp").
Get("", new(ocsp.IndexAction)).
Post("/reset", new(ocsp.ResetAction)).
Post("/resetAll", new(ocsp.ResetAllAction)).
Post("/ignore", new(ocsp.IgnoreAction)).
//
EndAll()
})
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ocsp
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type IgnoreAction struct {
actionutils.ParentAction
}
func (this *IgnoreAction) RunPost(params struct {
CertIds []int64
}) {
defer this.CreateLogInfo(codes.SSLCert_LogOCSPIgnoreOCSPStatus)
_, err := this.RPC().SSLCertRPC().IgnoreSSLCertsWithOCSPError(this.AdminContext(), &pb.IgnoreSSLCertsWithOCSPErrorRequest{SslCertIds: params.CertIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,65 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ocsp
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.SecondMenu("ocsp")
}
func (this *IndexAction) RunGet(params struct {
Keyword string
}) {
this.Data["keyword"] = params.Keyword
countResp, err := this.RPC().SSLCertRPC().CountAllSSLCertsWithOCSPError(this.AdminContext(), &pb.CountAllSSLCertsWithOCSPErrorRequest{Keyword: params.Keyword})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
certsResp, err := this.RPC().SSLCertRPC().ListSSLCertsWithOCSPError(this.AdminContext(), &pb.ListSSLCertsWithOCSPErrorRequest{
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var certMaps = []maps.Map{}
for _, cert := range certsResp.SslCerts {
certMaps = append(certMaps, maps.Map{
"id": cert.Id,
"isOn": cert.IsOn,
"dnsNames": cert.DnsNames,
"commonNames": cert.CommonNames,
"hasOCSP": len(cert.Ocsp) > 0,
"ocspIsUpdated": cert.OcspIsUpdated,
"ocspError": cert.OcspError,
"isCA": cert.IsCA,
"isACME": cert.IsACME,
"name": cert.Name,
"isExpired": cert.TimeEndAt < time.Now().Unix(),
"beginDay": timeutil.FormatTime("Y-m-d", cert.TimeBeginAt),
"endDay": timeutil.FormatTime("Y-m-d", cert.TimeEndAt),
})
}
this.Data["certs"] = certMaps
this.Show()
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ocsp
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ResetAction struct {
actionutils.ParentAction
}
func (this *ResetAction) RunPost(params struct {
CertIds []int64
}) {
defer this.CreateLogInfo(codes.SSLCert_LogOCSPResetOCSPStatus)
_, err := this.RPC().SSLCertRPC().ResetSSLCertsWithOCSPError(this.AdminContext(), &pb.ResetSSLCertsWithOCSPErrorRequest{SslCertIds: params.CertIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,25 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package ocsp
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ResetAllAction struct {
actionutils.ParentAction
}
func (this *ResetAllAction) RunPost(params struct{}) {
defer this.CreateLogInfo(codes.SSLCert_LogOCSPResetAllOCSPStatus)
_, err := this.RPC().SSLCertRPC().ResetAllSSLCertsWithOCSPError(this.AdminContext(), &pb.ResetAllSSLCertsWithOCSPErrorRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,214 @@
package certs
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"strings"
"time"
)
// SelectPopupAction 选择证书
type SelectPopupAction struct {
actionutils.ParentAction
}
func (this *SelectPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectPopupAction) RunGet(params struct {
ServerId int64 // 搜索的服务
UserId int64 // 搜索的用户名
SearchingDomains string // 搜索的域名
SearchingType string // 搜索类型match|all
ViewSize string
SelectedCertIds string
Keyword string
}) {
this.Data["searchingServerId"] = params.ServerId
// 服务相关
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
if server.UserId > 0 {
params.UserId = server.UserId
}
// 读取所有ServerNames
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.AdminContext(), &pb.FindServerNamesRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
if len(serverNamesResp.ServerNamesJSON) > 0 {
var serverNames = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(serverNamesResp.ServerNamesJSON, &serverNames)
if err != nil {
this.ErrorPage(err)
return
}
params.SearchingDomains = strings.Join(serverconfigs.PlainServerNames(serverNames), ",")
}
}
}
// 用户相关
this.Data["userId"] = params.UserId // 可变
this.Data["searchingUserId"] = params.UserId
// 域名搜索相关
var url = this.Request.URL.Path
var query = this.Request.URL.Query()
query.Del("searchingType")
this.Data["baseURL"] = url + "?" + query.Encode()
var searchingDomains = []string{}
if len(params.SearchingDomains) > 0 {
searchingDomains = strings.Split(params.SearchingDomains, ",")
}
const maxDomains = 2_000 // 限制搜索的域名数量
if len(searchingDomains) > maxDomains {
searchingDomains = searchingDomains[:maxDomains]
}
this.Data["allSearchingDomains"] = params.SearchingDomains
this.Data["searchingDomains"] = searchingDomains
this.Data["keyword"] = params.Keyword
this.Data["selectedCertIds"] = params.SelectedCertIds
var searchingType = params.SearchingType
if len(searchingType) == 0 {
if len(params.SearchingDomains) == 0 {
searchingType = "all"
} else {
searchingType = "match"
}
}
if searchingType != "all" && searchingType != "match" {
this.ErrorPage(errors.New("invalid searching type '" + searchingType + "'"))
return
}
this.Data["searchingType"] = searchingType
// 已经选择的证书
var selectedCertIds = []string{}
if len(params.SelectedCertIds) > 0 {
selectedCertIds = strings.Split(params.SelectedCertIds, ",")
}
if len(params.ViewSize) == 0 {
params.ViewSize = "normal"
}
this.Data["viewSize"] = params.ViewSize
// 全部证书数量
countAllResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var totalAll = countAllResp.Count
this.Data["totalAll"] = totalAll
// 已匹配证书数量
var totalMatch int64 = 0
if len(searchingDomains) > 0 {
countMatchResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.AdminContext(), &pb.CountSSLCertRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Domains: searchingDomains,
})
if err != nil {
this.ErrorPage(err)
return
}
totalMatch = countMatchResp.Count
}
this.Data["totalMatch"] = totalMatch
var totalCerts int64
if searchingType == "all" {
totalCerts = totalAll
} else if searchingType == "match" {
totalCerts = totalMatch
}
var page = this.NewPage(totalCerts)
this.Data["page"] = page.AsHTML()
var listResp *pb.ListSSLCertsResponse
if searchingType == "all" {
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
} else if searchingType == "match" {
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.AdminContext(), &pb.ListSSLCertsRequest{
UserId: params.UserId,
Keyword: params.Keyword,
Domains: searchingDomains,
Offset: page.Offset,
Size: page.Size,
})
}
if err != nil {
this.ErrorPage(err)
return
}
if listResp == nil {
this.ErrorPage(errors.New("'listResp' should not be nil"))
return
}
var certConfigs = []*sslconfigs.SSLCertConfig{}
err = json.Unmarshal(listResp.SslCertsJSON, &certConfigs)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["certs"] = certConfigs
var certMaps = []maps.Map{}
var nowTime = time.Now().Unix()
for _, certConfig := range certConfigs {
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.AdminContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: certConfig.Id})
if err != nil {
this.ErrorPage(err)
return
}
certMaps = append(certMaps, maps.Map{
"beginDay": timeutil.FormatTime("Y-m-d", certConfig.TimeBeginAt),
"endDay": timeutil.FormatTime("Y-m-d", certConfig.TimeEndAt),
"isExpired": nowTime > certConfig.TimeEndAt,
"isAvailable": nowTime <= certConfig.TimeEndAt,
"countServers": countServersResp.Count,
"isSelected": lists.ContainsString(selectedCertIds, numberutils.FormatInt64(certConfig.Id)),
})
}
this.Data["certInfos"] = certMaps
this.Show()
}

View File

@@ -0,0 +1,165 @@
package certs
import (
"context"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
CertId int64
}) {
certConfigResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
var certConfigJSON = certConfigResp.SslCertJSON
if len(certConfigJSON) == 0 {
this.NotFound("cert", params.CertId)
return
}
var certConfig = &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certConfigJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
certConfig.CertData = nil // cert & key 不需要在界面上显示
certConfig.KeyData = nil
this.Data["certConfig"] = certConfig
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
CertId int64
TextMode bool
Name string
IsCA bool
Description string
IsOn bool
CertFile *actions.File
KeyFile *actions.File
CertText string
KeyText string
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.SSLCert_LogUpdateSSLCert, params.CertId)
// 查询Cert
certConfigResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
var certConfigJSON = certConfigResp.SslCertJSON
if len(certConfigJSON) == 0 {
this.NotFound("cert", params.CertId)
return
}
var certConfig = &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certConfigJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
// 校验参数
params.Must.
Field("name", params.Name).
Require("请输入证书说明")
if params.TextMode {
if len(params.CertText) > 0 {
certConfig.CertData = []byte(params.CertText)
}
if !params.IsCA {
if len(params.KeyText) > 0 {
certConfig.KeyData = []byte(params.KeyText)
}
}
} else {
if params.CertFile != nil {
certConfig.CertData, err = params.CertFile.Read()
if err != nil {
this.FailField("certFile", "读取证书文件内容错误,请重新上传")
}
}
if !params.IsCA {
if params.KeyFile != nil {
certConfig.KeyData, err = params.KeyFile.Read()
if err != nil {
this.FailField("keyFile", "读取私钥文件内容错误,请重新上传")
}
}
}
}
// 校验
certConfig.IsCA = params.IsCA
err = certConfig.Init(context.TODO())
if err != nil {
if params.IsCA {
this.Fail("证书校验错误:" + err.Error())
} else {
this.Fail("证书或密钥校验错误:" + err.Error())
}
}
if len(timeutil.Format("Y", certConfig.TimeEnd())) != 4 {
this.Fail("证书格式错误:无法读取到证书有效期")
}
if certConfig.TimeBeginAt < 0 {
this.Fail("证书校验错误有效期开始时间过小不能小于1970年1月1日")
}
if certConfig.TimeEndAt < 0 {
this.Fail("证书校验错误有效期结束时间过小不能小于1970年1月1日")
}
// 保存
_, err = this.RPC().SSLCertRPC().UpdateSSLCert(this.AdminContext(), &pb.UpdateSSLCertRequest{
SslCertId: params.CertId,
IsOn: params.IsOn,
Name: params.Name,
Description: params.Description,
ServerName: "",
IsCA: params.IsCA,
CertData: certConfig.CertData,
KeyData: certConfig.KeyData,
TimeBeginAt: certConfig.TimeBeginAt,
TimeEndAt: certConfig.TimeEndAt,
DnsNames: certConfig.DNSNames,
CommonNames: certConfig.CommonNames,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,227 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package certs
import (
"bytes"
"context"
"crypto/tls"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"io"
"mime/multipart"
"strings"
)
// UploadBatchPopupAction 批量上传证书
type UploadBatchPopupAction struct {
actionutils.ParentAction
}
func (this *UploadBatchPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UploadBatchPopupAction) RunGet(params struct {
ServerId int64
UserId int64
}) {
// 读取服务用户
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
params.UserId = server.UserId
}
}
this.Data["userId"] = params.UserId
this.Data["maxFiles"] = this.maxFiles()
this.Show()
}
func (this *UploadBatchPopupAction) RunPost(params struct {
UserId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.SSLCert_LogUploadSSLCertBatch)
var files = this.Request.MultipartForm.File["certFiles"]
if len(files) == 0 {
this.Fail("请选择要上传的证书和私钥文件")
return
}
// 限制每次上传的文件数量
var maxFiles = this.maxFiles()
if len(files) > maxFiles {
this.Fail("每次上传最多不能超过" + types.String(maxFiles) + "个文件")
return
}
type certInfo struct {
filename string
data []byte
}
var certDataList = []*certInfo{}
var keyDataList = [][]byte{}
var failMessages = []string{}
for _, file := range files {
func(file *multipart.FileHeader) {
fp, err := file.Open()
if err != nil {
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:"+err.Error())
return
}
defer func() {
_ = fp.Close()
}()
data, err := io.ReadAll(fp)
if err != nil {
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:"+err.Error())
return
}
if bytes.Contains(data, []byte("CERTIFICATE-")) {
certDataList = append(certDataList, &certInfo{
filename: file.Filename,
data: data,
})
} else if bytes.Contains(data, []byte("PRIVATE KEY-")) {
keyDataList = append(keyDataList, data)
} else {
failMessages = append(failMessages, "文件"+file.Filename+"读取失败:文件格式错误,无法识别是证书还是私钥")
return
}
}(file)
}
if len(failMessages) > 0 {
this.Fail("发生了错误:" + strings.Join(failMessages, ""))
return
}
// 对比证书和私钥数量是否一致
if len(certDataList) != len(keyDataList) {
this.Fail("证书文件数量(" + types.String(len(certDataList)) + ")和私钥文件数量(" + types.String(len(keyDataList)) + ")不一致")
return
}
// 自动匹配
var pairs = [][2][]byte{} // [] { cert, key }
var keyIndexMap = map[int]bool{} // 方便下面跳过已匹配的Key
for _, cert := range certDataList {
var found = false
for keyIndex, keyData := range keyDataList {
if keyIndexMap[keyIndex] {
continue
}
_, err := tls.X509KeyPair(cert.data, keyData)
if err == nil {
found = true
pairs = append(pairs, [2][]byte{cert.data, keyData})
keyIndexMap[keyIndex] = true
break
}
}
if !found {
this.Fail("找不到" + cert.filename + "对应的私钥")
return
}
}
// 组织 CertConfig
var pbCerts = []*pb.CreateSSLCertsRequestCert{}
var certConfigs = []*sslconfigs.SSLCertConfig{}
for _, pair := range pairs {
certData, keyData := pair[0], pair[1]
var certConfig = &sslconfigs.SSLCertConfig{
IsCA: false,
CertData: certData,
KeyData: keyData,
}
err := certConfig.Init(context.TODO())
if err != nil {
this.Fail("证书验证失败:" + err.Error())
return
}
certConfigs = append(certConfigs, certConfig)
var certName = ""
if len(certConfig.DNSNames) > 0 {
certName = certConfig.DNSNames[0]
if len(certConfig.DNSNames) > 1 {
certName += "等" + types.String(len(certConfig.DNSNames)) + "个域名"
}
}
certConfig.Name = certName
pbCerts = append(pbCerts, &pb.CreateSSLCertsRequestCert{
IsOn: true,
Name: certName,
Description: "",
ServerName: "",
IsCA: false,
CertData: certData,
KeyData: keyData,
TimeBeginAt: certConfig.TimeBeginAt,
TimeEndAt: certConfig.TimeEndAt,
DnsNames: certConfig.DNSNames,
CommonNames: certConfig.CommonNames,
})
}
createResp, err := this.RPC().SSLCertRPC().CreateSSLCerts(this.AdminContext(), &pb.CreateSSLCertsRequest{
UserId: params.UserId,
SSLCerts: pbCerts,
})
if err != nil {
this.ErrorPage(err)
return
}
var certIds = createResp.SslCertIds
if len(certIds) != len(certConfigs) {
this.Fail("上传成功但API返回的证书ID数量错误请反馈给开发者")
return
}
// 返回数据
this.Data["count"] = len(pbCerts)
var certRefs = []*sslconfigs.SSLCertRef{}
for index, cert := range certConfigs {
// ID
cert.Id = certIds[index]
// 减少不必要的数据
cert.CertData = nil
cert.KeyData = nil
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
IsOn: true,
CertId: cert.Id,
})
}
this.Data["certs"] = certConfigs
this.Data["certRefs"] = certRefs
this.Success()
}

View File

@@ -0,0 +1,8 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package certs
func (this *UploadBatchPopupAction) maxFiles() int {
return 20
}

View File

@@ -0,0 +1,13 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package certs
import teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
func (this *UploadBatchPopupAction) maxFiles() int {
if !teaconst.IsPlus {
return 20
}
return 10_000
}

View File

@@ -0,0 +1,172 @@
package certs
import (
"context"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type UploadPopupAction struct {
actionutils.ParentAction
}
func (this *UploadPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UploadPopupAction) RunGet(params struct {
ServerId int64
UserId int64
}) {
// 读取服务用户
if params.ServerId > 0 {
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.AdminContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server != nil {
params.UserId = server.UserId
}
}
this.Data["userId"] = params.UserId
this.Show()
}
func (this *UploadPopupAction) RunPost(params struct {
UserId int64
TextMode bool
Name string
IsCA bool
Description string
IsOn bool
CertFile *actions.File
KeyFile *actions.File
CertText string
KeyText string
Must *actions.Must
}) {
params.Must.
Field("name", params.Name).
Require("请输入证书说明")
var certData []byte
var keyData []byte
if params.TextMode {
if len(params.CertText) == 0 {
this.FailField("certText", "请输入证书内容")
}
if !params.IsCA {
if len(params.KeyText) == 0 {
this.FailField("keyText", "请输入私钥内容")
}
}
certData = []byte(params.CertText)
keyData = []byte(params.KeyText)
} else {
if params.CertFile == nil {
this.FailField("certFile", "请选择要上传的证书文件")
}
var err error
certData, err = params.CertFile.Read()
if err != nil {
this.FailField("certFile", "读取证书文件内容错误,请重新上传")
}
if !params.IsCA {
if params.KeyFile == nil {
this.FailField("keyFile", "请选择要上传的私钥文件")
} else {
keyData, err = params.KeyFile.Read()
if err != nil {
this.FailField("keyFile", "读取密钥文件内容错误,请重新上传")
}
}
}
}
// 校验
var certConfig = &sslconfigs.SSLCertConfig{
IsCA: params.IsCA,
CertData: certData,
KeyData: keyData,
}
err := certConfig.Init(context.TODO())
if err != nil {
if params.IsCA {
this.Fail("证书校验错误:" + err.Error())
} else {
this.Fail("证书或密钥校验错误:" + err.Error())
}
}
if len(timeutil.Format("Y", certConfig.TimeEnd())) != 4 {
this.Fail("证书格式错误:无法读取到证书有效期")
}
if certConfig.TimeBeginAt < 0 {
this.Fail("证书校验错误有效期开始时间过小不能小于1970年1月1日")
}
if certConfig.TimeEndAt < 0 {
this.Fail("证书校验错误有效期结束时间过小不能小于1970年1月1日")
}
// 保存
createResp, err := this.RPC().SSLCertRPC().CreateSSLCert(this.AdminContext(), &pb.CreateSSLCertRequest{
IsOn: params.IsOn,
UserId: params.UserId,
Name: params.Name,
Description: params.Description,
ServerName: "",
IsCA: params.IsCA,
CertData: certData,
KeyData: keyData,
TimeBeginAt: certConfig.TimeBeginAt,
TimeEndAt: certConfig.TimeEndAt,
DnsNames: certConfig.DNSNames,
CommonNames: certConfig.CommonNames,
})
if err != nil {
this.ErrorPage(err)
return
}
// 查询已创建的证书并返回,方便调用者进行后续处理
var certId = createResp.SslCertId
configResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: certId})
if err != nil {
this.ErrorPage(err)
return
}
certConfig = &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(configResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
certConfig.CertData = nil // 去掉不必要的数据
certConfig.KeyData = nil // 去掉不必要的数据
this.Data["cert"] = certConfig
this.Data["certRef"] = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
}
// 创建日志
defer this.CreateLogInfo(codes.SSLCert_LogUploadSSLCert, certId)
this.Success()
}

View File

@@ -0,0 +1,39 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
)
type ViewCertAction struct {
actionutils.ParentAction
}
func (this *ViewCertAction) Init() {
this.Nav("", "", "")
}
func (this *ViewCertAction) RunGet(params struct {
CertId int64
}) {
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
if len(certResp.SslCertJSON) == 0 {
this.NotFound("sslCert", params.CertId)
return
}
certConfig := &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, _ = this.Write(certConfig.CertData)
}

View File

@@ -0,0 +1,34 @@
package certs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
)
type ViewKeyAction struct {
actionutils.ParentAction
}
func (this *ViewKeyAction) Init() {
this.Nav("", "", "")
}
func (this *ViewKeyAction) RunGet(params struct {
CertId int64
}) {
certResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.AdminContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
if err != nil {
this.ErrorPage(err)
return
}
certConfig := &sslconfigs.SSLCertConfig{}
err = json.Unmarshal(certResp.SslCertJSON, certConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, _ = this.Write(certConfig.KeyData)
}

View File

@@ -0,0 +1,29 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteTaskAction struct {
actionutils.ParentAction
}
func (this *DeleteTaskAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo(codes.HTTPCacheTask_LogDeleteHTTPCacheTask, params.TaskId)
_, err := this.RPC().HTTPCacheTaskRPC().DeleteHTTPCacheTask(this.AdminContext(), &pb.DeleteHTTPCacheTaskRequest{
HttpCacheTaskId: params.TaskId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,98 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache/cacheutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strings"
)
type FetchAction struct {
actionutils.ParentAction
}
func (this *FetchAction) Init() {
this.Nav("", "", "fetch")
}
func (this *FetchAction) RunGet(params struct{}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
this.Show()
}
func (this *FetchAction) RunPost(params struct {
Keys string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.HTTPCacheTask_LogCreateHTTPCacheTaskFetch)
if len(params.Keys) == 0 {
this.Fail("请输入要预热的Key列表")
}
// 检查Key
var realKeys = []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
if len(realKeys) == 0 {
this.Fail("请输入要预热的Key列表")
}
// 校验Key
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.AdminContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: realKeys})
if err != nil {
this.ErrorPage(err)
return
}
var failKeyMaps = []maps.Map{}
if len(validateResp.FailKeys) > 0 {
for _, key := range validateResp.FailKeys {
failKeyMaps = append(failKeyMaps, maps.Map{
"key": key.Key,
"reason": cacheutils.KeyFailReason(key.ReasonCode),
})
}
}
this.Data["failKeys"] = failKeyMaps
if len(failKeyMaps) > 0 {
this.Fail("有" + types.String(len(failKeyMaps)) + "个Key无法完成操作请删除后重试")
}
// 提交任务
_, err = this.RPC().HTTPCacheTaskRPC().CreateHTTPCacheTask(this.AdminContext(), &pb.CreateHTTPCacheTaskRequest{
Type: "fetch",
KeyType: "key",
Keys: realKeys,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,103 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache/cacheutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "purge")
}
func (this *IndexAction) RunGet(params struct {
KeyType string
}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
this.Data["keyType"] = params.KeyType
this.Show()
}
func (this *IndexAction) RunPost(params struct {
KeyType string
Keys string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.HTTPCacheTask_LogCreateHTTPCacheTaskPurge)
if len(params.Keys) == 0 {
this.Fail("请输入要刷新的Key列表")
}
// 检查Key
var realKeys = []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
if len(realKeys) == 0 {
this.Fail("请输入要刷新的Key列表")
}
// 校验Key
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.AdminContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: realKeys})
if err != nil {
this.ErrorPage(err)
return
}
var failKeyMaps = []maps.Map{}
if len(validateResp.FailKeys) > 0 {
for _, key := range validateResp.FailKeys {
failKeyMaps = append(failKeyMaps, maps.Map{
"key": key.Key,
"reason": cacheutils.KeyFailReason(key.ReasonCode),
})
}
}
this.Data["failKeys"] = failKeyMaps
if len(failKeyMaps) > 0 {
this.Fail("有" + types.String(len(failKeyMaps)) + "个Key无法完成操作请删除后重试")
}
// 提交任务
_, err = this.RPC().HTTPCacheTaskRPC().CreateHTTPCacheTask(this.AdminContext(), &pb.CreateHTTPCacheTaskRequest{
Type: "purge",
KeyType: params.KeyType,
Keys: realKeys,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,24 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Data("teaMenu", "servers").
Data("teaSubMenu", "cacheBatch").
Prefix("/servers/components/cache/batch").
GetPost("", new(IndexAction)).
GetPost("/fetch", new(FetchAction)).
Get("/tasks", new(TasksAction)).
GetPost("/task", new(TaskAction)).
Post("/deleteTask", new(DeleteTaskAction)).
Post("/resetTask", new(ResetTaskAction)).
EndAll()
})
}

View File

@@ -0,0 +1,27 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ResetTaskAction struct {
actionutils.ParentAction
}
func (this *ResetTaskAction) RunPost(params struct {
TaskId int64
}) {
this.CreateLogInfo(codes.HTTPCacheTask_LogResetHTTPCacheTask, params.TaskId)
_, err := this.RPC().HTTPCacheTaskRPC().ResetHTTPCacheTask(this.AdminContext(), &pb.ResetHTTPCacheTaskRequest{HttpCacheTaskId: params.TaskId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,137 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"sort"
)
type TaskAction struct {
actionutils.ParentAction
}
func (this *TaskAction) Init() {
this.Nav("", "", "task")
}
func (this *TaskAction) RunGet(params struct {
TaskId int64
}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
if !this.readTask(params.TaskId) {
return
}
this.Show()
}
func (this *TaskAction) RunPost(params struct {
TaskId int64
}) {
if !this.readTask(params.TaskId) {
return
}
this.Success()
}
// 读取任务信息
func (this *TaskAction) readTask(taskId int64) (ok bool) {
taskResp, err := this.RPC().HTTPCacheTaskRPC().FindEnabledHTTPCacheTask(this.AdminContext(), &pb.FindEnabledHTTPCacheTaskRequest{HttpCacheTaskId: taskId})
if err != nil {
this.ErrorPage(err)
return
}
var task = taskResp.HttpCacheTask
if task == nil {
this.NotFound("HTTPCacheTask", taskId)
return
}
// 用户
var userMap = maps.Map{"id": 0, "username": "", "fullname": ""}
if task.User != nil {
userMap = maps.Map{
"id": task.User.Id,
"username": task.User.Username,
"fullname": task.User.Fullname,
}
}
// keys
var keyMaps = []maps.Map{}
for _, key := range task.HttpCacheTaskKeys {
// 错误信息
var errorMaps = []maps.Map{}
if len(key.ErrorsJSON) > 0 {
var m = map[int64]string{}
err = json.Unmarshal(key.ErrorsJSON, &m)
if err != nil {
this.ErrorPage(err)
return
}
for nodeId, errString := range m {
errorMaps = append(errorMaps, maps.Map{
"nodeId": nodeId,
"error": errString,
})
}
}
// 错误信息排序
if len(errorMaps) > 0 {
sort.Slice(errorMaps, func(i, j int) bool {
var m1 = errorMaps[i]
var m2 = errorMaps[j]
return m1.GetInt64("nodeId") < m2.GetInt64("nodeId")
})
}
// 集群信息
var clusterMap = maps.Map{
"id": 0,
"name": "",
}
if key.NodeCluster != nil {
clusterMap = maps.Map{
"id": key.NodeCluster.Id,
"name": key.NodeCluster.Name,
}
}
keyMaps = append(keyMaps, maps.Map{
"key": key.Key,
"isDone": key.IsDone,
"isDoing": key.IsDoing,
"errors": errorMaps,
"cluster": clusterMap,
})
}
this.Data["task"] = maps.Map{
"id": task.Id,
"type": task.Type,
"keyType": task.KeyType,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
"doneTime": timeutil.FormatTime("Y-m-d H:i:s", task.DoneAt),
"isDone": task.IsDone,
"isOk": task.IsOk,
"keys": keyMaps,
"user": userMap,
}
ok = true
return
}

View File

@@ -0,0 +1,73 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type TasksAction struct {
actionutils.ParentAction
}
func (this *TasksAction) Init() {
this.Nav("", "", "task")
}
func (this *TasksAction) RunGet(params struct{}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
// 任务数量
countResp, err := this.RPC().HTTPCacheTaskRPC().CountHTTPCacheTasks(this.AdminContext(), &pb.CountHTTPCacheTasksRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
// 任务列表
var taskMaps = []maps.Map{}
tasksResp, err := this.RPC().HTTPCacheTaskRPC().ListHTTPCacheTasks(this.AdminContext(), &pb.ListHTTPCacheTasksRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
for _, task := range tasksResp.HttpCacheTasks {
var userMap = maps.Map{"id": 0, "username": "", "fullname": ""}
if task.User != nil {
userMap = maps.Map{
"id": task.User.Id,
"username": task.User.Username,
"fullname": task.User.Fullname,
}
}
taskMaps = append(taskMaps, maps.Map{
"id": task.Id,
"type": task.Type,
"keyType": task.KeyType,
"isDone": task.IsDone,
"isOk": task.IsOk,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
"description": task.Description,
"user": userMap,
})
}
this.Data["tasks"] = taskMaps
this.Show()
}

View File

@@ -0,0 +1,24 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
func InitMenu(parent *actionutils.ParentAction) error {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
countTasksResp, err := rpcClient.HTTPCacheTaskRPC().CountDoingHTTPCacheTasks(parent.AdminContext(), &pb.CountDoingHTTPCacheTasksRequest{})
if err != nil {
return err
}
parent.Data["countDoingTasks"] = countTasksResp.Count
return nil
}

View File

@@ -0,0 +1,56 @@
package cacheutils
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/errors"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
)
// FindCachePolicyNameWithoutError 查找缓存策略名称并忽略错误
func FindCachePolicyNameWithoutError(parent *actionutils.ParentAction, cachePolicyId int64) string {
policy, err := FindCachePolicy(parent, cachePolicyId)
if err != nil {
return ""
}
if policy == nil {
return ""
}
return policy.Name
}
// FindCachePolicy 查找缓存策略配置
func FindCachePolicy(parent *actionutils.ParentAction, cachePolicyId int64) (*serverconfigs.HTTPCachePolicy, error) {
resp, err := parent.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(parent.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: cachePolicyId})
if err != nil {
return nil, err
}
if len(resp.HttpCachePolicyJSON) == 0 {
return nil, errors.New("cache policy not found")
}
var config = &serverconfigs.HTTPCachePolicy{}
err = json.Unmarshal(resp.HttpCachePolicyJSON, config)
if err != nil {
return nil, err
}
return config, nil
}
// KeyFailReason Key相关失败原因
func KeyFailReason(reasonCode string) string {
switch reasonCode {
case "requireKey":
return "空的Key"
case "requireDomain":
return "找不到Key对应的域名"
case "requireServer":
return "找不到Key对应的网站"
case "requireUser":
return "该域名不属于当前用户"
case "requireClusterId":
return "该网站没有部署到集群"
}
return "未知错误"
}

View File

@@ -0,0 +1,98 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
"strconv"
)
type CleanAction struct {
actionutils.ParentAction
}
func (this *CleanAction) Init() {
this.Nav("", "", "clean")
}
func (this *CleanAction) RunGet(params struct {
CachePolicyId int64
}) {
// 默认的集群ID
cookie, err := this.Request.Cookie("cache_cluster_id")
if cookie != nil && err == nil {
this.Data["clusterId"] = types.Int64(cookie.Value)
}
// 集群列表
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}
func (this *CleanAction) RunPost(params struct {
CachePolicyId int64
ClusterId int64
Must *actions.Must
}) {
// 记录clusterId
this.AddCookie(&http.Cookie{
Name: "cache_cluster_id",
Value: strconv.FormatInt(params.ClusterId, 10),
})
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
// 发送命令
msg := &messageconfigs.CleanCacheMessage{
CachePolicyJSON: cachePolicyJSON,
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodeCleanCache, msg, 60, false)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogCleanAll, params.CachePolicyId)
this.Success()
}

View File

@@ -0,0 +1,22 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// 计算可用缓存策略数量
type CountAction struct {
actionutils.ParentAction
}
func (this *CountAction) RunPost(params struct{}) {
countResp, err := this.RPC().HTTPCachePolicyRPC().CountAllEnabledHTTPCachePolicies(this.AdminContext(), &pb.CountAllEnabledHTTPCachePoliciesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["count"] = countResp.Count
this.Success()
}

View File

@@ -0,0 +1,142 @@
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"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{}) {
this.Data["types"] = serverconfigs.AllCachePolicyStorageTypes
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
Type string
// file
FileDir string
FileMemoryCapacityJSON []byte
FileOpenFileCacheMax int
FileEnableSendfile bool
FileMinFreeSizeJSON []byte
CapacityJSON []byte
MaxSizeJSON []byte
FetchTimeoutJSON []byte
SyncCompressionCache bool
EnableMMAP bool
EnableIncompletePartialContent bool
Description string
IsOn bool
Must *actions.Must
}) {
params.Must.
Field("name", params.Name).
Require("请输入策略名称")
// 校验选项
var options any
switch params.Type {
case serverconfigs.CachePolicyStorageFile:
params.Must.
Field("fileDir", params.FileDir).
Require("请输入缓存目录")
var memoryCapacity = &shared.SizeCapacity{}
if len(params.FileMemoryCapacityJSON) > 0 {
err := json.Unmarshal(params.FileMemoryCapacityJSON, memoryCapacity)
if err != nil {
this.ErrorPage(err)
return
}
}
var openFileCacheConfig *serverconfigs.OpenFileCacheConfig
if params.FileOpenFileCacheMax > 0 {
openFileCacheConfig = &serverconfigs.OpenFileCacheConfig{
IsOn: true,
Max: params.FileOpenFileCacheMax,
}
}
var minFreeSize = &shared.SizeCapacity{}
var minFreeSizeJSON = params.FileMinFreeSizeJSON
if !utils.JSONIsNull(minFreeSizeJSON) {
_, err := utils.JSONDecodeConfig(minFreeSizeJSON, minFreeSize)
if err != nil {
this.ErrorPage(err)
return
}
if minFreeSize.Count < 0 {
minFreeSize.Count = 0
}
}
options = &serverconfigs.HTTPFileCacheStorage{
Dir: params.FileDir,
MemoryPolicy: &serverconfigs.HTTPCachePolicy{
Capacity: memoryCapacity,
},
OpenFileCache: openFileCacheConfig,
EnableSendfile: params.FileEnableSendfile,
MinFreeSize: minFreeSize,
EnableMMAP: params.EnableMMAP,
EnableIncompletePartialContent: params.EnableIncompletePartialContent,
}
case serverconfigs.CachePolicyStorageMemory:
options = &serverconfigs.HTTPMemoryCacheStorage{}
default:
this.Fail("请选择正确的缓存类型")
}
optionsJSON, err := json.Marshal(options)
if err != nil {
this.ErrorPage(err)
return
}
createResp, err := this.RPC().HTTPCachePolicyRPC().CreateHTTPCachePolicy(this.AdminContext(), &pb.CreateHTTPCachePolicyRequest{
IsOn: params.IsOn,
Name: params.Name,
Description: params.Description,
CapacityJSON: params.CapacityJSON,
MaxSizeJSON: params.MaxSizeJSON,
FetchTimeoutJSON: params.FetchTimeoutJSON,
Type: params.Type,
OptionsJSON: optionsJSON,
SyncCompressionCache: params.SyncCompressionCache,
})
if err != nil {
this.ErrorPage(err)
return
}
// 返回数据
this.Data["cachePolicy"] = maps.Map{
"id": createResp.HttpCachePolicyId,
"name": params.Name,
}
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogCreateCachePolicy, createResp.HttpCachePolicyId)
this.Success()
}

View File

@@ -0,0 +1,36 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
CachePolicyId int64
}) {
// 检查是否被引用
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此缓存策略正在被有些集群引用,请修改后再删除。")
}
_, err = this.RPC().HTTPCachePolicyRPC().DeleteHTTPCachePolicy(this.AdminContext(), &pb.DeleteHTTPCachePolicyRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogDeleteCachePolicy, params.CachePolicyId)
this.Success()
}

View File

@@ -0,0 +1,128 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache/cacheutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
"strconv"
"strings"
)
type FetchAction struct {
actionutils.ParentAction
}
func (this *FetchAction) Init() {
this.Nav("", "", "fetch")
}
func (this *FetchAction) RunGet(params struct {
CachePolicyId int64
}) {
// 默认的集群ID
cookie, err := this.Request.Cookie("cache_cluster_id")
if cookie != nil && err == nil {
this.Data["clusterId"] = types.Int64(cookie.Value)
}
// 集群列表
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}
func (this *FetchAction) RunPost(params struct {
CachePolicyId int64
ClusterId int64
Keys string
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogFetchCaches, params.CachePolicyId)
// 记录clusterId
this.AddCookie(&http.Cookie{
Name: "cache_cluster_id",
Value: strconv.FormatInt(params.ClusterId, 10),
})
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
if len(params.Keys) == 0 {
this.Fail("请输入要预热的Key列表")
}
realKeys := []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
// 校验Key
// 这里暂时不校验服务ID
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.AdminContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: realKeys})
if err != nil {
this.ErrorPage(err)
return
}
var failKeyMaps = []maps.Map{}
if len(validateResp.FailKeys) > 0 {
for _, key := range validateResp.FailKeys {
failKeyMaps = append(failKeyMaps, maps.Map{
"key": key.Key,
"reason": cacheutils.KeyFailReason(key.ReasonCode),
})
}
}
this.Data["failKeys"] = failKeyMaps
if len(failKeyMaps) > 0 {
this.Fail("有" + types.String(len(failKeyMaps)) + "个Key无法完成操作请删除后重试")
}
// 提交任务
_, err = this.RPC().HTTPCacheTaskRPC().CreateHTTPCacheTask(this.AdminContext(), &pb.CreateHTTPCacheTaskRequest{
Type: "fetch",
KeyType: "key",
Keys: realKeys,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,36 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache/cacheutils"
"github.com/iwind/TeaGo/actions"
"net/http"
)
type Helper struct {
}
func NewHelper() *Helper {
return &Helper{}
}
func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) {
action := actionPtr.Object()
if action.Request.Method != http.MethodGet {
return
}
action.Data["mainTab"] = "component"
action.Data["secondMenuItem"] = "cache"
cachePolicyId := action.ParamInt64("cachePolicyId")
action.Data["cachePolicyId"] = cachePolicyId
parentActionObj, ok := actionPtr.(interface {
Parent() *actionutils.ParentAction
})
if ok {
var parentAction = parentActionObj.Parent()
action.Data["cachePolicyName"] = cacheutils.FindCachePolicyNameWithoutError(parentAction, cachePolicyId)
}
}

View File

@@ -0,0 +1,81 @@
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.FirstMenu("index")
}
func (this *IndexAction) RunGet(params struct {
ClusterId int64
Keyword string
StorageType string
}) {
this.Data["keyword"] = params.Keyword
this.Data["clusterId"] = params.ClusterId
this.Data["storageType"] = params.StorageType
countResp, err := this.RPC().HTTPCachePolicyRPC().CountAllEnabledHTTPCachePolicies(this.AdminContext(), &pb.CountAllEnabledHTTPCachePoliciesRequest{
NodeClusterId: params.ClusterId,
Keyword: params.Keyword,
Type: params.StorageType,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
this.Data["page"] = page.AsHTML()
listResp, err := this.RPC().HTTPCachePolicyRPC().ListEnabledHTTPCachePolicies(this.AdminContext(), &pb.ListEnabledHTTPCachePoliciesRequest{
Keyword: params.Keyword,
NodeClusterId: params.ClusterId,
Type: params.StorageType,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
cachePoliciesJSON := listResp.HttpCachePoliciesJSON
cachePolicies := []*serverconfigs.HTTPCachePolicy{}
err = json.Unmarshal(cachePoliciesJSON, &cachePolicies)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["cachePolicies"] = cachePolicies
infos := []maps.Map{}
for _, cachePolicy := range cachePolicies {
countClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: cachePolicy.Id})
if err != nil {
this.ErrorPage(err)
return
}
countClusters := countClustersResp.Count
infos = append(infos, maps.Map{
"typeName": serverconfigs.FindCachePolicyStorageName(cachePolicy.Type),
"countClusters": countClusters,
})
}
this.Data["infos"] = infos
// 所有的存储类型
this.Data["storageTypes"] = serverconfigs.AllCachePolicyStorageTypes
this.Show()
}

View File

@@ -0,0 +1,34 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(NewHelper()).
Data("teaMenu", "servers").
Data("teaSubMenu", "cache").
Prefix("/servers/components/cache").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
Get("/policy", new(PolicyAction)).
GetPost("/update", new(UpdateAction)).
GetPost("/clean", new(CleanAction)).
GetPost("/fetch", new(FetchAction)).
GetPost("/purge", new(PurgeAction)).
GetPost("/stat", new(StatAction)).
GetPost("/test", new(TestAction)).
Post("/delete", new(DeleteAction)).
Post("/testRead", new(TestReadAction)).
Post("/testWrite", new(TestWriteAction)).
Get("/selectPopup", new(SelectPopupAction)).
Post("/count", new(CountAction)).
Post("/updateRefs", new(UpdateRefsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,53 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache/cacheutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
)
type PolicyAction struct {
actionutils.ParentAction
}
func (this *PolicyAction) Init() {
this.Nav("", "", "index")
}
func (this *PolicyAction) RunGet(params struct {
CachePolicyId int64
}) {
cachePolicy, err := cacheutils.FindCachePolicy(this.Parent(), params.CachePolicyId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["cachePolicy"] = cachePolicy
// 预热超时时间
this.Data["fetchTimeoutString"] = ""
if cachePolicy.FetchTimeout != nil && cachePolicy.FetchTimeout.Count > 0 {
this.Data["fetchTimeoutString"] = cachePolicy.FetchTimeout.Description()
}
this.Data["typeName"] = serverconfigs.FindCachePolicyStorageName(cachePolicy.Type)
// 正在使用此策略的集群
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var clusterMaps = []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}

View File

@@ -0,0 +1,126 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/cache/cacheutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
"strconv"
"strings"
)
type PurgeAction struct {
actionutils.ParentAction
}
func (this *PurgeAction) Init() {
this.Nav("", "", "purge")
}
func (this *PurgeAction) RunGet(params struct {
CachePolicyId int64
}) {
// 默认的集群ID
cookie, err := this.Request.Cookie("cache_cluster_id")
if cookie != nil && err == nil {
this.Data["clusterId"] = types.Int64(cookie.Value)
}
// 集群列表
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}
func (this *PurgeAction) RunPost(params struct {
CachePolicyId int64
ClusterId int64
KeyType string
Keys string
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogPurgeCaches, params.CachePolicyId)
// 记录clusterId
this.AddCookie(&http.Cookie{
Name: "cache_cluster_id",
Value: strconv.FormatInt(params.ClusterId, 10),
})
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
if len(params.Keys) == 0 {
this.Fail("请输入要删除的Key列表")
}
realKeys := []string{}
for _, key := range strings.Split(params.Keys, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(realKeys, key) {
continue
}
realKeys = append(realKeys, key)
}
// 校验Key
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.AdminContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: realKeys})
if err != nil {
this.ErrorPage(err)
return
}
var failKeyMaps = []maps.Map{}
if len(validateResp.FailKeys) > 0 {
for _, key := range validateResp.FailKeys {
failKeyMaps = append(failKeyMaps, maps.Map{
"key": key.Key,
"reason": cacheutils.KeyFailReason(key.ReasonCode),
})
}
}
this.Data["failKeys"] = failKeyMaps
if len(failKeyMaps) > 0 {
this.Fail("有" + types.String(len(failKeyMaps)) + "个Key无法完成操作请删除后重试")
}
// 提交任务
_, err = this.RPC().HTTPCacheTaskRPC().CreateHTTPCacheTask(this.AdminContext(), &pb.CreateHTTPCacheTaskRequest{
Type: "purge",
KeyType: params.KeyType,
Keys: realKeys,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,60 @@
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/maps"
)
type SelectPopupAction struct {
actionutils.ParentAction
}
func (this *SelectPopupAction) Init() {
this.Nav("", "", "")
}
func (this *SelectPopupAction) RunGet(params struct{}) {
countResp, err := this.RPC().HTTPCachePolicyRPC().CountAllEnabledHTTPCachePolicies(this.AdminContext(), &pb.CountAllEnabledHTTPCachePoliciesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
this.Data["page"] = page.AsHTML()
cachePoliciesResp, err := this.RPC().HTTPCachePolicyRPC().ListEnabledHTTPCachePolicies(this.AdminContext(), &pb.ListEnabledHTTPCachePoliciesRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicies := []*serverconfigs.HTTPCachePolicy{}
if len(cachePoliciesResp.HttpCachePoliciesJSON) > 0 {
err = json.Unmarshal(cachePoliciesResp.HttpCachePoliciesJSON, &cachePolicies)
if err != nil {
this.ErrorPage(err)
return
}
}
policyMaps := []maps.Map{}
for _, cachePolicy := range cachePolicies {
policyMaps = append(policyMaps, maps.Map{
"id": cachePolicy.Id,
"name": cachePolicy.Name,
"description": cachePolicy.Description,
"isOn": cachePolicy.IsOn,
})
}
this.Data["cachePolicies"] = policyMaps
this.Show()
}

View File

@@ -0,0 +1,98 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
"strconv"
)
type StatAction struct {
actionutils.ParentAction
}
func (this *StatAction) Init() {
this.Nav("", "", "stat")
}
func (this *StatAction) RunGet(params struct {
CachePolicyId int64
}) {
// 默认的集群ID
cookie, err := this.Request.Cookie("cache_cluster_id")
if cookie != nil && err == nil {
this.Data["clusterId"] = types.Int64(cookie.Value)
}
// 集群列表
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}
func (this *StatAction) RunPost(params struct {
CachePolicyId int64
ClusterId int64
Must *actions.Must
}) {
// 记录clusterId
this.AddCookie(&http.Cookie{
Name: "cache_cluster_id",
Value: strconv.FormatInt(params.ClusterId, 10),
})
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
// 发送命令
msg := &messageconfigs.StatCacheMessage{
CachePolicyJSON: cachePolicyJSON,
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodeStatCache, msg, 10, false)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogStatCaches, params.CachePolicyId)
this.Success()
}

View File

@@ -0,0 +1,43 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type TestAction struct {
actionutils.ParentAction
}
func (this *TestAction) Init() {
this.Nav("", "", "test")
}
func (this *TestAction) RunGet(params struct {
CachePolicyId int64
}) {
// 默认的集群ID
cookie, err := this.Request.Cookie("cache_cluster_id")
if cookie != nil && err == nil {
this.Data["clusterId"] = types.Int64(cookie.Value)
}
// 集群列表
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPCachePolicyId(this.AdminContext(), &pb.FindAllEnabledNodeClustersWithHTTPCachePolicyIdRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
clusterMaps := []maps.Map{}
for _, cluster := range clustersResp.NodeClusters {
clusterMaps = append(clusterMaps, maps.Map{
"id": cluster.Id,
"name": cluster.Name,
})
}
this.Data["clusters"] = clusterMaps
this.Show()
}

View File

@@ -0,0 +1,64 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"net/http"
"strconv"
)
type TestReadAction struct {
actionutils.ParentAction
}
func (this *TestReadAction) RunPost(params struct {
ClusterId int64
CachePolicyId int64
Key string
}) {
// 记录clusterId
this.AddCookie(&http.Cookie{
Name: "cache_cluster_id",
Value: strconv.FormatInt(params.ClusterId, 10),
})
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
// 发送命令
msg := &messageconfigs.ReadCacheMessage{
CachePolicyJSON: cachePolicyJSON,
Key: params.Key,
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodeReadCache, msg, 10, false)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogTestReading, params.CachePolicyId)
this.Success()
}

View File

@@ -0,0 +1,67 @@
package cache
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"net/http"
"strconv"
)
type TestWriteAction struct {
actionutils.ParentAction
}
func (this *TestWriteAction) RunPost(params struct {
ClusterId int64
CachePolicyId int64
Key string
Value string
}) {
// 记录clusterId
this.AddCookie(&http.Cookie{
Name: "cache_cluster_id",
Value: strconv.FormatInt(params.ClusterId, 10),
})
cachePolicyResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
cachePolicyJSON := cachePolicyResp.HttpCachePolicyJSON
if len(cachePolicyJSON) == 0 {
this.Fail("找不到要操作的缓存策略")
}
// 发送命令
msg := &messageconfigs.WriteCacheMessage{
CachePolicyJSON: cachePolicyJSON,
Key: params.Key,
Value: []byte(params.Value),
LifeSeconds: 3600,
}
results, err := nodeutils.SendMessageToCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodeWriteCache, msg, 10, false)
if err != nil {
this.ErrorPage(err)
return
}
isAllOk := true
for _, result := range results {
if !result.IsOK {
isAllOk = false
break
}
}
this.Data["isAllOk"] = isAllOk
this.Data["results"] = results
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogTestWriting, params.CachePolicyId)
this.Success()
}

View File

@@ -0,0 +1,219 @@
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "", "update")
}
func (this *UpdateAction) RunGet(params struct {
CachePolicyId int64
}) {
configResp, err := this.RPC().HTTPCachePolicyRPC().FindEnabledHTTPCachePolicyConfig(this.AdminContext(), &pb.FindEnabledHTTPCachePolicyConfigRequest{HttpCachePolicyId: params.CachePolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var configJSON = configResp.HttpCachePolicyJSON
if len(configJSON) == 0 {
this.NotFound("cachePolicy", params.CachePolicyId)
return
}
var cachePolicy = &serverconfigs.HTTPCachePolicy{}
err = json.Unmarshal(configJSON, cachePolicy)
if err != nil {
this.ErrorPage(err)
return
}
if cachePolicy.Type == serverconfigs.CachePolicyStorageFile && cachePolicy.Options != nil {
// fix min free size
{
_, ok := cachePolicy.Options["minFreeSize"]
if !ok {
cachePolicy.Options["minFreeSize"] = &shared.SizeCapacity{
Count: 0,
Unit: shared.SizeCapacityUnitGB,
}
}
}
// fix enableMMAP
{
_, ok := cachePolicy.Options["enableMMAP"]
if !ok {
cachePolicy.Options["enableMMAP"] = false
}
}
// fix enableIncompletePartialContent
{
_, ok := cachePolicy.Options["enableIncompletePartialContent"]
if !ok {
cachePolicy.Options["enableIncompletePartialContent"] = true
}
}
}
this.Data["cachePolicy"] = cachePolicy
// 其他选项
this.Data["types"] = serverconfigs.AllCachePolicyStorageTypes
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
CachePolicyId int64
Name string
Type string
// file
FileDir string
FileMemoryCapacityJSON []byte
FileOpenFileCacheMax int
FileEnableSendfile bool
FileMinFreeSizeJSON []byte
CapacityJSON []byte
MaxSizeJSON []byte
SyncCompressionCache bool
FetchTimeoutJSON []byte
EnableMMAP bool
EnableIncompletePartialContent bool
Description string
IsOn bool
RefsJSON []byte
Must *actions.Must
}) {
// 创建日志
defer this.CreateLogInfo(codes.ServerCachePolicy_LogUpdateCachePolicy, params.CachePolicyId)
params.Must.
Field("name", params.Name).
Require("请输入策略名称")
// 校验选项
var options interface{}
switch params.Type {
case serverconfigs.CachePolicyStorageFile:
params.Must.
Field("fileDir", params.FileDir).
Require("请输入缓存目录")
var memoryCapacity = &shared.SizeCapacity{}
if len(params.FileMemoryCapacityJSON) > 0 {
err := json.Unmarshal(params.FileMemoryCapacityJSON, memoryCapacity)
if err != nil {
this.ErrorPage(err)
return
}
}
var openFileCacheConfig *serverconfigs.OpenFileCacheConfig
if params.FileOpenFileCacheMax > 0 {
openFileCacheConfig = &serverconfigs.OpenFileCacheConfig{
IsOn: true,
Max: params.FileOpenFileCacheMax,
}
}
var minFreeSize = &shared.SizeCapacity{}
var minFreeSizeJSON = params.FileMinFreeSizeJSON
if !utils.JSONIsNull(minFreeSizeJSON) {
_, err := utils.JSONDecodeConfig(minFreeSizeJSON, minFreeSize)
if err != nil {
this.ErrorPage(err)
return
}
if minFreeSize.Count < 0 {
minFreeSize.Count = 0
}
}
options = &serverconfigs.HTTPFileCacheStorage{
Dir: params.FileDir,
MemoryPolicy: &serverconfigs.HTTPCachePolicy{
Capacity: memoryCapacity,
},
OpenFileCache: openFileCacheConfig,
EnableSendfile: params.FileEnableSendfile,
MinFreeSize: minFreeSize,
EnableMMAP: params.EnableMMAP,
EnableIncompletePartialContent: params.EnableIncompletePartialContent,
}
case serverconfigs.CachePolicyStorageMemory:
options = &serverconfigs.HTTPMemoryCacheStorage{}
default:
this.Fail("请选择正确的缓存类型")
}
optionsJSON, err := json.Marshal(options)
if err != nil {
this.ErrorPage(err)
return
}
// 校验缓存条件
var refs = []*serverconfigs.HTTPCacheRef{}
if len(params.RefsJSON) > 0 {
err = json.Unmarshal(params.RefsJSON, &refs)
if err != nil {
this.Fail("缓存条件解析失败:" + err.Error())
}
for _, ref := range refs {
err = ref.Init()
if err != nil {
this.Fail("缓存条件校验失败:" + err.Error())
}
}
}
_, err = this.RPC().HTTPCachePolicyRPC().UpdateHTTPCachePolicy(this.AdminContext(), &pb.UpdateHTTPCachePolicyRequest{
HttpCachePolicyId: params.CachePolicyId,
IsOn: params.IsOn,
Name: params.Name,
Description: params.Description,
CapacityJSON: params.CapacityJSON,
MaxSizeJSON: params.MaxSizeJSON,
Type: params.Type,
OptionsJSON: optionsJSON,
SyncCompressionCache: params.SyncCompressionCache,
FetchTimeoutJSON: params.FetchTimeoutJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// 修改缓存条件
_, err = this.RPC().HTTPCachePolicyRPC().UpdateHTTPCachePolicyRefs(this.AdminContext(), &pb.UpdateHTTPCachePolicyRefsRequest{
HttpCachePolicyId: params.CachePolicyId,
RefsJSON: params.RefsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

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

View File

@@ -0,0 +1,22 @@
package log
import (
"github.com/iwind/TeaGo/actions"
"net/http"
)
type Helper struct {
}
func NewHelper() *Helper {
return &Helper{}
}
func (this *Helper) BeforeAction(action *actions.ActionObject) {
if action.Request.Method != http.MethodGet {
return
}
action.Data["mainTab"] = "component"
action.Data["secondMenuItem"] = "log"
}

View File

@@ -0,0 +1,18 @@
package log
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.FirstMenu("index")
}
func (this *IndexAction) RunGet(params struct{}) {
this.Show()
}

View File

@@ -0,0 +1,18 @@
package log
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(NewHelper()).
Prefix("/servers/components/log").
Get("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,21 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type CountAction struct {
actionutils.ParentAction
}
func (this *CountAction) RunPost(params struct{}) {
countResp, err := this.RPC().HTTPFirewallPolicyRPC().CountAllEnabledHTTPFirewallPolicies(this.AdminContext(), &pb.CountAllEnabledHTTPFirewallPoliciesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["count"] = countResp.Count
this.Success()
}

View File

@@ -0,0 +1,104 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/actions"
)
type CreateGroupPopupAction struct {
actionutils.ParentAction
}
func (this *CreateGroupPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateGroupPopupAction) RunGet(params struct {
Type string
}) {
this.Data["type"] = params.Type
this.Show()
}
func (this *CreateGroupPopupAction) RunPost(params struct {
FirewallPolicyId int64
Type string
Name string
Code string
Description string
IsOn bool
Must *actions.Must
}) {
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if firewallPolicy == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
}
params.Must.
Field("name", params.Name).
Require("请输入分组名称")
createResp, err := this.RPC().HTTPFirewallRuleGroupRPC().CreateHTTPFirewallRuleGroup(this.AdminContext(), &pb.CreateHTTPFirewallRuleGroupRequest{
IsOn: params.IsOn,
Name: params.Name,
Code: params.Code,
Description: params.Description,
})
if err != nil {
this.ErrorPage(err)
return
}
groupId := createResp.FirewallRuleGroupId
switch params.Type {
case "inbound":
firewallPolicy.Inbound.GroupRefs = append(firewallPolicy.Inbound.GroupRefs, &firewallconfigs.HTTPFirewallRuleGroupRef{
IsOn: true,
GroupId: groupId,
})
default:
firewallPolicy.Outbound.GroupRefs = append(firewallPolicy.Outbound.GroupRefs, &firewallconfigs.HTTPFirewallRuleGroupRef{
IsOn: true,
GroupId: groupId,
})
}
inboundJSON, err := firewallPolicy.InboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
outboundJSON, err := firewallPolicy.OutboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicyGroups(this.AdminContext(), &pb.UpdateHTTPFirewallPolicyGroupsRequest{
HttpFirewallPolicyId: params.FirewallPolicyId,
InboundJSON: inboundJSON,
OutboundJSON: outboundJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// 日志
defer this.CreateLogInfo(codes.WAFRuleGroup_LogCreateRuleGroup, groupId, params.Name)
this.Success()
}

View File

@@ -0,0 +1,70 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"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{}) {
// 预置分组
groups := []maps.Map{}
templatePolicy := firewallconfigs.HTTPFirewallTemplate()
for _, group := range templatePolicy.AllRuleGroups() {
groups = append(groups, maps.Map{
"code": group.Code,
"name": group.Name,
"isOn": group.IsOn,
})
}
this.Data["groups"] = groups
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
GroupCodes []string
Description string
IsOn bool
Must *actions.Must
}) {
params.Must.
Field("name", params.Name).
Require("请输入策略名称")
createResp, err := this.RPC().HTTPFirewallPolicyRPC().CreateHTTPFirewallPolicy(this.AdminContext(), &pb.CreateHTTPFirewallPolicyRequest{
IsOn: params.IsOn,
Name: params.Name,
Description: params.Description,
HttpFirewallGroupCodes: params.GroupCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
// 返回数据
this.Data["firewallPolicy"] = maps.Map{
"id": createResp.HttpFirewallPolicyId,
"name": params.Name,
"description": params.Description,
}
// 日志
defer this.CreateLogInfo(codes.WAFPolicy_LogCreateWAFPolicy, createResp.HttpFirewallPolicyId)
this.Success()
}

View File

@@ -0,0 +1,126 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CreateRulePopupAction struct {
actionutils.ParentAction
}
func (this *CreateRulePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateRulePopupAction) RunGet(params struct {
Type string
}) {
// check points
var checkpointList = []maps.Map{}
for _, checkpoint := range firewallconfigs.AllCheckpoints {
if (params.Type == "inbound" && checkpoint.IsRequest) || params.Type == "outbound" {
checkpointList = append(checkpointList, maps.Map{
"name": checkpoint.Name,
"prefix": checkpoint.Prefix,
"description": checkpoint.Description,
"hasParams": checkpoint.HasParams,
"params": checkpoint.Params,
"options": checkpoint.Options,
"isComposed": checkpoint.IsComposed,
"dataType": checkpoint.DataType,
})
}
}
// operators
var operatorMaps = []maps.Map{}
for _, operator := range firewallconfigs.AllRuleOperators {
operatorMaps = append(operatorMaps, maps.Map{
"name": operator.Name,
"code": operator.Code,
"description": operator.Description,
"case": operator.CaseInsensitive,
"dataType": operator.DataType,
})
}
this.Data["operators"] = operatorMaps
this.Data["checkpoints"] = checkpointList
this.Show()
}
func (this *CreateRulePopupAction) RunPost(params struct {
RuleId int64
Prefix string
Operator string
Param string
ParamFiltersJSON []byte
OptionsJSON []byte
Value string
Case bool
Description string
Must *actions.Must
}) {
params.Must.
Field("prefix", params.Prefix).
Require("请选择参数")
if len(params.Value) > 4096 {
this.FailField("value", "对比值内容长度不能超过4096个字符")
return
}
var rule = &firewallconfigs.HTTPFirewallRule{
Id: params.RuleId,
IsOn: true,
}
if len(params.Param) > 0 {
rule.Param = "${" + params.Prefix + "." + params.Param + "}"
} else {
rule.Param = "${" + params.Prefix + "}"
}
var paramFilters = []*firewallconfigs.ParamFilter{}
if len(params.ParamFiltersJSON) > 0 {
err := json.Unmarshal(params.ParamFiltersJSON, &paramFilters)
if err != nil {
this.ErrorPage(err)
return
}
}
rule.ParamFilters = paramFilters
rule.Operator = params.Operator
rule.Value = params.Value
rule.IsCaseInsensitive = params.Case
rule.Description = params.Description
if len(params.OptionsJSON) > 0 {
options := []maps.Map{}
err := json.Unmarshal(params.OptionsJSON, &options)
if err != nil {
this.ErrorPage(err)
return
}
rule.CheckpointOptions = map[string]interface{}{}
for _, option := range options {
rule.CheckpointOptions[option.GetString("code")] = option.Get("value")
}
}
// 校验
err := rule.Init()
if err != nil {
this.Fail("校验规则 '" + rule.Param + " " + rule.Operator + " " + rule.Value + "' 失败,原因:" + err.Error())
}
this.Data["rule"] = rule
this.Success()
}

View File

@@ -0,0 +1,231 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strconv"
)
type CreateSetPopupAction struct {
actionutils.ParentAction
}
func (this *CreateSetPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateSetPopupAction) RunGet(params struct {
FirewallPolicyId int64
GroupId int64
Type string
}) {
this.Data["groupId"] = params.GroupId
this.Data["type"] = params.Type
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if firewallPolicy == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
this.Data["firewallPolicy"] = firewallPolicy
// 一些配置
this.Data["connectors"] = []maps.Map{
{
"name": this.Lang(codes.WAF_ConnectorAnd),
"value": firewallconfigs.HTTPFirewallRuleConnectorAnd,
"description": this.Lang(codes.WAF_ConnectorAndDescription),
},
{
"name": this.Lang(codes.WAF_ConnectorOr),
"value": firewallconfigs.HTTPFirewallRuleConnectorOr,
"description": this.Lang(codes.WAF_ConnectorOrDescription),
},
}
// 所有可选的动作
var actionMaps = []maps.Map{}
for _, action := range firewallconfigs.AllActions {
actionMaps = append(actionMaps, maps.Map{
"name": action.Name,
"description": action.Description,
"code": action.Code,
})
}
this.Data["actions"] = actionMaps
// 是否为全局
this.Data["isGlobalPolicy"] = firewallPolicy.ServerId == 0
this.Show()
}
func (this *CreateSetPopupAction) RunPost(params struct {
GroupId int64
Name string
FormType string
// normal
RulesJSON []byte
Connector string
ActionsJSON []byte
IgnoreLocal bool
IgnoreSearchEngine bool
// code
Code string
Must *actions.Must
}) {
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.AdminContext(), params.GroupId)
if err != nil {
this.ErrorPage(err)
return
}
if groupConfig == nil {
this.Fail("找不到分组Id" + strconv.FormatInt(params.GroupId, 10))
return
}
params.Must.
Field("name", params.Name).
Require("请输入规则集名称")
var setConfigJSON []byte
if params.FormType == "normal" {
if len(params.RulesJSON) == 0 {
this.Fail("请添加至少一个规则")
return
}
var rules = []*firewallconfigs.HTTPFirewallRule{}
err = json.Unmarshal(params.RulesJSON, &rules)
if err != nil {
this.ErrorPage(err)
return
}
if len(rules) == 0 {
this.Fail("请添加至少一个规则")
return
}
var actionConfigs = []*firewallconfigs.HTTPFirewallActionConfig{}
if len(params.ActionsJSON) > 0 {
err = json.Unmarshal(params.ActionsJSON, &actionConfigs)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(actionConfigs) == 0 {
this.Fail("请添加至少一个动作")
return
}
var setConfig = &firewallconfigs.HTTPFirewallRuleSet{
Id: 0,
IsOn: true,
Name: params.Name,
Code: "",
Description: "",
Connector: params.Connector,
RuleRefs: nil,
Rules: rules,
Actions: actionConfigs,
IgnoreLocal: params.IgnoreLocal,
IgnoreSearchEngine: params.IgnoreSearchEngine,
}
setConfigJSON, err = json.Marshal(setConfig)
if err != nil {
this.ErrorPage(err)
return
}
} else if params.FormType == "code" {
var codeJSON = []byte(params.Code)
if len(codeJSON) == 0 {
this.FailField("code", "请输入规则集代码")
return
}
var setConfig = &firewallconfigs.HTTPFirewallRuleSet{}
err = json.Unmarshal(codeJSON, setConfig)
if err != nil {
this.FailField("code", "解析规则集代码失败:"+err.Error())
return
}
if len(setConfig.Rules) == 0 {
this.FailField("code", "规则集代码中必须包含至少一个规则")
return
}
if len(setConfig.Actions) == 0 {
this.FailField("code", "规则集代码中必须包含至少一个动作")
return
}
setConfig.Name = params.Name
setConfig.IsOn = true
// 重置ID
setConfig.Id = 0
setConfig.RuleRefs = nil
for _, rule := range setConfig.Rules {
rule.Id = 0
}
err = setConfig.Init()
if err != nil {
this.FailField("code", "校验规则集代码失败:"+err.Error())
return
}
setConfigJSON, err = json.Marshal(setConfig)
} else {
this.Fail("错误的参数'formType': " + params.FormType)
return
}
createUpdateResp, err := this.RPC().HTTPFirewallRuleSetRPC().CreateOrUpdateHTTPFirewallRuleSetFromConfig(this.AdminContext(), &pb.CreateOrUpdateHTTPFirewallRuleSetFromConfigRequest{FirewallRuleSetConfigJSON: setConfigJSON})
if err != nil {
this.ErrorPage(err)
return
}
groupConfig.SetRefs = append(groupConfig.SetRefs, &firewallconfigs.HTTPFirewallRuleSetRef{
IsOn: true,
SetId: createUpdateResp.FirewallRuleSetId,
})
setRefsJSON, err := json.Marshal(groupConfig.SetRefs)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallRuleGroupRPC().UpdateHTTPFirewallRuleGroupSets(this.AdminContext(), &pb.UpdateHTTPFirewallRuleGroupSetsRequest{
FirewallRuleGroupId: params.GroupId,
FirewallRuleSetsJSON: setRefsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["setId"] = createUpdateResp.FirewallRuleSetId
this.Success()
}

View File

@@ -0,0 +1,35 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
FirewallPolicyId int64
}) {
// 日志
defer this.CreateLogInfo(codes.WAFPolicy_LogDeleteWAFPolicy, params.FirewallPolicyId)
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithHTTPFirewallPolicyId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithHTTPFirewallPolicyIdRequest{HttpFirewallPolicyId: params.FirewallPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
if countResp.Count > 0 {
this.Fail("此WAF策略正在被有些集群引用请修改后再删除。")
}
_, err = this.RPC().HTTPFirewallPolicyRPC().DeleteHTTPFirewallPolicy(this.AdminContext(), &pb.DeleteHTTPFirewallPolicyRequest{HttpFirewallPolicyId: params.FirewallPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,56 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteGroupAction struct {
actionutils.ParentAction
}
func (this *DeleteGroupAction) RunPost(params struct {
FirewallPolicyId int64
GroupId int64
}) {
// 日志
defer this.CreateLogInfo(codes.WAFRuleGroup_LogDeleteRuleGroup, params.FirewallPolicyId, params.GroupId)
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if firewallPolicy == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
firewallPolicy.RemoveRuleGroup(params.GroupId)
inboundJSON, err := firewallPolicy.InboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
outboundJSON, err := firewallPolicy.OutboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicyGroups(this.AdminContext(), &pb.UpdateHTTPFirewallPolicyGroupsRequest{
HttpFirewallPolicyId: params.FirewallPolicyId,
InboundJSON: inboundJSON,
OutboundJSON: outboundJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,54 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
)
type DeleteSetAction struct {
actionutils.ParentAction
}
func (this *DeleteSetAction) RunPost(params struct {
GroupId int64
SetId int64
}) {
// 日志
defer this.CreateLogInfo(codes.WAFRuleSet_LogDeleteRuleSet, params.GroupId, params.SetId)
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.AdminContext(), params.GroupId)
if err != nil {
this.ErrorPage(err)
return
}
if groupConfig == nil {
this.NotFound("firewallRuleGroup", params.GroupId)
return
}
newRefs := []*firewallconfigs.HTTPFirewallRuleSetRef{}
for _, ref := range groupConfig.SetRefs {
if ref.SetId != params.SetId {
newRefs = append(newRefs, ref)
}
}
newRefsJSON, err := json.Marshal(newRefs)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallRuleGroupRPC().UpdateHTTPFirewallRuleGroupSets(this.AdminContext(), &pb.UpdateHTTPFirewallRuleGroupSetsRequest{
FirewallRuleGroupId: params.GroupId,
FirewallRuleSetsJSON: newRefsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,134 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/ttlcache"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/rands"
"time"
)
type ExportAction struct {
actionutils.ParentAction
}
func (this *ExportAction) Init() {
this.Nav("", "", "export")
}
func (this *ExportAction) RunGet(params struct {
FirewallPolicyId int64
}) {
policy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if policy == nil {
this.NotFound("firewallPolicy", policy.Id)
return
}
enabledInboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
enabledOutboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
disabledInboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
disabledOutboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
if policy.Inbound != nil {
for _, g := range policy.Inbound.Groups {
if g.IsOn {
enabledInboundGroups = append(enabledInboundGroups, g)
} else {
disabledInboundGroups = append(disabledInboundGroups, g)
}
}
}
if policy.Outbound != nil {
for _, g := range policy.Outbound.Groups {
if g.IsOn {
enabledOutboundGroups = append(enabledOutboundGroups, g)
} else {
disabledOutboundGroups = append(disabledOutboundGroups, g)
}
}
}
this.Data["enabledInboundGroups"] = enabledInboundGroups
this.Data["enabledOutboundGroups"] = enabledOutboundGroups
this.Data["disabledInboundGroups"] = disabledInboundGroups
this.Data["disabledOutboundGroups"] = disabledOutboundGroups
this.Show()
}
func (this *ExportAction) RunPost(params struct {
FirewallPolicyId int64
InboundGroupIds []int64
OutboundGroupIds []int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.WAFPolicy_LogExportWAFPolicy, params.FirewallPolicyId)
policy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if policy == nil {
this.NotFound("firewallPolicy", policy.Id)
return
}
// inbound
newInboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
for _, inboundGroupId := range params.InboundGroupIds {
group := policy.FindRuleGroup(inboundGroupId)
if group != nil {
newInboundGroups = append(newInboundGroups, group)
}
}
if policy.Inbound == nil {
policy.Inbound = &firewallconfigs.HTTPFirewallInboundConfig{
IsOn: true,
}
}
policy.Inbound.Groups = newInboundGroups
policy.Inbound.GroupRefs = nil
// outbound
newOutboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
for _, outboundGroupId := range params.OutboundGroupIds {
group := policy.FindRuleGroup(outboundGroupId)
if group != nil {
newOutboundGroups = append(newOutboundGroups, group)
}
}
if policy.Outbound == nil {
policy.Outbound = &firewallconfigs.HTTPFirewallOutboundConfig{
IsOn: true,
}
}
policy.Outbound.Groups = newOutboundGroups
policy.Outbound.GroupRefs = nil
configJSON, err := json.Marshal(policy)
if err != nil {
this.ErrorPage(err)
return
}
key := "waf." + rands.HexString(32)
ttlcache.DefaultCache.Write(key, configJSON, time.Now().Unix()+600)
this.Data["key"] = key
this.Data["id"] = params.FirewallPolicyId
this.Success()
}

View File

@@ -0,0 +1,39 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/ttlcache"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/types"
"strconv"
)
type ExportDownloadAction struct {
actionutils.ParentAction
}
func (this *ExportDownloadAction) Init() {
this.Nav("", "", "")
}
func (this *ExportDownloadAction) RunGet(params struct {
Key string
PolicyId int64
}) {
item := ttlcache.DefaultCache.Read(params.Key)
if item == nil || item.Value == nil {
this.WriteString("找不到要导出的内容")
return
}
ttlcache.DefaultCache.Delete(params.Key)
data, ok := item.Value.([]byte)
if ok {
this.AddHeader("Content-Disposition", "attachment; filename=\"WAF-"+types.String(params.PolicyId)+".json\";")
this.AddHeader("Content-Length", strconv.Itoa(len(data)))
_, _ = this.Write(data)
} else {
this.WriteString("找不到要导出的内容")
return
}
}

View File

@@ -0,0 +1,105 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"strings"
)
type GroupAction struct {
actionutils.ParentAction
}
func (this *GroupAction) Init() {
this.Nav("", "", this.ParamString("type"))
}
func (this *GroupAction) RunGet(params struct {
FirewallPolicyId int64
GroupId int64
Type string
}) {
this.Data["type"] = params.Type
//stime := time.Now()
// policy
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if firewallPolicy == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
//fmt.Printf("调用 FindEnabledHTTPFirewallPolicyConfig 方法 耗时: %v\n", time.Now().Sub(stime))
// group config
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.AdminContext(), params.GroupId)
if err != nil {
this.ErrorPage(err)
return
}
if groupConfig == nil {
this.NotFound("firewallRuleGroup", params.GroupId)
return
}
//fmt.Printf("调用 FindRuleGroupConfig 方法 耗时: %v\n", time.Now().Sub(stime))
this.Data["group"] = groupConfig
// rule sets
this.Data["sets"] = lists.Map(groupConfig.Sets, func(k int, v interface{}) interface{} {
set := v.(*firewallconfigs.HTTPFirewallRuleSet)
// 动作说明
var actionMaps = []maps.Map{}
for _, action := range set.Actions {
def := firewallconfigs.FindActionDefinition(action.Code)
if def == nil {
continue
}
actionMaps = append(actionMaps, maps.Map{
"code": strings.ToUpper(action.Code),
"name": def.Name,
"category": def.Category,
"options": action.Options,
})
}
return maps.Map{
"id": set.Id,
"name": set.Name,
"rules": lists.Map(set.Rules, func(k int, v interface{}) interface{} {
rule := v.(*firewallconfigs.HTTPFirewallRule)
// 校验
var errString = ""
var err = rule.Init()
if err != nil {
errString = err.Error()
}
return maps.Map{
"param": rule.Param,
"paramFilters": rule.ParamFilters,
"operator": rule.Operator,
"value": rule.Value,
"isCaseInsensitive": rule.IsCaseInsensitive,
"description": rule.Description,
"isComposed": firewallconfigs.CheckCheckpointIsComposed(rule.Prefix()),
"checkpointOptions": rule.CheckpointOptions,
"err": errString,
}
}),
"isOn": set.IsOn,
"actions": actionMaps,
"connector": strings.ToUpper(set.Connector),
}
})
//fmt.Printf("调用 list.map 方法 耗时: %v\n", time.Now().Sub(stime))
this.Show()
}

View File

@@ -0,0 +1,74 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/iwind/TeaGo/maps"
)
type GroupsAction struct {
actionutils.ParentAction
}
func (this *GroupsAction) Init() {
this.Nav("", "", this.ParamString("type"))
}
func (this *GroupsAction) RunGet(params struct {
FirewallPolicyId int64
Type string
}) {
this.Data["type"] = params.Type
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if firewallPolicy == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
groupMaps := []maps.Map{}
// inbound
if params.Type == "inbound" {
if firewallPolicy.Inbound != nil {
for _, g := range firewallPolicy.Inbound.Groups {
groupMaps = append(groupMaps, maps.Map{
"id": g.Id,
"name": g.Name,
"code": g.Code,
"isOn": g.IsOn,
"description": g.Description,
"countSets": len(g.Sets),
"isTemplate": g.IsTemplate,
"canDelete": true, //!g.IsTemplate,
})
}
}
}
// outbound
if params.Type == "outbound" {
if firewallPolicy.Outbound != nil {
for _, g := range firewallPolicy.Outbound.Groups {
groupMaps = append(groupMaps, maps.Map{
"id": g.Id,
"name": g.Name,
"code": g.Code,
"isOn": g.IsOn,
"description": g.Description,
"countSets": len(g.Sets),
"isTemplate": g.IsTemplate,
"canDelete": true, //!g.IsTemplate,
})
}
}
}
this.Data["groups"] = groupMaps
this.Show()
}

View File

@@ -0,0 +1,71 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/actions"
"net/http"
)
type Helper struct {
}
func NewHelper() *Helper {
return &Helper{}
}
func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) {
action := actionPtr.Object()
if action.Request.Method != http.MethodGet {
return true
}
action.Data["mainTab"] = "component"
action.Data["secondMenuItem"] = "waf"
// 显示当前的防火墙名称
firewallPolicyId := action.ParamInt64("firewallPolicyId")
if firewallPolicyId > 0 {
action.Data["firewallPolicyId"] = firewallPolicyId
action.Data["countInboundGroups"] = 0
action.Data["countOutboundGroups"] = 0
parentAction := actionutils.FindParentAction(actionPtr)
if parentAction != nil {
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicy(parentAction.AdminContext(), firewallPolicyId)
if err != nil {
parentAction.ErrorPage(err)
return
}
if firewallPolicy == nil {
action.WriteString("can not find firewall policy")
return
}
action.Data["firewallPolicyName"] = firewallPolicy.Name
// inbound
if len(firewallPolicy.InboundJSON) > 0 {
inboundConfig := &firewallconfigs.HTTPFirewallInboundConfig{}
err = json.Unmarshal(firewallPolicy.InboundJSON, inboundConfig)
if err != nil {
parentAction.ErrorPage(err)
return
}
action.Data["countInboundGroups"] = len(inboundConfig.GroupRefs)
}
// outbound
if len(firewallPolicy.OutboundJSON) > 0 {
outboundConfig := &firewallconfigs.HTTPFirewallOutboundConfig{}
err = json.Unmarshal(firewallPolicy.OutboundJSON, outboundConfig)
if err != nil {
parentAction.ErrorPage(err)
return
}
action.Data["countOutboundGroups"] = len(outboundConfig.GroupRefs)
}
}
}
return true
}

View File

@@ -0,0 +1,326 @@
package waf
import (
"bytes"
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/exce"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/actions"
stringutil "github.com/iwind/TeaGo/utils/string"
"os"
)
type ImportAction struct {
actionutils.ParentAction
}
func (this *ImportAction) Init() {
this.Nav("", "", "import")
}
func (this *ImportAction) RunGet(params struct{}) {
this.Show()
}
func (this *ImportAction) RunPost(params struct {
FirewallPolicyId int64
File *actions.File
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.WAFPolicy_LogImportWAFPolicy, params.FirewallPolicyId)
if params.File == nil {
this.Fail("请上传要导入的文件")
}
if params.File.Ext != ".json" {
this.Fail("规则文件的扩展名只能是.json")
}
data, err := params.File.Read()
if err != nil {
this.Fail("读取文件时发生错误:" + err.Error())
}
config := &firewallconfigs.HTTPFirewallPolicy{}
err = json.Unmarshal(data, config)
if err != nil {
this.Fail("解析文件时发生错误:" + err.Error())
}
_, err = this.RPC().HTTPFirewallPolicyRPC().ImportHTTPFirewallPolicy(this.AdminContext(), &pb.ImportHTTPFirewallPolicyRequest{
HttpFirewallPolicyId: params.FirewallPolicyId,
HttpFirewallPolicyJSON: data,
})
if err != nil {
this.Fail("导入失败:" + err.Error())
}
this.Success()
}
func (this *ImportAction) importXlsxWAFRules(data []byte, firewallPolicyId int64) {
// 解析文件
errRulesBuff, rules, err := exce.ParseRules(bytes.NewReader(data))
if err != nil {
this.Fail("导入失败:" + err.Error())
}
groupSetMaps := map[int64]map[string]int64{}
inboundGroupNameMaps := map[string]int64{}
outboundGroupNameMaps := map[string]int64{}
newInboundGroup := []int64{}
newOutboundGroup := []int64{}
// 批量加入waf规则
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), firewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if firewallPolicy == nil {
this.NotFound("firewallPolicy", firewallPolicyId)
}
for _, g := range firewallPolicy.Inbound.GroupRefs {
// 查询group 下的规则集
group, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.AdminContext(), g.GroupId)
if err != nil {
this.Fail("获取策略下规则分组信息失败:" + err.Error())
}
inboundGroupNameMaps[group.Name] = group.Id
groupSetMaps[g.GroupId] = make(map[string]int64, len(group.Sets))
for _, set := range group.Sets {
groupSetMaps[g.GroupId][set.Name] = set.Id
}
}
for _, g := range firewallPolicy.Outbound.GroupRefs {
// 查询group 下的规则集
group, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.AdminContext(), g.GroupId)
if err != nil {
this.Fail("获取策略下规则分组信息失败:" + err.Error())
}
outboundGroupNameMaps[group.Name] = group.Id
groupSetMaps[g.GroupId] = make(map[string]int64, len(group.Sets))
for _, set := range group.Sets {
groupSetMaps[g.GroupId][set.Name] = set.Id
}
}
for _, rule := range rules {
var inboundGroupId int64
var outboundGroupId int64
var ok bool
if rule.Inbound {
inboundGroupId, ok = inboundGroupNameMaps[rule.Type]
if !ok { // 如果不存在 则创建新的入站规则分组
createResp, err := this.RPC().HTTPFirewallRuleGroupRPC().CreateHTTPFirewallRuleGroup(this.AdminContext(), &pb.CreateHTTPFirewallRuleGroupRequest{
IsOn: true,
Name: rule.Type,
Code: rule.Type,
Description: "WAF规则导入" + rule.Type,
})
if err != nil {
this.ErrorPage(err)
return
}
inboundGroupId = createResp.FirewallRuleGroupId
newInboundGroup = append(newInboundGroup, inboundGroupId)
inboundGroupNameMaps[rule.Type] = inboundGroupId
}
}
if rule.Outbound {
outboundGroupId, ok = outboundGroupNameMaps[rule.Type]
if !ok { // 如果不存在 则创建新的入站规则分组
createResp, err := this.RPC().HTTPFirewallRuleGroupRPC().CreateHTTPFirewallRuleGroup(this.AdminContext(), &pb.CreateHTTPFirewallRuleGroupRequest{
IsOn: true,
Name: rule.Type,
Code: rule.Type,
Description: "WAF规则导入" + rule.Type,
})
if err != nil {
this.ErrorPage(err)
return
}
outboundGroupId = createResp.FirewallRuleGroupId
newOutboundGroup = append(newOutboundGroup, outboundGroupId)
outboundGroupNameMaps[rule.Type] = outboundGroupId
}
}
// 往规则分组中加入新规则集
var rs = []*firewallconfigs.HTTPFirewallRule{} //入站规则
var outRs = make([]*firewallconfigs.HTTPFirewallRule, 1) // 出站规则
for idx, arg := range rule.Position {
if arg == "" {
continue
}
if arg == "${responseBody}" { // 出站规则
outRs[0] = &firewallconfigs.HTTPFirewallRule{
Id: 0,
IsOn: true,
Param: "{responseBody}",
Operator: "match",
Value: rule.Regular,
IsCaseInsensitive: true,
IsComposed: false,
}
} else {
// 入站规则才支持多规则
if rule.IsAnd {
rs = append(rs, &firewallconfigs.HTTPFirewallRule{
Id: 0,
IsOn: true,
Param: arg,
Operator: "match",
Value: rule.Regulars[idx],
IsCaseInsensitive: true,
IsComposed: false,
})
} else {
rs = append(rs, &firewallconfigs.HTTPFirewallRule{
Id: 0,
IsOn: true,
Param: arg,
Operator: "match",
Value: rule.Regular,
IsCaseInsensitive: true,
IsComposed: false,
})
}
}
}
var set = &firewallconfigs.HTTPFirewallRuleSet{
Id: 0,
IsOn: true,
Name: rule.Name,
Code: "",
Description: rule.Description,
Connector: "or",
RuleRefs: nil,
Level: rule.Level,
CVE: rule.CVE,
// 默认动作为403显示网页
Actions: []*firewallconfigs.HTTPFirewallActionConfig{
{
Code: firewallconfigs.HTTPFirewallActionPage,
Options: map[string]interface{}{
"useDefault": true,
"status": 403,
"body": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>403 Forbidden</title>\n\t<style>\n\t\taddress { line-height: 1.8; }\n\t</style>\n</head>\n<body>\n<h1>403 Forbidden</h1>\n<address>Connection: ${remoteAddr} (Client) -&gt; ${serverAddr} (Server)</address>\n<address>Request ID: ${requestId}</address>\n</body>\n</html>",
},
},
},
IgnoreLocal: false,
}
if rule.IsAnd {
set.Connector = "and"
}
// 判断规则集中是否存在相同的规则集名字 如果有替换
// 如果是已存在的分组则判断
if rule.Inbound {
if ok {
set.Id, ok = groupSetMaps[inboundGroupId][set.Name]
}
set.Rules = rs
setConfigJSON, err := json.Marshal(set)
if err != nil {
this.ErrorPage(err)
return
}
if ok { // 替换
_, err = this.RPC().HTTPFirewallRuleSetRPC().CreateOrUpdateHTTPFirewallRuleSetFromConfig(this.AdminContext(), &pb.CreateOrUpdateHTTPFirewallRuleSetFromConfigRequest{
FirewallRuleSetConfigJSON: setConfigJSON,
})
} else {
_, err = this.RPC().HTTPFirewallRuleGroupRPC().AddHTTPFirewallRuleGroupSet(this.AdminContext(), &pb.AddHTTPFirewallRuleGroupSetRequest{
FirewallRuleGroupId: inboundGroupId,
FirewallRuleSetConfigJSON: setConfigJSON,
})
}
if err != nil {
this.ErrorPage(err)
return
}
}
if rule.Outbound {
if ok {
set.Id, ok = groupSetMaps[outboundGroupId][set.Name]
}
set.Rules = outRs
setConfigJSON, err := json.Marshal(set)
if err != nil {
this.ErrorPage(err)
return
}
if ok { // 替换
_, err = this.RPC().HTTPFirewallRuleSetRPC().CreateOrUpdateHTTPFirewallRuleSetFromConfig(this.AdminContext(), &pb.CreateOrUpdateHTTPFirewallRuleSetFromConfigRequest{
FirewallRuleSetConfigJSON: setConfigJSON,
})
} else {
_, err = this.RPC().HTTPFirewallRuleGroupRPC().AddHTTPFirewallRuleGroupSet(this.AdminContext(), &pb.AddHTTPFirewallRuleGroupSetRequest{
FirewallRuleGroupId: outboundGroupId,
FirewallRuleSetConfigJSON: setConfigJSON,
})
}
if err != nil {
this.ErrorPage(err)
return
}
}
}
// 修改策略 当前的入站规则分组
for _, groupId := range newInboundGroup {
firewallPolicy.Inbound.GroupRefs = append(firewallPolicy.Inbound.GroupRefs, &firewallconfigs.HTTPFirewallRuleGroupRef{
IsOn: true,
GroupId: groupId,
})
}
// 修改策略 当前的出站规则分组
for _, groupId := range newOutboundGroup {
firewallPolicy.Outbound.GroupRefs = append(firewallPolicy.Outbound.GroupRefs, &firewallconfigs.HTTPFirewallRuleGroupRef{
IsOn: true,
GroupId: groupId,
})
}
inboundJSON, err := firewallPolicy.InboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
outboundJSON, err := firewallPolicy.OutboundJSON()
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicyGroups(this.AdminContext(), &pb.UpdateHTTPFirewallPolicyGroupsRequest{
HttpFirewallPolicyId: firewallPolicyId,
InboundJSON: inboundJSON,
OutboundJSON: outboundJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
if errRulesBuff != nil { // 导入规则中存在无法分析的规则,将其统计并返回给客户
md := stringutil.Md5(errRulesBuff.String())
_ = os.MkdirAll(Tea.TmpDir(), 0755)
_ = os.WriteFile(Tea.TmpFile(md+".xlsx"), errRulesBuff.Bytes(), 0755)
//this.AddHeader("Content-Disposition", "attachment; filename=\"WAF-ALL-Error.xlsx\";")
//this.AddHeader("Content-Length", strconv.Itoa(errRulesBuff.Len()))
//this.AddHeader("Content-Type", "application/vnd.ms-excel; charset=utf-8")
//this.AddHeader("Content-Transfer-Encoding", "binary")
//_, _ = this.Write(errRulesBuff.Bytes())
this.Data["key"] = md
}
this.Success()
}

View File

@@ -0,0 +1,48 @@
/*
@Author: dp
@Description:
@File: importErr
@Version: 1.0.0
@Date: 2025-03-24 17:23
*/
/*
@Author: 1usir
@Description:
@File: importErr.go
@Version: 1.0.0
@Date: 2024/2/28 14:58
*/
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/Tea"
"os"
"strconv"
)
type ImportErrAction struct {
actionutils.ParentAction
}
func (this *ImportErrAction) Init() {
this.Nav("", "", "")
}
func (this *ImportErrAction) RunGet(params struct {
Key string
}) {
filename := Tea.TmpFile(params.Key + ".xlsx")
fs, err := os.ReadFile(filename)
if err != nil {
this.WriteString("找不到要导出的内容")
return
}
this.AddHeader("Content-Disposition", "attachment; filename=\"WAF-ALL-Error.xlsx\";")
this.AddHeader("Content-Length", strconv.Itoa(len(fs)))
this.AddHeader("Content-Type", "application/vnd.ms-excel; charset=utf-8")
this.AddHeader("Content-Transfer-Encoding", "binary")
_, _ = this.Write(fs)
_ = os.RemoveAll(filename)
}

View File

@@ -0,0 +1,99 @@
package waf
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.FirstMenu("index")
}
func (this *IndexAction) RunGet(params struct {
Keyword string
ClusterId int64
}) {
this.Data["keyword"] = params.Keyword
this.Data["clusterId"] = params.ClusterId
countResp, err := this.RPC().HTTPFirewallPolicyRPC().CountAllEnabledHTTPFirewallPolicies(this.AdminContext(), &pb.CountAllEnabledHTTPFirewallPoliciesRequest{
NodeClusterId: params.ClusterId,
Keyword: params.Keyword,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
listResp, err := this.RPC().HTTPFirewallPolicyRPC().ListEnabledHTTPFirewallPolicies(this.AdminContext(), &pb.ListEnabledHTTPFirewallPoliciesRequest{
NodeClusterId: params.ClusterId,
Keyword: params.Keyword,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var policyMaps = []maps.Map{}
for _, policy := range listResp.HttpFirewallPolicies {
var countInbound = 0
var countOutbound = 0
if len(policy.InboundJSON) > 0 {
inboundConfig := &firewallconfigs.HTTPFirewallInboundConfig{}
err = json.Unmarshal(policy.InboundJSON, inboundConfig)
if err != nil {
this.ErrorPage(err)
return
}
countInbound = len(inboundConfig.GroupRefs)
}
if len(policy.OutboundJSON) > 0 {
outboundConfig := &firewallconfigs.HTTPFirewallInboundConfig{}
err = json.Unmarshal(policy.OutboundJSON, outboundConfig)
if err != nil {
this.ErrorPage(err)
return
}
countOutbound = len(outboundConfig.GroupRefs)
}
countClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithHTTPFirewallPolicyId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithHTTPFirewallPolicyIdRequest{HttpFirewallPolicyId: policy.Id})
if err != nil {
this.ErrorPage(err)
return
}
var countClusters = countClustersResp.Count
// mode
if len(policy.Mode) == 0 {
policy.Mode = firewallconfigs.FirewallModeDefend
}
policyMaps = append(policyMaps, maps.Map{
"id": policy.Id,
"isOn": policy.IsOn,
"name": policy.Name,
"mode": policy.Mode,
"modeInfo": firewallconfigs.FindFirewallMode(policy.Mode),
"countInbound": countInbound,
"countOutbound": countOutbound,
"countClusters": countClusters,
})
}
this.Data["policies"] = policyMaps
this.Data["page"] = page.AsHTML()
this.Show()
}

View File

@@ -0,0 +1,57 @@
package waf
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/waf/ipadmin"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)).
Helper(NewHelper()).
Data("teaMenu", "servers").
Data("teaSubMenu", "waf").
Prefix("/servers/components/waf").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
Post("/delete", new(DeleteAction)).
Get("/policy", new(PolicyAction)).
Post("/upgradeTemplate", new(UpgradeTemplateAction)).
Get("/groups", new(GroupsAction)).
Get("/group", new(GroupAction)).
Get("/log", new(LogAction)).
GetPost("/update", new(UpdateAction)).
GetPost("/test", new(TestAction)).
GetPost("/export", new(ExportAction)).
Get("/exportDownload", new(ExportDownloadAction)).
GetPost("/import", new(ImportAction)).
Get("/importErr", new(ImportErrAction)).
Post("/updateGroupOn", new(UpdateGroupOnAction)).
Post("/deleteGroup", new(DeleteGroupAction)).
GetPost("/createGroupPopup", new(CreateGroupPopupAction)).
Post("/sortGroups", new(SortGroupsAction)).
GetPost("/updateGroupPopup", new(UpdateGroupPopupAction)).
GetPost("/createSetPopup", new(CreateSetPopupAction)).
GetPost("/createRulePopup", new(CreateRulePopupAction)).
Post("/sortSets", new(SortSetsAction)).
Post("/updateSetOn", new(UpdateSetOnAction)).
Post("/deleteSet", new(DeleteSetAction)).
GetPost("/updateSetPopup", new(UpdateSetPopupAction)).
Get("/setCodePopup", new(SetCodePopupAction)).
Post("/count", new(CountAction)).
Get("/selectPopup", new(SelectPopupAction)).
Post("/testRegexp", new(TestRegexpAction)).
// IP管理
GetPost("/ipadmin", new(ipadmin.IndexAction)).
GetPost("/ipadmin/provinces", new(ipadmin.ProvincesAction)).
Get("/ipadmin/lists", new(ipadmin.ListsAction)).
GetPost("/ipadmin/updateIPPopup", new(ipadmin.UpdateIPPopupAction)).
Post("/ipadmin/deleteIP", new(ipadmin.DeleteIPAction)).
GetPost("/ipadmin/test", new(ipadmin.TestAction)).
EndAll()
})
}

View File

@@ -0,0 +1,29 @@
package ipadmin
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteIPAction struct {
actionutils.ParentAction
}
func (this *DeleteIPAction) RunPost(params struct {
FirewallPolicyId int64
ItemId int64
}) {
// 日志
defer this.CreateLogInfo(codes.WAF_LogDeleteIPFromWAFPolicy, params.FirewallPolicyId, params.ItemId)
// TODO 判断权限
_, err := this.RPC().IPItemRPC().DeleteIPItem(this.AdminContext(), &pb.DeleteIPItemRequest{IpItemId: params.ItemId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,175 @@
package ipadmin
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "ipadmin")
}
func (this *IndexAction) RunGet(params struct {
FirewallPolicyId int64
}) {
this.Data["subMenuItem"] = "region"
// 当前选中的地区
policyConfig, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if policyConfig == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
var deniedCountryIds = []int64{}
var allowedCountryIds = []int64{}
var countryHTML string
var allowSearchEngine bool
if policyConfig.Inbound != nil && policyConfig.Inbound.Region != nil {
deniedCountryIds = policyConfig.Inbound.Region.DenyCountryIds
allowedCountryIds = policyConfig.Inbound.Region.AllowCountryIds
countryHTML = policyConfig.Inbound.Region.CountryHTML
allowSearchEngine = policyConfig.Inbound.Region.AllowSearchEngine
}
this.Data["countryHTML"] = countryHTML
this.Data["allowSearchEngine"] = allowSearchEngine
countriesResp, err := this.RPC().RegionCountryRPC().FindAllRegionCountries(this.AdminContext(), &pb.FindAllRegionCountriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var deniesCountryMaps = []maps.Map{}
var allowedCountryMaps = []maps.Map{}
for _, country := range countriesResp.RegionCountries {
var countryMap = maps.Map{
"id": country.Id,
"name": country.DisplayName,
"letter": strings.ToUpper(string(country.Pinyin[0][0])),
}
if lists.ContainsInt64(deniedCountryIds, country.Id) {
deniesCountryMaps = append(deniesCountryMaps, countryMap)
}
if lists.ContainsInt64(allowedCountryIds, country.Id) {
allowedCountryMaps = append(allowedCountryMaps, countryMap)
}
}
this.Data["deniedCountries"] = deniesCountryMaps
this.Data["allowedCountries"] = allowedCountryMaps
// except & only URL Patterns
this.Data["exceptURLPatterns"] = []*shared.URLPattern{}
this.Data["onlyURLPatterns"] = []*shared.URLPattern{}
if policyConfig.Inbound != nil && policyConfig.Inbound.Region != nil {
if len(policyConfig.Inbound.Region.CountryExceptURLPatterns) > 0 {
this.Data["exceptURLPatterns"] = policyConfig.Inbound.Region.CountryExceptURLPatterns
}
if len(policyConfig.Inbound.Region.CountryOnlyURLPatterns) > 0 {
this.Data["onlyURLPatterns"] = policyConfig.Inbound.Region.CountryOnlyURLPatterns
}
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
FirewallPolicyId int64
DenyCountryIds []int64
AllowCountryIds []int64
ExceptURLPatternsJSON []byte
OnlyURLPatternsJSON []byte
CountryHTML string
AllowSearchEngine bool
Must *actions.Must
}) {
// 日志
defer this.CreateLogInfo(codes.WAF_LogUpdateForbiddenCountries, params.FirewallPolicyId)
policyConfig, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.AdminContext(), params.FirewallPolicyId)
if err != nil {
this.ErrorPage(err)
return
}
if policyConfig == nil {
this.NotFound("firewallPolicy", params.FirewallPolicyId)
return
}
if policyConfig.Inbound == nil {
policyConfig.Inbound = &firewallconfigs.HTTPFirewallInboundConfig{IsOn: true}
}
if policyConfig.Inbound.Region == nil {
policyConfig.Inbound.Region = &firewallconfigs.HTTPFirewallRegionConfig{
IsOn: true,
}
}
policyConfig.Inbound.Region.DenyCountryIds = params.DenyCountryIds
policyConfig.Inbound.Region.AllowCountryIds = params.AllowCountryIds
// 例外URL
var exceptURLPatterns = []*shared.URLPattern{}
if len(params.ExceptURLPatternsJSON) > 0 {
err = json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
if err != nil {
this.Fail("校验例外URL参数失败" + err.Error())
return
}
}
policyConfig.Inbound.Region.CountryExceptURLPatterns = exceptURLPatterns
// 自定义提示
if len(params.CountryHTML) > 32<<10 {
this.Fail("提示内容长度不能超出32K")
return
}
policyConfig.Inbound.Region.CountryHTML = params.CountryHTML
policyConfig.Inbound.Region.AllowSearchEngine = params.AllowSearchEngine
// 限制URL
var onlyURLPatterns = []*shared.URLPattern{}
if len(params.OnlyURLPatternsJSON) > 0 {
err = json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
if err != nil {
this.Fail("校验限制URL参数失败" + err.Error())
return
}
}
policyConfig.Inbound.Region.CountryOnlyURLPatterns = onlyURLPatterns
inboundJSON, err := json.Marshal(policyConfig.Inbound)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallInboundConfig(this.AdminContext(), &pb.UpdateHTTPFirewallInboundConfigRequest{
HttpFirewallPolicyId: params.FirewallPolicyId,
InboundJSON: inboundJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

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