Initial commit (code only without large binaries)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type AddPortPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *AddPortPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *AddPortPopupAction) RunGet(params struct {
|
||||
ServerType string
|
||||
Protocol string
|
||||
}) {
|
||||
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.Show()
|
||||
}
|
||||
|
||||
func (this *AddPortPopupAction) RunPost(params struct {
|
||||
Protocol string
|
||||
Address string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// 校验地址
|
||||
addr := maps.Map{
|
||||
"protocol": params.Protocol,
|
||||
"host": "",
|
||||
"portRange": "",
|
||||
}
|
||||
|
||||
digitRegexp := regexp.MustCompile(`^\d+$`)
|
||||
if !digitRegexp.MatchString(params.Address) {
|
||||
this.Fail("端口号只能是一个数字")
|
||||
}
|
||||
|
||||
port := types.Int32(params.Address)
|
||||
if port < 1024 || port > 65534 {
|
||||
this.Fail("端口范围错误")
|
||||
}
|
||||
|
||||
addr["portRange"] = params.Address
|
||||
|
||||
this.Data["address"] = addr
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/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,
|
||||
}
|
||||
} else {
|
||||
this.Fail("错误的mode参数")
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
20
EdgeUser/internal/web/actions/default/servers/cache/cacheutils/utils.go
vendored
Normal file
20
EdgeUser/internal/web/actions/default/servers/cache/cacheutils/utils.go
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package cacheutils
|
||||
|
||||
// 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 "未知错误"
|
||||
}
|
||||
29
EdgeUser/internal/web/actions/default/servers/cache/deleteTask.go
vendored
Normal file
29
EdgeUser/internal/web/actions/default/servers/cache/deleteTask.go
vendored
Normal 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/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
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.UserContext(), &pb.DeleteHTTPCacheTaskRequest{
|
||||
HttpCacheTaskId: params.TaskId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
162
EdgeUser/internal/web/actions/default/servers/cache/fetch.go
vendored
Normal file
162
EdgeUser/internal/web/actions/default/servers/cache/fetch.go
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"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/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/cache/cacheutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"net"
|
||||
"net/url"
|
||||
"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
|
||||
}
|
||||
|
||||
// 后台配置
|
||||
var maxKeysPerTask = userconfigs.MaxCacheKeysPerTask
|
||||
var maxKeysPerDay = userconfigs.MaxCacheKeysPerDay
|
||||
serverConfig, err := configloaders.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if serverConfig != nil && serverConfig.HTTPCacheTaskFetchConfig != nil {
|
||||
if serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerTask > 0 {
|
||||
maxKeysPerTask = serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerTask
|
||||
}
|
||||
if serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerDay > 0 {
|
||||
maxKeysPerDay = serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerDay
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["maxKeysPerTask"] = maxKeysPerTask
|
||||
this.Data["maxKeysPerDay"] = maxKeysPerDay
|
||||
|
||||
// 剩余配额
|
||||
this.Data["quotaKeysToday"] = 0
|
||||
if maxKeysPerDay > 0 {
|
||||
countKeysResp, err := this.RPC().HTTPCacheTaskKeyRPC().CountHTTPCacheTaskKeysWithDay(this.UserContext(), &pb.CountHTTPCacheTaskKeysWithDayRequest{
|
||||
KeyType: "fetch",
|
||||
Day: timeutil.Format("Ymd"),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var quota = maxKeysPerDay - types.Int32(countKeysResp.Count)
|
||||
if quota < 0 {
|
||||
quota = 0
|
||||
}
|
||||
this.Data["quotaKeysToday"] = quota
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *FetchAction) RunPost(params struct {
|
||||
UrlList string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.HTTPCacheTask_LogCreateHTTPCacheTaskFetch)
|
||||
|
||||
// 查找当前用户的所有域名
|
||||
serverNamesResp, err := this.RPC().ServerRPC().FindAllEnabledServerNamesWithUserId(this.UserContext(), &pb.FindAllEnabledServerNamesWithUserIdRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var serverNames = serverNamesResp.ServerNames
|
||||
|
||||
var keys = []string{}
|
||||
for _, key := range strings.Split(params.UrlList, "\n") {
|
||||
key = strings.TrimSpace(key)
|
||||
if len(key) == 0 {
|
||||
continue
|
||||
}
|
||||
if lists.ContainsString(keys, key) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查域名
|
||||
u, err := url.Parse(key)
|
||||
if err != nil || len(u.Host) == 0 || (u.Scheme != "http" && u.Scheme != "https") {
|
||||
this.Fail("'" + key + "'不是正确的URL格式")
|
||||
}
|
||||
|
||||
var host = u.Host
|
||||
|
||||
// 去掉端口
|
||||
hostString, _, err := net.SplitHostPort(host)
|
||||
if err == nil {
|
||||
host = hostString
|
||||
}
|
||||
|
||||
if !configutils.MatchDomains(serverNames, host) {
|
||||
this.Fail("'" + key + "'中域名'" + u.Host + "'没有绑定")
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
this.Fail("请输入要刷新的URL列表")
|
||||
}
|
||||
|
||||
// 校验Key
|
||||
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.UserContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: keys})
|
||||
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.UserContext(), &pb.CreateHTTPCacheTaskRequest{
|
||||
Type: "fetch",
|
||||
KeyType: "key",
|
||||
Keys: keys,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
177
EdgeUser/internal/web/actions/default/servers/cache/index.go
vendored
Normal file
177
EdgeUser/internal/web/actions/default/servers/cache/index.go
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"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/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/cache/cacheutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "purge")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
// 初始化菜单数据
|
||||
err := InitMenu(this.Parent())
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 后台配置
|
||||
var maxKeysPerTask = userconfigs.MaxCacheKeysPerTask
|
||||
var maxKeysPerDay = userconfigs.MaxCacheKeysPerDay
|
||||
serverConfig, err := configloaders.LoadServerConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if serverConfig != nil && serverConfig.HTTPCacheTaskPurgeConfig != nil {
|
||||
if serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerTask > 0 {
|
||||
maxKeysPerTask = serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerTask
|
||||
}
|
||||
if serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerDay > 0 {
|
||||
maxKeysPerDay = serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerDay
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["maxKeysPerTask"] = maxKeysPerTask
|
||||
this.Data["maxKeysPerDay"] = maxKeysPerDay
|
||||
|
||||
// 剩余配额
|
||||
this.Data["quotaKeysToday"] = 0
|
||||
if maxKeysPerDay > 0 {
|
||||
countKeysResp, err := this.RPC().HTTPCacheTaskKeyRPC().CountHTTPCacheTaskKeysWithDay(this.UserContext(), &pb.CountHTTPCacheTaskKeysWithDayRequest{
|
||||
KeyType: "purge",
|
||||
Day: timeutil.Format("Ymd"),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var quota = maxKeysPerDay - types.Int32(countKeysResp.Count)
|
||||
if quota < 0 {
|
||||
quota = 0
|
||||
}
|
||||
this.Data["quotaKeysToday"] = quota
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
KeyType string
|
||||
UrlList string
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.HTTPCacheTask_LogCreateHTTPCacheTaskPurge)
|
||||
|
||||
switch params.KeyType {
|
||||
case "key", "prefix":
|
||||
default:
|
||||
this.Fail("请选择正确的刷新类型")
|
||||
}
|
||||
|
||||
// 查找当前用户的所有域名
|
||||
serverNamesResp, err := this.RPC().ServerRPC().FindAllEnabledServerNamesWithUserId(this.UserContext(), &pb.FindAllEnabledServerNamesWithUserIdRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var serverNames = serverNamesResp.ServerNames
|
||||
|
||||
var keys = []string{}
|
||||
for _, key := range strings.Split(params.UrlList, "\n") {
|
||||
key = strings.TrimSpace(key)
|
||||
if len(key) == 0 {
|
||||
continue
|
||||
}
|
||||
if lists.ContainsString(keys, key) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查域名
|
||||
u, err := url.Parse(key)
|
||||
if err != nil || len(u.Host) == 0 || (u.Scheme != "http" && u.Scheme != "https") {
|
||||
this.Fail("'" + key + "'不是正确的URL格式")
|
||||
}
|
||||
|
||||
var host = u.Host
|
||||
|
||||
// 去掉端口
|
||||
hostString, _, err := net.SplitHostPort(host)
|
||||
if err == nil {
|
||||
host = hostString
|
||||
}
|
||||
|
||||
if !configutils.MatchDomains(serverNames, host) {
|
||||
this.Fail("'" + key + "'中域名'" + u.Host + "'没有绑定")
|
||||
}
|
||||
|
||||
if params.KeyType == "prefix" && !strings.HasSuffix(key, "/") {
|
||||
key += "/"
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
if params.KeyType == "key" {
|
||||
this.Fail("请输入要刷新的URL列表")
|
||||
} else if params.KeyType == "prefix" {
|
||||
this.Fail("请输入要刷新的目录列表")
|
||||
}
|
||||
}
|
||||
|
||||
// 校验Key
|
||||
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.UserContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: keys})
|
||||
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.UserContext(), &pb.CreateHTTPCacheTaskRequest{
|
||||
Type: "purge",
|
||||
KeyType: params.KeyType,
|
||||
Keys: keys,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
22
EdgeUser/internal/web/actions/default/servers/cache/init.go
vendored
Normal file
22
EdgeUser/internal/web/actions/default/servers/cache/init.go
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "servers").
|
||||
Data("teaSubMenu", "cache").
|
||||
Prefix("/servers/cache").
|
||||
GetPost("", new(IndexAction)).
|
||||
GetPost("/fetch", new(FetchAction)).
|
||||
Get("/tasks", new(TasksAction)).
|
||||
GetPost("/task", new(TaskAction)).
|
||||
Post("/deleteTask", new(DeleteTaskAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
123
EdgeUser/internal/web/actions/default/servers/cache/task.go
vendored
Normal file
123
EdgeUser/internal/web/actions/default/servers/cache/task.go
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"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.UserContext(), &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")
|
||||
})
|
||||
}
|
||||
|
||||
keyMaps = append(keyMaps, maps.Map{
|
||||
"key": key.Key,
|
||||
"isDone": key.IsDone,
|
||||
"isDoing": key.IsDoing,
|
||||
"errors": errorMaps,
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
72
EdgeUser/internal/web/actions/default/servers/cache/tasks.go
vendored
Normal file
72
EdgeUser/internal/web/actions/default/servers/cache/tasks.go
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type 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.UserContext(), &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.UserContext(), &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),
|
||||
"user": userMap,
|
||||
})
|
||||
}
|
||||
|
||||
this.Data["tasks"] = taskMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
24
EdgeUser/internal/web/actions/default/servers/cache/utils.go
vendored
Normal file
24
EdgeUser/internal/web/actions/default/servers/cache/utils.go
vendored
Normal 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/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
func InitMenu(parent *actionutils.ParentAction) error {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
countTasksResp, err := rpcClient.HTTPCacheTaskRPC().CountDoingHTTPCacheTasks(parent.UserContext(), &pb.CountDoingHTTPCacheTasksRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parent.Data["countDoingTasks"] = countTasksResp.Count
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type 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.UserContext(), &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.UserContext(), &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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
AccountId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ACMEProviderAccount_LogDeleteACMEProviderAccount, params.AccountId)
|
||||
|
||||
_, err := this.RPC().ACMEProviderAccountRPC().DeleteACMEProviderAccount(this.UserContext(), &pb.DeleteACMEProviderAccountRequest{AcmeProviderAccountId: params.AccountId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "account")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().ACMEProviderAccountRPC().CountAllEnabledACMEProviderAccounts(this.UserContext(), &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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
AccountId int64
|
||||
}) {
|
||||
// 账号信息
|
||||
accountResp, err := this.RPC().ACMEProviderAccountRPC().FindEnabledACMEProviderAccount(this.UserContext(), &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.UserContext(), &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.UserContext(), &pb.UpdateACMEProviderAccountRequest{
|
||||
AcmeProviderAccountId: params.AccountId,
|
||||
Name: params.Name,
|
||||
EabKid: params.EabKid,
|
||||
EabKey: params.EabKey,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
|
||||
"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{}) {
|
||||
// 检查权限
|
||||
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerACME, 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取所有可用的用户
|
||||
usersResp, err := this.RPC().ACMEUserRPC().FindAllACMEUsers(this.UserContext(), &pb.FindAllACMEUsersRequest{
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
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
|
||||
|
||||
// 证书服务商
|
||||
providersResp, err := this.RPC().ACMEProviderRPC().FindAllACMEProviders(this.UserContext(), &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.NullUserContext(), &pb.FindAllEnabledDNSProvidersRequest{
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
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 {
|
||||
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("请输入证书域名列表")
|
||||
}
|
||||
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.UserContext(), &pb.CreateACMETaskRequest{
|
||||
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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
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.UserContext(), &pb.DeleteACMETaskRequest{AcmeTaskId: params.TaskId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "task")
|
||||
this.SecondMenu("list")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Type string
|
||||
Keyword string
|
||||
}) {
|
||||
// 检查权限
|
||||
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerACME, 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["type"] = params.Type
|
||||
this.Data["keyword"] = params.Keyword
|
||||
|
||||
countAll := int64(0)
|
||||
countAvailable := int64(0)
|
||||
countExpired := int64(0)
|
||||
count7Days := int64(0)
|
||||
count30Days := int64(0)
|
||||
|
||||
// 计算数量
|
||||
{
|
||||
// all
|
||||
resp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.UserContext(), &pb.CountAllEnabledACMETasksRequest{
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countAll = resp.Count
|
||||
|
||||
// available
|
||||
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.UserContext(), &pb.CountAllEnabledACMETasksRequest{
|
||||
IsAvailable: true,
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countAvailable = resp.Count
|
||||
|
||||
// expired
|
||||
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.UserContext(), &pb.CountAllEnabledACMETasksRequest{
|
||||
IsExpired: true,
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countExpired = resp.Count
|
||||
|
||||
// expire in 7 days
|
||||
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.UserContext(), &pb.CountAllEnabledACMETasksRequest{
|
||||
ExpiringDays: 7,
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count7Days = resp.Count
|
||||
|
||||
// expire in 30 days
|
||||
resp, err = this.RPC().ACMETaskRPC().CountAllEnabledACMETasks(this.UserContext(), &pb.CountAllEnabledACMETasksRequest{
|
||||
ExpiringDays: 30,
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
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.UserContext(), &pb.ListEnabledACMETasksRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
Keyword: params.Keyword,
|
||||
})
|
||||
case "available":
|
||||
page = this.NewPage(countAvailable)
|
||||
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.UserContext(), &pb.ListEnabledACMETasksRequest{IsAvailable: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
|
||||
case "expired":
|
||||
page = this.NewPage(countExpired)
|
||||
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.UserContext(), &pb.ListEnabledACMETasksRequest{IsExpired: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
|
||||
case "7days":
|
||||
page = this.NewPage(count7Days)
|
||||
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.UserContext(), &pb.ListEnabledACMETasksRequest{ExpiringDays: 7, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
|
||||
case "30days":
|
||||
page = this.NewPage(count30Days)
|
||||
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.UserContext(), &pb.ListEnabledACMETasksRequest{ExpiringDays: 30, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword})
|
||||
default:
|
||||
page = this.NewPage(countAll)
|
||||
tasksResp, err = this.RPC().ACMETaskRPC().ListEnabledACMETasks(this.UserContext(), &pb.ListEnabledACMETasksRequest{
|
||||
Keyword: params.Keyword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
this.Data["tasks"] = taskMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type RunAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *RunAction) RunPost(params struct {
|
||||
TaskId int64
|
||||
}) {
|
||||
// 检查权限
|
||||
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerACME, 0) {
|
||||
return
|
||||
}
|
||||
|
||||
runResp, err := this.RPC().ACMETaskRPC().RunACMETask(this.UserContext(), &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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/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.UserContext(), &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.UserContext(), &pb.FindAllEnabledDNSProvidersRequest{
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
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("请选择一个申请证书的用户")
|
||||
}
|
||||
|
||||
var 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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type 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.UserContext(), &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 {
|
||||
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.UserContext(), &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.UserContext(), &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.UserContext(), &pb.CreateACMEUserRequest{
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
UserId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ACMEUser_LogDeleteACMEUser, params.UserId)
|
||||
|
||||
countResp, err := this.RPC().ACMETaskRPC().CountAllEnabledACMETasksWithACMEUserId(this.UserContext(), &pb.CountAllEnabledACMETasksWithACMEUserIdRequest{AcmeUserId: params.UserId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if countResp.Count > 0 {
|
||||
this.Fail("有任务正在和这个用户关联,所以不能删除")
|
||||
}
|
||||
|
||||
_, err = this.RPC().ACMEUserRPC().DeleteACMEUser(this.UserContext(), &pb.DeleteACMEUserRequest{AcmeUserId: params.UserId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "user")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().ACMEUserRPC().CountACMEUsers(this.UserContext(), &pb.CountAcmeUsersRequest{
|
||||
AdminId: 0,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
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.UserContext(), &pb.ListACMEUsersRequest{
|
||||
UserId: this.UserId(),
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package users
|
||||
|
||||
import "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
|
||||
type SelectPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
UserId int64
|
||||
}) {
|
||||
userResp, err := this.RPC().ACMEUserRPC().FindEnabledACMEUser(this.UserContext(), &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.UserContext(), &pb.UpdateACMEUserRequest{
|
||||
AcmeUserId: params.UserId,
|
||||
Description: params.Description,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), &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
|
||||
}
|
||||
|
||||
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.UserContext(), &pb.FindAllEnabledServersWithSSLCertIdRequest{SslCertId: params.CertId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
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()
|
||||
}
|
||||
23
EdgeUser/internal/web/actions/default/servers/certs/count.go
Normal file
23
EdgeUser/internal/web/actions/default/servers/certs/count.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type CountAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CountAction) RunPost(params struct{}) {
|
||||
countResp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["count"] = countResp.Count
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
// 所有相关数据
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
CertId int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.SSLCert_LogDeleteSSLCert, params.CertId)
|
||||
|
||||
// 是否正在被使用
|
||||
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.UserContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: params.CertId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if countResp.Count > 0 {
|
||||
this.Fail("此证书正在被某些服务引用,请先修改服务后再删除。")
|
||||
}
|
||||
|
||||
_, err = this.RPC().SSLCertRPC().DeleteSSLCert(this.UserContext(), &pb.DeleteSSLCertRequest{SslCertId: params.CertId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), &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)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), &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)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), &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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"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["teaMenu"] = "servers"
|
||||
|
||||
action.Data["leftMenuItems"] = []maps.Map{
|
||||
{
|
||||
"name": "证书",
|
||||
"url": "/servers/certs",
|
||||
"isActive": action.Data.GetString("leftMenuItem") == "cert",
|
||||
},
|
||||
// TODO 暂时不开放
|
||||
/**{
|
||||
"name": "申请证书",
|
||||
"url": "/servers/certs/acme",
|
||||
"isActive": action.Data.GetString("leftMenuItem") == "acme",
|
||||
},**/
|
||||
}
|
||||
}
|
||||
193
EdgeUser/internal/web/actions/default/servers/certs/index.go
Normal file
193
EdgeUser/internal/web/actions/default/servers/certs/index.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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 {
|
||||
Type string
|
||||
Keyword string
|
||||
}) {
|
||||
this.Data["type"] = params.Type
|
||||
this.Data["keyword"] = params.Keyword
|
||||
|
||||
countAll := int64(0)
|
||||
countCA := int64(0)
|
||||
countAvailable := int64(0)
|
||||
countExpired := int64(0)
|
||||
count7Days := int64(0)
|
||||
count30Days := int64(0)
|
||||
|
||||
// 计算数量
|
||||
{
|
||||
// all
|
||||
resp, err := this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countAll = resp.Count
|
||||
|
||||
// CA
|
||||
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
IsCA: true,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countCA = resp.Count
|
||||
|
||||
// available
|
||||
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
IsAvailable: true,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countAvailable = resp.Count
|
||||
|
||||
// expired
|
||||
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
IsExpired: true,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countExpired = resp.Count
|
||||
|
||||
// expire in 7 days
|
||||
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
ExpiringDays: 7,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count7Days = resp.Count
|
||||
|
||||
// expire in 30 days
|
||||
resp, err = this.RPC().SSLCertRPC().CountSSLCerts(this.UserContext(), &pb.CountSSLCertRequest{
|
||||
ExpiringDays: 30,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
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.UserContext(), &pb.ListSSLCertsRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
case "ca":
|
||||
page = this.NewPage(countCA)
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{
|
||||
IsCA: true,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
case "available":
|
||||
page = this.NewPage(countAvailable)
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{IsAvailable: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword, UserId: this.UserId()})
|
||||
case "expired":
|
||||
page = this.NewPage(countExpired)
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{IsExpired: true, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword, UserId: this.UserId()})
|
||||
case "7days":
|
||||
page = this.NewPage(count7Days)
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{ExpiringDays: 7, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword, UserId: this.UserId()})
|
||||
case "30days":
|
||||
page = this.NewPage(count30Days)
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{ExpiringDays: 30, Offset: page.Offset, Size: page.Size, Keyword: params.Keyword, UserId: this.UserId()})
|
||||
default:
|
||||
page = this.NewPage(countAll)
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
certConfigs := []*sslconfigs.SSLCertConfig{}
|
||||
err = json.Unmarshal(listResp.SslCertsJSON, &certConfigs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["certs"] = certConfigs
|
||||
|
||||
certMaps := []maps.Map{}
|
||||
nowTime := time.Now().Unix()
|
||||
for _, certConfig := range certConfigs {
|
||||
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithSSLCertId(this.UserContext(), &pb.CountAllEnabledServersWithSSLCertIdRequest{SslCertId: certConfig.Id})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
this.Data["certInfos"] = certMaps
|
||||
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
62
EdgeUser/internal/web/actions/default/servers/certs/init.go
Normal file
62
EdgeUser/internal/web/actions/default/servers/certs/init.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/certs/acme"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/certs/acme/accounts"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/certs/acme/users"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Helper(new(Helper)).
|
||||
Data("teaMenu", "servers").
|
||||
Data("teaSubMenu", "certs").
|
||||
Prefix("/servers/certs").
|
||||
Data("leftMenuItem", "cert").
|
||||
Get("", new(IndexAction)).
|
||||
Post("/count", new(CountAction)).
|
||||
Get("/selectPopup", new(SelectPopupAction)).
|
||||
GetPost("/uploadPopup", new(UploadPopupAction)).
|
||||
GetPost("/uploadBatchPopup", new(UploadBatchPopupAction)).
|
||||
Get("/datajs", new(DatajsAction)).
|
||||
Get("/viewKey", new(ViewKeyAction)).
|
||||
Get("/viewCert", new(ViewCertAction)).
|
||||
Get("/downloadKey", new(DownloadKeyAction)).
|
||||
Get("/downloadCert", new(DownloadCertAction)).
|
||||
Get("/downloadZip", new(DownloadZipAction)).
|
||||
Get("/certPopup", new(CertPopupAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
GetPost("/updatePopup", new(UpdatePopupAction)).
|
||||
|
||||
// ACME任务
|
||||
Prefix("/servers/certs/acme").
|
||||
Data("teaMenu", "servers").
|
||||
Data("teaSubMenu", "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)).
|
||||
|
||||
// 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)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 选择证书
|
||||
type SelectPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunGet(params struct {
|
||||
ServerId int64 // 搜索的服务
|
||||
SearchingDomains string // 搜索的域名
|
||||
SearchingType string // 搜索类型:match|all
|
||||
|
||||
ViewSize string
|
||||
SelectedCertIds string
|
||||
Keyword string
|
||||
}) {
|
||||
// 服务相关
|
||||
if params.ServerId > 0 {
|
||||
// 读取所有ServerNames
|
||||
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.UserContext(), &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), ",")
|
||||
}
|
||||
}
|
||||
|
||||
// 域名搜索相关
|
||||
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["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.UserContext(), &pb.CountSSLCertRequest{
|
||||
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.UserContext(), &pb.CountSSLCertRequest{
|
||||
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.UserContext(), &pb.ListSSLCertsRequest{
|
||||
Keyword: params.Keyword,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
} else if searchingType == "match" {
|
||||
listResp, err = this.RPC().SSLCertRPC().ListSSLCerts(this.UserContext(), &pb.ListSSLCertsRequest{
|
||||
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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), &pb.FindEnabledSSLCertConfigRequest{SslCertId: params.CertId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
certConfigJSON := certConfigResp.SslCertJSON
|
||||
if len(certConfigJSON) == 0 {
|
||||
this.NotFound("cert", params.CertId)
|
||||
return
|
||||
}
|
||||
|
||||
certConfig := &sslconfigs.SSLCertConfig{}
|
||||
err = json.Unmarshal(certConfigJSON, certConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
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.UserContext(), &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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
// 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/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UploadBatchPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UploadBatchPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UploadBatchPopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UploadBatchPopupAction) RunPost(params struct {
|
||||
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
|
||||
}
|
||||
|
||||
// 限制每次上传的文件数量
|
||||
const maxFiles = 10_000
|
||||
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.UserContext(), &pb.CreateSSLCertsRequest{
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UploadPopupAction) RunPost(params struct {
|
||||
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 sslConfig = &sslconfigs.SSLCertConfig{
|
||||
IsCA: params.IsCA,
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
}
|
||||
err := sslConfig.Init(context.TODO())
|
||||
if err != nil {
|
||||
if params.IsCA {
|
||||
this.Fail("证书校验错误:" + err.Error())
|
||||
} else {
|
||||
this.Fail("证书或密钥校验错误:" + err.Error())
|
||||
}
|
||||
}
|
||||
if len(timeutil.Format("Y", sslConfig.TimeEnd())) != 4 {
|
||||
this.Fail("证书格式错误:无法读取到证书有效期")
|
||||
}
|
||||
|
||||
if sslConfig.TimeBeginAt < 0 {
|
||||
this.Fail("证书校验错误:有效期开始时间过小,不能小于1970年1月1日")
|
||||
}
|
||||
if sslConfig.TimeEndAt < 0 {
|
||||
this.Fail("证书校验错误:有效期结束时间过小,不能小于1970年1月1日")
|
||||
}
|
||||
|
||||
// 保存
|
||||
createResp, err := this.RPC().SSLCertRPC().CreateSSLCert(this.UserContext(), &pb.CreateSSLCertRequest{
|
||||
IsOn: params.IsOn,
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
ServerName: "",
|
||||
IsCA: params.IsCA,
|
||||
CertData: certData,
|
||||
KeyData: keyData,
|
||||
TimeBeginAt: sslConfig.TimeBeginAt,
|
||||
TimeEndAt: sslConfig.TimeEndAt,
|
||||
DnsNames: sslConfig.DNSNames,
|
||||
CommonNames: sslConfig.CommonNames,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 查询已创建的证书并返回,方便调用者进行后续处理
|
||||
certId := createResp.SslCertId
|
||||
configResp, err := this.RPC().SSLCertRPC().FindEnabledSSLCertConfig(this.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
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.UserContext(), &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)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
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.UserContext(), &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)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type CountAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CountAction) RunPost(params struct{}) {
|
||||
countResp, err := this.RPC().HTTPFirewallPolicyRPC().CountAllEnabledHTTPFirewallPolicies(this.UserContext(), &pb.CountAllEnabledHTTPFirewallPoliciesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["count"] = countResp.Count
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"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/EdgeUser/internal/web/actions/actionutils"
|
||||
"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
|
||||
Description string
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), 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.UserContext(), &pb.CreateHTTPFirewallRuleGroupRequest{
|
||||
IsOn: params.IsOn,
|
||||
Name: params.Name,
|
||||
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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
// 预置分组
|
||||
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.UserContext(), &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()
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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" && !checkpoint.IsRequest) {
|
||||
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
|
||||
|
||||
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, ¶mFilters)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
rule.ParamFilters = paramFilters
|
||||
|
||||
rule.Operator = params.Operator
|
||||
rule.Value = params.Value
|
||||
rule.IsCaseInsensitive = params.Case
|
||||
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), 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": "和(AND)",
|
||||
"value": firewallconfigs.HTTPFirewallRuleConnectorAnd,
|
||||
"description": "所有规则都满足才视为匹配",
|
||||
},
|
||||
{
|
||||
"name": "或(OR)",
|
||||
"value": firewallconfigs.HTTPFirewallRuleConnectorOr,
|
||||
"description": "任一规则满足了就视为匹配",
|
||||
},
|
||||
}
|
||||
|
||||
var actionMaps = []maps.Map{}
|
||||
for _, action := range firewallconfigs.AllActions {
|
||||
// 用户端暂时屏蔽notify等动作
|
||||
if action.Code == firewallconfigs.HTTPFirewallActionNotify || action.Code == firewallconfigs.HTTPFirewallActionRecordIP {
|
||||
continue
|
||||
}
|
||||
actionMaps = append(actionMaps, maps.Map{
|
||||
"name": action.Name,
|
||||
"description": action.Description,
|
||||
"code": action.Code,
|
||||
})
|
||||
}
|
||||
this.Data["actions"] = actionMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreateSetPopupAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
|
||||
Name string
|
||||
RulesJSON []byte
|
||||
Connector string
|
||||
ActionsJSON []byte
|
||||
IgnoreLocal bool
|
||||
IgnoreSearchEngine bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.UserContext(), 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("请输入规则集名称")
|
||||
|
||||
if len(params.RulesJSON) == 0 {
|
||||
this.Fail("请添加至少一个规则")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
createUpdateResp, err := this.RPC().HTTPFirewallRuleSetRPC().CreateOrUpdateHTTPFirewallRuleSetFromConfig(this.UserContext(), &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.UserContext(), &pb.UpdateHTTPFirewallRuleGroupSetsRequest{
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
FirewallRuleSetsJSON: setRefsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["setId"] = createUpdateResp.FirewallRuleSetId
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
FirewallPolicyId int64
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFPolicy_LogDeleteWAFPolicy, params.FirewallPolicyId)
|
||||
|
||||
countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithHTTPFirewallPolicyId(this.UserContext(), &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.UserContext(), &pb.DeleteHTTPFirewallPolicyRequest{HttpFirewallPolicyId: params.FirewallPolicyId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
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.UserContext(), 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.UserContext(), &pb.UpdateHTTPFirewallPolicyGroupsRequest{
|
||||
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||
InboundJSON: inboundJSON,
|
||||
OutboundJSON: outboundJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
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.UserContext(), 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.UserContext(), &pb.UpdateHTTPFirewallRuleGroupSetsRequest{
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
FirewallRuleSetsJSON: newRefsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), params.FirewallPolicyId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if policy == nil {
|
||||
this.NotFound("firewallPolicy", policy.Id)
|
||||
return
|
||||
}
|
||||
|
||||
inboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
|
||||
outboundGroups := []*firewallconfigs.HTTPFirewallRuleGroup{}
|
||||
if policy.Inbound != nil {
|
||||
for _, g := range policy.Inbound.Groups {
|
||||
if g.IsOn {
|
||||
inboundGroups = append(inboundGroups, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
if policy.Outbound != nil {
|
||||
for _, g := range policy.Outbound.Groups {
|
||||
if g.IsOn {
|
||||
outboundGroups = append(outboundGroups, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["inboundGroups"] = inboundGroups
|
||||
this.Data["outboundGroups"] = outboundGroups
|
||||
|
||||
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.UserContext(), 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.Success()
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ExportDownloadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ExportDownloadAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *ExportDownloadAction) RunGet(params struct {
|
||||
Key string
|
||||
}) {
|
||||
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.json\";")
|
||||
this.AddHeader("Content-Length", strconv.Itoa(len(data)))
|
||||
_, _ = this.Write(data)
|
||||
} else {
|
||||
this.WriteString("找不到要导出的内容")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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
|
||||
|
||||
// policy
|
||||
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), params.FirewallPolicyId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if firewallPolicy == nil {
|
||||
this.NotFound("firewallPolicy", params.FirewallPolicyId)
|
||||
return
|
||||
}
|
||||
|
||||
// group config
|
||||
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.UserContext(), params.GroupId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if groupConfig == nil {
|
||||
this.NotFound("firewallRuleGroup", params.GroupId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = groupConfig
|
||||
|
||||
// rule sets
|
||||
this.Data["sets"] = lists.Map(groupConfig.Sets, func(k int, v interface{}) interface{} {
|
||||
var set = v.(*firewallconfigs.HTTPFirewallRuleSet)
|
||||
return maps.Map{
|
||||
"id": set.Id,
|
||||
"name": set.Name,
|
||||
"rules": lists.Map(set.Rules, func(k int, v interface{}) interface{} {
|
||||
var rule = v.(*firewallconfigs.HTTPFirewallRule)
|
||||
return maps.Map{
|
||||
"param": rule.Param,
|
||||
"paramFilters": rule.ParamFilters,
|
||||
"operator": rule.Operator,
|
||||
"value": rule.Value,
|
||||
"isCaseInsensitive": rule.IsCaseInsensitive,
|
||||
"isComposed": firewallconfigs.CheckCheckpointIsComposed(rule.Prefix()),
|
||||
"checkpointOptions": rule.CheckpointOptions,
|
||||
}
|
||||
}),
|
||||
"isOn": set.IsOn,
|
||||
"actions": set.Actions,
|
||||
"connector": strings.ToUpper(set.Connector),
|
||||
}
|
||||
})
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), 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),
|
||||
"canDelete": len(g.Code) == 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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),
|
||||
"canDelete": len(g.Code) == 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"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.UserContext(), 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
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
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.UserContext(), &pb.ImportHTTPFirewallPolicyRequest{
|
||||
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||
HttpFirewallPolicyJSON: data,
|
||||
})
|
||||
if err != nil {
|
||||
this.Fail("导入失败:" + err.Error())
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.FirstMenu("index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().HTTPFirewallPolicyRPC().CountAllEnabledHTTPFirewallPolicies(this.UserContext(), &pb.CountAllEnabledHTTPFirewallPoliciesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
|
||||
listResp, err := this.RPC().HTTPFirewallPolicyRPC().ListEnabledHTTPFirewallPolicies(this.UserContext(), &pb.ListEnabledHTTPFirewallPoliciesRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
policyMaps := []maps.Map{}
|
||||
for _, policy := range listResp.HttpFirewallPolicies {
|
||||
countInbound := 0
|
||||
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.UserContext(), &pb.CountAllEnabledNodeClustersWithHTTPFirewallPolicyIdRequest{HttpFirewallPolicyId: policy.Id})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countClusters := countClustersResp.Count
|
||||
|
||||
policyMaps = append(policyMaps, maps.Map{
|
||||
"id": policy.Id,
|
||||
"isOn": policy.IsOn,
|
||||
"name": policy.Name,
|
||||
"countInbound": countInbound,
|
||||
"countOutbound": countOutbound,
|
||||
"countClusters": countClusters,
|
||||
})
|
||||
}
|
||||
|
||||
this.Data["policies"] = policyMaps
|
||||
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
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)).
|
||||
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)).
|
||||
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)).
|
||||
Post("/count", new(CountAction)).
|
||||
Get("/selectPopup", new(SelectPopupAction)).
|
||||
Post("/testRegexp", new(TestRegexpAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LogAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *LogAction) Init() {
|
||||
this.Nav("", "", "log")
|
||||
}
|
||||
|
||||
func (this *LogAction) RunGet(params struct {
|
||||
Day string
|
||||
RequestId string
|
||||
FirewallPolicyId int64
|
||||
GroupId int64
|
||||
Partition int32 `default:"-1"`
|
||||
}) {
|
||||
if len(params.Day) == 0 {
|
||||
params.Day = timeutil.Format("Y-m-d")
|
||||
}
|
||||
|
||||
this.Data["path"] = this.Request.URL.Path
|
||||
this.Data["day"] = params.Day
|
||||
this.Data["groupId"] = params.GroupId
|
||||
this.Data["accessLogs"] = []interface{}{}
|
||||
|
||||
day := params.Day
|
||||
if len(day) > 0 && regexp.MustCompile(`\d{4}-\d{2}-\d{2}`).MatchString(day) {
|
||||
day = strings.ReplaceAll(day, "-", "")
|
||||
size := int64(10)
|
||||
|
||||
resp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.UserContext(), &pb.ListHTTPAccessLogsRequest{
|
||||
Partition: params.Partition,
|
||||
RequestId: params.RequestId,
|
||||
FirewallPolicyId: params.FirewallPolicyId,
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
Day: day,
|
||||
Size: size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(resp.HttpAccessLogs) == 0 {
|
||||
this.Data["accessLogs"] = []interface{}{}
|
||||
} else {
|
||||
this.Data["accessLogs"] = resp.HttpAccessLogs
|
||||
}
|
||||
this.Data["hasMore"] = resp.HasMore
|
||||
this.Data["nextRequestId"] = resp.RequestId
|
||||
|
||||
// 上一个requestId
|
||||
this.Data["hasPrev"] = false
|
||||
this.Data["lastRequestId"] = ""
|
||||
if len(params.RequestId) > 0 {
|
||||
this.Data["hasPrev"] = true
|
||||
prevResp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.UserContext(), &pb.ListHTTPAccessLogsRequest{
|
||||
Partition: params.Partition,
|
||||
RequestId: params.RequestId,
|
||||
FirewallPolicyId: params.FirewallPolicyId,
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
Day: day,
|
||||
Size: size,
|
||||
Reverse: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if int64(len(prevResp.HttpAccessLogs)) == size {
|
||||
this.Data["lastRequestId"] = prevResp.RequestId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 所有分组
|
||||
policyResp, err := this.RPC().HTTPFirewallPolicyRPC().FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPFirewallPolicyConfigRequest{
|
||||
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
policyConfig := &firewallconfigs.HTTPFirewallPolicy{}
|
||||
err = json.Unmarshal(policyResp.HttpFirewallPolicyJSON, policyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
groupMaps := []maps.Map{}
|
||||
for _, group := range policyConfig.AllRuleGroups() {
|
||||
groupMaps = append(groupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type PolicyAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *PolicyAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *PolicyAction) RunGet(params struct {
|
||||
FirewallPolicyId int64
|
||||
}) {
|
||||
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), params.FirewallPolicyId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if firewallPolicy == nil {
|
||||
this.NotFound("firewallPolicy", params.FirewallPolicyId)
|
||||
return
|
||||
}
|
||||
|
||||
internalGroups := []maps.Map{}
|
||||
if firewallPolicy.Inbound != nil {
|
||||
for _, group := range firewallPolicy.Inbound.Groups {
|
||||
internalGroups = append(internalGroups, maps.Map{
|
||||
"name": group.Name,
|
||||
"isOn": group.IsOn,
|
||||
})
|
||||
}
|
||||
}
|
||||
if firewallPolicy.Outbound != nil {
|
||||
for _, group := range firewallPolicy.Outbound.Groups {
|
||||
internalGroups = append(internalGroups, maps.Map{
|
||||
"name": group.Name,
|
||||
"isOn": group.IsOn,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["firewallPolicy"] = maps.Map{
|
||||
"id": firewallPolicy.Id,
|
||||
"name": firewallPolicy.Name,
|
||||
"isOn": firewallPolicy.IsOn,
|
||||
"description": firewallPolicy.Description,
|
||||
"groups": internalGroups,
|
||||
"blockOptions": firewallPolicy.BlockOptions,
|
||||
}
|
||||
|
||||
// 正在使用此策略的集群
|
||||
clustersResp, err := this.RPC().NodeClusterRPC().FindAllEnabledNodeClustersWithHTTPFirewallPolicyId(this.UserContext(), &pb.FindAllEnabledNodeClustersWithHTTPFirewallPolicyIdRequest{HttpFirewallPolicyId: params.FirewallPolicyId})
|
||||
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()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type SelectPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) Init() {
|
||||
this.FirstMenu("index")
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunGet(params struct{}) {
|
||||
countResp, err := this.RPC().HTTPFirewallPolicyRPC().CountAllEnabledHTTPFirewallPolicies(this.UserContext(), &pb.CountAllEnabledHTTPFirewallPoliciesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
count := countResp.Count
|
||||
page := this.NewPage(count)
|
||||
|
||||
listResp, err := this.RPC().HTTPFirewallPolicyRPC().ListEnabledHTTPFirewallPolicies(this.UserContext(), &pb.ListEnabledHTTPFirewallPoliciesRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
policyMaps := []maps.Map{}
|
||||
for _, policy := range listResp.HttpFirewallPolicies {
|
||||
countInbound := 0
|
||||
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)
|
||||
}
|
||||
|
||||
policyMaps = append(policyMaps, maps.Map{
|
||||
"id": policy.Id,
|
||||
"isOn": policy.IsOn,
|
||||
"name": policy.Name,
|
||||
"countInbound": countInbound,
|
||||
"countOutbound": countOutbound,
|
||||
})
|
||||
}
|
||||
|
||||
this.Data["policies"] = policyMaps
|
||||
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"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/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type SortGroupsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SortGroupsAction) RunPost(params struct {
|
||||
FirewallPolicyId int64
|
||||
Type string
|
||||
GroupIds []int64
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFRuleGroup_LogSortRuleGroups, params.FirewallPolicyId)
|
||||
|
||||
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), params.FirewallPolicyId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if firewallPolicy == nil {
|
||||
this.NotFound("firewallPolicy", params.FirewallPolicyId)
|
||||
return
|
||||
}
|
||||
|
||||
switch params.Type {
|
||||
case "inbound":
|
||||
refMapping := map[int64]*firewallconfigs.HTTPFirewallRuleGroupRef{}
|
||||
for _, ref := range firewallPolicy.Inbound.GroupRefs {
|
||||
refMapping[ref.GroupId] = ref
|
||||
}
|
||||
newRefs := []*firewallconfigs.HTTPFirewallRuleGroupRef{}
|
||||
for _, groupId := range params.GroupIds {
|
||||
ref, ok := refMapping[groupId]
|
||||
if ok {
|
||||
newRefs = append(newRefs, ref)
|
||||
}
|
||||
}
|
||||
firewallPolicy.Inbound.GroupRefs = newRefs
|
||||
case "outbound":
|
||||
refMapping := map[int64]*firewallconfigs.HTTPFirewallRuleGroupRef{}
|
||||
for _, ref := range firewallPolicy.Outbound.GroupRefs {
|
||||
refMapping[ref.GroupId] = ref
|
||||
}
|
||||
newRefs := []*firewallconfigs.HTTPFirewallRuleGroupRef{}
|
||||
for _, groupId := range params.GroupIds {
|
||||
ref, ok := refMapping[groupId]
|
||||
if ok {
|
||||
newRefs = append(newRefs, ref)
|
||||
}
|
||||
}
|
||||
firewallPolicy.Outbound.GroupRefs = newRefs
|
||||
}
|
||||
|
||||
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.UserContext(), &pb.UpdateHTTPFirewallPolicyGroupsRequest{
|
||||
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||
InboundJSON: inboundJSON,
|
||||
OutboundJSON: outboundJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type SortSetsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SortSetsAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
SetIds []int64
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFRuleSet_LogSortRuleSets, params.GroupId)
|
||||
|
||||
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.UserContext(), params.GroupId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if groupConfig == nil {
|
||||
this.NotFound("firewallRuleGroup", params.GroupId)
|
||||
return
|
||||
}
|
||||
|
||||
setMap := map[int64]*firewallconfigs.HTTPFirewallRuleSetRef{}
|
||||
for _, setRef := range groupConfig.SetRefs {
|
||||
setMap[setRef.SetId] = setRef
|
||||
}
|
||||
|
||||
newRefs := []*firewallconfigs.HTTPFirewallRuleSetRef{}
|
||||
for _, setId := range params.SetIds {
|
||||
ref, ok := setMap[setId]
|
||||
if ok {
|
||||
newRefs = append(newRefs, ref)
|
||||
}
|
||||
}
|
||||
newRefsJSON, err := json.Marshal(newRefs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPFirewallRuleGroupRPC().UpdateHTTPFirewallRuleGroupSets(this.UserContext(), &pb.UpdateHTTPFirewallRuleGroupSetsRequest{
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
FirewallRuleSetsJSON: newRefsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package waf
|
||||
|
||||
import "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
|
||||
type TestAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestAction) Init() {
|
||||
this.Nav("", "", "test")
|
||||
}
|
||||
|
||||
func (this *TestAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TestRegexpAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *TestRegexpAction) RunPost(params struct {
|
||||
Regexp string
|
||||
IsCaseInsensitive bool
|
||||
Body string
|
||||
}) {
|
||||
var exp = params.Regexp
|
||||
if params.IsCaseInsensitive && !strings.HasPrefix(params.Regexp, "(?i)") {
|
||||
exp = "(?i)" + exp
|
||||
}
|
||||
reg, err := regexp.Compile(exp)
|
||||
if err != nil {
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": false,
|
||||
"message": "解析正则出错:" + err.Error(),
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
|
||||
if reg.MatchString(params.Body) {
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": true,
|
||||
"message": "匹配成功",
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": false,
|
||||
"message": "匹配失败",
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"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/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "update")
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunGet(params struct {
|
||||
FirewallPolicyId int64
|
||||
}) {
|
||||
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), params.FirewallPolicyId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if firewallPolicy == nil {
|
||||
this.NotFound("firewallPolicy", params.FirewallPolicyId)
|
||||
return
|
||||
}
|
||||
|
||||
if firewallPolicy.BlockOptions == nil {
|
||||
firewallPolicy.BlockOptions = &firewallconfigs.HTTPFirewallBlockAction{
|
||||
StatusCode: http.StatusForbidden,
|
||||
Body: "Blocked By WAF",
|
||||
URL: "",
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["firewallPolicy"] = maps.Map{
|
||||
"id": firewallPolicy.Id,
|
||||
"name": firewallPolicy.Name,
|
||||
"description": firewallPolicy.Description,
|
||||
"isOn": firewallPolicy.IsOn,
|
||||
"blockOptions": firewallPolicy.BlockOptions,
|
||||
}
|
||||
|
||||
// 预置分组
|
||||
groups := []maps.Map{}
|
||||
templatePolicy := firewallconfigs.HTTPFirewallTemplate()
|
||||
for _, group := range templatePolicy.AllRuleGroups() {
|
||||
if len(group.Code) > 0 {
|
||||
usedGroup := firewallPolicy.FindRuleGroupWithCode(group.Code)
|
||||
if usedGroup != nil {
|
||||
group.IsOn = usedGroup.IsOn
|
||||
}
|
||||
}
|
||||
|
||||
groups = append(groups, maps.Map{
|
||||
"code": group.Code,
|
||||
"name": group.Name,
|
||||
"isOn": group.IsOn,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groups
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
FirewallPolicyId int64
|
||||
Name string
|
||||
GroupCodes []string
|
||||
BlockOptionsJSON []byte
|
||||
Description string
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFPolicy_LogUpdateWAFPolicy, params.FirewallPolicyId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入策略名称")
|
||||
|
||||
_, err := this.RPC().HTTPFirewallPolicyRPC().UpdateHTTPFirewallPolicy(this.UserContext(), &pb.UpdateHTTPFirewallPolicyRequest{
|
||||
HttpFirewallPolicyId: params.FirewallPolicyId,
|
||||
IsOn: params.IsOn,
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
FirewallGroupCodes: params.GroupCodes,
|
||||
BlockOptionsJSON: params.BlockOptionsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type UpdateGroupOnAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateGroupOnAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
IsOn bool
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFRuleGroup_LogUpdateRuleGroupIsOn, params.GroupId)
|
||||
|
||||
_, err := this.RPC().HTTPFirewallRuleGroupRPC().UpdateHTTPFirewallRuleGroupIsOn(this.UserContext(), &pb.UpdateHTTPFirewallRuleGroupIsOnRequest{
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdateGroupPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateGroupPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdateGroupPopupAction) RunGet(params struct {
|
||||
GroupId int64
|
||||
}) {
|
||||
groupConfig, err := dao.SharedHTTPFirewallRuleGroupDAO.FindRuleGroupConfig(this.UserContext(), params.GroupId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if groupConfig == nil {
|
||||
this.NotFound("ruleGroup", params.GroupId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = maps.Map{
|
||||
"id": groupConfig.Id,
|
||||
"name": groupConfig.Name,
|
||||
"description": groupConfig.Description,
|
||||
"isOn": groupConfig.IsOn,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateGroupPopupAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
Name string
|
||||
Description string
|
||||
IsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFRuleGroup_LogUpdateRuleGroup, params.GroupId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入分组名称")
|
||||
|
||||
_, err := this.RPC().HTTPFirewallRuleGroupRPC().UpdateHTTPFirewallRuleGroup(this.UserContext(), &pb.UpdateHTTPFirewallRuleGroupRequest{
|
||||
FirewallRuleGroupId: params.GroupId,
|
||||
IsOn: params.IsOn,
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type UpdateSetOnAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateSetOnAction) RunPost(params struct {
|
||||
SetId int64
|
||||
IsOn bool
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFRuleSet_LogUpdateRuleSetIsOn, params.SetId)
|
||||
|
||||
_, err := this.RPC().HTTPFirewallRuleSetRPC().UpdateHTTPFirewallRuleSetIsOn(this.UserContext(), &pb.UpdateHTTPFirewallRuleSetIsOnRequest{
|
||||
FirewallRuleSetId: params.SetId,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package waf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdateSetPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateSetPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdateSetPopupAction) RunGet(params struct {
|
||||
FirewallPolicyId int64
|
||||
GroupId int64
|
||||
Type string
|
||||
SetId int64
|
||||
}) {
|
||||
// 日志
|
||||
defer this.CreateLogInfo(codes.WAFRuleSet_LogUpdateRuleSet, params.SetId)
|
||||
|
||||
this.Data["groupId"] = params.GroupId
|
||||
this.Data["type"] = params.Type
|
||||
|
||||
firewallPolicy, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledHTTPFirewallPolicyConfig(this.UserContext(), 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": "和(AND)",
|
||||
"value": firewallconfigs.HTTPFirewallRuleConnectorAnd,
|
||||
"description": "所有规则都满足才视为匹配",
|
||||
},
|
||||
{
|
||||
"name": "或(OR)",
|
||||
"value": firewallconfigs.HTTPFirewallRuleConnectorOr,
|
||||
"description": "任一规则满足了就视为匹配",
|
||||
},
|
||||
}
|
||||
|
||||
actionMaps := []maps.Map{}
|
||||
for _, action := range firewallconfigs.AllActions {
|
||||
// 用户端暂时屏蔽notify等动作
|
||||
if action.Code == firewallconfigs.HTTPFirewallActionNotify || action.Code == firewallconfigs.HTTPFirewallActionRecordIP {
|
||||
continue
|
||||
}
|
||||
actionMaps = append(actionMaps, maps.Map{
|
||||
"name": action.Name,
|
||||
"description": action.Description,
|
||||
"code": action.Code,
|
||||
})
|
||||
}
|
||||
this.Data["actions"] = actionMaps
|
||||
|
||||
// 规则集信息
|
||||
setConfig, err := dao.SharedHTTPFirewallRuleSetDAO.FindRuleSetConfig(this.UserContext(), params.SetId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if setConfig == nil {
|
||||
this.NotFound("firewallRuleSet", params.SetId)
|
||||
return
|
||||
}
|
||||
this.Data["setConfig"] = setConfig
|
||||
|
||||
// action configs
|
||||
actionConfigs, err := dao.SharedHTTPFirewallPolicyDAO.FindHTTPFirewallActionConfigs(this.UserContext(), setConfig.Actions)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["actionConfigs"] = actionConfigs
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateSetPopupAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
SetId int64
|
||||
|
||||
Name string
|
||||
RulesJSON []byte
|
||||
Connector string
|
||||
ActionsJSON []byte
|
||||
IgnoreLocal bool
|
||||
IgnoreSearchEngine bool
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// 规则集信息
|
||||
setConfig, err := dao.SharedHTTPFirewallRuleSetDAO.FindRuleSetConfig(this.UserContext(), params.SetId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if setConfig == nil {
|
||||
this.NotFound("firewallRuleSet", params.SetId)
|
||||
return
|
||||
}
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入规则集名称")
|
||||
|
||||
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("请添加至少一个动作")
|
||||
}
|
||||
|
||||
setConfig.Name = params.Name
|
||||
setConfig.Connector = params.Connector
|
||||
setConfig.Rules = rules
|
||||
setConfig.Actions = actionConfigs
|
||||
setConfig.IgnoreLocal = params.IgnoreLocal
|
||||
setConfig.IgnoreSearchEngine = params.IgnoreSearchEngine
|
||||
|
||||
setConfigJSON, err := json.Marshal(setConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPFirewallRuleSetRPC().CreateOrUpdateHTTPFirewallRuleSetFromConfig(this.UserContext(), &pb.CreateOrUpdateHTTPFirewallRuleSetFromConfigRequest{FirewallRuleSetConfigJSON: setConfigJSON})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
699
EdgeUser/internal/web/actions/default/servers/create.go
Normal file
699
EdgeUser/internal/web/actions/default/servers/create.go
Normal file
@@ -0,0 +1,699 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/domainutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CreateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreateAction) Init() {
|
||||
this.Nav("", "", "create")
|
||||
}
|
||||
|
||||
func (this *CreateAction) RunGet(params struct{}) {
|
||||
// 检查用户状态
|
||||
this.CheckUserStatus()
|
||||
|
||||
clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["clusterId"] = clusterIdResp.NodeClusterId
|
||||
|
||||
// 套餐
|
||||
userPlansResp, err := this.RPC().UserPlanRPC().FindAllEnabledUserPlansForServer(this.UserContext(), &pb.FindAllEnabledUserPlansForServerRequest{
|
||||
UserId: this.UserId(),
|
||||
ServerId: 0,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var userPlanMaps = []maps.Map{}
|
||||
for _, userPlan := range userPlansResp.UserPlans {
|
||||
if userPlan.Plan == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var name = userPlan.Plan.Name
|
||||
if len(userPlan.Name) > 0 {
|
||||
name += "-" + userPlan.Name
|
||||
}
|
||||
|
||||
userPlanMaps = append(userPlanMaps, maps.Map{
|
||||
"id": userPlan.Id,
|
||||
"name": name,
|
||||
"dayTo": userPlan.DayTo,
|
||||
})
|
||||
}
|
||||
this.Data["userPlans"] = userPlanMaps
|
||||
|
||||
// 是否必须使用套餐
|
||||
this.Data["requirePlan"] = false
|
||||
userServerConfig, err := configloaders.LoadServerConfig()
|
||||
if err == nil && userServerConfig.RequirePlan {
|
||||
this.Data["requirePlan"] = true
|
||||
}
|
||||
|
||||
// OSS
|
||||
this.Data["ossTypes"] = ossconfigs.FindAllOSSTypes()
|
||||
this.Data["ossBucketParams"] = ossconfigs.FindAllOSSBucketParamDefinitions()
|
||||
|
||||
// 默认开启的选择
|
||||
this.Data["accessLogIsOn"] = true
|
||||
this.Data["websocketIsOn"] = true
|
||||
this.Data["cacheIsOn"] = true
|
||||
this.Data["wafIsOn"] = true
|
||||
this.Data["remoteAddrIsOn"] = true
|
||||
this.Data["statIsOn"] = true
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreateAction) RunPost(params struct {
|
||||
ServerNames []byte
|
||||
Protocols []string
|
||||
CertIdsJSON []byte
|
||||
OriginsJSON []byte
|
||||
RequestHostType int32
|
||||
RequestHost string
|
||||
CacheCondsJSON []byte
|
||||
|
||||
GroupIds []int64
|
||||
UserPlanId int64
|
||||
|
||||
AccessLogIsOn bool
|
||||
WebsocketIsOn bool
|
||||
CacheIsOn bool
|
||||
WafIsOn bool
|
||||
RemoteAddrIsOn bool
|
||||
StatIsOn bool
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
// 检查用户所在集群
|
||||
clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var clusterId = clusterIdResp.NodeClusterId
|
||||
|
||||
if len(params.ServerNames) == 0 {
|
||||
this.Data["requireServerNames"] = true
|
||||
this.Fail("请添加要加速的域名")
|
||||
}
|
||||
|
||||
var serverNames = []*serverconfigs.ServerNameConfig{}
|
||||
err = json.Unmarshal(params.ServerNames, &serverNames)
|
||||
if err != nil {
|
||||
this.Fail("域名参数解析错误:" + err.Error())
|
||||
}
|
||||
serverconfigs.NormalizeServerNames(serverNames)
|
||||
|
||||
var allDomainNames = serverconfigs.PlainServerNames(serverNames)
|
||||
if len(allDomainNames) == 0 {
|
||||
this.Data["requireServerNames"] = true
|
||||
this.Fail("请添加要加速的域名")
|
||||
}
|
||||
|
||||
for _, domainName := range allDomainNames {
|
||||
if !domainutils.ValidateDomainFormat(strings.ReplaceAll(domainName, "*.", "") /** 支持泛域名 **/) {
|
||||
this.Fail("域名'" + domainName + "'输入错误")
|
||||
}
|
||||
}
|
||||
|
||||
// 检查域名是否已经存在
|
||||
dupResp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.UserContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
|
||||
ServerNames: allDomainNames,
|
||||
NodeClusterId: clusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(dupResp.DuplicatedServerNames) > 0 {
|
||||
this.Fail("域名 " + strings.Join(dupResp.DuplicatedServerNames, ", ") + " 已经被其他网站所占用,不能重复使用")
|
||||
}
|
||||
|
||||
serverNamesJSON, err := json.Marshal(serverNames)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 协议:未选择时默认 HTTP
|
||||
if len(params.Protocols) == 0 {
|
||||
params.Protocols = []string{"http"}
|
||||
}
|
||||
|
||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
|
||||
|
||||
// HTTP
|
||||
if lists.ContainsString(params.Protocols, "http") {
|
||||
httpConfig.IsOn = true
|
||||
httpConfig.Listen = []*serverconfigs.NetworkAddressConfig{
|
||||
{
|
||||
Protocol: serverconfigs.ProtocolHTTP,
|
||||
Host: "",
|
||||
PortRange: "80",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPS
|
||||
if lists.ContainsString(params.Protocols, "https") {
|
||||
httpsConfig.IsOn = true
|
||||
httpsConfig.Listen = []*serverconfigs.NetworkAddressConfig{
|
||||
{
|
||||
Protocol: serverconfigs.ProtocolHTTPS,
|
||||
Host: "",
|
||||
PortRange: "443",
|
||||
},
|
||||
}
|
||||
|
||||
if len(params.CertIdsJSON) == 0 {
|
||||
this.Fail("请选择或者上传HTTPS证书")
|
||||
}
|
||||
certIds := []int64{}
|
||||
err := json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(certIds) == 0 {
|
||||
this.Fail("请选择或者上传HTTPS证书")
|
||||
}
|
||||
|
||||
certRefs := []*sslconfigs.SSLCertRef{}
|
||||
for _, certId := range certIds {
|
||||
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
})
|
||||
}
|
||||
certRefsJSON, err := json.Marshal(certRefs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建策略
|
||||
sslPolicyIdResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.UserContext(), &pb.CreateSSLPolicyRequest{
|
||||
Http2Enabled: false,
|
||||
Http3Enabled: false,
|
||||
MinVersion: "TLS 1.1",
|
||||
SslCertsJSON: certRefsJSON,
|
||||
HstsJSON: nil,
|
||||
OcspIsOn: false,
|
||||
ClientAuthType: 0,
|
||||
ClientCACertsJSON: nil,
|
||||
CipherSuites: nil,
|
||||
CipherSuitesIsOn: false,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
||||
IsOn: true,
|
||||
SSLPolicyId: sslPolicyIdResp.SslPolicyId,
|
||||
}
|
||||
}
|
||||
|
||||
// 源站信息
|
||||
var originMaps = []maps.Map{}
|
||||
if len(params.OriginsJSON) == 0 {
|
||||
this.Fail("请输入源站信息")
|
||||
}
|
||||
err = json.Unmarshal(params.OriginsJSON, &originMaps)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(originMaps) == 0 {
|
||||
this.Fail("请输入源站信息")
|
||||
}
|
||||
var primaryOriginRefs = []*serverconfigs.OriginRef{}
|
||||
var backupOriginRefs = []*serverconfigs.OriginRef{}
|
||||
|
||||
var hasOSS = false
|
||||
|
||||
for _, originMap := range originMaps {
|
||||
var host = originMap.GetString("host")
|
||||
var isPrimary = originMap.GetBool("isPrimary")
|
||||
var scheme = originMap.GetString("scheme")
|
||||
|
||||
if ossconfigs.IsOSSProtocol(scheme) {
|
||||
hasOSS = true
|
||||
continue
|
||||
}
|
||||
|
||||
if len(host) == 0 {
|
||||
this.Fail("源站地址不能为空")
|
||||
}
|
||||
if strings.Index(host, ":") > 0 {
|
||||
_, _, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
this.Fail("源站地址'" + host + "'格式错误")
|
||||
}
|
||||
} else if !domainutils.ValidateDomainFormat(host) {
|
||||
this.Fail("源站地址'" + host + "'格式错误")
|
||||
}
|
||||
|
||||
if scheme != "http" && scheme != "https" {
|
||||
this.Fail("错误的源站协议")
|
||||
}
|
||||
|
||||
addrHost, addrPort, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
addrHost = host
|
||||
if scheme == "http" {
|
||||
addrPort = "80"
|
||||
} else if scheme == "https" {
|
||||
addrPort = "443"
|
||||
}
|
||||
}
|
||||
|
||||
originIdResp, err := this.RPC().OriginRPC().CreateOrigin(this.UserContext(), &pb.CreateOriginRequest{
|
||||
Name: "",
|
||||
Addr: &pb.NetworkAddress{
|
||||
Protocol: scheme,
|
||||
Host: addrHost,
|
||||
PortRange: addrPort,
|
||||
},
|
||||
Description: "",
|
||||
Weight: 10,
|
||||
IsOn: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if isPrimary {
|
||||
primaryOriginRefs = append(primaryOriginRefs, &serverconfigs.OriginRef{
|
||||
IsOn: true,
|
||||
OriginId: originIdResp.OriginId,
|
||||
})
|
||||
} else {
|
||||
backupOriginRefs = append(backupOriginRefs, &serverconfigs.OriginRef{
|
||||
IsOn: true,
|
||||
OriginId: originIdResp.OriginId,
|
||||
})
|
||||
}
|
||||
}
|
||||
primaryOriginsJSON, err := json.Marshal(primaryOriginRefs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
backupOriginsJSON, err := json.Marshal(backupOriginRefs)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var scheduling = &serverconfigs.SchedulingConfig{
|
||||
Code: "random",
|
||||
Options: nil,
|
||||
}
|
||||
schedulingJSON, err := json.Marshal(scheduling)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 反向代理
|
||||
reverseProxyResp, err := this.RPC().ReverseProxyRPC().CreateReverseProxy(this.UserContext(), &pb.CreateReverseProxyRequest{
|
||||
SchedulingJSON: schedulingJSON,
|
||||
PrimaryOriginsJSON: primaryOriginsJSON,
|
||||
BackupOriginsJSON: backupOriginsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var reverseProxyId = reverseProxyResp.ReverseProxyId
|
||||
var reverseProxyRef = &serverconfigs.ReverseProxyRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
ReverseProxyId: reverseProxyId,
|
||||
}
|
||||
reverseProxyRefJSON, err := json.Marshal(reverseProxyRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if params.RequestHostType > 0 {
|
||||
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxy(this.UserContext(), &pb.UpdateReverseProxyRequest{
|
||||
ReverseProxyId: reverseProxyId,
|
||||
RequestHostType: params.RequestHostType,
|
||||
RequestHost: params.RequestHost,
|
||||
RequestURI: "",
|
||||
StripPrefix: "",
|
||||
AutoFlush: false,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存设置
|
||||
var cacheCondMaps = []maps.Map{}
|
||||
if len(params.CacheCondsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CacheCondsJSON, &cacheCondMaps)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var cacheRefs = []*serverconfigs.HTTPCacheRef{}
|
||||
if len(cacheCondMaps) > 0 {
|
||||
for _, condMap := range cacheCondMaps {
|
||||
var durationMap = condMap.GetMap("duration")
|
||||
if durationMap == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var duration = &shared.TimeDuration{
|
||||
Count: durationMap.GetInt64("count"),
|
||||
Unit: durationMap.GetString("unit"),
|
||||
}
|
||||
|
||||
var value string
|
||||
var param string
|
||||
var operator string
|
||||
var condType = condMap.GetString("type")
|
||||
switch condType {
|
||||
case "url-extension":
|
||||
param = "${requestPathExtension}"
|
||||
operator = shared.RequestCondOperatorIn
|
||||
result := []string{}
|
||||
v := condMap.GetString("value")
|
||||
if len(v) > 0 {
|
||||
err = json.Unmarshal([]byte(v), &result)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
value = v
|
||||
}
|
||||
case "url-prefix":
|
||||
param = "${requestPath}"
|
||||
operator = shared.RequestCondOperatorHasPrefix
|
||||
value = condMap.GetString("value")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
var conds = &shared.HTTPRequestCondsConfig{
|
||||
IsOn: true,
|
||||
Connector: "or",
|
||||
Groups: []*shared.HTTPRequestCondGroup{
|
||||
{
|
||||
IsOn: true,
|
||||
Connector: "or",
|
||||
Conds: []*shared.HTTPRequestCond{
|
||||
{
|
||||
IsRequest: true,
|
||||
Type: condType,
|
||||
Param: param,
|
||||
Operator: operator,
|
||||
Value: value,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var cacheRef = &serverconfigs.HTTPCacheRef{
|
||||
IsOn: true,
|
||||
CachePolicyId: 0,
|
||||
Key: "${scheme}://${host}${requestURI}",
|
||||
Life: duration,
|
||||
Status: []int{200},
|
||||
MaxSize: &shared.SizeCapacity{
|
||||
Count: 128,
|
||||
Unit: shared.SizeCapacityUnitMB,
|
||||
},
|
||||
SkipResponseCacheControlValues: nil,
|
||||
SkipResponseSetCookie: true,
|
||||
EnableRequestCachePragma: false,
|
||||
Conds: conds,
|
||||
CachePolicy: nil,
|
||||
AllowChunkedEncoding: true,
|
||||
AllowPartialContent: false,
|
||||
}
|
||||
cacheRefs = append(cacheRefs, cacheRef)
|
||||
}
|
||||
}
|
||||
|
||||
var cacheConfig = &serverconfigs.HTTPCacheConfig{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
CacheRefs: cacheRefs,
|
||||
AddStatusHeader: true,
|
||||
}
|
||||
cacheConfigJSON, err := cacheConfig.AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否必须使用套餐
|
||||
userServerConfig, err := configloaders.LoadServerConfig()
|
||||
if err == nil {
|
||||
if params.UserPlanId <= 0 && userServerConfig.RequirePlan {
|
||||
this.Fail("请选择套餐")
|
||||
}
|
||||
}
|
||||
|
||||
// 检查套餐
|
||||
if params.UserPlanId > 0 {
|
||||
userPlanResp, err := this.RPC().UserPlanRPC().FindEnabledUserPlan(this.UserContext(), &pb.FindEnabledUserPlanRequest{UserPlanId: params.UserPlanId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var userPlan = userPlanResp.UserPlan
|
||||
if userPlan == nil || userPlan.UserId != this.UserId() {
|
||||
this.Fail("找不到要使用的套餐")
|
||||
}
|
||||
}
|
||||
|
||||
// 开始保存
|
||||
httpJSON, err := httpConfig.AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpsJSON, err := httpsConfig.AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().ServerRPC().CreateServer(this.UserContext(), &pb.CreateServerRequest{
|
||||
UserId: this.UserId(),
|
||||
AdminId: 0,
|
||||
Type: serverconfigs.ServerTypeHTTPProxy,
|
||||
Name: serverNames[0].Name,
|
||||
Description: "",
|
||||
ServerNamesJSON: serverNamesJSON,
|
||||
HttpJSON: httpJSON,
|
||||
HttpsJSON: httpsJSON,
|
||||
TcpJSON: nil,
|
||||
TlsJSON: nil,
|
||||
UdpJSON: nil,
|
||||
WebId: 0,
|
||||
ReverseProxyJSON: reverseProxyRefJSON,
|
||||
ServerGroupIds: params.GroupIds,
|
||||
NodeClusterId: clusterId,
|
||||
IncludeNodesJSON: nil,
|
||||
ExcludeNodesJSON: nil,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var serverId = createResp.ServerId
|
||||
|
||||
defer this.CreateLogInfo(codes.Server_LogCreateServer, serverId)
|
||||
|
||||
// 保存缓存设置
|
||||
webIdResp, err := this.RPC().HTTPWebRPC().CreateHTTPWeb(this.UserContext(), &pb.CreateHTTPWebRequest{RootJSON: nil})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var webId = webIdResp.HttpWebId
|
||||
_, err = this.RPC().ServerRPC().UpdateServerWeb(this.UserContext(), &pb.UpdateServerWebRequest{
|
||||
ServerId: serverId,
|
||||
WebId: webId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.UserContext(), &pb.UpdateHTTPWebCacheRequest{
|
||||
HttpWebId: webId,
|
||||
CacheJSON: cacheConfigJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// ========== 默认开启的功能 ==========
|
||||
|
||||
// 1. 访问日志
|
||||
if params.AccessLogIsOn {
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.UserContext(), &pb.UpdateHTTPWebAccessLogRequest{
|
||||
HttpWebId: webId,
|
||||
AccessLogJSON: []byte(`{
|
||||
"isPrior": false,
|
||||
"isOn": true,
|
||||
"fields": [1, 2, 6, 7],
|
||||
"status1": true,
|
||||
"status2": true,
|
||||
"status3": true,
|
||||
"status4": true,
|
||||
"status5": true,
|
||||
"storageOnly": false,
|
||||
"storagePolicies": [],
|
||||
"firewallOnly": false
|
||||
}`),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 2. WebSocket
|
||||
if params.WebsocketIsOn {
|
||||
createWebSocketResp, err := this.RPC().HTTPWebsocketRPC().CreateHTTPWebsocket(this.UserContext(), &pb.CreateHTTPWebsocketRequest{
|
||||
HandshakeTimeoutJSON: []byte(`{"count": 30, "unit": "second"}`),
|
||||
AllowAllOrigins: true,
|
||||
AllowedOrigins: nil,
|
||||
RequestSameOrigin: true,
|
||||
RequestOrigin: "",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.UserContext(), &pb.UpdateHTTPWebWebsocketRequest{
|
||||
HttpWebId: webId,
|
||||
WebsocketJSON: []byte(`{"isPrior": false, "isOn": true, "websocketId": ` + types.String(createWebSocketResp.WebsocketId) + `}`),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 3. WAF 防火墙
|
||||
if params.WafIsOn {
|
||||
var firewallRef = &firewallconfigs.HTTPFirewallRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
FirewallPolicyId: 0,
|
||||
}
|
||||
firewallRefJSON, err := json.Marshal(firewallRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.UserContext(), &pb.UpdateHTTPWebFirewallRequest{
|
||||
HttpWebId: webId,
|
||||
FirewallJSON: firewallRefJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 从上级代理中读取IP
|
||||
if params.RemoteAddrIsOn {
|
||||
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
|
||||
IsOn: true,
|
||||
Value: "${remoteAddr}",
|
||||
Type: serverconfigs.HTTPRemoteAddrTypeProxy,
|
||||
}
|
||||
remoteAddrConfigJSON, err := json.Marshal(remoteAddrConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRemoteAddr(this.UserContext(), &pb.UpdateHTTPWebRemoteAddrRequest{
|
||||
HttpWebId: webId,
|
||||
RemoteAddrJSON: remoteAddrConfigJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 统计
|
||||
if params.StatIsOn {
|
||||
var statConfig = &serverconfigs.HTTPStatRef{
|
||||
IsPrior: false,
|
||||
IsOn: true,
|
||||
}
|
||||
statJSON, err := json.Marshal(statConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.UserContext(), &pb.UpdateHTTPWebStatRequest{
|
||||
HttpWebId: webId,
|
||||
StatJSON: statJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定套餐
|
||||
_, err = this.RPC().ServerRPC().UpdateServerUserPlan(this.UserContext(), &pb.UpdateServerUserPlanRequest{
|
||||
ServerId: serverId,
|
||||
UserPlanId: params.UserPlanId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["serverId"] = serverId
|
||||
this.Data["hasOSS"] = hasOSS
|
||||
|
||||
this.Success()
|
||||
}
|
||||
25
EdgeUser/internal/web/actions/default/servers/delete.go
Normal file
25
EdgeUser/internal/web/actions/default/servers/delete.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
ServerId int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Server_LogDeleteServer, params.ServerId)
|
||||
|
||||
_, err := this.RPC().ServerRPC().DeleteServer(this.UserContext(), &pb.DeleteServerRequest{ServerId: params.ServerId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
// DeleteServersAction 删除一组网站
|
||||
type DeleteServersAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteServersAction) RunPost(params struct {
|
||||
ServerIds []int64
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.Server_LogDeleteServers)
|
||||
|
||||
_, err := this.RPC().ServerRPC().DeleteServers(this.UserContext(), &pb.DeleteServersRequest{ServerIds: params.ServerIds})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
141
EdgeUser/internal/web/actions/default/servers/fee/calculator.go
Normal file
141
EdgeUser/internal/web/actions/default/servers/fee/calculator.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package fee
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CalculatorAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CalculatorAction) Init() {
|
||||
this.Nav("", "", "calculator")
|
||||
}
|
||||
|
||||
func (this *CalculatorAction) RunGet(params struct{}) {
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn || !config.UserUI.ShowPrices {
|
||||
this.WriteString("管理员未开启此配置")
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["showPrices"] = true
|
||||
|
||||
// 配置
|
||||
// 仅显示有限的配置,防止信息泄露
|
||||
var priceType = config.DefaultPriceType
|
||||
var canChangePriceType = false
|
||||
|
||||
if config.UserCanChangePriceType {
|
||||
canChangePriceType = true
|
||||
|
||||
priceInfoResp, err := this.RPC().UserRPC().FindUserPriceInfo(this.UserContext(), &pb.FindUserPriceInfoRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(priceInfoResp.PriceType) > 0 && userconfigs.IsValidPriceType(priceInfoResp.PriceType) {
|
||||
priceType = priceInfoResp.PriceType
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["config"] = maps.Map{
|
||||
"priceType": priceType,
|
||||
"canChangePriceType": canChangePriceType,
|
||||
}
|
||||
|
||||
// 所有区域
|
||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.UserContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var regionMaps = []maps.Map{}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
regionMaps = append(regionMaps, maps.Map{
|
||||
"id": region.Id,
|
||||
"name": region.Name,
|
||||
})
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CalculatorAction) RunPost(params struct {
|
||||
PriceType string
|
||||
|
||||
Traffic float64
|
||||
TrafficUnit string
|
||||
|
||||
Bandwidth float64
|
||||
BandwidthUnit string
|
||||
|
||||
RegionId int64
|
||||
}) {
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.UserUI.ShowPrices {
|
||||
this.Fail("管理员未开启此配置")
|
||||
return
|
||||
}
|
||||
|
||||
var trafficGB float64 = 0
|
||||
var bandwidthMB float64 = 0
|
||||
|
||||
switch params.PriceType {
|
||||
case userconfigs.PriceTypeTraffic:
|
||||
switch params.TrafficUnit {
|
||||
case "gb":
|
||||
trafficGB = params.Traffic
|
||||
case "tb":
|
||||
trafficGB = params.Traffic * (1 << 10)
|
||||
case "eb":
|
||||
trafficGB = params.Traffic * (1 << 20)
|
||||
}
|
||||
case userconfigs.PriceTypeBandwidth:
|
||||
switch params.BandwidthUnit {
|
||||
case "mb":
|
||||
bandwidthMB = params.Bandwidth
|
||||
case "gb":
|
||||
bandwidthMB = params.Bandwidth * (1 << 10)
|
||||
case "tb":
|
||||
bandwidthMB = params.Bandwidth * (1 << 20)
|
||||
}
|
||||
default:
|
||||
this.Fail("请选择正确的计费类型")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := this.RPC().PriceRPC().CalculatePrice(this.UserContext(), &pb.CalculatePriceRequest{
|
||||
PriceType: params.PriceType,
|
||||
TrafficGB: trafficGB,
|
||||
BandwidthMB: bandwidthMB,
|
||||
NodeRegionId: params.RegionId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["amount"] = resp.Amount
|
||||
this.Data["amountFormatted"] = numberutils.FormatFloat(resp.Amount, 2)
|
||||
this.Data["hasRegionPrice"] = resp.HasNodeRegionPrice
|
||||
this.Success()
|
||||
}
|
||||
70
EdgeUser/internal/web/actions/default/servers/fee/index.go
Normal file
70
EdgeUser/internal/web/actions/default/servers/fee/index.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package fee
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn {
|
||||
this.WriteString("管理员未开启此配置")
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["showPrices"] = config.UserUI.ShowPrices
|
||||
|
||||
// 配置
|
||||
// 仅显示有限的配置,防止信息泄露
|
||||
var priceType = config.DefaultPriceType
|
||||
var pricePeriod = config.DefaultPricePeriod
|
||||
|
||||
priceInfoResp, err := this.RPC().UserRPC().FindUserPriceInfo(this.UserContext(), &pb.FindUserPriceInfoRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 计费方式
|
||||
if config.UserCanChangePriceType {
|
||||
if len(priceInfoResp.PriceType) > 0 && userconfigs.IsValidPriceType(priceInfoResp.PriceType) {
|
||||
priceType = priceInfoResp.PriceType
|
||||
}
|
||||
}
|
||||
|
||||
// 计费周期
|
||||
if config.UserCanChangePricePeriod {
|
||||
if len(priceInfoResp.PricePeriod) > 0 && userconfigs.IsValidPricePeriod(priceInfoResp.PricePeriod) {
|
||||
pricePeriod = priceInfoResp.PricePeriod
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["config"] = maps.Map{
|
||||
"priceType": priceType,
|
||||
"priceTypeName": userconfigs.PriceTypeName(priceType),
|
||||
"pricePeriod": pricePeriod,
|
||||
"pricePeriodName": userconfigs.PricePeriodName(pricePeriod),
|
||||
"canChangeType": config.UserCanChangePriceType,
|
||||
"canChangePeriod": config.UserCanChangePricePeriod,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
24
EdgeUser/internal/web/actions/default/servers/fee/init.go
Normal file
24
EdgeUser/internal/web/actions/default/servers/fee/init.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package fee
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "servers").
|
||||
Data("teaSubMenu", "fee").
|
||||
Prefix("/servers/fee").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/updatePriceTypePopup", new(UpdatePriceTypePopupAction)).
|
||||
GetPost("/updatePricePeriodPopup", new(UpdatePricePeriodPopupAction)).
|
||||
Get("/prices", new(PricesAction)).
|
||||
GetPost("/calculator", new(CalculatorAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
204
EdgeUser/internal/web/actions/default/servers/fee/prices.go
Normal file
204
EdgeUser/internal/web/actions/default/servers/fee/prices.go
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package fee
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type PricesAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *PricesAction) Init() {
|
||||
this.Nav("", "", "price")
|
||||
}
|
||||
|
||||
func (this *PricesAction) RunGet(params struct{}) {
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn || !config.UserUI.ShowPrices {
|
||||
this.WriteString("管理员未开启此配置")
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["showPrices"] = true
|
||||
|
||||
// 初始化
|
||||
this.Data["regions"] = []maps.Map{}
|
||||
this.Data["bandwidthItems"] = []maps.Map{}
|
||||
this.Data["trafficItems"] = []maps.Map{}
|
||||
|
||||
var bandwidthItemIds = []int64{}
|
||||
var trafficItemIds = []int64{}
|
||||
|
||||
// 带宽价格
|
||||
var supportRegions = false
|
||||
var supportBandwidth = false
|
||||
if (config.UserCanChangePriceType || config.DefaultPriceType == userconfigs.PriceTypeBandwidth) && config.DefaultBandwidthPriceConfig != nil {
|
||||
supportBandwidth = true
|
||||
this.Data["bandwidthPrices"] = maps.Map{
|
||||
"percentile": config.DefaultBandwidthPriceConfig.Percentile,
|
||||
"base": config.DefaultBandwidthPriceConfig.Base,
|
||||
"ranges": config.DefaultBandwidthPriceConfig.Ranges,
|
||||
"supportRegions": config.DefaultBandwidthPriceConfig.SupportRegions,
|
||||
}
|
||||
|
||||
if config.DefaultBandwidthPriceConfig.SupportRegions {
|
||||
supportRegions = true
|
||||
|
||||
bandwidthItemIds, err = this.bandwidthPriceItems()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["supportBandwidth"] = supportBandwidth
|
||||
|
||||
// 流量价格
|
||||
var supportTraffic = false
|
||||
if (config.UserCanChangePriceType || config.DefaultPriceType == userconfigs.PriceTypeTraffic) && config.DefaultTrafficPriceConfig != nil {
|
||||
supportTraffic = true
|
||||
this.Data["trafficPrices"] = maps.Map{
|
||||
"base": config.DefaultTrafficPriceConfig.Base,
|
||||
"ranges": config.DefaultTrafficPriceConfig.Ranges,
|
||||
"supportRegions": config.DefaultTrafficPriceConfig.SupportRegions,
|
||||
}
|
||||
if config.DefaultTrafficPriceConfig.SupportRegions {
|
||||
supportRegions = true
|
||||
|
||||
trafficItemIds, err = this.trafficPriceItems()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["supportTraffic"] = supportTraffic
|
||||
|
||||
// 支付方式
|
||||
this.Data["priceType"] = config.DefaultPriceType
|
||||
|
||||
// 区域
|
||||
if supportRegions {
|
||||
// 所有区域
|
||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.UserContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var regionMaps = []maps.Map{}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
var pricesMap = map[int64]float32{}
|
||||
if len(region.PricesJSON) > 0 {
|
||||
err = json.Unmarshal(region.PricesJSON, &pricesMap)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 跳过没有设置的
|
||||
if len(pricesMap) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 支持的计费方式
|
||||
var regionSupportBandwidth = false
|
||||
var regionSupportTraffic = false
|
||||
|
||||
for itemId := range pricesMap {
|
||||
if lists.ContainsInt64(bandwidthItemIds, itemId) {
|
||||
regionSupportBandwidth = true
|
||||
}
|
||||
if lists.ContainsInt64(trafficItemIds, itemId) {
|
||||
regionSupportTraffic = true
|
||||
}
|
||||
}
|
||||
|
||||
if regionSupportBandwidth || regionSupportTraffic {
|
||||
regionMaps = append(regionMaps, maps.Map{
|
||||
"id": region.Id,
|
||||
"isOn": region.IsOn,
|
||||
"name": region.Name,
|
||||
"prices": pricesMap,
|
||||
"supportBandwidth": regionSupportBandwidth,
|
||||
"supportTraffic": regionSupportTraffic,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *PricesAction) bandwidthPriceItems() (itemIds []int64, err error) {
|
||||
// 所有价格项目
|
||||
itemsResp, err := this.RPC().NodePriceItemRPC().FindAllAvailableNodePriceItems(this.UserContext(), &pb.FindAllAvailableNodePriceItemsRequest{Type: userconfigs.PriceTypeBandwidth})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var itemMaps = []maps.Map{}
|
||||
for _, item := range itemsResp.NodePriceItems {
|
||||
itemIds = append(itemIds, item.Id)
|
||||
|
||||
var maxSize string
|
||||
if item.BitsTo == 0 {
|
||||
maxSize = "∞"
|
||||
} else {
|
||||
maxSize = numberutils.FormatBits(item.BitsTo)
|
||||
}
|
||||
|
||||
itemMaps = append(itemMaps, maps.Map{
|
||||
"id": item.Id,
|
||||
"name": item.Name,
|
||||
"minSize": numberutils.FormatBits(item.BitsFrom),
|
||||
"maxSize": maxSize,
|
||||
})
|
||||
}
|
||||
this.Data["bandwidthItems"] = itemMaps
|
||||
|
||||
return itemIds, nil
|
||||
}
|
||||
|
||||
func (this *PricesAction) trafficPriceItems() (itemIds []int64, err error) {
|
||||
// 所有价格项目
|
||||
itemsResp, err := this.RPC().NodePriceItemRPC().FindAllAvailableNodePriceItems(this.UserContext(), &pb.FindAllAvailableNodePriceItemsRequest{Type: userconfigs.PriceTypeTraffic})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var itemMaps = []maps.Map{}
|
||||
for _, item := range itemsResp.NodePriceItems {
|
||||
itemIds = append(itemIds, item.Id)
|
||||
|
||||
var maxSize string
|
||||
if item.BitsTo == 0 {
|
||||
maxSize = "∞"
|
||||
} else {
|
||||
maxSize = numberutils.FormatBytes(item.BitsTo / 8)
|
||||
}
|
||||
|
||||
itemMaps = append(itemMaps, maps.Map{
|
||||
"id": item.Id,
|
||||
"name": item.Name,
|
||||
"minSize": numberutils.FormatBytes(item.BitsFrom / 8),
|
||||
"maxSize": maxSize,
|
||||
})
|
||||
}
|
||||
this.Data["trafficItems"] = itemMaps
|
||||
|
||||
return itemIds, nil
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package fee
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdatePricePeriodPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePricePeriodPopupAction) RunGet(params struct {
|
||||
}) {
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn || !config.UserCanChangePricePeriod {
|
||||
this.WriteString("管理员未开启此配置")
|
||||
return
|
||||
}
|
||||
|
||||
var pricePeriod = config.DefaultPricePeriod
|
||||
|
||||
userPriceInfo, err := this.RPC().UserRPC().FindUserPriceInfo(this.UserContext(), &pb.FindUserPriceInfoRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(userPriceInfo.PricePeriod) > 0 && userconfigs.IsValidPricePeriod(userPriceInfo.PricePeriod) {
|
||||
pricePeriod = userPriceInfo.PricePeriod
|
||||
}
|
||||
this.Data["pricePeriod"] = pricePeriod
|
||||
|
||||
this.Data["pricePeriods"] = []maps.Map{
|
||||
{
|
||||
"code": userconfigs.PricePeriodMonthly,
|
||||
"name": "按月",
|
||||
"description": "按自然月计费,每个月出上一个月的账单。",
|
||||
},
|
||||
{
|
||||
"code": userconfigs.PricePeriodDaily,
|
||||
"name": "按日",
|
||||
"description": "按自然日计费,每天出上一天的账单。",
|
||||
},
|
||||
}
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePricePeriodPopupAction) RunPost(params struct {
|
||||
PricePeriod string
|
||||
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.User_LogUpdateUserPricePeriod, params.PricePeriod)
|
||||
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn || !config.UserCanChangePricePeriod {
|
||||
this.Fail("管理员未开启此配置")
|
||||
}
|
||||
|
||||
if !userconfigs.IsValidPricePeriod(params.PricePeriod) {
|
||||
this.Fail("计费方式错误:'" + params.PricePeriod + "'")
|
||||
}
|
||||
|
||||
_, err = this.RPC().UserRPC().UpdateUserPricePeriod(this.UserContext(), &pb.UpdateUserPricePeriodRequest{
|
||||
UserId: this.UserId(),
|
||||
PricePeriod: params.PricePeriod,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package fee
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type UpdatePriceTypePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePriceTypePopupAction) RunGet(params struct {
|
||||
}) {
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn || !config.UserCanChangePriceType || config.DefaultBandwidthPriceConfig == nil {
|
||||
this.WriteString("管理员未开启此配置")
|
||||
return
|
||||
}
|
||||
|
||||
var percentile = config.DefaultBandwidthPriceConfig.Percentile
|
||||
|
||||
var priceType = config.DefaultPriceType
|
||||
|
||||
userPriceInfo, err := this.RPC().UserRPC().FindUserPriceInfo(this.UserContext(), &pb.FindUserPriceInfoRequest{UserId: this.UserId()})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if len(userPriceInfo.PriceType) > 0 && userconfigs.IsValidPriceType(userPriceInfo.PriceType) {
|
||||
priceType = userPriceInfo.PriceType
|
||||
}
|
||||
this.Data["priceType"] = priceType
|
||||
|
||||
this.Data["priceTypes"] = []maps.Map{
|
||||
{
|
||||
"code": userconfigs.PriceTypeBandwidth,
|
||||
"name": "带宽",
|
||||
"description": "使用在计费周期内的" + types.String(percentile) + "th带宽计费。",
|
||||
},
|
||||
{
|
||||
"code": userconfigs.PriceTypeTraffic,
|
||||
"name": "流量",
|
||||
"description": "使用在计费周期内产生的流量总和进行计费。",
|
||||
},
|
||||
}
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePriceTypePopupAction) RunPost(params struct {
|
||||
PriceType string
|
||||
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.User_LogUpdateUserPriceType, params.PriceType)
|
||||
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.IsOn || !config.UserCanChangePriceType {
|
||||
this.Fail("管理员未开启此配置")
|
||||
}
|
||||
|
||||
if !userconfigs.IsValidPriceType(params.PriceType) {
|
||||
this.Fail("计费方式错误:'" + params.PriceType + "'")
|
||||
}
|
||||
|
||||
_, err = this.RPC().UserRPC().UpdateUserPriceType(this.UserContext(), &pb.UpdateUserPriceTypeRequest{
|
||||
UserId: this.UserId(),
|
||||
PriceType: params.PriceType,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct{}) {
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
Name string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入分组名称")
|
||||
createResp, err := this.RPC().ServerGroupRPC().CreateServerGroup(this.UserContext(), &pb.CreateServerGroupRequest{
|
||||
Name: params.Name,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = maps.Map{
|
||||
"id": createResp.ServerGroupId,
|
||||
"name": params.Name,
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.ServerGroup_LogCreateServerGroup, createResp.ServerGroupId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package group
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.ServerGroup_LogDeleteServerGroup, params.GroupId)
|
||||
|
||||
// 检查是否正在使用
|
||||
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithServerGroupId(this.UserContext(), &pb.CountAllEnabledServersWithServerGroupIdRequest{ServerGroupId: params.GroupId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if countResp.Count > 0 {
|
||||
this.Fail("此分组正在被使用不能删除,请修改相关服务后再删除")
|
||||
}
|
||||
|
||||
_, err = this.RPC().ServerGroupRPC().DeleteServerGroup(this.UserContext(), &pb.DeleteServerGroupRequest{ServerGroupId: params.GroupId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/groups/group/servergrouputils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "group.index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
GroupId int64
|
||||
Keyword string
|
||||
}) {
|
||||
group, err := servergrouputils.InitGroup(this.Parent(), params.GroupId, "")
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package servergrouputils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
// InitGroup 初始化分组信息
|
||||
func InitGroup(parent *actionutils.ParentAction, groupId int64, menuItem string) (*pb.ServerGroup, error) {
|
||||
groupResp, err := parent.RPC().ServerGroupRPC().FindEnabledServerGroup(parent.UserContext(), &pb.FindEnabledServerGroupRequest{ServerGroupId: groupId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var group = groupResp.ServerGroup
|
||||
if group == nil {
|
||||
return nil, errors.New("group with id '" + types.String(groupId) + "' not found")
|
||||
}
|
||||
|
||||
parent.Data["group"] = maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
}
|
||||
|
||||
// 初始化设置菜单
|
||||
if len(menuItem) > 0 {
|
||||
// 获取设置概要信息
|
||||
configInfoResp, err := parent.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(parent.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{ServerGroupId: groupId})
|
||||
if err != nil {
|
||||
return group, err
|
||||
}
|
||||
|
||||
var urlPrefix = "/servers/groups/group/settings"
|
||||
var leftMenuItems = []maps.Map{
|
||||
{
|
||||
"name": "HTTP代理",
|
||||
"url": urlPrefix + "/httpReverseProxy?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "httpReverseProxy",
|
||||
"isOn": configInfoResp.HasHTTPReverseProxy,
|
||||
},
|
||||
{
|
||||
"name": "TCP代理",
|
||||
"url": urlPrefix + "/tcpReverseProxy?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "tcpReverseProxy",
|
||||
"isOn": configInfoResp.HasTCPReverseProxy,
|
||||
},
|
||||
{
|
||||
"name": "UDP代理",
|
||||
"url": urlPrefix + "/udpReverseProxy?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "udpReverseProxy",
|
||||
"isOn": configInfoResp.HasUDPReverseProxy,
|
||||
},
|
||||
}
|
||||
|
||||
leftMenuItems = append([]maps.Map{
|
||||
{
|
||||
"name": "Web设置",
|
||||
"url": urlPrefix + "/web?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "web",
|
||||
"isOn": configInfoResp.HasRootConfig,
|
||||
},
|
||||
}, leftMenuItems...)
|
||||
leftMenuItems = append(leftMenuItems, []maps.Map{
|
||||
{
|
||||
"name": "-",
|
||||
"url": "",
|
||||
},
|
||||
{
|
||||
"name": "WAF",
|
||||
"url": urlPrefix + "/waf?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "waf",
|
||||
"isOn": configInfoResp.HasWAFConfig,
|
||||
},
|
||||
{
|
||||
"name": "缓存",
|
||||
"url": urlPrefix + "//cache?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "cache",
|
||||
"isOn": configInfoResp.HasCacheConfig,
|
||||
},
|
||||
{
|
||||
"name": "字符编码",
|
||||
"url": urlPrefix + "/charset?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "charset",
|
||||
"isOn": configInfoResp.HasCharsetConfig,
|
||||
},
|
||||
{
|
||||
"name": "访问日志",
|
||||
"url": urlPrefix + "/accessLog?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "accessLog",
|
||||
"isOn": configInfoResp.HasAccessLogConfig,
|
||||
},
|
||||
{
|
||||
"name": "统计",
|
||||
"url": urlPrefix + "/stat?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "stat",
|
||||
"isOn": configInfoResp.HasStatConfig,
|
||||
},
|
||||
{
|
||||
"name": "内容压缩",
|
||||
"url": urlPrefix + "/compression?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "compression",
|
||||
"isOn": configInfoResp.HasCompressionConfig,
|
||||
},
|
||||
{
|
||||
"name": "自定义页面",
|
||||
"url": urlPrefix + "/pages?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "pages",
|
||||
"isOn": configInfoResp.HasPagesConfig,
|
||||
},
|
||||
{
|
||||
"name": "HTTP报头",
|
||||
"url": urlPrefix + "/headers?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "headers",
|
||||
"isOn": configInfoResp.HasRequestHeadersConfig || configInfoResp.HasResponseHeadersConfig,
|
||||
},
|
||||
{
|
||||
"name": "Websocket",
|
||||
"url": urlPrefix + "/websocket?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "websocket",
|
||||
"isOn": configInfoResp.HasWebsocketConfig,
|
||||
},
|
||||
{
|
||||
"name": "WebP",
|
||||
"url": urlPrefix + "/webp?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "webp",
|
||||
"isOn": configInfoResp.HasWebPConfig,
|
||||
},
|
||||
}...)
|
||||
|
||||
leftMenuItems = append(leftMenuItems, maps.Map{
|
||||
"name": "-",
|
||||
"url": "",
|
||||
})
|
||||
leftMenuItems = append(leftMenuItems, maps.Map{
|
||||
"name": "访客IP地址",
|
||||
"url": urlPrefix + "/remoteAddr?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "remoteAddr",
|
||||
"isOn": configInfoResp.HasRemoteAddrConfig,
|
||||
})
|
||||
|
||||
leftMenuItems = append(leftMenuItems, maps.Map{
|
||||
"name": "请求限制",
|
||||
"url": urlPrefix + "/requestLimit?groupId=" + types.String(groupId),
|
||||
"isActive": menuItem == "requestLimit",
|
||||
"isOn": configInfoResp.HasRequestLimitConfig,
|
||||
})
|
||||
parent.Data["leftMenuItems"] = leftMenuItems
|
||||
}
|
||||
|
||||
return group, nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package group
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/groups/group/servergrouputils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpdateAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdateAction) Init() {
|
||||
this.Nav("", "", "group.update")
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunGet(params struct {
|
||||
GroupId int64
|
||||
}) {
|
||||
group, err := servergrouputils.InitGroup(this.Parent(), params.GroupId, "")
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
Name string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.ServerGroup_LogUpdateServerGroup, params.GroupId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入分组名称")
|
||||
_, err := this.RPC().ServerGroupRPC().UpdateServerGroup(this.UserContext(), &pb.UpdateServerGroupRequest{
|
||||
ServerGroupId: params.GroupId,
|
||||
Name: params.Name,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "group")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
groupsResp, err := this.RPC().ServerGroupRPC().FindAllEnabledServerGroups(this.UserContext(), &pb.FindAllEnabledServerGroupsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
var groupMaps = []maps.Map{}
|
||||
for _, group := range groupsResp.ServerGroups {
|
||||
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithServerGroupId(this.UserContext(), &pb.CountAllEnabledServersWithServerGroupIdRequest{ServerGroupId: group.Id})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
countServers := countResp.Count
|
||||
|
||||
groupMaps = append(groupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
"countServers": countServers,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
27
EdgeUser/internal/web/actions/default/servers/groups/init.go
Normal file
27
EdgeUser/internal/web/actions/default/servers/groups/init.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/groups/group"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "servers").
|
||||
Prefix("/servers/groups").
|
||||
GetPost("", new(IndexAction)).
|
||||
GetPost("/createPopup", new(CreatePopupAction)).
|
||||
Post("/sort", new(SortAction)).
|
||||
GetPost("/selectPopup", new(SelectPopupAction)).
|
||||
|
||||
// 详情
|
||||
Prefix("/servers/groups/group").
|
||||
Get("", new(group.IndexAction)).
|
||||
Post("/delete", new(group.DeleteAction)).
|
||||
GetPost("/update", new(group.UpdateAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SelectPopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunGet(params struct {
|
||||
SelectedGroupIds string
|
||||
}) {
|
||||
groupsResp, err := this.RPC().ServerGroupRPC().FindAllEnabledServerGroups(this.UserContext(), &pb.FindAllEnabledServerGroupsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
}
|
||||
|
||||
selectedGroupIds := []int64{}
|
||||
if len(params.SelectedGroupIds) > 0 {
|
||||
for _, v := range strings.Split(params.SelectedGroupIds, ",") {
|
||||
selectedGroupIds = append(selectedGroupIds, types.Int64(v))
|
||||
}
|
||||
}
|
||||
|
||||
groupMaps := []maps.Map{}
|
||||
for _, group := range groupsResp.ServerGroups {
|
||||
// 已经选过的就跳过
|
||||
if lists.ContainsInt64(selectedGroupIds, group.Id) {
|
||||
continue
|
||||
}
|
||||
groupMaps = append(groupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *SelectPopupAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
if params.GroupId <= 0 {
|
||||
this.Fail("请选择要使用的分组")
|
||||
}
|
||||
|
||||
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroup(this.UserContext(), &pb.FindEnabledServerGroupRequest{ServerGroupId: params.GroupId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
group := groupResp.ServerGroup
|
||||
if group == nil {
|
||||
this.NotFound("serverGroup", params.GroupId)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["group"] = maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
26
EdgeUser/internal/web/actions/default/servers/groups/sort.go
Normal file
26
EdgeUser/internal/web/actions/default/servers/groups/sort.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type SortAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *SortAction) RunPost(params struct {
|
||||
GroupIds []int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.ServerGroup_LogSortServerGroups)
|
||||
|
||||
_, err := this.RPC().ServerGroupRPC().UpdateServerGroupOrders(this.UserContext(), &pb.UpdateServerGroupOrdersRequest{ServerGroupIds: params.GroupIds})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package headers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type OptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *OptionsAction) RunPost(params struct {
|
||||
Type string
|
||||
}) {
|
||||
if params.Type == "request" {
|
||||
this.Data["headers"] = serverconfigs.AllHTTPCommonRequestHeaders
|
||||
} else if params.Type == "response" {
|
||||
this.Data["headers"] = serverconfigs.AllHTTPCommonResponseHeaders
|
||||
} else {
|
||||
this.Data["headers"] = []string{}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
235
EdgeUser/internal/web/actions/default/servers/index.go
Normal file
235
EdgeUser/internal/web/actions/default/servers/index.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Keyword string
|
||||
GroupId int64
|
||||
}) {
|
||||
// 检查用户状态
|
||||
this.CheckUserStatus()
|
||||
|
||||
// 更新用户可用状态
|
||||
stateResp, err := this.RPC().UserRPC().RenewUserServersState(this.UserContext(), &pb.RenewUserServersStateRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["serversIsEnabled"] = stateResp.IsEnabled
|
||||
|
||||
// 提醒有逾期未支付的账单
|
||||
this.Data["countUnpaidBills"] = 0
|
||||
priceConfig, _ := configloaders.LoadCacheableUserPriceConfig()
|
||||
if priceConfig != nil && priceConfig.IsOn && priceConfig.UnpaidBillPolicy.IsOn && (priceConfig.UnpaidBillPolicy.MinDailyBillDays > 0 || priceConfig.UnpaidBillPolicy.MinMonthlyBillDays > 0) {
|
||||
countResp, err := this.RPC().UserBillRPC().CountAllUserBills(this.UserContext(), &pb.CountAllUserBillsRequest{
|
||||
PaidFlag: 0,
|
||||
UserId: this.UserId(),
|
||||
Month: "",
|
||||
TrafficRelated: true,
|
||||
MinDailyBillDays: priceConfig.UnpaidBillPolicy.MinDailyBillDays,
|
||||
MinMonthlyBillDays: priceConfig.UnpaidBillPolicy.MinMonthlyBillDays,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["countUnpaidBills"] = countResp.Count
|
||||
}
|
||||
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["groupId"] = params.GroupId
|
||||
|
||||
// 分组
|
||||
groupsResp, err := this.RPC().ServerGroupRPC().FindAllEnabledServerGroups(this.UserContext(), &pb.FindAllEnabledServerGroupsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var groupMaps = []maps.Map{}
|
||||
for _, group := range groupsResp.ServerGroups {
|
||||
groupMaps = append(groupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
})
|
||||
}
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
// 分页
|
||||
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersMatch(this.UserContext(), &pb.CountAllEnabledServersMatchRequest{
|
||||
ServerGroupId: params.GroupId,
|
||||
Keyword: params.Keyword,
|
||||
UserId: this.UserId(),
|
||||
ProtocolFamily: "http",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
// 列表
|
||||
serversResp, err := this.RPC().ServerRPC().ListEnabledServersMatch(this.UserContext(), &pb.ListEnabledServersMatchRequest{
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
ServerGroupId: params.GroupId,
|
||||
Keyword: params.Keyword,
|
||||
ProtocolFamily: "http",
|
||||
UserId: this.UserId(),
|
||||
IgnoreServerNames: true,
|
||||
IgnoreSSLCerts: true,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var serverMaps = []maps.Map{}
|
||||
for _, server := range serversResp.Servers {
|
||||
// 域名列表
|
||||
var serverNames = []*serverconfigs.ServerNameConfig{}
|
||||
if server.IsAuditing || (server.AuditingResult != nil && !server.AuditingResult.IsOk) {
|
||||
server.ServerNamesJSON = server.AuditingServerNamesJSON
|
||||
}
|
||||
if len(server.ServerNamesJSON) > 0 {
|
||||
err = json.Unmarshal(server.ServerNamesJSON, &serverNames)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 域名和限流状态
|
||||
var trafficLimitStatus *serverconfigs.TrafficLimitStatus
|
||||
if len(server.Config) > 0 {
|
||||
var serverConfig = &serverconfigs.ServerConfig{}
|
||||
err = json.Unmarshal(server.Config, serverConfig)
|
||||
if err == nil {
|
||||
if len(serverNames) == 0 {
|
||||
serverNames = serverConfig.ServerNames
|
||||
}
|
||||
|
||||
if serverConfig.TrafficLimitStatus != nil && serverConfig.TrafficLimitStatus.IsValid() {
|
||||
trafficLimitStatus = serverConfig.TrafficLimitStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CNAME
|
||||
var cname = ""
|
||||
if server.NodeCluster != nil {
|
||||
clusterId := server.NodeCluster.Id
|
||||
if clusterId > 0 {
|
||||
dnsInfoResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.UserContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: clusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if dnsInfoResp.Domain != nil {
|
||||
cname = server.DnsName + "." + dnsInfoResp.Domain.Name + "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP
|
||||
var httpIsOn = false
|
||||
if len(server.HttpJSON) > 0 {
|
||||
httpConfig, err := serverconfigs.NewHTTPProtocolConfigFromJSON(server.HttpJSON)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpIsOn = httpConfig.IsOn && len(httpConfig.Listen) > 0
|
||||
}
|
||||
|
||||
// HTTPS
|
||||
var httpsIsOn = false
|
||||
if len(server.HttpsJSON) > 0 {
|
||||
httpsConfig, err := serverconfigs.NewHTTPSProtocolConfigFromJSON(server.HttpsJSON)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpsIsOn = httpsConfig.IsOn && len(httpsConfig.Listen) > 0
|
||||
}
|
||||
|
||||
// 套餐
|
||||
var userPlanMap = maps.Map{"id": 0}
|
||||
if server.UserPlanId > 0 {
|
||||
userPlanResp, err := this.RPC().UserPlanRPC().FindEnabledUserPlan(this.UserContext(), &pb.FindEnabledUserPlanRequest{
|
||||
UserPlanId: server.UserPlanId,
|
||||
})
|
||||
if err != nil {
|
||||
if !utils.IsNotFound(err) {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var userPlan = userPlanResp.UserPlan
|
||||
if userPlan != nil && userPlan.Plan != nil {
|
||||
if len(userPlan.Name) == 0 {
|
||||
userPlan.Name = userPlan.Plan.Name
|
||||
}
|
||||
userPlanMap = maps.Map{
|
||||
"id": userPlan.Id,
|
||||
"name": userPlan.Name,
|
||||
"dayTo": userPlan.DayTo,
|
||||
"isExpired": userPlan.DayTo <= timeutil.Format("Y-m-d"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分组
|
||||
var serverGroupMaps = []maps.Map{}
|
||||
for _, group := range server.ServerGroups {
|
||||
if group.UserId != this.UserId() {
|
||||
continue
|
||||
}
|
||||
serverGroupMaps = append(serverGroupMaps, maps.Map{
|
||||
"id": group.Id,
|
||||
"name": group.Name,
|
||||
})
|
||||
}
|
||||
|
||||
serverMaps = append(serverMaps, maps.Map{
|
||||
"id": server.Id,
|
||||
"name": server.Name,
|
||||
"serverNames": serverNames,
|
||||
"countServerNames": server.CountServerNames,
|
||||
"isAuditing": false,
|
||||
"cname": cname,
|
||||
"httpIsOn": httpIsOn,
|
||||
"httpsIsOn": httpsIsOn,
|
||||
"status": maps.Map{
|
||||
"isOk": false,
|
||||
"message": "",
|
||||
"type": "",
|
||||
},
|
||||
"isOn": server.IsOn,
|
||||
"userPlan": userPlanMap,
|
||||
"groups": serverGroupMaps,
|
||||
"trafficLimitStatus": trafficLimitStatus,
|
||||
})
|
||||
}
|
||||
this.Data["servers"] = serverMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
33
EdgeUser/internal/web/actions/default/servers/init.go
Normal file
33
EdgeUser/internal/web/actions/default/servers/init.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/headers"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "servers").
|
||||
Prefix("/servers").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/create", new(CreateAction)).
|
||||
Get("/serverNamesPopup", new(ServerNamesPopupAction)).
|
||||
GetPost("/addServerNamePopup", new(AddServerNamePopupAction)).
|
||||
Post("/status", new(StatusAction)).
|
||||
Post("/delete", new(DeleteAction)).
|
||||
Post("/deleteServers", new(DeleteServersAction)).
|
||||
Post("/updateOn", new(UpdateOnAction)).
|
||||
Get("/server", new(ServerAction)).
|
||||
GetPost("/addPortPopup", new(AddPortPopupAction)).
|
||||
GetPost("/updateNamePopup", new(UpdateNamePopupAction)).
|
||||
|
||||
// header
|
||||
Post("/headers/options", new(headers.OptionsAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package iplists
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "servers").
|
||||
Data("teaSubMenu", "iplist").
|
||||
Prefix("/servers/iplists").
|
||||
|
||||
// 选项数据
|
||||
Post("/levelOptions", new(LevelOptionsAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package iplists
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type LevelOptionsAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *LevelOptionsAction) RunPost(params struct{}) {
|
||||
this.Data["levels"] = firewallconfigs.FindAllFirewallEventLevels()
|
||||
|
||||
this.Success()
|
||||
}
|
||||
114
EdgeUser/internal/web/actions/default/servers/packages/buy.go
Normal file
114
EdgeUser/internal/web/actions/default/servers/packages/buy.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
type BuyAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *BuyAction) Init() {
|
||||
this.Nav("", "", "buy")
|
||||
}
|
||||
|
||||
func (this *BuyAction) RunGet(params struct{}) {
|
||||
// 检查权限
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if config == nil || !config.IsOn || config.EnablePlans || !config.EnableTrafficPackages {
|
||||
this.WriteString("管理员未开启相关配置")
|
||||
return
|
||||
}
|
||||
|
||||
// 价格
|
||||
pricesResp, err := this.RPC().TrafficPackagePriceRPC().FindAllTrafficPackagePrices(this.UserContext(), &pb.FindAllTrafficPackagePricesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var priceMap = map[string]float64{} // packageId@regionId@periodId => price
|
||||
var allPackageIdMap = map[int64]bool{}
|
||||
var allRegionIdMap = map[int64]bool{}
|
||||
var allPeriodIdMap = map[int64]bool{}
|
||||
for _, price := range pricesResp.TrafficPackagePrices {
|
||||
if price.Price > 0 {
|
||||
allPackageIdMap[price.TrafficPackageId] = true
|
||||
allRegionIdMap[price.NodeRegionId] = true
|
||||
allPeriodIdMap[price.TrafficPackagePeriodId] = true
|
||||
|
||||
var key = types.String(price.TrafficPackageId) + "@" + types.String(price.NodeRegionId) + "@" + types.String(price.TrafficPackagePeriodId)
|
||||
priceMap[key] = price.Price
|
||||
}
|
||||
}
|
||||
this.Data["prices"] = priceMap
|
||||
|
||||
// 流量包
|
||||
var packageMaps = []maps.Map{}
|
||||
packagesResp, err := this.RPC().TrafficPackageRPC().FindAllAvailableTrafficPackages(this.UserContext(), &pb.FindAllAvailableTrafficPackagesRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, p := range packagesResp.TrafficPackages {
|
||||
_, hasPrice := allPackageIdMap[p.Id]
|
||||
if hasPrice {
|
||||
packageMaps = append(packageMaps, maps.Map{
|
||||
"id": p.Id,
|
||||
"size": p.Size,
|
||||
"unit": p.Unit,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["packages"] = packageMaps
|
||||
|
||||
// 区域
|
||||
var regionMaps = []maps.Map{}
|
||||
regionsResp, err := this.RPC().NodeRegionRPC().FindAllAvailableNodeRegions(this.UserContext(), &pb.FindAllAvailableNodeRegionsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, region := range regionsResp.NodeRegions {
|
||||
_, hasPrice := allRegionIdMap[region.Id]
|
||||
if hasPrice {
|
||||
regionMaps = append(regionMaps, maps.Map{
|
||||
"id": region.Id,
|
||||
"name": region.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMaps
|
||||
|
||||
// 周期
|
||||
var periodMaps = []maps.Map{}
|
||||
periodResp, err := this.RPC().TrafficPackagePeriodRPC().FindAllAvailableTrafficPackagePeriods(this.UserContext(), &pb.FindAllAvailableTrafficPackagePeriodsRequest{})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
for _, period := range periodResp.TrafficPackagePeriods {
|
||||
_, hasPrice := allPeriodIdMap[period.Id]
|
||||
if hasPrice {
|
||||
periodMaps = append(periodMaps, maps.Map{
|
||||
"id": period.Id,
|
||||
"count": period.Count,
|
||||
"unit": period.Unit,
|
||||
"unitName": userconfigs.TrafficPackagePeriodUnitName(period.Unit),
|
||||
})
|
||||
}
|
||||
}
|
||||
this.Data["periods"] = periodMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/finance/financeutils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ConfirmAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ConfirmAction) Init() {
|
||||
this.Nav("", "", "buy")
|
||||
}
|
||||
|
||||
func (this *ConfirmAction) RunGet(params struct {
|
||||
PackageId int64
|
||||
RegionId int64
|
||||
PeriodId int64
|
||||
Count int32
|
||||
}) {
|
||||
// parameters
|
||||
this.Data["packageId"] = params.PackageId
|
||||
this.Data["regionId"] = params.RegionId
|
||||
this.Data["periodId"] = params.PeriodId
|
||||
|
||||
// 检查权限
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if config == nil || !config.IsOn || config.EnablePlans || !config.EnableTrafficPackages {
|
||||
this.WriteString("管理员未开启相关配置")
|
||||
return
|
||||
}
|
||||
|
||||
// package
|
||||
packageResp, err := this.RPC().TrafficPackageRPC().FindTrafficPackage(this.UserContext(), &pb.FindTrafficPackageRequest{TrafficPackageId: params.PackageId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var p = packageResp.TrafficPackage
|
||||
if p == nil || !p.IsOn {
|
||||
this.NotFound("TrafficPackage", params.PackageId)
|
||||
return
|
||||
}
|
||||
this.Data["packageName"] = types.String(p.Size) + strings.ToUpper(p.Unit)
|
||||
|
||||
// region
|
||||
regionResp, err := this.RPC().NodeRegionRPC().FindEnabledNodeRegion(this.UserContext(), &pb.FindEnabledNodeRegionRequest{NodeRegionId: params.RegionId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var region = regionResp.NodeRegion
|
||||
if region == nil || !region.IsOn {
|
||||
this.NotFound("NodeRegion", params.RegionId)
|
||||
return
|
||||
}
|
||||
this.Data["regionName"] = region.Name
|
||||
|
||||
// period
|
||||
periodResp, err := this.RPC().TrafficPackagePeriodRPC().FindTrafficPackagePeriod(this.UserContext(), &pb.FindTrafficPackagePeriodRequest{TrafficPackagePeriodId: params.PeriodId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var period = periodResp.TrafficPackagePeriod
|
||||
if period == nil || !period.IsOn {
|
||||
this.NotFound("TrafficPackagePeriod", params.PeriodId)
|
||||
return
|
||||
}
|
||||
this.Data["periodName"] = types.String(period.Count) + userconfigs.TrafficPackagePeriodUnitName(period.Unit)
|
||||
|
||||
// count
|
||||
if params.Count <= 0 {
|
||||
this.ErrorPage(errors.New("invalid 'count' parameter"))
|
||||
return
|
||||
}
|
||||
this.Data["count"] = params.Count
|
||||
|
||||
// price
|
||||
priceResp, err := this.RPC().TrafficPackagePriceRPC().FindTrafficPackagePrice(this.UserContext(), &pb.FindTrafficPackagePriceRequest{
|
||||
TrafficPackageId: params.PackageId,
|
||||
NodeRegionId: params.RegionId,
|
||||
TrafficPackagePeriodId: params.PeriodId,
|
||||
Count: params.Count,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if priceResp.Price == 0 {
|
||||
this.ErrorPage(errors.New("invalid parameters: could not retrieve price"))
|
||||
return
|
||||
}
|
||||
this.Data["amount"] = priceResp.Amount
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *ConfirmAction) RunPost(params struct {
|
||||
PackageId int64
|
||||
RegionId int64
|
||||
PeriodId int64
|
||||
Count int32
|
||||
|
||||
MethodCode string
|
||||
}) {
|
||||
// 检查权限
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if config == nil || !config.IsOn || config.EnablePlans || !config.EnableTrafficPackages {
|
||||
this.Fail("管理员未开启相关配置")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查参数
|
||||
if params.PackageId <= 0 {
|
||||
this.Fail("请选择流量包规格")
|
||||
return
|
||||
}
|
||||
if params.RegionId <= 0 {
|
||||
this.Fail("请选择区域")
|
||||
return
|
||||
}
|
||||
if params.PeriodId <= 0 {
|
||||
this.Fail("请选择有效期")
|
||||
return
|
||||
}
|
||||
if params.Count <= 0 {
|
||||
this.Fail("请选择数量")
|
||||
return
|
||||
}
|
||||
if params.Count > 20 {
|
||||
this.Fail("一次性最多只能购买20个")
|
||||
return
|
||||
}
|
||||
|
||||
// period
|
||||
periodResp, err := this.RPC().TrafficPackagePeriodRPC().FindTrafficPackagePeriod(this.UserContext(), &pb.FindTrafficPackagePeriodRequest{TrafficPackagePeriodId: params.PeriodId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var period = periodResp.TrafficPackagePeriod
|
||||
if period == nil || !period.IsOn {
|
||||
this.NotFound("TrafficPackagePeriod", params.PeriodId)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查价格
|
||||
priceResp, err := this.RPC().TrafficPackagePriceRPC().FindTrafficPackagePrice(this.UserContext(), &pb.FindTrafficPackagePriceRequest{
|
||||
TrafficPackageId: params.PackageId,
|
||||
NodeRegionId: params.RegionId,
|
||||
TrafficPackagePeriodId: params.PeriodId,
|
||||
Count: params.Count,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if priceResp.Amount <= 0 {
|
||||
this.Fail("当前流量包没有设定价格,无法购买")
|
||||
return
|
||||
}
|
||||
var amount = priceResp.Amount
|
||||
|
||||
this.Data["packageId"] = params.PackageId
|
||||
this.Data["regionId"] = params.RegionId
|
||||
this.Data["periodId"] = params.PeriodId
|
||||
this.Data["count"] = params.Count
|
||||
|
||||
// 使用余额购买
|
||||
this.Data["success"] = false
|
||||
this.Data["orderCode"] = ""
|
||||
if params.MethodCode == "@balance" {
|
||||
balance, err := financeutils.FindUserBalance(this.UserContext())
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if amount > balance {
|
||||
this.Fail("当前余额不足,需要:" + fmt.Sprintf("%.2f元", amount) + ",现有:" + fmt.Sprintf("%.2f元", balance) + " ,请充值后再试")
|
||||
return
|
||||
}
|
||||
|
||||
// 直接购买
|
||||
_, err = this.RPC().UserTrafficPackageRPC().BuyUserTrafficPackage(this.UserContext(), &pb.BuyUserTrafficPackageRequest{
|
||||
UserId: this.UserId(),
|
||||
TrafficPackageId: params.PackageId,
|
||||
NodeRegionId: params.RegionId,
|
||||
TrafficPackagePeriodId: params.PeriodId,
|
||||
Count: params.Count,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["success"] = true
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
// 生成订单
|
||||
var orderParams = &userconfigs.OrderTypeBuyTrafficPackageParams{
|
||||
PackageId: params.PackageId,
|
||||
RegionId: params.RegionId,
|
||||
PeriodId: params.PeriodId,
|
||||
PeriodCount: period.Count,
|
||||
PeriodUnit: period.Unit,
|
||||
Count: params.Count,
|
||||
}
|
||||
orderParamsJSON, err := json.Marshal(orderParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := this.RPC().UserOrderRPC().CreateUserOrder(this.UserContext(), &pb.CreateUserOrderRequest{
|
||||
Type: userconfigs.OrderTypeBuyTrafficPackage,
|
||||
OrderMethodCode: params.MethodCode,
|
||||
Amount: amount,
|
||||
ParamsJSON: orderParamsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["orderCode"] = resp.Code
|
||||
|
||||
this.Success()
|
||||
}
|
||||
104
EdgeUser/internal/web/actions/default/servers/packages/index.go
Normal file
104
EdgeUser/internal/web/actions/default/servers/packages/index.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/dateutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "", "index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Type string
|
||||
}) {
|
||||
this.Data["type"] = params.Type
|
||||
|
||||
// 检查权限
|
||||
config, err := configloaders.LoadCacheableUserPriceConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if config == nil || !config.IsOn || config.EnablePlans || !config.EnableTrafficPackages {
|
||||
this.WriteString("管理员未开启相关配置")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查有效的流量包数量
|
||||
countResp, err := this.RPC().UserTrafficPackageRPC().CountUserTrafficPackages(this.UserContext(), &pb.CountUserTrafficPackagesRequest{
|
||||
UserId: this.UserId(),
|
||||
AvailableOnly: params.Type == "",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var count = countResp.Count
|
||||
var page = this.NewPage(count)
|
||||
this.Data["page"] = page.AsHTML()
|
||||
|
||||
// 列表
|
||||
userPackagesResp, err := this.RPC().UserTrafficPackageRPC().ListUserTrafficPackages(this.UserContext(), &pb.ListUserTrafficPackagesRequest{
|
||||
UserId: this.UserId(),
|
||||
AvailableOnly: params.Type == "",
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var userPackageMaps = []maps.Map{}
|
||||
for _, userPackage := range userPackagesResp.UserTrafficPackages {
|
||||
// package
|
||||
var packageMap maps.Map
|
||||
if userPackage.TrafficPackage != nil {
|
||||
packageMap = maps.Map{
|
||||
"id": userPackage.TrafficPackage.Id,
|
||||
"size": userPackage.TrafficPackage.Size,
|
||||
"unit": userPackage.TrafficPackage.Unit,
|
||||
}
|
||||
}
|
||||
|
||||
// region
|
||||
var regionMap maps.Map
|
||||
if userPackage.NodeRegion != nil {
|
||||
regionMap = maps.Map{
|
||||
"id": userPackage.NodeRegion.Id,
|
||||
"name": userPackage.NodeRegion.Name,
|
||||
}
|
||||
}
|
||||
|
||||
userPackageMaps = append(userPackageMaps, maps.Map{
|
||||
"id": userPackage.Id,
|
||||
"dayFrom": dateutils.SplitYmd(userPackage.DayFrom),
|
||||
"dayTo": dateutils.SplitYmd(userPackage.DayTo),
|
||||
"package": packageMap,
|
||||
"region": regionMap,
|
||||
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", userPackage.CreatedAt),
|
||||
"periodCount": userPackage.TrafficPackagePeriodCount,
|
||||
"periodUnitName": userconfigs.TrafficPackagePeriodUnitName(userPackage.TrafficPackagePeriodUnit),
|
||||
"usedSize": numberutils.FormatBytes(userPackage.UsedBytes),
|
||||
"availableSize": numberutils.FormatBytes(userPackage.TotalBytes - userPackage.UsedBytes),
|
||||
"createTime": timeutil.FormatTime("Y-m-d H:i:s", userPackage.CreatedAt),
|
||||
"isExpired": userPackage.DayTo < timeutil.Format("Ymd"),
|
||||
"isUsedAll": userPackage.UsedBytes >= userPackage.TotalBytes,
|
||||
})
|
||||
}
|
||||
this.Data["userPackages"] = userPackageMaps
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth("")).
|
||||
Data("teaMenu", "servers").
|
||||
Data("teaSubMenu", "trafficPackage").
|
||||
Prefix("/servers/packages").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/buy", new(BuyAction)).
|
||||
GetPost("/confirm", new(ConfirmAction)).
|
||||
Post("/price", new(PriceAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type PriceAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *PriceAction) RunPost(params struct {
|
||||
PackageId int64
|
||||
RegionId int64
|
||||
PeriodId int64
|
||||
Count int32
|
||||
}) {
|
||||
if params.PackageId <= 0 || params.RegionId <= 0 || params.PeriodId <= 0 || params.Count <= 0 {
|
||||
this.Data["price"] = 0
|
||||
this.Data["amount"] = 0
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := this.RPC().TrafficPackagePriceRPC().FindTrafficPackagePrice(this.UserContext(), &pb.FindTrafficPackagePriceRequest{
|
||||
TrafficPackageId: params.PackageId,
|
||||
NodeRegionId: params.RegionId,
|
||||
TrafficPackagePeriodId: params.PeriodId,
|
||||
Count: params.Count,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["price"] = resp.Price
|
||||
this.Data["amount"] = resp.Amount
|
||||
|
||||
this.Success()
|
||||
}
|
||||
21
EdgeUser/internal/web/actions/default/servers/server.go
Normal file
21
EdgeUser/internal/web/actions/default/servers/server.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
)
|
||||
|
||||
type ServerAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ServerAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *ServerAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
}) {
|
||||
// TODO 先跳转到设置页面,将来实现日志查看、统计看板等
|
||||
this.RedirectURL("/servers/server/settings/serverNames?serverId=" + numberutils.FormatInt64(params.ServerId))
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HistoryAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *HistoryAction) Init() {
|
||||
this.Nav("", "log", "")
|
||||
this.SecondMenu("history")
|
||||
}
|
||||
|
||||
func (this *HistoryAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
Day string
|
||||
|
||||
Ip string
|
||||
Domain string
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
Keyword string
|
||||
RequestId string
|
||||
HasError int
|
||||
|
||||
Partition int32 `default:"-1"`
|
||||
}) {
|
||||
this.Data["featureIsOn"] = true
|
||||
|
||||
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerViewAccessLog, params.ServerId) {
|
||||
this.Data["featureIsOn"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["ip"] = params.Ip
|
||||
this.Data["domain"] = params.Domain
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["path"] = this.Request.URL.Path
|
||||
this.Data["clusterId"] = params.ClusterId
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
if len(params.Day) == 0 {
|
||||
params.Day = timeutil.Format("Y-m-d")
|
||||
}
|
||||
|
||||
this.Data["path"] = this.Request.URL.Path
|
||||
this.Data["day"] = params.Day
|
||||
this.Data["accessLogs"] = []interface{}{}
|
||||
this.Data["hasError"] = params.HasError
|
||||
|
||||
day := params.Day
|
||||
ipList := []string{}
|
||||
|
||||
if len(day) > 0 && regexp.MustCompile(`\d{4}-\d{2}-\d{2}`).MatchString(day) {
|
||||
day = strings.ReplaceAll(day, "-", "")
|
||||
var size = int64(20)
|
||||
|
||||
this.Data["hasError"] = params.HasError
|
||||
|
||||
resp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.UserContext(), &pb.ListHTTPAccessLogsRequest{
|
||||
Partition: params.Partition,
|
||||
RequestId: params.RequestId,
|
||||
ServerId: params.ServerId,
|
||||
HasError: params.HasError > 0,
|
||||
Day: day,
|
||||
Size: size,
|
||||
Keyword: params.Keyword,
|
||||
Ip: params.Ip,
|
||||
Domain: params.Domain,
|
||||
NodeId: params.NodeId,
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(resp.HttpAccessLogs) == 0 {
|
||||
this.Data["accessLogs"] = []interface{}{}
|
||||
} else {
|
||||
this.Data["accessLogs"] = resp.HttpAccessLogs
|
||||
for _, accessLog := range resp.HttpAccessLogs {
|
||||
if len(accessLog.RemoteAddr) > 0 {
|
||||
if !lists.ContainsString(ipList, accessLog.RemoteAddr) {
|
||||
ipList = append(ipList, accessLog.RemoteAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["hasMore"] = resp.HasMore
|
||||
this.Data["nextRequestId"] = resp.RequestId
|
||||
|
||||
// 上一个requestId
|
||||
this.Data["hasPrev"] = false
|
||||
this.Data["lastRequestId"] = ""
|
||||
if len(params.RequestId) > 0 {
|
||||
this.Data["hasPrev"] = true
|
||||
prevResp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.UserContext(), &pb.ListHTTPAccessLogsRequest{
|
||||
Partition: params.Partition,
|
||||
RequestId: params.RequestId,
|
||||
ServerId: params.ServerId,
|
||||
HasError: params.HasError > 0,
|
||||
Day: day,
|
||||
Size: size,
|
||||
Reverse: true,
|
||||
Keyword: params.Keyword,
|
||||
Ip: params.Ip,
|
||||
Domain: params.Domain,
|
||||
NodeId: params.NodeId,
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if int64(len(prevResp.HttpAccessLogs)) == size {
|
||||
this.Data["lastRequestId"] = prevResp.RequestId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据IP查询区域
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
this.Show()
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IndexAction) Init() {
|
||||
this.Nav("", "log", "")
|
||||
this.SecondMenu("index")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
RequestId string
|
||||
Ip string
|
||||
Domain string
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
Keyword string
|
||||
}) {
|
||||
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerViewAccessLog, params.ServerId)
|
||||
|
||||
this.Data["serverId"] = params.ServerId
|
||||
this.Data["requestId"] = params.RequestId
|
||||
this.Data["ip"] = params.Ip
|
||||
this.Data["domain"] = params.Domain
|
||||
this.Data["keyword"] = params.Keyword
|
||||
this.Data["path"] = this.Request.URL.Path
|
||||
this.Data["clusterId"] = params.ClusterId
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
ServerId int64
|
||||
RequestId string
|
||||
Keyword string
|
||||
Ip string
|
||||
Domain string
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
|
||||
Partition int32 `default:"-1"`
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerViewAccessLog, params.ServerId) {
|
||||
return
|
||||
}
|
||||
|
||||
isReverse := len(params.RequestId) > 0
|
||||
accessLogsResp, err := this.RPC().HTTPAccessLogRPC().ListHTTPAccessLogs(this.UserContext(), &pb.ListHTTPAccessLogsRequest{
|
||||
Partition: params.Partition,
|
||||
ServerId: params.ServerId,
|
||||
RequestId: params.RequestId,
|
||||
Size: 20,
|
||||
Day: timeutil.Format("Ymd"),
|
||||
Keyword: params.Keyword,
|
||||
Ip: params.Ip,
|
||||
Domain: params.Domain,
|
||||
NodeId: params.NodeId,
|
||||
NodeClusterId: params.ClusterId,
|
||||
Reverse: isReverse,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
ipList := []string{}
|
||||
accessLogs := accessLogsResp.HttpAccessLogs
|
||||
if len(accessLogs) == 0 {
|
||||
accessLogs = []*pb.HTTPAccessLog{}
|
||||
} else {
|
||||
for _, accessLog := range accessLogs {
|
||||
if len(accessLog.RemoteAddr) > 0 {
|
||||
if !lists.ContainsString(ipList, accessLog.RemoteAddr) {
|
||||
ipList = append(ipList, accessLog.RemoteAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["accessLogs"] = accessLogs
|
||||
if len(accessLogs) > 0 {
|
||||
this.Data["requestId"] = accessLogs[0].RequestId
|
||||
} else {
|
||||
this.Data["requestId"] = params.RequestId
|
||||
}
|
||||
this.Data["hasMore"] = accessLogsResp.HasMore
|
||||
|
||||
// 根据IP查询区域
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user