Initial commit (code only without large binaries)

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

View File

@@ -0,0 +1,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()
}

View File

@@ -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()
}

View File

@@ -0,0 +1,21 @@
package log
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/log").
GetPost("", new(IndexAction)).
GetPost("/today", new(TodayAction)).
GetPost("/history", new(HistoryAction)).
Get("/viewPopup", new(ViewPopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,117 @@
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"
)
type TodayAction struct {
actionutils.ParentAction
}
func (this *TodayAction) Init() {
this.Nav("", "log", "")
this.SecondMenu("today")
}
func (this *TodayAction) RunGet(params struct {
RequestId string
ServerId int64
HasError int
Ip string
Domain string
ClusterId int64
NodeId int64
Keyword string
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
var size = int64(20)
this.Data["path"] = this.Request.URL.Path
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: timeutil.Format("Ymd"),
Size: size,
Keyword: params.Keyword,
Ip: params.Ip,
Domain: params.Domain,
NodeId: params.NodeId,
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
ipList := []string{}
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: timeutil.Format("Ymd"),
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()
}

View File

@@ -0,0 +1,132 @@
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/errors"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
"net/http"
"strings"
)
type ViewPopupAction struct {
actionutils.ParentAction
}
func (this *ViewPopupAction) Init() {
this.Nav("", "", "")
}
func (this *ViewPopupAction) RunGet(params struct {
RequestId string
}) {
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerViewAccessLog, 0) {
return
}
accessLogResp, err := this.RPC().HTTPAccessLogRPC().FindHTTPAccessLog(this.UserContext(), &pb.FindHTTPAccessLogRequest{RequestId: params.RequestId})
if err != nil {
this.ErrorPage(err)
return
}
accessLog := accessLogResp.HttpAccessLog
if accessLog == nil {
this.WriteString("not found: " + params.RequestId)
return
}
// 状态
if len(accessLog.StatusMessage) == 0 {
accessLog.StatusMessage = http.StatusText(int(accessLog.Status))
}
this.Data["accessLog"] = accessLog
// WAF相关
var wafMap maps.Map = nil
if accessLog.FirewallPolicyId > 0 {
policyResp, err := this.RPC().HTTPFirewallPolicyRPC().FindEnabledHTTPFirewallPolicy(this.UserContext(), &pb.FindEnabledHTTPFirewallPolicyRequest{HttpFirewallPolicyId: accessLog.FirewallPolicyId})
if err != nil {
// 如果没有权限查看,则只显示系统策略
if errors.IsResourceNotFound(err) {
wafMap = maps.Map{
"policy": maps.Map{
"id": 0,
"name": "系统策略",
},
"group": maps.Map{
"id": 0,
"name": "系统策略",
},
}
} else {
this.ErrorPage(err)
return
}
} else if policyResp.HttpFirewallPolicy != nil {
wafMap = maps.Map{
"policy": maps.Map{
"id": policyResp.HttpFirewallPolicy.Id,
"name": policyResp.HttpFirewallPolicy.Name,
},
}
if accessLog.FirewallRuleGroupId > 0 {
groupResp, err := this.RPC().HTTPFirewallRuleGroupRPC().FindEnabledHTTPFirewallRuleGroup(this.UserContext(), &pb.FindEnabledHTTPFirewallRuleGroupRequest{FirewallRuleGroupId: accessLog.FirewallRuleGroupId})
if err != nil {
this.ErrorPage(err)
return
}
if groupResp.FirewallRuleGroup != nil {
wafMap["group"] = maps.Map{
"id": groupResp.FirewallRuleGroup.Id,
"name": groupResp.FirewallRuleGroup.Name,
}
if accessLog.FirewallRuleSetId > 0 {
setResp, err := this.RPC().HTTPFirewallRuleSetRPC().FindEnabledHTTPFirewallRuleSet(this.UserContext(), &pb.FindEnabledHTTPFirewallRuleSetRequest{FirewallRuleSetId: accessLog.FirewallRuleSetId})
if err != nil {
this.ErrorPage(err)
return
}
if setResp.FirewallRuleSet != nil {
wafMap["set"] = maps.Map{
"id": setResp.FirewallRuleSet.Id,
"name": setResp.FirewallRuleSet.Name,
}
}
}
}
}
}
}
this.Data["wafInfo"] = wafMap
// 地域相关
var regionMap maps.Map = nil
var ipRegion = iplibrary.LookupIP(accessLog.RemoteAddr)
if ipRegion != nil && ipRegion.IsOk() {
regionMap = maps.Map{
"full": ipRegion.RegionSummary(),
"isp": ipRegion.ProviderName(),
}
}
this.Data["region"] = regionMap
// 请求内容
this.Data["requestBody"] = string(accessLog.RequestBody)
this.Data["requestContentType"] = "text/plain"
requestContentType, ok := accessLog.Header["Content-Type"]
if ok {
if len(requestContentType.Values) > 0 {
var contentType = requestContentType.Values[0]
if strings.HasSuffix(contentType, "/json") || strings.Contains(contentType, "/json;") {
this.Data["requestContentType"] = "application/json"
}
}
}
this.Show()
}

View File

@@ -0,0 +1,237 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package access
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
teaconst "github.com/TeaOSLab/EdgeUser/internal/const"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Data["authTypes"] = serverconfigs.FindAllHTTPAuthTypes(teaconst.Role)
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Name string
Type string
// TypeA
TypeASecret string
TypeASignParamName string
TypeALife int
// TypeB
TypeBSecret string
TypeBLife int
// TypeC
TypeCSecret string
TypeCLife int
// TypeD
TypeDSecret string
TypeDSignParamName string
TypeDTimestampParamName string
TypeDLife int
// BasicAuth
HttpAuthBasicAuthUsersJSON []byte
BasicAuthRealm string
BasicAuthCharset string
// SubRequest
SubRequestURL string
SubRequestMethod string
SubRequestFollowRequest bool
Exts []string
DomainsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
params.Must.
Field("name", params.Name).
Require("请输入名称").
Field("type", params.Type).
Require("请输入鉴权类型")
var ref = &serverconfigs.HTTPAuthPolicyRef{IsOn: true}
var method serverconfigs.HTTPAuthMethodInterface
// 扩展名
var exts = utils.NewStringsStream(params.Exts).
Map(strings.TrimSpace, strings.ToLower).
Filter(utils.FilterNotEmpty).
Map(utils.MapAddPrefixFunc(".")).
Unique().
Result()
// 域名
var domains = []string{}
if len(params.DomainsJSON) > 0 {
var rawDomains = []string{}
err := json.Unmarshal(params.DomainsJSON, &rawDomains)
if err != nil {
this.ErrorPage(err)
return
}
// TODO 如果用户填写了一个网址,应该分析域名并填入
domains = utils.NewStringsStream(rawDomains).
Map(strings.TrimSpace, strings.ToLower).
Filter(utils.FilterNotEmpty).
Unique().
Result()
}
switch params.Type {
case serverconfigs.HTTPAuthTypeTypeA:
params.Must.
Field("typeASecret", params.TypeASecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
Field("typeASignParamName", params.TypeASignParamName).
Require("请输入签名参数").
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线")
if params.TypeALife < 0 {
params.TypeALife = 0
}
method = &serverconfigs.HTTPAuthTypeAMethod{
Secret: params.TypeASecret,
SignParamName: params.TypeASignParamName,
Life: params.TypeALife,
}
case serverconfigs.HTTPAuthTypeTypeB:
params.Must.
Field("typeBSecret", params.TypeBSecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
method = &serverconfigs.HTTPAuthTypeBMethod{
Secret: params.TypeBSecret,
Life: params.TypeBLife,
}
case serverconfigs.HTTPAuthTypeTypeC:
params.Must.
Field("typeCSecret", params.TypeCSecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
method = &serverconfigs.HTTPAuthTypeCMethod{
Secret: params.TypeCSecret,
Life: params.TypeCLife,
}
case serverconfigs.HTTPAuthTypeTypeD:
params.Must.
Field("typeDSecret", params.TypeDSecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
Field("typeDSignParamName", params.TypeDSignParamName).
Require("请输入签名参数").
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线").
Field("typeDTimestampParamName", params.TypeDTimestampParamName).
Require("请输入时间戳参数").
Match(`^[a-zA-Z0-9_]{1,40}$`, "时间戳参数中只能包含字母、数字、下划线")
method = &serverconfigs.HTTPAuthTypeDMethod{
Secret: params.TypeDSecret,
SignParamName: params.TypeDSignParamName,
TimestampParamName: params.TypeDTimestampParamName,
Life: params.TypeDLife,
}
case serverconfigs.HTTPAuthTypeBasicAuth:
var users = []*serverconfigs.HTTPAuthBasicMethodUser{}
err := json.Unmarshal(params.HttpAuthBasicAuthUsersJSON, &users)
if err != nil {
this.ErrorPage(err)
return
}
if len(users) == 0 {
this.Fail("请添加至少一个用户")
}
method = &serverconfigs.HTTPAuthBasicMethod{
Users: users,
Realm: params.BasicAuthRealm,
Charset: params.BasicAuthCharset,
}
case serverconfigs.HTTPAuthTypeSubRequest:
params.Must.Field("subRequestURL", params.SubRequestURL).
Require("请输入子请求URL")
if params.SubRequestFollowRequest {
params.SubRequestMethod = ""
}
method = &serverconfigs.HTTPAuthSubRequestMethod{
URL: params.SubRequestURL,
Method: params.SubRequestMethod,
}
default:
this.Fail("不支持的鉴权类型'" + params.Type + "'")
}
if method == nil {
this.Fail("找不到对应的鉴权方式")
}
method.SetExts(exts)
method.SetDomains(domains)
methodJSON, err := json.Marshal(method)
if err != nil {
this.ErrorPage(err)
return
}
var paramsMap = maps.Map{}
err = json.Unmarshal(methodJSON, &paramsMap)
if err != nil {
this.ErrorPage(err)
return
}
createResp, err := this.RPC().HTTPAuthPolicyRPC().CreateHTTPAuthPolicy(this.UserContext(), &pb.CreateHTTPAuthPolicyRequest{
Name: params.Name,
Type: params.Type,
ParamsJSON: methodJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
defer this.CreateLogInfo(codes.HTTPAuthPolicy_LogCreateHTTPAuthPolicy, createResp.HttpAuthPolicyId)
ref.AuthPolicyId = createResp.HttpAuthPolicyId
ref.AuthPolicy = &serverconfigs.HTTPAuthPolicy{
Id: createResp.HttpAuthPolicyId,
Name: params.Name,
IsOn: true,
Type: params.Type,
Params: paramsMap,
}
this.Data["policyRef"] = ref
this.Success()
}

View File

@@ -0,0 +1,91 @@
package access
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("access")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerAuth, params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["authConfig"] = webConfig.Auth
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
AuthJSON []byte
Must *actions.Must
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerAuth, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerAuth_LogUpdateHTTPAuthSettings, params.WebId)
var authConfig = &serverconfigs.HTTPAuthConfig{}
err = json.Unmarshal(params.AuthJSON, authConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = authConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
// 保存之前删除多于的配置信息
for _, ref := range authConfig.PolicyRefs {
ref.AuthPolicy = nil
}
configJSON, err := json.Marshal(authConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAuth(this.UserContext(), &pb.UpdateHTTPWebAuthRequest{
HttpWebId: params.WebId,
AuthJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,21 @@
package access
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/access").
GetPost("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/random", new(RandomAction)).
EndAll()
})
}

View File

@@ -0,0 +1,18 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package access
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/rands"
)
type RandomAction struct {
actionutils.ParentAction
}
func (this *RandomAction) RunPost(params struct{}) {
this.Data["random"] = rands.HexString(32)
this.Success()
}

View File

@@ -0,0 +1,282 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package access
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
teaconst "github.com/TeaOSLab/EdgeUser/internal/const"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
PolicyId int64
}) {
var authMethods = serverconfigs.FindAllHTTPAuthTypes(teaconst.Role)
this.Data["authTypes"] = authMethods
policyResp, err := this.RPC().HTTPAuthPolicyRPC().FindEnabledHTTPAuthPolicy(this.UserContext(), &pb.FindEnabledHTTPAuthPolicyRequest{HttpAuthPolicyId: params.PolicyId})
if err != nil {
this.ErrorPage(err)
return
}
policy := policyResp.HttpAuthPolicy
if policy == nil {
this.NotFound("httpAuthPolicy", params.PolicyId)
return
}
var authParams = map[string]interface{}{}
if len(policy.ParamsJSON) > 0 {
err = json.Unmarshal(policy.ParamsJSON, &authParams)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["policy"] = maps.Map{
"id": policy.Id,
"isOn": policy.IsOn,
"name": policy.Name,
"type": policy.Type,
"params": authParams,
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
PolicyId int64
Name string
IsOn bool
// TypeA
TypeASecret string
TypeASignParamName string
TypeALife int
// TypeB
TypeBSecret string
TypeBLife int
// TypeC
TypeCSecret string
TypeCLife int
// TypeD
TypeDSecret string
TypeDSignParamName string
TypeDTimestampParamName string
TypeDLife int
// BasicAuth
HttpAuthBasicAuthUsersJSON []byte
BasicAuthRealm string
BasicAuthCharset string
// SubRequest
SubRequestURL string
SubRequestMethod string
SubRequestFollowRequest bool
Exts []string
DomainsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.HTTPAuthPolicy_LogUpdateHTTPAuthPolicy, params.PolicyId)
policyResp, err := this.RPC().HTTPAuthPolicyRPC().FindEnabledHTTPAuthPolicy(this.UserContext(), &pb.FindEnabledHTTPAuthPolicyRequest{HttpAuthPolicyId: params.PolicyId})
if err != nil {
this.ErrorPage(err)
return
}
policy := policyResp.HttpAuthPolicy
if policy == nil {
this.NotFound("httpAuthPolicy", params.PolicyId)
return
}
policyType := policy.Type
params.Must.
Field("name", params.Name).
Require("请输入名称")
var ref = &serverconfigs.HTTPAuthPolicyRef{IsOn: true}
var method serverconfigs.HTTPAuthMethodInterface
// 扩展名
var exts = utils.NewStringsStream(params.Exts).
Map(strings.TrimSpace, strings.ToLower).
Filter(utils.FilterNotEmpty).
Map(utils.MapAddPrefixFunc(".")).
Unique().
Result()
// 域名
var domains = []string{}
if len(params.DomainsJSON) > 0 {
var rawDomains = []string{}
err := json.Unmarshal(params.DomainsJSON, &rawDomains)
if err != nil {
this.ErrorPage(err)
return
}
// TODO 如果用户填写了一个网址,应该分析域名并填入
domains = utils.NewStringsStream(rawDomains).
Map(strings.TrimSpace, strings.ToLower).
Filter(utils.FilterNotEmpty).
Unique().
Result()
}
switch policyType {
case serverconfigs.HTTPAuthTypeTypeA:
params.Must.
Field("typeASecret", params.TypeASecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
Field("typeASignParamName", params.TypeASignParamName).
Require("请输入签名参数").
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线")
if params.TypeALife < 0 {
params.TypeALife = 0
}
method = &serverconfigs.HTTPAuthTypeAMethod{
Secret: params.TypeASecret,
SignParamName: params.TypeASignParamName,
Life: params.TypeALife,
}
case serverconfigs.HTTPAuthTypeTypeB:
params.Must.
Field("typeBSecret", params.TypeBSecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
method = &serverconfigs.HTTPAuthTypeBMethod{
Secret: params.TypeBSecret,
Life: params.TypeBLife,
}
case serverconfigs.HTTPAuthTypeTypeC:
params.Must.
Field("typeCSecret", params.TypeCSecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字")
method = &serverconfigs.HTTPAuthTypeCMethod{
Secret: params.TypeCSecret,
Life: params.TypeCLife,
}
case serverconfigs.HTTPAuthTypeTypeD:
params.Must.
Field("typeDSecret", params.TypeDSecret).
Require("请输入鉴权密钥").
MaxLength(40, "鉴权密钥不能超过40个字符").
Match(`^[a-zA-Z0-9]{1,40}$`, "鉴权密钥中只能包含字母、数字").
Field("typeDSignParamName", params.TypeDSignParamName).
Require("请输入签名参数").
Match(`^[a-zA-Z0-9_]{1,40}$`, "签名参数中只能包含字母、数字、下划线").
Field("typeDTimestampParamName", params.TypeDTimestampParamName).
Require("请输入时间戳参数").
Match(`^[a-zA-Z0-9_]{1,40}$`, "时间戳参数中只能包含字母、数字、下划线")
method = &serverconfigs.HTTPAuthTypeDMethod{
Secret: params.TypeDSecret,
SignParamName: params.TypeDSignParamName,
TimestampParamName: params.TypeDTimestampParamName,
Life: params.TypeDLife,
}
case serverconfigs.HTTPAuthTypeBasicAuth:
users := []*serverconfigs.HTTPAuthBasicMethodUser{}
err := json.Unmarshal(params.HttpAuthBasicAuthUsersJSON, &users)
if err != nil {
this.ErrorPage(err)
return
}
if len(users) == 0 {
this.Fail("请添加至少一个用户")
}
method = &serverconfigs.HTTPAuthBasicMethod{
Users: users,
Realm: params.BasicAuthRealm,
Charset: params.BasicAuthCharset,
}
case serverconfigs.HTTPAuthTypeSubRequest:
params.Must.Field("subRequestURL", params.SubRequestURL).
Require("请输入子请求URL")
if params.SubRequestFollowRequest {
params.SubRequestMethod = ""
}
method = &serverconfigs.HTTPAuthSubRequestMethod{
URL: params.SubRequestURL,
Method: params.SubRequestMethod,
}
default:
this.Fail("不支持的鉴权类型'" + policyType + "'")
}
if method == nil {
this.Fail("找不到鉴权方法")
}
method.SetExts(exts)
method.SetDomains(domains)
methodJSON, err := json.Marshal(method)
if err != nil {
this.ErrorPage(err)
return
}
var paramsMap map[string]interface{}
err = json.Unmarshal(methodJSON, &paramsMap)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPAuthPolicyRPC().UpdateHTTPAuthPolicy(this.UserContext(), &pb.UpdateHTTPAuthPolicyRequest{
HttpAuthPolicyId: params.PolicyId,
Name: params.Name,
ParamsJSON: methodJSON,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
ref.AuthPolicy = &serverconfigs.HTTPAuthPolicy{
Id: params.PolicyId,
Name: params.Name,
IsOn: params.IsOn,
Type: policyType,
Params: paramsMap,
}
this.Data["policyRef"] = ref
this.Success()
}

View File

@@ -0,0 +1,80 @@
package accessLog
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("accessLog")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerAccessLog, params.ServerId)
// 获取配置
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["accessLogConfig"] = webConfig.AccessLogRef
// 可选的缓存策略
this.Data["accessLogPolicies"] = []maps.Map{}
// 通用变量
this.Data["fields"] = serverconfigs.HTTPAccessLogShortFields
this.Data["defaultFieldCodes"] = serverconfigs.HTTPAccessLogDefaultFieldsCodes
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
AccessLogJSON []byte
Must *actions.Must
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerAccessLog, serverId) {
this.Fail("permission denied")
return
}
// 日志
defer this.CreateLogInfo(codes.ServerAccessLog_LogUpdateAccessLogSetting, params.WebId)
// TODO 检查参数
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.UserContext(), &pb.UpdateHTTPWebAccessLogRequest{
HttpWebId: params.WebId,
AccessLogJSON: params.AccessLogJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package accessLog
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/accessLog").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,95 @@
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"strings"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
IsReverse bool
}) {
this.Data["isReverse"] = params.IsReverse
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
CacheRefJSON []byte
CondType string
CondJSON []byte
CondIsCaseInsensitive bool
Must *actions.Must
}) {
var cacheRef = &serverconfigs.HTTPCacheRef{}
err := json.Unmarshal(params.CacheRefJSON, cacheRef)
if err != nil {
this.Fail("解析条件出错:" + err.Error() + ", JSON: " + string(params.CacheRefJSON))
return
}
if len(params.CondJSON) > 0 {
var cond = &shared.HTTPRequestCond{}
err = json.Unmarshal(params.CondJSON, cond)
if err != nil {
this.Fail("解析条件出错:" + err.Error() + ", JSON: " + string(params.CondJSON))
return
}
cond.Type = params.CondType
cond.IsCaseInsensitive = params.CondIsCaseInsensitive
cacheRef.SimpleCond = cond
// 将组合条件置为空
cacheRef.Conds = &shared.HTTPRequestCondsConfig{}
}
err = cacheRef.Init()
if err != nil {
this.Fail("解析条件出错:" + err.Error())
return
}
if len(cacheRef.Key) == 0 {
this.Fail("请输入缓存Key")
}
if !strings.HasPrefix(cacheRef.Key, "${host}") &&
!strings.HasPrefix(cacheRef.Key, "${scheme}://${host}") &&
!strings.HasPrefix(cacheRef.Key, "//${host}") {
this.Fail("缓存Key必须以${host}或${scheme}://${host}开头")
}
if (cacheRef.Conds == nil || len(cacheRef.Conds.Groups) == 0) && cacheRef.SimpleCond == nil {
this.Fail("请填写匹配条件分组")
}
this.Data["cacheRef"] = cacheRef
cacheRefClone, err := utils.JSONClone(cacheRef)
if err != nil {
this.Fail(err.Error())
}
err = cacheRefClone.(*serverconfigs.HTTPCacheRef).Init()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["cacheRef"] = cacheRef
this.Success()
}

View File

@@ -0,0 +1,152 @@
package cache
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"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/utils/domainutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("cache")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["cacheConfig"] = webConfig.Cache
// 当前集群的缓存策略
cachePolicy, err := dao.SharedHTTPCachePolicyDAO.FindEnabledHTTPCachePolicyWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
if cachePolicy != nil {
var maxBytes = &shared.SizeCapacity{}
if !utils.JSONIsNull(cachePolicy.MaxBytesJSON) {
err = json.Unmarshal(cachePolicy.MaxBytesJSON, maxBytes)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["cachePolicy"] = maps.Map{
"id": cachePolicy.Id,
"name": cachePolicy.Name,
"isOn": cachePolicy.IsOn,
"maxBytes": maxBytes,
}
} else {
this.Data["cachePolicy"] = nil
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
CacheJSON []byte
Must *actions.Must
}) {
// 日志
defer this.CreateLogInfo(codes.ServerCache_LogUpdateCacheSettings, params.WebId)
var cacheConfig = &serverconfigs.HTTPCacheConfig{}
err := json.Unmarshal(params.CacheJSON, cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
// 检查Key
if cacheConfig.Key != nil && cacheConfig.Key.IsOn {
if cacheConfig.Key.Scheme != "http" && cacheConfig.Key.Scheme != "https" {
this.Fail("缓存主域名协议只能是http或者https")
return
}
if len(cacheConfig.Key.Host) == 0 {
this.Fail("请输入缓存主域名")
return
}
cacheConfig.Key.Host = strings.ToLower(strings.TrimSuffix(cacheConfig.Key.Host, "/"))
if !domainutils.ValidateDomainFormat(cacheConfig.Key.Host) {
this.Fail("请输入正确的缓存主域名")
return
}
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if serverId <= 0 {
this.Fail("找不到要操作的网站")
return
}
existServerNameResp, err := this.RPC().ServerRPC().CheckServerNameInServer(this.UserContext(), &pb.CheckServerNameInServerRequest{
ServerId: serverId,
ServerName: cacheConfig.Key.Host,
})
if err != nil {
this.ErrorPage(err)
return
}
if !existServerNameResp.Exists {
this.Fail("域名 '" + cacheConfig.Key.Host + "' 在当前网站中并未绑定,不能作为缓存主域名")
return
}
}
// 去除不必要的部分
for _, cacheRef := range cacheConfig.CacheRefs {
cacheRef.CachePolicy = nil
}
err = cacheConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
cacheJSON, err := json.Marshal(cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.UserContext(), &pb.UpdateHTTPWebCacheRequest{
HttpWebId: params.WebId,
CacheJSON: cacheJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,20 @@
package cache
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/cache").
GetPost("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
Post("/updateRefs", new(UpdateRefsAction)).
EndAll()
})
}

View File

@@ -0,0 +1,84 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package cache
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type UpdateRefsAction struct {
actionutils.ParentAction
}
func (this *UpdateRefsAction) RunPost(params struct {
WebId int64
RefsJSON []byte
}) {
// 日志
defer this.CreateLogInfo(codes.ServerCache_LogUpdateCacheSettings, params.WebId)
this.Data["isUpdated"] = false
webConfigResp, err := this.RPC().HTTPWebRPC().FindEnabledHTTPWebConfig(this.UserContext(), &pb.FindEnabledHTTPWebConfigRequest{HttpWebId: params.WebId})
if err != nil {
this.ErrorPage(err)
return
}
var webConfig = &serverconfigs.HTTPWebConfig{}
err = json.Unmarshal(webConfigResp.HttpWebJSON, webConfig)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["cacheConfig"] = webConfig.Cache
// 校验配置
var cacheConfig = webConfig.Cache
if cacheConfig == nil {
this.Success()
return
}
var refs = []*serverconfigs.HTTPCacheRef{}
err = json.Unmarshal(params.RefsJSON, &refs)
if err != nil {
this.ErrorPage(errors.New("decode refs json failed: " + err.Error()))
return
}
cacheConfig.CacheRefs = refs
err = cacheConfig.Init()
if err != nil {
this.Fail("检查配置失败:" + err.Error())
}
// 去除不必要的部分
for _, cacheRef := range cacheConfig.CacheRefs {
cacheRef.CachePolicy = nil
}
cacheJSON, err := json.Marshal(cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.UserContext(), &pb.UpdateHTTPWebCacheRequest{
HttpWebId: params.WebId,
CacheJSON: cacheJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["isUpdated"] = true
this.Success()
}

View File

@@ -0,0 +1,105 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package cc
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("cc")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
// 校验权限
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerCC, params.ServerId)
this.Data["serverId"] = params.ServerId
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
var ccConfig = serverconfigs.DefaultHTTPCCConfig()
resp, err := this.RPC().HTTPWebRPC().FindHTTPWebCC(this.UserContext(), &pb.FindHTTPWebCCRequest{HttpWebId: webConfig.Id})
if err != nil {
this.ErrorPage(err)
return
}
if len(resp.CcJSON) > 0 {
err = json.Unmarshal(resp.CcJSON, ccConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["ccConfig"] = ccConfig
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
CcJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerCC, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerCC_LogUpdateCCSettings, params.WebId)
// 校验配置
var ccConfig = serverconfigs.DefaultHTTPCCConfig()
err = json.Unmarshal(params.CcJSON, ccConfig)
if err != nil {
this.Fail("配置解析失败:" + err.Error())
return
}
err = ccConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCC(this.UserContext(), &pb.UpdateHTTPWebCCRequest{
HttpWebId: params.WebId,
CcJSON: params.CcJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,20 @@
//go:build plus
package cc
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/cc").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

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

View File

@@ -0,0 +1,57 @@
package charset
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"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"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("charset")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["charsetConfig"] = webConfig.Charset
this.Data["usualCharsets"] = configutils.UsualCharsets
this.Data["allCharsets"] = configutils.AllCharsets
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
CharsetJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.ServerCharset_LogUpdateCharsetSetting, params.WebId)
_, err := this.RPC().HTTPWebRPC().UpdateHTTPWebCharset(this.UserContext(), &pb.UpdateHTTPWebCharsetRequest{
HttpWebId: params.WebId,
CharsetJSON: params.CharsetJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package charset
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/charset").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,99 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package compression
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("compression")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerCompression, params.ServerId)
// 服务分组设置
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(this.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasGroupConfig"] = groupResp.HasCompressionConfig
this.Data["groupSettingURL"] = "/servers/groups/group/settings/compression?groupId=" + types.String(groupResp.ServerGroupId)
// WebId
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["compressionConfig"] = webConfig.Compression
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
CompressionJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerCompression, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerCompression_LogUpdateCompressionSettings, params.WebId)
// 校验配置
var compressionConfig = &serverconfigs.HTTPCompressionConfig{}
err = json.Unmarshal(params.CompressionJSON, compressionConfig)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
err = compressionConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCompression(this.UserContext(), &pb.UpdateHTTPWebCompressionRequest{
HttpWebId: params.WebId,
CompressionJSON: params.CompressionJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package compression
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/compression").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,43 @@
package conds
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/server/settings/conds/condutils"
"github.com/iwind/TeaGo/actions"
)
type AddCondPopupAction struct {
actionutils.ParentAction
}
func (this *AddCondPopupAction) Init() {
}
func (this *AddCondPopupAction) RunGet(params struct{}) {
this.Data["components"] = condutils.ReadAllAvailableCondTypes()
this.Show()
}
func (this *AddCondPopupAction) RunPost(params struct {
CondType string
CondJSON []byte
Must *actions.Must
}) {
condConfig := &shared.HTTPRequestCond{}
err := json.Unmarshal(params.CondJSON, condConfig)
if err != nil {
this.Fail("解析条件设置时发生了错误:" + err.Error() + ", JSON: " + string(params.CondJSON))
}
err = condConfig.Init()
if err != nil {
this.Fail("校验条件设置时失败:" + err.Error())
}
condConfig.Type = params.CondType
this.Data["cond"] = condConfig
this.Success()
}

View File

@@ -0,0 +1,42 @@
package conds
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/server/settings/conds/condutils"
"github.com/iwind/TeaGo/actions"
)
type AddGroupPopupAction struct {
actionutils.ParentAction
}
func (this *AddGroupPopupAction) Init() {
}
func (this *AddGroupPopupAction) RunGet(params struct{}) {
this.Data["components"] = condutils.ReadAllAvailableCondTypes()
this.Show()
}
func (this *AddGroupPopupAction) RunPost(params struct {
CondGroupJSON []byte
Must *actions.Must
}) {
groupConfig := &shared.HTTPRequestCondGroup{}
err := json.Unmarshal(params.CondGroupJSON, groupConfig)
if err != nil {
this.Fail("解析条件时发生错误:" + err.Error())
}
err = groupConfig.Init()
if err != nil {
this.Fail("校验条件设置时失败:" + err.Error())
}
this.Data["group"] = groupConfig
this.Success()
}

View File

@@ -0,0 +1,51 @@
package condutils
import (
"encoding/json"
"github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/files"
"github.com/iwind/TeaGo/logs"
"path/filepath"
)
type CondJSComponent struct {
Type string `json:"type"`
Name string `json:"name"`
Description string `json:"description"`
Component string `json:"component"`
ParamsTitle string `json:"paramsTitle"`
IsRequest bool `json:"isRequest"`
CaseInsensitive bool `json:"caseInsensitive"`
}
// ReadAllAvailableCondTypes 读取所有可用的条件
func ReadAllAvailableCondTypes() []*CondJSComponent {
result := []*CondJSComponent{}
dir := Tea.Root + "/web/"
if Tea.IsTesting() {
dir = filepath.Dir(Tea.Root) + "/web"
}
dir += "/public/js/conds/"
jsonFiles := files.NewFile(dir).List()
for _, file := range jsonFiles {
if file.Ext() == ".json" {
data, err := file.ReadAll()
if err != nil {
logs.Println("[COND]read data from json file: " + err.Error())
continue
}
c := []*CondJSComponent{}
err = json.Unmarshal(data, &c)
if err != nil {
logs.Println("[COND]decode json failed: " + err.Error())
continue
}
result = append(result, c...)
}
}
return result
}

View File

@@ -0,0 +1,7 @@
package condutils
import "testing"
func TestReadAllAvailableCondTypes(t *testing.T) {
t.Log(ReadAllAvailableCondTypes())
}

View File

@@ -0,0 +1,17 @@
package conds
import (
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Prefix("/servers/server/settings/conds").
GetPost("/addGroupPopup", new(AddGroupPopupAction)).
GetPost("/addCondPopup", new(AddCondPopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,76 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package copys
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/types"
"strings"
)
// CountServersAction 通过参数读取对应服务数量
type CountServersAction struct {
actionutils.ParentAction
}
func (this *CountServersAction) RunPost(params struct {
Targets []string
}) {
this.Data["countServers"] = -1
var countServers int64 = 0
var hasValidTargets = false
for _, target := range params.Targets {
targetType, targetValue, found := strings.Cut(target, ":")
if !found {
continue
}
switch targetType {
case "servers":
var targetServerIds = utils.SplitNumbers(targetValue)
if len(targetServerIds) == 0 {
continue
}
countServers += int64(len(targetServerIds))
hasValidTargets = true
case "groups":
var targetGroupIds = utils.SplitNumbers(targetValue)
if len(targetGroupIds) == 0 {
continue
}
for _, groupId := range targetGroupIds {
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithServerGroupId(this.UserContext(), &pb.CountAllEnabledServersWithServerGroupIdRequest{ServerGroupId: groupId})
if err != nil {
this.ErrorPage(err)
return
}
countServers += countResp.Count
}
hasValidTargets = true
case "user":
var targetUserId = types.Int64(targetValue)
if targetUserId <= 0 {
continue
}
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersMatch(this.UserContext(), &pb.CountAllEnabledServersMatchRequest{UserId: targetUserId})
if err != nil {
this.ErrorPage(err)
return
}
countServers += countResp.Count
hasValidTargets = true
}
if !hasValidTargets {
this.Data["countServers"] = -1
} else {
this.Data["countServers"] = countServers
}
this.Success()
}
}

View File

@@ -0,0 +1,188 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package copys
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
ConfigCode string
}) {
this.Data["serverId"] = params.ServerId
this.Data["configCode"] = params.ConfigCode
this.Data["userId"] = 0
var targetOptions = []maps.Map{}
// 当前分组
serverResp, err := this.RPC().ServerRPC().FindEnabledUserServerBasic(this.UserContext(), &pb.FindEnabledUserServerBasicRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server == nil {
this.NotFound("server", params.ServerId)
return
}
if len(server.ServerGroups) > 0 {
for _, group := range server.ServerGroups {
if server.UserId <= 0 || server.UserId == group.UserId { // 确保分组是用户自己的
targetOptions = append(targetOptions, maps.Map{
"code": "group:" + types.String(group.Id),
"name": "当前分组:" + group.Name,
})
}
}
}
// 当前用户
this.Data["userGroups"] = []maps.Map{}
if server.UserId > 0 {
this.Data["userId"] = server.UserId
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.UserContext(), &pb.FindEnabledUserRequest{UserId: server.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user != nil {
targetOptions = append(targetOptions, maps.Map{
"code": "user:" + types.String(user.Id),
"name": "当前用户",
})
}
// 当前用户的分组
groupsResp, err := this.RPC().ServerGroupRPC().FindAllEnabledServerGroups(this.UserContext(), &pb.FindAllEnabledServerGroupsRequest{
UserId: server.UserId,
})
if err != nil {
this.ErrorPage(err)
return
}
var userGroupMaps = []maps.Map{}
for _, group := range groupsResp.ServerGroups {
if !group.IsOn {
continue
}
userGroupMaps = append(userGroupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
})
}
this.Data["userGroups"] = userGroupMaps
}
// 其他
// TODO 支持选择服务
targetOptions = append(targetOptions, maps.Map{
"code": "group",
"name": "选择分组",
}, /**, maps.Map{
"code": "server",
"name": "选择服务",
}**/)
this.Data["targetOptions"] = targetOptions
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
ConfigCode string
TargetsJSON []byte
// waf params
WafCopyRegions bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.Server_LogCopyServerConfigs, params.ServerId, params.ConfigCode)
var targets = []string{}
err := json.Unmarshal(params.TargetsJSON, &targets)
if err != nil {
this.Fail("解析复制目标失败:" + err.Error())
return
}
if len(targets) == 0 {
this.Fail("请选择要复制的目标")
return
}
for _, target := range targets {
targetType, targetValue, found := strings.Cut(target, ":")
if !found {
this.Fail("错误的目标格式:" + target)
return
}
var targetServerIds = []int64{}
var targetGroupIds = []int64{}
var targetClusterId int64
var targetUserId int64
switch targetType {
case "servers":
targetServerIds = utils.SplitNumbers(targetValue)
if len(targetServerIds) == 0 {
continue
}
case "groups":
targetGroupIds = utils.SplitNumbers(targetValue)
if len(targetGroupIds) == 0 {
continue
}
case "user":
targetUserId = types.Int64(targetValue)
if targetUserId <= 0 {
continue
}
default:
this.Fail("错误的目标格式:" + target)
return
}
_, err := this.RPC().ServerRPC().CopyServerConfig(this.UserContext(), &pb.CopyServerConfigRequest{
ServerId: params.ServerId,
ConfigCode: params.ConfigCode,
TargetType: targetType,
TargetServerIds: targetServerIds,
TargetServerGroupIds: targetGroupIds,
TargetClusterId: targetClusterId,
TargetUserId: targetUserId,
WafCopyRegions: params.WafCopyRegions,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,19 @@
package copys
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/copy").
GetPost("", new(IndexAction)).
Post("/countServers", new(CountServersAction)).
EndAll()
})
}

View File

@@ -0,0 +1,33 @@
package dns
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("dns")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
dnsInfoResp, err := this.RPC().ServerRPC().FindEnabledServerDNS(this.UserContext(), &pb.FindEnabledServerDNSRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["dnsName"] = dnsInfoResp.DnsName
if dnsInfoResp.Domain != nil {
this.Data["dnsDomain"] = dnsInfoResp.Domain.Name
} else {
this.Data["dnsDomain"] = ""
}
this.Show()
}

View File

@@ -0,0 +1,18 @@
package dns
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/dns").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,75 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package groups
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("group")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["serverId"] = params.ServerId
serverResp, err := this.RPC().ServerRPC().FindEnabledServer(this.UserContext(), &pb.FindEnabledServerRequest{
ServerId: params.ServerId,
IgnoreSSLCerts: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var server = serverResp.Server
if server == nil {
this.NotFound("server", params.ServerId)
return
}
var groupMaps = []maps.Map{}
for _, group := range server.ServerGroups {
if group.UserId != this.UserId() {
continue
}
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
})
}
this.Data["groups"] = groupMaps
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
GroupIds []int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.Server_LogUpdateServerGroups, params.ServerId)
_, err := this.RPC().ServerRPC().UpdateServerGroupIds(this.UserContext(), &pb.UpdateServerGroupIdsRequest{
ServerId: params.ServerId,
ServerGroupIds: params.GroupIds,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package groups
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/groups").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,67 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type CreateDeletePopupAction struct {
actionutils.ParentAction
}
func (this *CreateDeletePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateDeletePopupAction) RunGet(params struct {
HeaderPolicyId int64
Type string
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
this.Data["type"] = params.Type
this.Show()
}
func (this *CreateDeletePopupAction) RunPost(params struct {
HeaderPolicyId int64
Name string
Must *actions.Must
}) {
// 日志
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogCreateDeletingHeader, params.HeaderPolicyId, params.Name)
params.Must.
Field("name", params.Name).
Require("名称不能为空")
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
var deleteHeaders = policyConfig.DeleteHeaders
deleteHeaders = append(deleteHeaders, params.Name)
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyDeletingHeaders(this.UserContext(), &pb.UpdateHTTPHeaderPolicyDeletingHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: deleteHeaders,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,67 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type CreateNonStandardPopupAction struct {
actionutils.ParentAction
}
func (this *CreateNonStandardPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateNonStandardPopupAction) RunGet(params struct {
HeaderPolicyId int64
Type string
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
this.Data["type"] = params.Type
this.Show()
}
func (this *CreateNonStandardPopupAction) RunPost(params struct {
HeaderPolicyId int64
Name string
Must *actions.Must
}) {
// 日志
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogCreateNonStandardHeader, params.HeaderPolicyId, params.Name)
params.Must.
Field("name", params.Name).
Require("名称不能为空")
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
var nonStandardHeaders = policyConfig.NonStandardHeaders
nonStandardHeaders = append(nonStandardHeaders, params.Name)
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyNonStandardHeaders(this.UserContext(), &pb.UpdateHTTPHeaderPolicyNonStandardHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: nonStandardHeaders,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,149 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type CreateSetPopupAction struct {
actionutils.ParentAction
}
func (this *CreateSetPopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreateSetPopupAction) RunGet(params struct {
HeaderPolicyId int64
Type string
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
this.Data["type"] = params.Type
this.Show()
}
func (this *CreateSetPopupAction) RunPost(params struct {
Type string
HeaderPolicyId int64
Name string
Value string
StatusListJSON []byte
MethodsJSON []byte
DomainsJSON []byte
ShouldAppend bool
DisableRedirect bool
ShouldReplace bool
ReplaceValuesJSON []byte
Must *actions.Must
}) {
// 日志
if params.Type == "request" {
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogCreateSettingRequestHeader, params.HeaderPolicyId, params.Name, params.Value)
} else {
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogCreateSettingResponseHeader, params.HeaderPolicyId, params.Name, params.Value)
}
params.Must.
Field("name", params.Name).
Require("请输入Header名称")
configResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
policyConfig := &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(configResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
// status list
var statusList = []int32{}
if len(params.StatusListJSON) > 0 {
err = json.Unmarshal(params.StatusListJSON, &statusList)
if err != nil {
this.ErrorPage(err)
return
}
}
// methods
var methods = []string{}
if len(params.MethodsJSON) > 0 {
err = json.Unmarshal(params.MethodsJSON, &methods)
if err != nil {
this.ErrorPage(err)
return
}
}
// domains
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err = json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.ErrorPage(err)
return
}
}
// replace values
var replaceValues = []*shared.HTTPHeaderReplaceValue{}
if len(params.ReplaceValuesJSON) > 0 {
err = json.Unmarshal(params.ReplaceValuesJSON, &replaceValues)
if err != nil {
this.ErrorPage(err)
return
}
}
// 创建Header
createHeaderResp, err := this.RPC().HTTPHeaderRPC().CreateHTTPHeader(this.UserContext(), &pb.CreateHTTPHeaderRequest{
Name: params.Name,
Value: params.Value,
Status: statusList,
Methods: methods,
Domains: domains,
ShouldAppend: params.ShouldAppend,
DisableRedirect: params.DisableRedirect,
ShouldReplace: params.ShouldReplace,
ReplaceValuesJSON: params.ReplaceValuesJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
headerId := createHeaderResp.HeaderId
// 保存
refs := policyConfig.SetHeaderRefs
refs = append(refs, &shared.HTTPHeaderRef{
IsOn: true,
HeaderId: headerId,
})
refsJSON, err := json.Marshal(refs)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicySettingHeaders(this.UserContext(), &pb.UpdateHTTPHeaderPolicySettingHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeadersJSON: refsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,61 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
// DeleteAction 删除Header
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
HeaderPolicyId int64
Type string
HeaderId int64
}) {
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogDeleteHeader, params.HeaderPolicyId, params.HeaderId)
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
})
if err != nil {
this.ErrorPage(err)
return
}
policyConfig := &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigResp.HttpHeaderPolicyJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
switch params.Type {
case "setHeader":
result := []*shared.HTTPHeaderRef{}
for _, h := range policyConfig.SetHeaderRefs {
if h.HeaderId != params.HeaderId {
result = append(result, h)
}
}
resultJSON, err := json.Marshal(result)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicySettingHeaders(this.UserContext(), &pb.UpdateHTTPHeaderPolicySettingHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeadersJSON: resultJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,52 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteDeletingHeaderAction struct {
actionutils.ParentAction
}
func (this *DeleteDeletingHeaderAction) RunPost(params struct {
HeaderPolicyId int64
HeaderName string
}) {
// 日志
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogDeleteDeletingHeader, params.HeaderPolicyId, params.HeaderName)
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policyConfigJSON = policyConfigResp.HttpHeaderPolicyJSON
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
var headerNames = []string{}
for _, h := range policyConfig.DeleteHeaders {
if h == params.HeaderName {
continue
}
headerNames = append(headerNames, h)
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyDeletingHeaders(this.UserContext(), &pb.UpdateHTTPHeaderPolicyDeletingHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: headerNames,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,52 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteNonStandardHeaderAction struct {
actionutils.ParentAction
}
func (this *DeleteNonStandardHeaderAction) RunPost(params struct {
HeaderPolicyId int64
HeaderName string
}) {
// 日志
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogDeleteNonStandardHeader, params.HeaderPolicyId, params.HeaderName)
policyConfigResp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var policyConfigJSON = policyConfigResp.HttpHeaderPolicyJSON
var policyConfig = &shared.HTTPHeaderPolicy{}
err = json.Unmarshal(policyConfigJSON, policyConfig)
if err != nil {
this.ErrorPage(err)
return
}
var headerNames = []string{}
for _, h := range policyConfig.NonStandardHeaders {
if h == params.HeaderName {
continue
}
headerNames = append(headerNames, h)
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyNonStandardHeaders(this.UserContext(), &pb.UpdateHTTPHeaderPolicyNonStandardHeadersRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
HeaderNames: headerNames,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,117 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("header")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerHTTPHeaders, params.ServerId)
// 服务分组设置
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(this.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasGroupRequestConfig"] = groupResp.HasRequestHeadersConfig
this.Data["hasGroupResponseConfig"] = groupResp.HasResponseHeadersConfig
this.Data["groupSettingURL"] = "/servers/groups/group/settings/headers?groupId=" + types.String(groupResp.ServerGroupId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
webId := webConfig.Id
isChanged := false
if webConfig.RequestHeaderPolicy == nil {
createHeaderPolicyResp, err := this.RPC().HTTPHeaderPolicyRPC().CreateHTTPHeaderPolicy(this.UserContext(), &pb.CreateHTTPHeaderPolicyRequest{})
if err != nil {
this.ErrorPage(err)
return
}
headerPolicyId := createHeaderPolicyResp.HttpHeaderPolicyId
ref := &shared.HTTPHeaderPolicyRef{
IsPrior: false,
IsOn: true,
HeaderPolicyId: headerPolicyId,
}
refJSON, err := json.Marshal(ref)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRequestHeader(this.UserContext(), &pb.UpdateHTTPWebRequestHeaderRequest{
HttpWebId: webId,
HeaderJSON: refJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
isChanged = true
}
if webConfig.ResponseHeaderPolicy == nil {
createHeaderPolicyResp, err := this.RPC().HTTPHeaderPolicyRPC().CreateHTTPHeaderPolicy(this.UserContext(), &pb.CreateHTTPHeaderPolicyRequest{})
if err != nil {
this.ErrorPage(err)
return
}
headerPolicyId := createHeaderPolicyResp.HttpHeaderPolicyId
ref := &shared.HTTPHeaderPolicyRef{
IsPrior: false,
IsOn: true,
HeaderPolicyId: headerPolicyId,
}
refJSON, err := json.Marshal(ref)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebResponseHeader(this.UserContext(), &pb.UpdateHTTPWebResponseHeaderRequest{
HttpWebId: webId,
HeaderJSON: refJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
isChanged = true
}
// 重新获取配置
if isChanged {
webConfig, err = dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["requestHeaderRef"] = webConfig.RequestHeaderPolicyRef
this.Data["requestHeaderPolicy"] = webConfig.RequestHeaderPolicy
this.Data["responseHeaderRef"] = webConfig.ResponseHeaderPolicyRef
this.Data["responseHeaderPolicy"] = webConfig.ResponseHeaderPolicy
this.Show()
}

View File

@@ -0,0 +1,26 @@
package headers
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/headers").
Get("", new(IndexAction)).
GetPost("/createSetPopup", new(CreateSetPopupAction)).
GetPost("/updateSetPopup", new(UpdateSetPopupAction)).
GetPost("/createDeletePopup", new(CreateDeletePopupAction)).
Post("/deleteDeletingHeader", new(DeleteDeletingHeaderAction)).
GetPost("/createNonStandardPopup", new(CreateNonStandardPopupAction)).
Post("/deleteNonStandardHeader", new(DeleteNonStandardHeaderAction)).
Post("/delete", new(DeleteAction)).
GetPost("/updateCORSPopup", new(UpdateCORSPopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,75 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type UpdateCORSPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateCORSPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateCORSPopupAction) RunGet(params struct {
HeaderPolicyId int64
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
resp, err := this.RPC().HTTPHeaderPolicyRPC().FindEnabledHTTPHeaderPolicyConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderPolicyConfigRequest{HttpHeaderPolicyId: params.HeaderPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
var headerPolicyJSON = resp.HttpHeaderPolicyJSON
var headerPolicy = &shared.HTTPHeaderPolicy{}
if len(headerPolicyJSON) > 0 {
err = json.Unmarshal(headerPolicyJSON, headerPolicy)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["cors"] = headerPolicy.CORS
this.Show()
}
func (this *UpdateCORSPopupAction) RunPost(params struct {
HeaderPolicyId int64
CorsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var config = shared.NewHTTPCORSHeaderConfig()
err := json.Unmarshal(params.CorsJSON, config)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
err = config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
_, err = this.RPC().HTTPHeaderPolicyRPC().UpdateHTTPHeaderPolicyCORS(this.UserContext(), &pb.UpdateHTTPHeaderPolicyCORSRequest{
HttpHeaderPolicyId: params.HeaderPolicyId,
CorsJSON: params.CorsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,124 @@
package headers
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type UpdateSetPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateSetPopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdateSetPopupAction) RunGet(params struct {
HeaderPolicyId int64
HeaderId int64
Type string
}) {
this.Data["headerPolicyId"] = params.HeaderPolicyId
this.Data["headerId"] = params.HeaderId
this.Data["type"] = params.Type
headerResp, err := this.RPC().HTTPHeaderRPC().FindEnabledHTTPHeaderConfig(this.UserContext(), &pb.FindEnabledHTTPHeaderConfigRequest{HeaderId: params.HeaderId})
if err != nil {
this.ErrorPage(err)
return
}
headerConfig := &shared.HTTPHeaderConfig{}
err = json.Unmarshal(headerResp.HeaderJSON, headerConfig)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["headerConfig"] = headerConfig
this.Show()
}
func (this *UpdateSetPopupAction) RunPost(params struct {
HeaderId int64
Name string
Value string
StatusListJSON []byte
MethodsJSON []byte
DomainsJSON []byte
ShouldAppend bool
DisableRedirect bool
ShouldReplace bool
ReplaceValuesJSON []byte
Must *actions.Must
}) {
// 日志
defer this.CreateLogInfo(codes.ServerHTTPHeader_LogUpdateSettingHeader, params.HeaderId, params.Name, params.Value)
params.Must.
Field("name", params.Name).
Require("请输入Header名称")
// status list
var statusList = []int32{}
if len(params.StatusListJSON) > 0 {
err := json.Unmarshal(params.StatusListJSON, &statusList)
if err != nil {
this.ErrorPage(err)
return
}
}
// methods
var methods = []string{}
if len(params.MethodsJSON) > 0 {
err := json.Unmarshal(params.MethodsJSON, &methods)
if err != nil {
this.ErrorPage(err)
return
}
}
// domains
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err := json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.ErrorPage(err)
return
}
}
// replace values
var replaceValues = []*shared.HTTPHeaderReplaceValue{}
if len(params.ReplaceValuesJSON) > 0 {
err := json.Unmarshal(params.ReplaceValuesJSON, &replaceValues)
if err != nil {
this.ErrorPage(err)
return
}
}
_, err := this.RPC().HTTPHeaderRPC().UpdateHTTPHeader(this.UserContext(), &pb.UpdateHTTPHeaderRequest{
HeaderId: params.HeaderId,
Name: params.Name,
Value: params.Value,
Status: statusList,
Methods: methods,
Domains: domains,
ShouldAppend: params.ShouldAppend,
DisableRedirect: params.DisableRedirect,
ShouldReplace: params.ShouldReplace,
ReplaceValuesJSON: params.ReplaceValuesJSON,
})
if err != nil {
this.ErrorPage(err)
}
this.Success()
}

View File

@@ -0,0 +1,126 @@
package http
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"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("http")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
serverConfig, err := dao.SharedServerDAO.FindEnabledServerConfig(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig == nil {
this.NotFound("server", params.ServerId)
return
}
httpConfig := serverConfig.HTTP
if httpConfig == nil {
httpConfig = &serverconfigs.HTTPProtocolConfig{}
httpConfig.IsOn = true
}
this.Data["serverType"] = serverConfig.Type
this.Data["httpConfig"] = maps.Map{
"isOn": httpConfig.IsOn,
"addresses": httpConfig.Listen,
}
// 跳转相关设置
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["redirectToHTTPSConfig"] = webConfig.RedirectToHttps
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
IsOn bool
Addresses string
WebId int64
RedirectToHTTPSJSON []byte
Must *actions.Must
}) {
// 记录日志
defer this.CreateLogInfo(codes.ServerHTTP_LogUpdateHTTPSettings, params.ServerId)
serverConfig, err := dao.SharedServerDAO.FindEnabledServerConfig(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig == nil {
this.NotFound("server", params.ServerId)
return
}
var httpConfig = serverConfig.HTTP
if httpConfig == nil {
httpConfig = &serverconfigs.HTTPProtocolConfig{}
}
httpConfig.IsOn = params.IsOn
// 如果启用HTTP时没有填写端口则默认为80
if params.IsOn && len(httpConfig.Listen) == 0 {
httpConfig.Listen = []*serverconfigs.NetworkAddressConfig{
{
Protocol: serverconfigs.ProtocolHTTP,
PortRange: "80",
},
}
}
configData, err := json.Marshal(httpConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().ServerRPC().UpdateServerHTTP(this.UserContext(), &pb.UpdateServerHTTPRequest{
ServerId: params.ServerId,
HttpJSON: configData,
})
if err != nil {
this.ErrorPage(err)
return
}
// 设置跳转到HTTPS
// TODO 校验设置
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRedirectToHTTPS(this.UserContext(), &pb.UpdateHTTPWebRedirectToHTTPSRequest{
HttpWebId: params.WebId,
RedirectToHTTPSJSON: params.RedirectToHTTPSJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package http
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/http").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,266 @@
package https
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"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"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("https")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["http3FeatureEnabled"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerHTTP3, params.ServerId)
serverConfig, err := dao.SharedServerDAO.FindEnabledServerConfig(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig == nil {
this.NotFound("server", params.ServerId)
return
}
var httpsConfig = serverConfig.HTTPS
if httpsConfig == nil {
httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
}
var sslPolicy *sslconfigs.SSLPolicy
var allDNSNames []string
if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 {
sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.UserContext(), &pb.FindEnabledSSLPolicyConfigRequest{
SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId,
IgnoreData: true,
})
if err != nil {
this.ErrorPage(err)
return
}
sslPolicyConfigJSON := sslPolicyConfigResp.SslPolicyJSON
if len(sslPolicyConfigJSON) > 0 {
sslPolicy = &sslconfigs.SSLPolicy{}
err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy)
if err != nil {
this.ErrorPage(err)
return
}
for _, cert := range sslPolicy.Certs {
for _, dnsName := range cert.DNSNames {
if !lists.ContainsString(allDNSNames, dnsName) {
allDNSNames = append(allDNSNames, dnsName)
}
}
}
}
}
// 当前集群是否支持HTTP/3
// TODO 检查当前服务所属用户是否支持HTTP/3
http3PolicyResp, err := this.RPC().NodeClusterRPC().FindNodeClusterHTTP3Policy(this.UserContext(), &pb.FindNodeClusterHTTP3PolicyRequest{NodeClusterId: serverConfig.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
var supportsHTTP3 = false
if len(http3PolicyResp.Http3PolicyJSON) > 0 {
var http3Policy = nodeconfigs.NewHTTP3Policy()
err = json.Unmarshal(http3PolicyResp.Http3PolicyJSON, http3Policy)
if err != nil {
this.ErrorPage(err)
return
}
supportsHTTP3 = http3Policy.IsOn
}
this.Data["serverType"] = serverConfig.Type
this.Data["httpsConfig"] = maps.Map{
"isOn": httpsConfig.IsOn,
"sslPolicy": sslPolicy,
"supportsHTTP3": supportsHTTP3,
}
// 检查域名是否都已经上传了证书
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.UserContext(), &pb.FindServerNamesRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
var allServerNames []string
if len(serverNamesResp.ServerNamesJSON) > 0 {
var serverNamesConfigs = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(serverNamesResp.ServerNamesJSON, &serverNamesConfigs)
if err != nil {
this.ErrorPage(err)
return
}
allServerNames = serverconfigs.PlainServerNames(serverNamesConfigs)
}
this.Data["missingCertServerNames"] = []string{}
if len(allServerNames) > 0 {
var missingServerNames []string
for _, serverName := range allServerNames {
if !configutils.MatchDomains(allDNSNames, serverName) {
missingServerNames = append(missingServerNames, serverName)
}
}
this.Data["missingCertServerNames"] = missingServerNames
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
IsOn bool
SslPolicyJSON []byte
Must *actions.Must
}) {
// 记录日志
defer this.CreateLogInfo(codes.ServerHTTPS_LogUpdateHTTPSSettings, params.ServerId)
serverConfig, err := dao.SharedServerDAO.FindEnabledServerConfig(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig == nil {
this.NotFound("server", params.ServerId)
return
}
// 校验SSL
var sslPolicyId = int64(0)
if params.SslPolicyJSON != nil {
sslPolicy := &sslconfigs.SSLPolicy{}
err = json.Unmarshal(params.SslPolicyJSON, sslPolicy)
if err != nil {
this.ErrorPage(errors.New("解析SSL配置时发生了错误" + err.Error()))
return
}
sslPolicyId = sslPolicy.Id
certsJSON, err := json.Marshal(sslPolicy.CertRefs)
if err != nil {
this.ErrorPage(err)
return
}
hstsJSON, err := json.Marshal(sslPolicy.HSTS)
if err != nil {
this.ErrorPage(err)
return
}
clientCACertsJSON, err := json.Marshal(sslPolicy.ClientCARefs)
if err != nil {
this.ErrorPage(err)
return
}
if sslPolicyId > 0 {
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.UserContext(), &pb.UpdateSSLPolicyRequest{
SslPolicyId: sslPolicyId,
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
OcspIsOn: sslPolicy.OCSPIsOn,
ClientAuthType: types.Int32(sslPolicy.ClientAuthType),
ClientCACertsJSON: clientCACertsJSON,
CipherSuitesIsOn: sslPolicy.CipherSuitesIsOn,
CipherSuites: sslPolicy.CipherSuites,
})
if err != nil {
this.ErrorPage(err)
return
}
} else {
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.UserContext(), &pb.CreateSSLPolicyRequest{
Http2Enabled: sslPolicy.HTTP2Enabled,
Http3Enabled: sslPolicy.HTTP3Enabled,
MinVersion: sslPolicy.MinVersion,
SslCertsJSON: certsJSON,
HstsJSON: hstsJSON,
OcspIsOn: sslPolicy.OCSPIsOn,
ClientAuthType: types.Int32(sslPolicy.ClientAuthType),
ClientCACertsJSON: clientCACertsJSON,
CipherSuitesIsOn: sslPolicy.CipherSuitesIsOn,
CipherSuites: sslPolicy.CipherSuites,
})
if err != nil {
this.ErrorPage(err)
return
}
sslPolicyId = resp.SslPolicyId
}
}
var httpsConfig = serverConfig.HTTPS
if httpsConfig == nil {
httpsConfig = &serverconfigs.HTTPSProtocolConfig{}
}
httpsConfig.SSLPolicy = nil
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
IsOn: true,
SSLPolicyId: sslPolicyId,
}
httpsConfig.IsOn = params.IsOn
// 如果启用HTTPS时没有填写端口则默认为443
if params.IsOn && len(httpsConfig.Listen) == 0 {
httpsConfig.Listen = []*serverconfigs.NetworkAddressConfig{
{
Protocol: serverconfigs.ProtocolHTTPS,
PortRange: "443",
},
}
}
configData, err := json.Marshal(httpsConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().ServerRPC().UpdateServerHTTPS(this.UserContext(), &pb.UpdateServerHTTPSRequest{
ServerId: params.ServerId,
HttpsJSON: configData,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,19 @@
package https
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/https").
GetPost("", new(IndexAction)).
GetPost("/requestCertPopup", new(RequestCertPopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,180 @@
package https
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/sslconfigs"
"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"
"strings"
)
type RequestCertPopupAction struct {
actionutils.ParentAction
}
func (this *RequestCertPopupAction) Init() {
this.Nav("", "", "")
}
func (this *RequestCertPopupAction) RunGet(params struct {
ServerId int64
ExcludeServerNames string
}) {
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.UserContext(), &pb.FindServerNamesRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
serverNameConfigs := []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(serverNamesResp.ServerNamesJSON, &serverNameConfigs)
if err != nil {
this.ErrorPage(err)
return
}
excludeServerNames := []string{}
if len(params.ExcludeServerNames) > 0 {
excludeServerNames = strings.Split(params.ExcludeServerNames, ",")
}
serverNames := []string{}
for _, c := range serverNameConfigs {
if len(c.SubNames) == 0 {
if domainutils.ValidateDomainFormat(c.Name) && !lists.ContainsString(excludeServerNames, c.Name) {
serverNames = append(serverNames, c.Name)
}
} else {
for _, subName := range c.SubNames {
if domainutils.ValidateDomainFormat(subName) && !lists.ContainsString(excludeServerNames, subName) {
serverNames = append(serverNames, subName)
}
}
}
}
this.Data["serverNames"] = serverNames
// 用户
acmeUsersResp, err := this.RPC().ACMEUserRPC().FindAllACMEUsers(this.UserContext(), &pb.FindAllACMEUsersRequest{
UserId: this.UserId(),
})
if err != nil {
this.ErrorPage(err)
return
}
var userMaps = []maps.Map{}
for _, user := range acmeUsersResp.AcmeUsers {
description := user.Description
if len(description) > 0 {
description = "" + description + ""
}
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"description": description,
"email": user.Email,
})
}
this.Data["users"] = userMaps
this.Show()
}
func (this *RequestCertPopupAction) RunPost(params struct {
ServerNames []string
UserId int64
UserEmail string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 检查域名
if len(params.ServerNames) == 0 {
this.Fail("必须包含至少一个或多个域名")
}
// 注册用户
var acmeUserId int64
if params.UserId > 0 {
// TODO 检查当前管理员是否可以使用此用户
acmeUserId = params.UserId
} else if len(params.UserEmail) > 0 {
params.Must.
Field("userEmail", params.UserEmail).
Email("Email格式错误")
createUserResp, err := this.RPC().ACMEUserRPC().CreateACMEUser(this.UserContext(), &pb.CreateACMEUserRequest{
Email: params.UserEmail,
Description: "",
})
if err != nil {
this.ErrorPage(err)
return
}
defer this.CreateLogInfo(codes.ACMEUser_LogCreateACMEUser, createUserResp.AcmeUserId)
acmeUserId = createUserResp.AcmeUserId
this.Data["acmeUser"] = maps.Map{
"id": acmeUserId,
"email": params.UserEmail,
}
} else {
this.Fail("请选择或者填写用户")
}
createTaskResp, err := this.RPC().ACMETaskRPC().CreateACMETask(this.UserContext(), &pb.CreateACMETaskRequest{
AcmeUserId: acmeUserId,
DnsProviderId: 0,
DnsDomain: "",
Domains: params.ServerNames,
AutoRenew: true,
AuthType: "http",
})
if err != nil {
this.ErrorPage(err)
return
}
taskId := createTaskResp.AcmeTaskId
defer this.CreateLogInfo(codes.ACMETask_LogRunACMETask, taskId)
runResp, err := this.RPC().ACMETaskRPC().RunACMETask(this.UserContext(), &pb.RunACMETaskRequest{AcmeTaskId: taskId})
if err != nil {
this.ErrorPage(err)
return
}
if runResp.IsOk {
certId := runResp.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,
}
this.Success()
} else {
this.Fail(runResp.Error)
}
}

View File

@@ -0,0 +1,93 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package referers
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"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type HlsAction struct {
actionutils.ParentAction
}
func (this *HlsAction) Init() {
this.Nav("", "setting", "hls")
this.SecondMenu("multimedia")
}
func (this *HlsAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
hlsResp, err := this.RPC().HTTPWebRPC().FindHTTPWebHLS(this.UserContext(), &pb.FindHTTPWebHLSRequest{HttpWebId: webConfig.Id})
if err != nil {
this.ErrorPage(err)
return
}
var hlsConfig = &serverconfigs.HLSConfig{}
if len(hlsResp.HlsJSON) > 0 {
err = json.Unmarshal(hlsResp.HlsJSON, hlsConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["hlsConfig"] = hlsConfig
this.Show()
}
func (this *HlsAction) RunPost(params struct {
WebId int64
HlsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.ServerHls_LogUpdateHls, params.WebId)
var hlsConfig = &serverconfigs.HLSConfig{}
err := json.Unmarshal(params.HlsJSON, hlsConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = hlsConfig.Init()
if err != nil {
this.ErrorPage(err)
return
}
hlsJSON, err := json.Marshal(hlsConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebHLS(this.UserContext(), &pb.UpdateHTTPWebHLSRequest{
HttpWebId: params.WebId,
HlsJSON: hlsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,24 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package referers
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("multimedia")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.RedirectURL("./multimedia/hls?serverId=" + types.String(params.ServerId))
}

View File

@@ -0,0 +1,21 @@
//go:build plus
package referers
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/multimedia").
GetPost("", new(IndexAction)).
GetPost("/hls", new(HlsAction)).
EndAll()
})
}

View File

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

View File

@@ -0,0 +1,106 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package optimization
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("optimization")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerOptimization, params.ServerId)
// 服务分组设置
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(this.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasGroupConfig"] = groupResp.HasOptimizationConfig
this.Data["groupSettingURL"] = "/servers/groups/group/settings/optimization?groupId=" + types.String(groupResp.ServerGroupId)
// WebId
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
if webConfig.Encryption == nil {
webConfig.Encryption = serverconfigs.NewHTTPPageEncryptionConfig()
}
this.Data["encryptionConfig"] = webConfig.Encryption
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
EncryptionJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerOptimization, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerOptimization_LogUpdateOptimizationSettings, params.WebId)
// 校验配置
var encryptionConfig = serverconfigs.NewHTTPPageEncryptionConfig()
err = json.Unmarshal(params.EncryptionJSON, encryptionConfig)
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
err = encryptionConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebEncryption(this.UserContext(), &pb.UpdateHTTPWebEncryptionRequest{
HttpWebId: params.WebId,
EncryptionJSON: params.EncryptionJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,20 @@
//go:build plus
package optimization
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/optimization").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

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

View File

@@ -0,0 +1,258 @@
package origins
import (
"encoding/json"
"errors"
"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/sslconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"net/url"
"regexp"
"strings"
)
// AddPopupAction 添加源站
type AddPopupAction struct {
actionutils.ParentAction
}
func (this *AddPopupAction) RunGet(params struct {
ServerId int64
ReverseProxyId int64
OriginType string
}) {
this.Data["reverseProxyId"] = params.ReverseProxyId
this.Data["originType"] = params.OriginType
serverTypeResp, err := this.RPC().ServerRPC().FindEnabledServerType(this.UserContext(), &pb.FindEnabledServerTypeRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
serverType := serverTypeResp.Type
this.Data["serverType"] = serverType
// 是否为HTTP
this.Data["isHTTP"] = serverType == "httpProxy" || serverType == "httpWeb"
// OSS
this.getOSSHook()
this.Show()
}
func (this *AddPopupAction) RunPost(params struct {
OriginType string
ReverseProxyId int64
Weight int32
Protocol string
Addr string
Name string
Description string
IsOn bool
CertIdsJSON []byte
DomainsJSON []byte
Host string
FollowPort bool
Http2Enabled bool
Must *actions.Must
}) {
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
if err != nil {
this.ErrorPage(err)
return
}
if !goNext {
return
}
// 初始化
var pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
}
var ossJSON []byte = nil
var certRefJSON []byte
if ossConfig != nil { // OSS
ossJSON, err = json.Marshal(ossConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = ossConfig.Init()
if err != nil {
this.Fail("校验OSS配置时出错" + err.Error())
return
}
} else { // 普通源站
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
}
addr = strings.ReplaceAll(addr, "", ":")
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
portIndex := strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.FailField("addr", "源站地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
var host = addr[:portIndex]
var port = addr[portIndex+1:]
if port == "0" {
this.FailField("addr", "源站端口号不能为0")
}
// 证书
var certIds = []int64{}
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(certIds) > 0 {
var certId = certIds[0]
if certId > 0 {
var certRef = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
}
certRefJSON, err = json.Marshal(certRef)
if err != nil {
this.ErrorPage(err)
return
}
}
}
pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
}
}
// 专属域名
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err = json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.ErrorPage(err)
return
}
// 去除可能误加的斜杠
for index, domain := range domains {
domains[index] = strings.TrimSuffix(domain, "/")
}
}
createResp, err := this.RPC().OriginRPC().CreateOrigin(this.UserContext(), &pb.CreateOriginRequest{
Name: params.Name,
Addr: pbAddr,
OssJSON: ossJSON,
Description: params.Description,
Weight: params.Weight,
IsOn: params.IsOn,
Domains: domains,
CertRefJSON: certRefJSON,
Host: params.Host,
FollowPort: params.FollowPort,
Http2Enabled: params.Http2Enabled,
})
if err != nil {
this.ErrorPage(err)
return
}
var originId = createResp.OriginId
var originRef = &serverconfigs.OriginRef{
IsOn: true,
OriginId: originId,
}
reverseProxyResp, err := this.RPC().ReverseProxyRPC().FindEnabledReverseProxy(this.UserContext(), &pb.FindEnabledReverseProxyRequest{ReverseProxyId: params.ReverseProxyId})
if err != nil {
this.ErrorPage(err)
return
}
var reverseProxy = reverseProxyResp.ReverseProxy
if reverseProxy == nil {
this.ErrorPage(errors.New("reverse proxy should not be nil"))
return
}
var origins = []*serverconfigs.OriginRef{}
switch params.OriginType {
case "primary":
if len(reverseProxy.PrimaryOriginsJSON) > 0 {
err = json.Unmarshal(reverseProxy.PrimaryOriginsJSON, &origins)
if err != nil {
this.ErrorPage(err)
return
}
}
case "backup":
if len(reverseProxy.BackupOriginsJSON) > 0 {
err = json.Unmarshal(reverseProxy.BackupOriginsJSON, &origins)
if err != nil {
this.ErrorPage(err)
return
}
}
}
origins = append(origins, originRef)
originsData, err := json.Marshal(origins)
if err != nil {
this.ErrorPage(err)
return
}
switch params.OriginType {
case "primary":
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxyPrimaryOrigins(this.UserContext(), &pb.UpdateReverseProxyPrimaryOriginsRequest{
ReverseProxyId: params.ReverseProxyId,
OriginsJSON: originsData,
})
case "backup":
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxyBackupOrigins(this.UserContext(), &pb.UpdateReverseProxyBackupOriginsRequest{
ReverseProxyId: params.ReverseProxyId,
OriginsJSON: originsData,
})
}
if err != nil {
this.ErrorPage(err)
return
}
// 日志
defer this.CreateLogInfo(codes.ServerOrigin_LogCreateOrigin, originId)
this.Success()
}

View File

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

View File

@@ -0,0 +1,89 @@
package origins
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
ReverseProxyId int64
OriginId int64
OriginType string
}) {
reverseProxyResp, err := this.RPC().ReverseProxyRPC().FindEnabledReverseProxy(this.UserContext(), &pb.FindEnabledReverseProxyRequest{ReverseProxyId: params.ReverseProxyId})
if err != nil {
this.ErrorPage(err)
return
}
reverseProxy := reverseProxyResp.ReverseProxy
if reverseProxy == nil {
this.ErrorPage(errors.New("reverse proxy is nil"))
return
}
origins := []*serverconfigs.OriginRef{}
switch params.OriginType {
case "primary":
err = json.Unmarshal(reverseProxy.PrimaryOriginsJSON, &origins)
if err != nil {
this.ErrorPage(err)
return
}
case "backup":
err = json.Unmarshal(reverseProxy.BackupOriginsJSON, &origins)
if err != nil {
this.ErrorPage(err)
return
}
default:
this.ErrorPage(errors.New("invalid origin type '" + params.OriginType + "'"))
return
}
result := []*serverconfigs.OriginRef{}
for _, origin := range origins {
if origin.OriginId == params.OriginId {
continue
}
result = append(result, origin)
}
resultData, err := json.Marshal(result)
if err != nil {
this.ErrorPage(err)
return
}
switch params.OriginType {
case "primary":
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxyPrimaryOrigins(this.UserContext(), &pb.UpdateReverseProxyPrimaryOriginsRequest{
ReverseProxyId: params.ReverseProxyId,
OriginsJSON: resultData,
})
if err != nil {
this.ErrorPage(err)
return
}
case "backup":
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxyBackupOrigins(this.UserContext(), &pb.UpdateReverseProxyBackupOriginsRequest{
ReverseProxyId: params.ReverseProxyId,
OriginsJSON: resultData,
})
if err != nil {
this.ErrorPage(err)
return
}
}
// 日志
defer this.CreateLogInfo(codes.ServerOrigin_LogDeleteOrigin, params.OriginId)
this.Success()
}

View File

@@ -0,0 +1,58 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package origins
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"net"
"net/url"
"strings"
"time"
)
type DetectHTTPSAction struct {
actionutils.ParentAction
}
func (this *DetectHTTPSAction) RunPost(params struct {
Addr string
}) {
this.Data["isOk"] = false
// parse from url
if strings.HasPrefix(params.Addr, "http://") || strings.HasPrefix(params.Addr, "https://") {
u, err := url.Parse(params.Addr)
if err == nil {
params.Addr = u.Host
}
}
this.Data["addr"] = params.Addr
if len(params.Addr) == 0 {
this.Success()
return
}
var realHost = params.Addr
host, port, err := net.SplitHostPort(params.Addr)
if err == nil {
if port != "80" {
this.Success()
return
}
realHost = host
}
conn, err := net.DialTimeout("tcp", configutils.QuoteIP(realHost)+":443", 3*time.Second)
if err != nil {
this.Success()
return
}
_ = conn.Close()
this.Data["isOk"] = true
this.Success()
}

View File

@@ -0,0 +1,20 @@
package origins
import (
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Prefix("/servers/server/settings/origins").
GetPost("/addPopup", new(AddPopupAction)).
Post("/delete", new(DeleteAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/updateIsOn", new(UpdateIsOnAction)).
Post("/detectHTTPS", new(DetectHTTPSAction)).
EndAll()
})
}

View File

@@ -0,0 +1,384 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package originutils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
"github.com/TeaOSLab/EdgeUser/internal/utils/domainutils"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/actions"
"net/url"
"os"
"regexp"
"strings"
)
// ParseOSSFromRequest 请Action提交的表单中分析OSS信息
func ParseOSSFromRequest(protocol string, action *actions.ActionObject) (config *ossconfigs.OSSConfig, goNext bool, err error) {
if !ossconfigs.IsOSSProtocol(protocol) {
goNext = true
return
}
var ossType = ossconfigs.FindOSSType(protocol)
if ossType == nil {
action.Fail("无法识别的OSS类型" + protocol)
return
}
switch protocol {
case ossconfigs.OSSTypeTencentCOS:
var secretId = action.ParamString("tencentCOSSecretId")
var secretKey = action.ParamString("tencentCOSSecretKey")
var region = action.ParamString("tencentCOSRegion")
if len(secretId) == 0 {
action.FailField("tencentCOSSecretId", "请输入密钥ID")
return
}
if len(secretKey) == 0 {
action.FailField("tencentCOSSecretKey", "请输入密钥Key")
return
}
if len(region) == 0 {
action.FailField("tencentCOSRegion", "请输入所属区域代号")
return
}
if !regexp.MustCompile(`^[\w-]+$`).MatchString(region) {
action.FailField("tencentCOSRegion", "输入的区域代号格式错误")
return
}
config = ossconfigs.NewOSSConfig()
config.Type = protocol
if !parseOSSBucketParam(action, config, ossType.BucketOptionName) {
return
}
var options = ossconfigs.NewTencentCOSProviderOptions()
options.SecretId = secretId
options.SecretKey = secretKey
options.Region = region
config.Options = options
err = config.Init()
if err != nil {
return nil, false, err
}
case ossconfigs.OSSTypeAliyunOSS:
var accessKeyId = action.ParamString("aliyunOSSAccessKeyId")
var accessKeySecret = action.ParamString("aliyunOSSAccessKeySecret")
var endpoint = action.ParamString("aliyunOSSEndpoint")
if len(accessKeyId) == 0 {
action.FailField("aliyunOSSAccessKeyId", "请输入密钥ID")
return
}
if len(accessKeySecret) == 0 {
action.FailField("aliyunOSSAccessKeySecret", "请输入密钥Key")
return
}
if len(endpoint) == 0 {
action.FailField("aliyunOSSEndpoint", "请输入地域节点")
return
}
if !domainutils.ValidateDomainFormat(endpoint) {
action.FailField("aliyunOSSEndpoint", "地域节点需要是一个域名")
return
}
config = ossconfigs.NewOSSConfig()
config.Type = protocol
if !parseOSSBucketParam(action, config, ossType.BucketOptionName) {
return
}
var options = ossconfigs.NewAliyunOSSProviderOptions()
options.AccessKeyId = accessKeyId
options.AccessKeySecret = accessKeySecret
options.Endpoint = endpoint
config.Options = options
err = config.Init()
if err != nil {
return nil, false, err
}
case ossconfigs.OSSTypeHuaweiOBS:
var accessKeyId = action.ParamString("huaweiOBSAccessKeyId")
var accessKeySecret = action.ParamString("huaweiOBSAccessKeySecret")
var endpoint = action.ParamString("huaweiOBSEndpoint")
if len(accessKeyId) == 0 {
action.FailField("huaweiOBSAccessKeyId", "请输入密钥ID")
return
}
if len(accessKeySecret) == 0 {
action.FailField("huaweiOBSAccessKeySecret", "请输入密钥Key")
return
}
if len(endpoint) == 0 {
action.FailField("huaweiOBSEndpoint", "请输入区域Endpoint")
return
}
if !domainutils.ValidateDomainFormat(endpoint) {
action.FailField("huaweiOBSEndpoint", "区域Endpoint需要是一个域名")
return
}
config = ossconfigs.NewOSSConfig()
config.Type = protocol
if !parseOSSBucketParam(action, config, ossType.BucketOptionName) {
return
}
var options = ossconfigs.NewHuaweiOBSProviderOptions()
options.AccessKeyId = accessKeyId
options.AccessKeySecret = accessKeySecret
options.Endpoint = endpoint
config.Options = options
err = config.Init()
if err != nil {
return nil, false, err
}
case ossconfigs.OSSTypeBaiduBOS:
var accessKey = action.ParamString("baiduBOSAccessKey")
var secretKey = action.ParamString("baiduBOSSecretKey")
var endpoint = action.ParamString("baiduBOSEndpoint")
if len(accessKey) == 0 {
action.FailField("baiduBOSAccessKey", "请输入AccessKey")
return
}
if len(secretKey) == 0 {
action.FailField("baiduBOSSecretKey", "请输入SecretKey")
return
}
if len(endpoint) == 0 {
action.FailField("baiduBOSEndpoint", "请输入区域Endpoint")
return
}
if !domainutils.ValidateDomainFormat(endpoint) {
action.FailField("baiduBOSEndpoint", "区域Endpoint需要是一个域名")
return
}
config = ossconfigs.NewOSSConfig()
config.Type = protocol
if !parseOSSBucketParam(action, config, ossType.BucketOptionName) {
return
}
var options = ossconfigs.NewBaiduBOSProviderOptions()
options.AccessKey = accessKey
options.SecretKey = secretKey
options.Endpoint = endpoint
config.Options = options
err = config.Init()
if err != nil {
return nil, false, err
}
case ossconfigs.OSSTypeQiniuKodo:
var accessKey = action.ParamString("qiniuKodoAccessKey")
var secretKey = action.ParamString("qiniuKodoSecretKey")
var domainProtocol = action.ParamString("qiniuKodoProtocol")
var domain = action.ParamString("qiniuKodoDomain")
var isPublicString = action.ParamString("qiniuKodoIsPublic")
var isPublic = isPublicString == "1" || isPublicString == "true"
if len(accessKey) == 0 {
action.FailField("qiniuKodoAccessKey", "请输入AccessKey")
return
}
if len(secretKey) == 0 {
action.FailField("qiniuKodoSecretKey", "请输入SecretKey")
}
if domainProtocol != "http" && domainProtocol != "https" {
action.FailField("qiniuKodoProtocol", "请选择域名访问协议")
return
}
if len(domain) == 0 {
action.FailField("qiniuKodoDomain", "请输入访问域名")
return
}
domain = strings.ToLower(domain)
u, parseErr := url.Parse(domain)
if parseErr == nil && len(u.Scheme) > 0 {
// parse from a full url
domain = u.Host
domainProtocol = u.Scheme
} else {
// remove path part
var slashIndex = strings.Index(domain, "/")
if slashIndex >= 0 {
domain = domain[:slashIndex]
}
}
// 再次检查
if len(domain) == 0 || !domainutils.ValidateDomainFormat(domain) {
action.FailField("qiniuKodoDomain", "请输入正确的访问域名")
return
}
config = ossconfigs.NewOSSConfig()
config.Type = protocol
if !parseOSSBucketParam(action, config, ossType.BucketOptionName) {
return
}
var options = ossconfigs.NewQiniuKodoProviderOptions()
options.AccessKey = accessKey
options.SecretKey = secretKey
options.Protocol = domainProtocol
options.Domain = domain
options.IsPublic = isPublic
config.Options = options
err = config.Init()
if err != nil {
return nil, false, err
}
case ossconfigs.OSSTypeAmazonS3, ossconfigs.OSSTypeB2:
var accessKeyId = action.ParamString("amazonS3AccessKeyId")
var accessKeySecret = action.ParamString("amazonS3AccessKeySecret")
var region = action.ParamString("amazonS3Region")
var endpoint = action.ParamString("amazonS3Endpoint")
var bucketAddressStyle = action.ParamString("bucketAddressStyle")
if len(accessKeyId) == 0 {
action.FailField("amazonS3AccessKeyId", "请输入密钥ID")
return
}
if len(accessKeySecret) == 0 {
action.FailField("amazonS3AccessKeySecret", "请输入密钥Key")
return
}
if len(region) == 0 {
action.FailField("amazonS3Region", "请输入区域代号")
return
}
if !regexp.MustCompile(`^[\w-]+$`).MatchString(region) {
action.FailField("amazonS3Region", "输入的区域代号格式错误")
return
}
if protocol != ossconfigs.OSSTypeAmazonS3 {
if len(endpoint) == 0 {
action.FailField("amazonS3Endpoint", "请输入Endpoint")
return
}
}
if len(endpoint) > 0 {
if !domainutils.ValidateDomainFormat(endpoint) {
u, urlErr := url.Parse(endpoint)
if urlErr != nil || len(u.Scheme) == 0 {
action.FailField("amazonS3Endpoint", "Endpoint需要是一个域名或者URL")
return
}
}
}
config = ossconfigs.NewOSSConfig()
config.Type = protocol
if !parseOSSBucketParam(action, config, ossType.BucketOptionName) {
return
}
var options = ossconfigs.NewAmazonS3ProviderOptions()
options.AccessKeyId = accessKeyId
options.AccessKeySecret = accessKeySecret
options.Region = region
options.Endpoint = endpoint
options.BucketAddressStyle = bucketAddressStyle
config.Options = options
err = config.Init()
if err != nil {
return nil, false, err
}
default:
action.Fail("无法识别的OSS类型" + protocol)
return
}
goNext = true
return
}
func parseOSSBucketParam(action *actions.ActionObject, config *ossconfigs.OSSConfig, optionName string) (goNext bool) {
var bucketParam = action.ParamString("bucketParam")
config.BucketParam = bucketParam
switch bucketParam {
case ossconfigs.OSSBucketParamInput:
var bucketName = action.ParamString("bucketName")
if len(bucketName) == 0 {
action.FailField("bucketName", "请输入"+optionName+"名称")
return
}
if !regexp.MustCompile(`^[\w._-]+$`).MatchString(bucketName) {
action.FailField("bucketName", "输入的"+optionName+"名称格式错误")
return
}
config.BucketName = bucketName
goNext = true
case ossconfigs.OSSBucketParamPrefix:
goNext = true
case ossconfigs.OSSBucketParamArg:
var bucketArgName = action.ParamString("bucketArgName")
if len(bucketArgName) == 0 {
action.FailField("bucketArgName", "请输入"+optionName+"参数名称")
return
}
if !regexp.MustCompile(`^[\w._-]+$`).MatchString(bucketArgName) {
action.FailField("bucketArgName", "输入的"+optionName+"参数名称格式错误")
return
}
config.BucketArgName = bucketArgName
goNext = true
case ossconfigs.OSSBucketParamSubDomain:
goNext = true
default:
action.Fail("请选择" + optionName + "名称获取方式")
}
return
}
// OSSTemplateFile 模板文件路径
func OSSTemplateFile(filename string) string {
var filenames = []string{filename}
if !Tea.IsTesting() {
filenames = append(filenames, strings.Replace(filename, "_plus.html", ".html", -1))
}
for _, name := range filenames {
var viewFile = "/views/@default/servers/server/settings/origins/" + name
var absPath string
if Tea.IsTesting() {
absPath = Tea.Root + "/../web" + viewFile
} else {
absPath = Tea.Root + "/web" + viewFile
}
_, err := os.Stat(absPath)
if err == nil {
return absPath
}
}
return filename
}

View File

@@ -0,0 +1,31 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package origins
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type UpdateIsOnAction struct {
actionutils.ParentAction
}
func (this *UpdateIsOnAction) RunPost(params struct {
OriginId int64
IsOn bool
}) {
defer this.CreateLogInfo(codes.ServerOrigin_LogUpdateOriginIsOn, params.OriginId)
_, err := this.RPC().OriginRPC().UpdateOriginIsOn(this.UserContext(), &pb.UpdateOriginIsOnRequest{
OriginId: params.OriginId,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,250 @@
package origins
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/sslconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"net/url"
"regexp"
"strings"
)
// UpdatePopupAction 修改源站
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
ServerId int64
ReverseProxyId int64
OriginType string
OriginId int64
}) {
this.Data["originType"] = params.OriginType
this.Data["reverseProxyId"] = params.ReverseProxyId
this.Data["originId"] = params.OriginId
serverTypeResp, err := this.RPC().ServerRPC().FindEnabledServerType(this.UserContext(), &pb.FindEnabledServerTypeRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["serverType"] = serverTypeResp.Type
// 是否为HTTP
var serverType = serverTypeResp.Type
this.Data["isHTTP"] = serverType == "httpProxy" || serverType == "httpWeb"
// 源站信息
originResp, err := this.RPC().OriginRPC().FindEnabledOriginConfig(this.UserContext(), &pb.FindEnabledOriginConfigRequest{OriginId: params.OriginId})
if err != nil {
this.ErrorPage(err)
return
}
var configData = originResp.OriginJSON
var config = &serverconfigs.OriginConfig{}
err = json.Unmarshal(configData, config)
if err != nil {
this.ErrorPage(err)
return
}
if len(config.Domains) == 0 {
config.Domains = []string{}
}
// 重置数据
if config.Cert != nil {
config.Cert.CertData = nil
config.Cert.KeyData = nil
}
this.Data["origin"] = maps.Map{
"id": config.Id,
"protocol": config.Addr.Protocol,
"addr": config.Addr.Host + ":" + config.Addr.PortRange,
"weight": config.Weight,
"name": config.Name,
"description": config.Description,
"isOn": config.IsOn,
"domains": config.Domains,
"cert": config.Cert,
"host": config.RequestHost,
"followPort": config.FollowPort,
"http2Enabled": config.HTTP2Enabled,
"oss": config.OSS,
}
// OSS
this.getOSSHook()
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
OriginType string
OriginId int64
ReverseProxyId int64
Protocol string
Addr string
Weight int32
Name string
Description string
IsOn bool
CertIdsJSON []byte
DomainsJSON []byte
Host string
FollowPort bool
Http2Enabled bool
Must *actions.Must
}) {
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
if err != nil {
this.ErrorPage(err)
return
}
if !goNext {
return
}
// 初始化
var pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
}
var ossJSON []byte
var certRefJSON []byte
if ossConfig != nil { // OSS
ossJSON, err = json.Marshal(ossConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = ossConfig.Init()
if err != nil {
this.Fail("校验OSS配置时出错" + err.Error())
return
}
} else { // 普通源站
params.Must.
Field("addr", params.Addr).
Require("请输入源站地址")
var addr = params.Addr
// 是否是完整的地址
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
u, err := url.Parse(addr)
if err == nil {
addr = u.Host
}
}
addr = strings.ReplaceAll(addr, "", ":")
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
portIndex := strings.LastIndex(addr, ":")
if portIndex < 0 {
if params.Protocol == "http" {
addr += ":80"
} else if params.Protocol == "https" {
addr += ":443"
} else {
this.FailField("addr", "地址中需要带有端口")
}
portIndex = strings.LastIndex(addr, ":")
}
host := addr[:portIndex]
port := addr[portIndex+1:]
if port == "0" {
this.FailField("addr", "端口号不能为0")
}
// 证书
var certIds = []int64{}
var err error
if len(params.CertIdsJSON) > 0 {
err = json.Unmarshal(params.CertIdsJSON, &certIds)
if err != nil {
this.ErrorPage(err)
return
}
}
if len(certIds) > 0 {
var certId = certIds[0]
if certId > 0 {
var certRef = &sslconfigs.SSLCertRef{
IsOn: true,
CertId: certId,
}
certRefJSON, err = json.Marshal(certRef)
if err != nil {
this.ErrorPage(err)
return
}
}
}
pbAddr = &pb.NetworkAddress{
Protocol: params.Protocol,
Host: host,
PortRange: port,
}
}
// 域名
var domains = []string{}
if len(params.DomainsJSON) > 0 {
err = json.Unmarshal(params.DomainsJSON, &domains)
if err != nil {
this.ErrorPage(err)
return
}
// 去除可能误加的斜杠
for index, domain := range domains {
domains[index] = strings.TrimSuffix(domain, "/")
}
}
_, err = this.RPC().OriginRPC().UpdateOrigin(this.UserContext(), &pb.UpdateOriginRequest{
OriginId: params.OriginId,
Name: params.Name,
Addr: pbAddr,
OssJSON: ossJSON,
Description: params.Description,
Weight: params.Weight,
IsOn: params.IsOn,
Domains: domains,
Host: params.Host,
CertRefJSON: certRefJSON,
FollowPort: params.FollowPort,
Http2Enabled: params.Http2Enabled,
})
if err != nil {
this.ErrorPage(err)
return
}
// 日志
defer this.CreateLogInfo(codes.ServerOrigin_LogUpdateOrigin, params.OriginId)
this.Success()
}

View File

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

View File

@@ -0,0 +1,130 @@
package pages
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/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Data["bodyTypes"] = serverconfigs.FindAllHTTPPageBodyTypes()
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Status string
BodyType string
URL string `alias:"url"`
Body string
ExceptURLPatternsJSON []byte
OnlyURLPatternsJSON []byte
NewStatus int
Must *actions.Must
}) {
params.Must.
Field("status", params.Status).
Require("请输入响应状态码")
if len(params.Status) != 3 {
this.FailField("status", "状态码长度必须为3位")
return
}
switch params.BodyType {
case serverconfigs.HTTPPageBodyTypeURL:
params.Must.
Field("url", params.URL).
Require("请输入要显示的URL").
Match(`^(?i)(http|https)://`, "请输入正确的URL")
case serverconfigs.HTTPPageBodyTypeRedirectURL:
params.Must.
Field("url", params.URL).
Require("请输入要跳转的URL").
Match(`^(?i)(http|https)://`, "请输入正确的URL")
case serverconfigs.HTTPPageBodyTypeHTML:
params.Must.
Field("body", params.Body).
Require("请输入要显示的HTML内容")
if len(params.Body) > 32*1024 {
this.FailField("body", "自定义页面内容不能超过32K")
return
}
}
var exceptURLPatterns = []*shared.URLPattern{}
if len(params.ExceptURLPatternsJSON) > 0 {
err := json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
if err != nil {
this.ErrorPage(err)
return
}
}
var onlyURLPatterns = []*shared.URLPattern{}
if len(params.OnlyURLPatternsJSON) > 0 {
err := json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
if err != nil {
this.ErrorPage(err)
return
}
}
createResp, err := this.RPC().HTTPPageRPC().CreateHTTPPage(this.UserContext(), &pb.CreateHTTPPageRequest{
StatusList: []string{params.Status},
BodyType: params.BodyType,
Url: params.URL,
Body: params.Body,
NewStatus: types.Int32(params.NewStatus),
ExceptURLPatternsJSON: params.ExceptURLPatternsJSON,
OnlyURLPatternsJSON: params.OnlyURLPatternsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
var pageId = createResp.HttpPageId
configResp, err := this.RPC().HTTPPageRPC().FindEnabledHTTPPageConfig(this.UserContext(), &pb.FindEnabledHTTPPageConfigRequest{HttpPageId: pageId})
if err != nil {
this.ErrorPage(err)
return
}
var pageConfig = &serverconfigs.HTTPPageConfig{}
err = json.Unmarshal(configResp.PageJSON, pageConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = pageConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
this.Data["page"] = pageConfig
// 日志
defer this.CreateLogInfo(codes.ServerPage_LogCreatePage, pageId)
this.Success()
}

View File

@@ -0,0 +1,165 @@
package pages
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"regexp"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("pages")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerPages, params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["enableGlobalPages"] = webConfig.EnableGlobalPages
this.Data["pages"] = webConfig.Pages
this.Data["shutdownConfig"] = webConfig.Shutdown
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
EnableGlobalPages bool
PagesJSON []byte
ShutdownJSON []byte
Must *actions.Must
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerPages, serverId) {
this.Fail("permission denied")
return
}
// 日志
defer this.CreateLogInfo(codes.ServerPage_LogUpdatePages, params.WebId)
// 检查配置
var urlReg = regexp.MustCompile(`^(?i)(http|https)://`)
// validate pages
if len(params.PagesJSON) > 0 {
var pages = []*serverconfigs.HTTPPageConfig{}
err := json.Unmarshal(params.PagesJSON, &pages)
if err != nil {
this.ErrorPage(err)
return
}
for _, page := range pages {
err = page.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
// check url
if page.BodyType == serverconfigs.HTTPPageBodyTypeURL && !urlReg.MatchString(page.URL) {
this.Fail("自定义页面中 '" + page.URL + "' 不是一个正确的URL请进行修改")
return
}
if page.BodyType == serverconfigs.HTTPPageBodyTypeRedirectURL && !urlReg.MatchString(page.URL) {
this.Fail("自定义页面中 '" + page.URL + "' 不是一个正确的URL请进行修改")
return
}
}
}
// validate shutdown page
if len(params.ShutdownJSON) > 0 {
var shutdownConfig = &serverconfigs.HTTPShutdownConfig{}
err := json.Unmarshal(params.ShutdownJSON, shutdownConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = shutdownConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
if shutdownConfig.BodyType == serverconfigs.HTTPPageBodyTypeURL {
if len(shutdownConfig.URL) > 512 {
this.Fail("临时关闭页面中URL过长不能超过512字节")
return
}
if shutdownConfig.IsOn /** 只有启用的时候才校验 **/ && !urlReg.MatchString(shutdownConfig.URL) {
this.Fail("临时关闭页面中 '" + shutdownConfig.URL + "' 不是一个正确的URL请进行修改")
return
}
} else if shutdownConfig.BodyType == serverconfigs.HTTPPageBodyTypeRedirectURL {
if len(shutdownConfig.URL) > 512 {
this.Fail("临时关闭页面中URL过长不能超过512字节")
return
}
if shutdownConfig.IsOn /** 只有启用的时候才校验 **/ && !urlReg.MatchString(shutdownConfig.URL) {
this.Fail("临时关闭页面中 '" + shutdownConfig.URL + "' 不是一个正确的URL请进行修改")
return
}
} else if shutdownConfig.Body == serverconfigs.HTTPPageBodyTypeHTML {
if len(shutdownConfig.Body) > 32*1024 {
this.Fail("临时关闭页面中HTML内容长度不能超过32K")
return
}
}
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebGlobalPagesEnabled(this.UserContext(), &pb.UpdateHTTPWebGlobalPagesEnabledRequest{
HttpWebId: params.WebId,
IsEnabled: params.EnableGlobalPages,
})
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebPages(this.UserContext(), &pb.UpdateHTTPWebPagesRequest{
HttpWebId: params.WebId,
PagesJSON: params.PagesJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebShutdown(this.UserContext(), &pb.UpdateHTTPWebShutdownRequest{
HttpWebId: params.WebId,
ShutdownJSON: params.ShutdownJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,20 @@
package pages
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/pages").
GetPost("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,151 @@
package pages
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/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *UpdatePopupAction) RunGet(params struct {
PageId int64
}) {
this.Data["bodyTypes"] = serverconfigs.FindAllHTTPPageBodyTypes()
configResp, err := this.RPC().HTTPPageRPC().FindEnabledHTTPPageConfig(this.UserContext(), &pb.FindEnabledHTTPPageConfigRequest{HttpPageId: params.PageId})
if err != nil {
this.ErrorPage(err)
return
}
var pageConfig = &serverconfigs.HTTPPageConfig{}
err = json.Unmarshal(configResp.PageJSON, pageConfig)
if err != nil {
this.ErrorPage(err)
return
}
if pageConfig.ExceptURLPatterns == nil {
pageConfig.ExceptURLPatterns = []*shared.URLPattern{}
}
if pageConfig.OnlyURLPatterns == nil {
pageConfig.OnlyURLPatterns = []*shared.URLPattern{}
}
this.Data["pageConfig"] = pageConfig
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
PageId int64
Status string
BodyType string
URL string `alias:"url"`
Body string
ExceptURLPatternsJSON []byte
OnlyURLPatternsJSON []byte
NewStatus int
Must *actions.Must
}) {
// 日志
defer this.CreateLogInfo(codes.ServerPage_LogUpdatePage, params.PageId)
params.Must.
Field("status", params.Status).
Require("请输入响应状态码")
switch params.BodyType {
case serverconfigs.HTTPPageBodyTypeURL:
params.Must.
Field("url", params.URL).
Require("请输入要显示的URL").
Match(`^(?i)(http|https)://`, "请输入正确的URL")
case serverconfigs.HTTPPageBodyTypeRedirectURL:
params.Must.
Field("url", params.URL).
Require("请输入要跳转的URL").
Match(`^(?i)(http|https)://`, "请输入正确的URL")
case serverconfigs.HTTPPageBodyTypeHTML:
params.Must.
Field("body", params.Body).
Require("请输入要显示的HTML内容")
if len(params.Body) > 32*1024 {
this.FailField("body", "自定义页面内容不能超过32K")
return
}
}
var exceptURLPatterns = []*shared.URLPattern{}
if len(params.ExceptURLPatternsJSON) > 0 {
err := json.Unmarshal(params.ExceptURLPatternsJSON, &exceptURLPatterns)
if err != nil {
this.ErrorPage(err)
return
}
}
var onlyURLPatterns = []*shared.URLPattern{}
if len(params.OnlyURLPatternsJSON) > 0 {
err := json.Unmarshal(params.OnlyURLPatternsJSON, &onlyURLPatterns)
if err != nil {
this.ErrorPage(err)
return
}
}
_, err := this.RPC().HTTPPageRPC().UpdateHTTPPage(this.UserContext(), &pb.UpdateHTTPPageRequest{
HttpPageId: params.PageId,
StatusList: []string{params.Status},
BodyType: params.BodyType,
Url: params.URL,
Body: params.Body,
NewStatus: types.Int32(params.NewStatus),
ExceptURLPatternsJSON: params.ExceptURLPatternsJSON,
OnlyURLPatternsJSON: params.OnlyURLPatternsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// 返回修改后的配置
configResp, err := this.RPC().HTTPPageRPC().FindEnabledHTTPPageConfig(this.UserContext(), &pb.FindEnabledHTTPPageConfigRequest{HttpPageId: params.PageId})
if err != nil {
this.ErrorPage(err)
return
}
var pageConfig = &serverconfigs.HTTPPageConfig{}
err = json.Unmarshal(configResp.PageJSON, pageConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = pageConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
this.Data["page"] = pageConfig
this.Success()
}

View File

@@ -0,0 +1,73 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package plan
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
// DataAction 套餐相关数据
type DataAction struct {
actionutils.ParentAction
}
func (this *DataAction) RunPost(params struct {
ServerId int64
UserPlanId int64
}) {
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.Plan == nil {
this.NotFound("userPlan", params.UserPlanId)
return
}
var plan = userPlan.Plan
// 网站数
countServersResp, err := this.RPC().ServerRPC().CountAllUserServers(this.UserContext(), &pb.CountAllUserServersRequest{
UserPlanId: params.UserPlanId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["servers"] = maps.Map{
"current": countServersResp.Count,
"max": plan.TotalServers,
"isValid": plan.TotalServers <= 0 || countServersResp.Count+1 <= int64(plan.TotalServers),
}
// 当前网站域名数
countServerNamesResp, err := this.RPC().ServerRPC().CountServerNames(this.UserContext(), &pb.CountServerNamesRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["serverNames"] = maps.Map{
"current": countServerNamesResp.Count,
"max": plan.TotalServerNamesPerServer,
"isValid": plan.TotalServerNamesPerServer <= 0 || countServerNamesResp.Count <= int64(plan.TotalServerNamesPerServer),
}
// 总域名数
countAllServerNamesResp, err := this.RPC().ServerRPC().CountAllServerNamesWithUserId(this.UserContext(), &pb.CountAllServerNamesWithUserIdRequest{
UserPlanId: params.UserPlanId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["allServerNames"] = maps.Map{
"current": countAllServerNamesResp.Count,
"max": plan.TotalServerNames,
"isValid": plan.TotalServerNames <= 0 || countAllServerNamesResp.Count+countServerNamesResp.Count <= int64(plan.TotalServerNames),
}
this.Success()
}

View File

@@ -0,0 +1,244 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package plan
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/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("plan")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
if !this.ValidateFeature(userconfigs.UserFeatureCodePlan, params.ServerId) {
return
}
// 所有可选套餐
userPlansResp, err := this.RPC().UserPlanRPC().FindAllEnabledUserPlansForServer(this.UserContext(), &pb.FindAllEnabledUserPlansForServerRequest{
UserId: this.UserId(),
ServerId: params.ServerId,
})
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,
"totalServers": userPlan.Plan.TotalServers,
"totalServerNames": userPlan.Plan.TotalServerNames,
"totalServerNamesPerServer": userPlan.Plan.TotalServerNamesPerServer,
})
}
this.Data["userPlans"] = userPlanMaps
// 当前使用的套餐
userPlanResp, err := this.RPC().ServerRPC().FindServerUserPlan(this.UserContext(), &pb.FindServerUserPlanRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
// 用户套餐信息
var userPlanMap = maps.Map{"id": 0}
var userPlan = userPlanResp.UserPlan
this.Data["hasTrafficLimit"] = false
if userPlan != nil {
userPlanMap = maps.Map{
"id": userPlan.Id,
"dayTo": userPlan.DayTo,
"isExpired": timeutil.Format("Y-m-d") > userPlan.DayTo,
"name": userPlan.Name,
"plan": nil,
}
var plan = userPlan.Plan
if plan != nil {
// 流量限制
var trafficLimit = &serverconfigs.TrafficLimitConfig{}
if len(plan.TrafficLimitJSON) > 0 {
err = json.Unmarshal(plan.TrafficLimitJSON, trafficLimit)
if err != nil {
this.ErrorPage(err)
return
}
}
userPlanMap["plan"] = maps.Map{
"id": plan.Id,
"name": plan.Name,
"trafficLimit": trafficLimit,
}
if !trafficLimit.IsEmpty() {
this.Data["hasTrafficLimit"] = true
}
}
}
this.Data["userPlan"] = userPlanMap
// 当前网站流量
{
trafficStatResp, err := this.RPC().ServerDailyRPC().SumServerDailyStats(this.UserContext(), &pb.SumServerDailyStatsRequest{
ServerId: params.ServerId,
Day: timeutil.Format("Ymd"),
})
if err != nil {
this.ErrorPage(err)
return
}
var trafficDailyBytes int64 = 0
if trafficStatResp.ServerDailyStat != nil {
trafficDailyBytes = trafficStatResp.ServerDailyStat.Bytes
}
this.Data["trafficDailyFormat"] = numberutils.FormatBytes(trafficDailyBytes)
}
{
trafficStatResp, err := this.RPC().ServerDailyRPC().SumServerMonthlyStats(this.UserContext(), &pb.SumServerMonthlyStatsRequest{
ServerId: params.ServerId,
Month: timeutil.Format("Ym"),
})
if err != nil {
this.ErrorPage(err)
return
}
var trafficMonthlyBytes int64 = 0
if trafficStatResp.ServerMonthlyStat != nil {
trafficMonthlyBytes = trafficStatResp.ServerMonthlyStat.Bytes
}
this.Data["trafficMonthlyFormat"] = numberutils.FormatBytes(trafficMonthlyBytes)
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
IsChanged bool
UserPlanId int64
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if !params.IsChanged {
this.Success()
return
}
if params.UserPlanId <= 0 { // 取消绑定
defer this.CreateLogInfo(codes.UserPlan_LogCancelUserPlanFromServer, params.ServerId)
} else { // 变更绑定
defer this.CreateLogInfo(codes.UserPlan_LogBindUserPlanToServer, params.ServerId, params.UserPlanId)
}
// 前后套餐是否一致
oldUserPlanResp, err := this.RPC().ServerRPC().FindServerUserPlan(this.UserContext(), &pb.FindServerUserPlanRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
if oldUserPlanResp.UserPlan != nil && oldUserPlanResp.UserPlan.Id == params.UserPlanId {
this.Success()
return
}
// 检查套餐
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.Plan == nil {
this.NotFound("userPlan", params.UserPlanId)
return
}
var plan = userPlan.Plan
// server
if plan.TotalServers > 0 {
countServersResp, err := this.RPC().ServerRPC().CountAllUserServers(this.UserContext(), &pb.CountAllUserServersRequest{
UserPlanId: params.UserPlanId,
})
if err != nil {
this.ErrorPage(err)
return
}
if countServersResp.Count+1 > int64(plan.TotalServers) {
this.Fail("已绑定网站数超出当前套餐限制")
return
}
}
countServerNamesResp, err := this.RPC().ServerRPC().CountServerNames(this.UserContext(), &pb.CountServerNamesRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
if plan.TotalServerNamesPerServer > 0 {
if countServerNamesResp.Count > int64(plan.TotalServerNamesPerServer) {
this.Fail("当前网站域名数超出当前套餐限制")
return
}
}
if plan.TotalServerNames > 0 {
countAllServerNamesResp, err := this.RPC().ServerRPC().CountAllServerNamesWithUserId(this.UserContext(), &pb.CountAllServerNamesWithUserIdRequest{
UserPlanId: params.UserPlanId,
})
if err != nil {
this.ErrorPage(err)
return
}
if countAllServerNamesResp.Count+countServerNamesResp.Count > int64(plan.TotalServerNames) {
this.Fail("已绑定域名数超出当前套餐限制")
return
}
}
}
// 提交修改
_, err = this.RPC().ServerRPC().UpdateServerUserPlan(this.UserContext(), &pb.UpdateServerUserPlanRequest{
ServerId: params.ServerId,
UserPlanId: params.UserPlanId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,19 @@
package plan
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/plan").
GetPost("", new(IndexAction)).
Post("/data", new(DataAction)).
EndAll()
})
}

View File

@@ -0,0 +1,241 @@
package redirects
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/dns/domains/domainutils"
"github.com/iwind/TeaGo/actions"
"net/url"
"regexp"
"strings"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
}) {
this.Data["featureIsOn"] = true // MUST BE always true
this.Data["statusList"] = serverconfigs.AllHTTPRedirectStatusList()
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Type string
// URL
Mode string
BeforeURL string
AfterURL string
MatchPrefix bool
MatchRegexp bool
KeepRequestURI bool
KeepArgs bool
// 域名
DomainsAll bool
DomainsBeforeJSON []byte
DomainBeforeIgnorePorts bool
DomainAfter string
DomainAfterScheme string
// 端口
PortsAll bool
PortsBefore []string
PortAfter int
PortAfterScheme string
Status int
ExceptDomainsJSON []byte
OnlyDomainsJSON []byte
CondsJSON []byte
IsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var config = &serverconfigs.HTTPHostRedirectConfig{}
config.Type = params.Type
config.Status = params.Status
config.IsOn = params.IsOn
switch params.Type {
case serverconfigs.HTTPHostRedirectTypeURL:
params.Must.
Field("beforeURL", params.BeforeURL).
Require("请填写跳转前的URL")
// 校验格式
if params.MatchRegexp {
_, err := regexp.Compile(params.BeforeURL)
if err != nil {
this.Fail("跳转前URL正则表达式错误" + err.Error())
}
} else {
u, err := url.Parse(params.BeforeURL)
if err != nil {
this.FailField("beforeURL", "请输入正确的跳转前URL")
}
if (u.Scheme != "http" && u.Scheme != "https") ||
len(u.Host) == 0 {
this.FailField("beforeURL", "请输入正确的跳转前URL")
}
}
params.Must.
Field("afterURL", params.AfterURL).
Require("请填写跳转后URL")
// 校验格式
if params.MatchRegexp {
// 正则表达式情况下不做校验
} else {
u, err := url.Parse(params.AfterURL)
if err != nil {
this.FailField("afterURL", "请输入正确的跳转后URL")
}
if (u.Scheme != "http" && u.Scheme != "https") ||
len(u.Host) == 0 {
this.FailField("afterURL", "请输入正确的跳转后URL")
}
}
config.Mode = params.Mode
config.BeforeURL = params.BeforeURL
config.AfterURL = params.AfterURL
config.MatchPrefix = params.MatchPrefix
config.MatchRegexp = params.MatchRegexp
config.KeepRequestURI = params.KeepRequestURI
config.KeepArgs = params.KeepArgs
case serverconfigs.HTTPHostRedirectTypeDomain:
config.DomainsAll = params.DomainsAll
var domainsBefore = []string{}
if len(params.DomainsBeforeJSON) > 0 {
err := json.Unmarshal(params.DomainsBeforeJSON, &domainsBefore)
if err != nil {
this.Fail("错误的域名格式:" + err.Error())
return
}
}
config.DomainsBefore = domainsBefore
if !params.DomainsAll {
if len(domainsBefore) == 0 {
this.Fail("请输入跳转前域名")
return
}
}
config.DomainBeforeIgnorePorts = params.DomainBeforeIgnorePorts
if len(params.DomainAfter) == 0 {
this.FailField("domainAfter", "请输入跳转后域名")
return
}
// 检查用户输入的是否为域名
if !domainutils.ValidateDomainFormat(params.DomainAfter) {
// 是否为URL
u, err := url.Parse(params.DomainAfter)
if err == nil {
if len(u.Host) == 0 {
this.FailField("domainAfter", "跳转后域名输入不正确")
return
}
params.DomainAfter = u.Host
} else {
this.FailField("domainAfter", "跳转后域名输入不正确")
return
}
}
config.DomainAfter = params.DomainAfter
config.DomainAfterScheme = params.DomainAfterScheme
case serverconfigs.HTTPHostRedirectTypePort:
config.PortsAll = params.PortsAll
config.PortsBefore = params.PortsBefore
var portReg = regexp.MustCompile(`^\d+$`)
var portRangeReg = regexp.MustCompile(`^\d+-\d+$`)
if !config.PortsAll {
for _, port := range params.PortsBefore {
port = strings.ReplaceAll(port, " ", "")
if !portReg.MatchString(port) && !portRangeReg.MatchString(port) {
this.Fail("端口号" + port + "填写错误(请输入单个端口号或一个端口范围)")
return
}
}
if len(params.PortsBefore) == 0 {
this.Fail("请输入跳转前端口")
return
}
}
if params.PortAfter <= 0 {
this.FailField("portAfter", "请输入跳转后端口")
return
}
config.PortAfter = params.PortAfter
config.PortAfterScheme = params.PortAfterScheme
}
params.Must.
Field("status", params.Status).
Gte(0, "请选择正确的跳转状态码")
// 域名
if len(params.ExceptDomainsJSON) > 0 {
var exceptDomains = []string{}
err := json.Unmarshal(params.ExceptDomainsJSON, &exceptDomains)
if err != nil {
this.ErrorPage(err)
return
}
config.ExceptDomains = exceptDomains
}
if len(params.OnlyDomainsJSON) > 0 {
var onlyDomains = []string{}
err := json.Unmarshal(params.OnlyDomainsJSON, &onlyDomains)
if err != nil {
this.ErrorPage(err)
return
}
config.OnlyDomains = onlyDomains
}
// 校验匹配条件
var conds *shared.HTTPRequestCondsConfig
if len(params.CondsJSON) > 0 {
conds = &shared.HTTPRequestCondsConfig{}
err := json.Unmarshal(params.CondsJSON, conds)
if err != nil {
this.Fail("匹配条件校验失败:" + err.Error())
}
err = conds.Init()
if err != nil {
this.Fail("匹配条件校验失败:" + err.Error())
}
}
config.Conds = conds
// 校验配置
err := config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
this.Data["redirect"] = config
this.Success()
}

View File

@@ -0,0 +1,58 @@
package redirects
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/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("redirects")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerHostRedirects, params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["redirects"] = webConfig.HostRedirects
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
WebId int64
HostRedirectsJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.ServerRedirect_LogUpdateRedirects, params.WebId)
_, err := this.RPC().HTTPWebRPC().UpdateHTTPWebHostRedirects(this.UserContext(), &pb.UpdateHTTPWebHostRedirectsRequest{
HttpWebId: params.WebId,
HostRedirectsJSON: params.HostRedirectsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,19 @@
package redirects
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/redirects").
GetPost("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
EndAll()
})
}

View File

@@ -0,0 +1,86 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package uam
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("referer")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["serverId"] = params.ServerId
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerReferers, params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
var referersConfig = webConfig.Referers
if referersConfig == nil {
referersConfig = &serverconfigs.ReferersConfig{
IsPrior: false,
IsOn: false,
AllowEmpty: true,
AllowSameDomain: true,
AllowDomains: nil,
CheckOrigin: true,
}
}
this.Data["referersConfig"] = referersConfig
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
ReferersJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerReferers, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerReferer_LogUpdateReferers, params.WebId)
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebReferers(this.UserContext(), &pb.UpdateHTTPWebReferersRequest{
HttpWebId: params.WebId,
ReferersJSON: params.ReferersJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package uam
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/referers").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,108 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package remoteAddr
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("remoteAddr")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
// 服务分组设置
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(this.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasGroupConfig"] = groupResp.HasRemoteAddrConfig
this.Data["groupSettingURL"] = "/servers/groups/group/settings/remoteAddr?groupId=" + types.String(groupResp.ServerGroupId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["remoteAddrConfig"] = webConfig.RemoteAddr
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
RemoteAddrJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{}
err := json.Unmarshal(params.RemoteAddrJSON, remoteAddrConfig)
if err != nil {
this.Fail("参数校验失败:" + err.Error())
return
}
remoteAddrConfig.Value = strings.TrimSpace(remoteAddrConfig.Value)
switch remoteAddrConfig.Type {
case serverconfigs.HTTPRemoteAddrTypeRequestHeader:
if len(remoteAddrConfig.RequestHeaderName) == 0 {
this.FailField("requestHeaderName", "请输入请求报头")
return
}
if !regexp.MustCompile(`^[\w-_,]+$`).MatchString(remoteAddrConfig.RequestHeaderName) {
this.FailField("requestHeaderName", "请求报头中只能含有数字、英文字母、下划线、中划线")
return
}
remoteAddrConfig.Value = "${header." + remoteAddrConfig.RequestHeaderName + "}"
case serverconfigs.HTTPRemoteAddrTypeVariable:
if len(remoteAddrConfig.Value) == 0 {
this.FailField("value", "请输入自定义变量")
return
}
}
err = remoteAddrConfig.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
return
}
remoteAddrJSON, err := json.Marshal(remoteAddrConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRemoteAddr(this.UserContext(), &pb.UpdateHTTPWebRemoteAddrRequest{
HttpWebId: params.WebId,
RemoteAddrJSON: remoteAddrJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package remoteAddr
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/remoteAddr").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,85 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package requestlimit
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/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("requestLimit")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerRequestLimit, params.ServerId)
// 服务分组设置
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(this.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasGroupConfig"] = groupResp.HasRequestLimitConfig
this.Data["groupSettingURL"] = "/servers/groups/group/settings/requestLimit?groupId=" + types.String(groupResp.ServerGroupId)
this.Data["serverId"] = params.ServerId
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["requestLimitConfig"] = webConfig.RequestLimit
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
RequestLimitJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerRequestLimit, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerRequestLimit_LogUpdateRequestLimitSettings, params.WebId)
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRequestLimit(this.UserContext(), &pb.UpdateHTTPWebRequestLimitRequest{
HttpWebId: params.WebId,
RequestLimitJSON: params.RequestLimitJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package requestlimit
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/requestLimit").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,126 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package requestScripts
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"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("requestScripts")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerScript, params.ServerId)
// 服务分组设置
groupResp, err := this.RPC().ServerGroupRPC().FindEnabledServerGroupConfigInfo(this.UserContext(), &pb.FindEnabledServerGroupConfigInfoRequest{
ServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["hasGroupConfig"] = groupResp.HasRequestScriptsConfig
this.Data["groupSettingURL"] = "/servers/groups/group/settings/requestScripts?groupId=" + types.String(groupResp.ServerGroupId)
this.Data["serverId"] = params.ServerId
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["requestScriptsConfig"] = webConfig.RequestScripts
// 审核状态
var auditingStatusMaps = []maps.Map{}
if webConfig.RequestScripts != nil {
for _, group := range webConfig.RequestScripts.AllGroups() {
for _, script := range group.Scripts {
if len(script.AuditingCodeMD5) > 0 {
userScriptResp, err := this.RPC().UserScriptRPC().FindUserScriptWithMD5(this.UserContext(), &pb.FindUserScriptWithMD5Request{CodeMD5: script.AuditingCodeMD5})
if err != nil {
this.ErrorPage(err)
return
}
var userScript = userScriptResp.UserScript
if userScript != nil {
auditingStatusMaps = append(auditingStatusMaps, maps.Map{
"md5": script.AuditingCodeMD5,
"isPassed": userScript.IsPassed,
"isRejected": userScript.IsRejected,
"rejectedReason": userScript.RejectedReason,
})
}
}
}
}
}
this.Data["auditingStatus"] = auditingStatusMaps
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
RequestScriptsJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
if !this.ValidateFeature(userconfigs.UserFeatureCodeServerScript, serverId) {
this.Fail("permission denied")
return
}
defer this.CreateLogInfo(codes.ServerScript_LogUpdateScripts, params.WebId)
var config = &serverconfigs.HTTPRequestScriptsConfig{}
err = json.Unmarshal(params.RequestScriptsJSON, config)
if err != nil {
this.Fail("解析配置发生错误:" + err.Error())
}
err = config.Init()
if err != nil {
this.Fail("解析配置发生错误:" + err.Error())
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRequestScripts(this.UserContext(), &pb.UpdateHTTPWebRequestScriptsRequest{
HttpWebId: params.WebId,
RequestScriptsJSON: params.RequestScriptsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package requestScripts
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/requestScripts").
GetPost("", new(IndexAction)).
EndAll()
})
}

View File

@@ -0,0 +1,101 @@
package reverseProxy
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
)
// IndexAction 源站列表
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.FirstMenu("index")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
serverTypeResp, err := this.RPC().ServerRPC().FindEnabledServerType(this.UserContext(), &pb.FindEnabledServerTypeRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var serverType = serverTypeResp.Type
reverseProxyResp, err := this.RPC().ServerRPC().FindAndInitServerReverseProxyConfig(this.UserContext(), &pb.FindAndInitServerReverseProxyConfigRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
err = json.Unmarshal(reverseProxyResp.ReverseProxyRefJSON, reverseProxyRef)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["reverseProxyRef"] = reverseProxyRef
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["reverseProxyConfig"] = reverseProxy
this.Data["serverType"] = serverType
var primaryOriginMaps = []maps.Map{}
var backupOriginMaps = []maps.Map{}
for _, originConfig := range reverseProxy.PrimaryOrigins {
var domains = originConfig.Domains
if len(domains) == 0 {
domains = []string{}
}
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": domains,
"hasCert": originConfig.Cert != nil,
"host": originConfig.RequestHost,
"followPort": originConfig.FollowPort,
"http2Enabled": originConfig.HTTP2Enabled,
}
primaryOriginMaps = append(primaryOriginMaps, m)
}
for _, originConfig := range reverseProxy.BackupOrigins {
var domains = originConfig.Domains
if len(domains) == 0 {
domains = []string{}
}
var m = maps.Map{
"id": originConfig.Id,
"weight": originConfig.Weight,
"addr": originConfig.AddrSummary(),
"isOSS": originConfig.IsOSS(),
"name": originConfig.Name,
"isOn": originConfig.IsOn,
"domains": domains,
"hasCert": originConfig.Cert != nil,
"host": originConfig.RequestHost,
"followPort": originConfig.FollowPort,
"http2Enabled": originConfig.HTTP2Enabled,
}
backupOriginMaps = append(backupOriginMaps, m)
}
this.Data["primaryOrigins"] = primaryOriginMaps
this.Data["backupOrigins"] = backupOriginMaps
this.Show()
}

View File

@@ -0,0 +1,23 @@
package reverseProxy
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Data("mainTab", "setting").
Data("secondMenuItem", "reverseProxy").
Prefix("/servers/server/settings/reverseProxy").
Get("", new(IndexAction)).
GetPost("/scheduling", new(SchedulingAction)).
GetPost("/updateSchedulingPopup", new(UpdateSchedulingPopupAction)).
GetPost("/setting", new(SettingAction)).
EndAll()
})
}

View File

@@ -0,0 +1,45 @@
package reverseProxy
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/schedulingconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type SchedulingAction struct {
actionutils.ParentAction
}
func (this *SchedulingAction) Init() {
this.FirstMenu("scheduling")
}
func (this *SchedulingAction) RunGet(params struct {
ServerId int64
}) {
reverseProxyResp, err := this.RPC().ServerRPC().FindAndInitServerReverseProxyConfig(this.UserContext(), &pb.FindAndInitServerReverseProxyConfigRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
reverseProxy := &serverconfigs.ReverseProxyConfig{}
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["reverseProxyId"] = reverseProxy.Id
schedulingCode := reverseProxy.FindSchedulingConfig().Code
schedulingMap := schedulingconfigs.FindSchedulingType(schedulingCode)
if schedulingMap == nil {
this.ErrorPage(errors.New("invalid scheduling code '" + schedulingCode + "'"))
return
}
this.Data["scheduling"] = schedulingMap
this.Show()
}

View File

@@ -0,0 +1,112 @@
package reverseProxy
import (
"context"
"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/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
)
type SettingAction struct {
actionutils.ParentAction
}
func (this *SettingAction) Init() {
this.FirstMenu("setting")
}
func (this *SettingAction) RunGet(params struct {
ServerId int64
}) {
reverseProxyResp, err := this.RPC().ServerRPC().FindAndInitServerReverseProxyConfig(this.UserContext(), &pb.FindAndInitServerReverseProxyConfigRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
err = json.Unmarshal(reverseProxyResp.ReverseProxyRefJSON, reverseProxyRef)
if err != nil {
this.ErrorPage(err)
return
}
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["reverseProxyRef"] = reverseProxyRef
this.Data["reverseProxyConfig"] = reverseProxy
this.Show()
}
func (this *SettingAction) RunPost(params struct {
ServerId int64
ReverseProxyRefJSON []byte
ReverseProxyJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.ServerReverseProxy_LogUpdateServerReverseProxySettings, params.ServerId)
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
if err != nil {
this.ErrorPage(err)
return
}
err = reverseProxyConfig.Init(context.TODO())
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
// 设置是否启用
_, err = this.RPC().ServerRPC().UpdateServerReverseProxy(this.UserContext(), &pb.UpdateServerReverseProxyRequest{
ServerId: params.ServerId,
ReverseProxyJSON: params.ReverseProxyRefJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// PROXY Protocol
var proxyProtocolJSON = []byte{}
if reverseProxyConfig.ProxyProtocol != nil {
proxyProtocolJSON, err = json.Marshal(reverseProxyConfig.ProxyProtocol)
if err != nil {
this.ErrorPage(err)
return
}
}
// 设置反向代理相关信息
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxy(this.UserContext(), &pb.UpdateReverseProxyRequest{
ReverseProxyId: reverseProxyConfig.Id,
RequestHostType: types.Int32(reverseProxyConfig.RequestHostType),
RequestHost: reverseProxyConfig.RequestHost,
RequestHostExcludingPort: reverseProxyConfig.RequestHostExcludingPort,
RequestURI: reverseProxyConfig.RequestURI,
StripPrefix: reverseProxyConfig.StripPrefix,
AutoFlush: reverseProxyConfig.AutoFlush,
AddHeaders: reverseProxyConfig.AddHeaders,
FollowRedirects: reverseProxyConfig.FollowRedirects,
ProxyProtocolJSON: proxyProtocolJSON,
Retry50X: reverseProxyConfig.Retry50X,
Retry40X: reverseProxyConfig.Retry40X,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,163 @@
package reverseProxy
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"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/schedulingconfigs"
"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"
)
// 修改调度算法
type UpdateSchedulingPopupAction struct {
actionutils.ParentAction
}
func (this *UpdateSchedulingPopupAction) Init() {
}
func (this *UpdateSchedulingPopupAction) RunGet(params struct {
Type string
ServerId int64
ReverseProxyId int64
}) {
serverConfig, err := dao.SharedServerDAO.FindEnabledServerConfig(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig == nil {
this.NotFound("server", params.ServerId)
return
}
this.Data["dataType"] = params.Type
this.Data["serverId"] = params.ServerId
this.Data["reverseProxyId"] = params.ReverseProxyId
reverseProxyResp, err := this.RPC().ReverseProxyRPC().FindEnabledReverseProxyConfig(this.UserContext(), &pb.FindEnabledReverseProxyConfigRequest{
ReverseProxyId: params.ReverseProxyId,
})
if err != nil {
this.ErrorPage(err)
return
}
configData := reverseProxyResp.ReverseProxyJSON
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
err = json.Unmarshal(configData, reverseProxyConfig)
if err != nil {
this.ErrorPage(err)
return
}
schedulingObject := &serverconfigs.SchedulingConfig{
Code: "random",
Options: nil,
}
if reverseProxyConfig.Scheduling != nil {
schedulingObject = reverseProxyConfig.Scheduling
}
this.Data["scheduling"] = schedulingObject
// 调度类型
schedulingTypes := []maps.Map{}
for _, m := range schedulingconfigs.AllSchedulingTypes() {
networks, ok := m["networks"]
if !ok {
continue
}
if !types.IsSlice(networks) {
continue
}
if (serverConfig.IsHTTPFamily() && lists.Contains(networks, "http")) ||
(serverConfig.IsTCPFamily() && lists.Contains(networks, "tcp")) ||
(serverConfig.IsUDPFamily() && lists.Contains(networks, "udp")) {
schedulingTypes = append(schedulingTypes, m)
}
}
this.Data["schedulingTypes"] = schedulingTypes
this.Show()
}
func (this *UpdateSchedulingPopupAction) RunPost(params struct {
ServerId int64
ReverseProxyId int64
Type string
HashKey string
StickyType string
StickyParam string
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.ReverseProxy_LogUpdateReverseProxyScheduling, params.ReverseProxyId)
reverseProxyResp, err := this.RPC().ReverseProxyRPC().FindEnabledReverseProxyConfig(this.UserContext(), &pb.FindEnabledReverseProxyConfigRequest{ReverseProxyId: params.ReverseProxyId})
if err != nil {
this.ErrorPage(err)
return
}
configData := reverseProxyResp.ReverseProxyJSON
reverseProxy := &serverconfigs.ReverseProxyConfig{}
err = json.Unmarshal(configData, reverseProxy)
if err != nil {
this.ErrorPage(err)
return
}
if reverseProxy.Scheduling == nil {
reverseProxy.FindSchedulingConfig()
}
options := maps.Map{}
if params.Type == "hash" {
params.Must.
Field("hashKey", params.HashKey).
Require("请输入Key")
options["key"] = params.HashKey
} else if params.Type == "sticky" {
params.Must.
Field("stickyType", params.StickyType).
Require("请选择参数类型").
Field("stickyParam", params.StickyParam).
Require("请输入参数名").
Match("^[a-zA-Z0-9]+$", "参数名只能是英文字母和数字的组合").
MaxCharacters(50, "参数名长度不能超过50位")
options["type"] = params.StickyType
options["param"] = params.StickyParam
}
if schedulingconfigs.FindSchedulingType(params.Type) == nil {
this.Fail("不支持此种算法")
}
reverseProxy.Scheduling.Code = params.Type
reverseProxy.Scheduling.Options = options
schedulingData, err := json.Marshal(reverseProxy.Scheduling)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxyScheduling(this.UserContext(), &pb.UpdateReverseProxySchedulingRequest{
ReverseProxyId: params.ReverseProxyId,
SchedulingJSON: schedulingData,
})
if err != nil {
this.ErrorPage(err)
}
this.Success()
}

View File

@@ -0,0 +1,133 @@
package rewrite
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"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"regexp"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
}
func (this *CreatePopupAction) RunGet(params struct {
WebId int64
}) {
// 获取网站ID
serverId, err := this.FindServerIdWithWebId(params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerRewriteRules, serverId)
this.Data["webId"] = params.WebId
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
WebId int64
Pattern string
Replace string
Mode string
RedirectStatus int
ProxyHost string
WithQuery bool
IsBreak bool
IsOn bool
CondsJSON []byte
Must *actions.Must
}) {
params.Must.
Field("pattern", params.Pattern).
Require("请输入匹配规则").
Expect(func() (message string, success bool) {
_, err := regexp.Compile(params.Pattern)
if err != nil {
return "匹配规则错误:" + err.Error(), false
}
return "", true
})
params.Must.
Field("replace", params.Replace).
Require("请输入目标URL")
// 校验匹配条件
if len(params.CondsJSON) > 0 {
conds := &shared.HTTPRequestCondsConfig{}
err := json.Unmarshal(params.CondsJSON, conds)
if err != nil {
this.Fail("匹配条件校验失败:" + err.Error())
}
err = conds.Init()
if err != nil {
this.Fail("匹配条件校验失败:" + err.Error())
}
}
// web配置
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.UserContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
// 创建
createResp, err := this.RPC().HTTPRewriteRuleRPC().CreateHTTPRewriteRule(this.UserContext(), &pb.CreateHTTPRewriteRuleRequest{
Pattern: params.Pattern,
Replace: params.Replace,
Mode: params.Mode,
RedirectStatus: types.Int32(params.RedirectStatus),
ProxyHost: params.ProxyHost,
WithQuery: params.WithQuery,
IsBreak: params.IsBreak,
IsOn: params.IsOn,
CondsJSON: params.CondsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
ref := &serverconfigs.HTTPRewriteRef{
IsOn: true,
RewriteRuleId: createResp.RewriteRuleId,
}
webConfig.RewriteRefs = append(webConfig.RewriteRefs, ref)
refsJSON, err := json.Marshal(webConfig.RewriteRefs)
if err != nil {
this.ErrorPage(err)
return
}
// 设置Web中的重写规则
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRewriteRules(this.UserContext(), &pb.UpdateHTTPWebRewriteRulesRequest{
HttpWebId: params.WebId,
RewriteRulesJSON: refsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// 日志
defer this.CreateLogInfo(codes.HTTPRewriteRule_LogCreateRewriteRule, params.WebId, createResp.RewriteRuleId)
this.Success()
}

View File

@@ -0,0 +1,51 @@
package rewrite
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"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
WebId int64
RewriteRuleId int64
}) {
defer this.CreateLogInfo(codes.HTTPRewriteRule_LogDeleteRewriteRule, params.WebId, params.RewriteRuleId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.UserContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
refs := []*serverconfigs.HTTPRewriteRef{}
for _, ref := range webConfig.RewriteRefs {
if ref.RewriteRuleId == params.RewriteRuleId {
continue
}
refs = append(refs, ref)
}
refsJSON, err := json.Marshal(refs)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRewriteRules(this.UserContext(), &pb.UpdateHTTPWebRewriteRulesRequest{
HttpWebId: params.WebId,
RewriteRulesJSON: refsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,37 @@
package rewrite
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("rewrite")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
this.Data["featureIsOn"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerRewriteRules, params.ServerId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
if len(webConfig.RewriteRules) == 0 {
this.Data["rewriteRules"] = []interface{}{}
} else {
this.Data["rewriteRules"] = webConfig.RewriteRules
}
this.Show()
}

View File

@@ -0,0 +1,22 @@
package rewrite
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/rewrite").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
GetPost("/updatePopup", new(UpdatePopupAction)).
Post("/delete", new(DeleteAction)).
Post("/sort", new(SortAction)).
EndAll()
})
}

View File

@@ -0,0 +1,53 @@
package rewrite
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"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type SortAction struct {
actionutils.ParentAction
}
func (this *SortAction) RunPost(params struct {
WebId int64
RewriteRuleIds []int64
}) {
defer this.CreateLogInfo(codes.HTTPRewriteRule_LogSortRewriteRules, params.WebId)
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.UserContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
refsMap := map[int64]*serverconfigs.HTTPRewriteRef{}
for _, ref := range webConfig.RewriteRefs {
refsMap[ref.RewriteRuleId] = ref
}
newRefs := []*serverconfigs.HTTPRewriteRef{}
for _, rewriteRuleId := range params.RewriteRuleIds {
ref, ok := refsMap[rewriteRuleId]
if ok {
newRefs = append(newRefs, ref)
}
}
refsJSON, err := json.Marshal(newRefs)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRewriteRules(this.UserContext(), &pb.UpdateHTTPWebRewriteRulesRequest{
HttpWebId: params.WebId,
RewriteRulesJSON: refsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,116 @@
package rewrite
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/shared"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/types"
"regexp"
)
type UpdatePopupAction struct {
actionutils.ParentAction
}
func (this *UpdatePopupAction) Init() {
}
func (this *UpdatePopupAction) RunGet(params struct {
WebId int64
RewriteRuleId int64
}) {
this.Data["webId"] = params.WebId
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithId(this.UserContext(), params.WebId)
if err != nil {
this.ErrorPage(err)
return
}
isFound := false
for _, rewriteRule := range webConfig.RewriteRules {
if rewriteRule.Id == params.RewriteRuleId {
this.Data["rewriteRule"] = rewriteRule
isFound = true
break
}
}
if !isFound {
this.WriteString("找不到要修改的重写规则")
return
}
this.Show()
}
func (this *UpdatePopupAction) RunPost(params struct {
WebId int64
RewriteRuleId int64
Pattern string
Replace string
Mode string
RedirectStatus int
ProxyHost string
WithQuery bool
IsBreak bool
IsOn bool
CondsJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.HTTPRewriteRule_LogUpdateRewriteRule, params.WebId, params.RewriteRuleId)
params.Must.
Field("pattern", params.Pattern).
Require("请输入匹配规则").
Expect(func() (message string, success bool) {
_, err := regexp.Compile(params.Pattern)
if err != nil {
return "匹配规则错误:" + err.Error(), false
}
return "", true
})
params.Must.
Field("replace", params.Replace).
Require("请输入目标URL")
// 校验匹配条件
if len(params.CondsJSON) > 0 {
conds := &shared.HTTPRequestCondsConfig{}
err := json.Unmarshal(params.CondsJSON, conds)
if err != nil {
this.Fail("匹配条件校验失败:" + err.Error())
}
err = conds.Init()
if err != nil {
this.Fail("匹配条件校验失败:" + err.Error())
}
}
// 修改
_, err := this.RPC().HTTPRewriteRuleRPC().UpdateHTTPRewriteRule(this.UserContext(), &pb.UpdateHTTPRewriteRuleRequest{
RewriteRuleId: params.RewriteRuleId,
Pattern: params.Pattern,
Replace: params.Replace,
Mode: params.Mode,
RedirectStatus: types.Int32(params.RedirectStatus),
ProxyHost: params.ProxyHost,
WithQuery: params.WithQuery,
IsBreak: params.IsBreak,
IsOn: params.IsOn,
CondsJSON: params.CondsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,40 @@
package serverNames
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
// 审核域名
type AuditAction struct {
actionutils.ParentAction
}
func (this *AuditAction) RunPost(params struct {
ServerId int64
AuditingOK bool
AuditingReason string
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.Server_LogSubmitAuditingServer, params.ServerId)
if !params.AuditingOK && len(params.AuditingReason) == 0 {
this.FailField("auditingReason", "请输入审核不通过原因")
}
_, err := this.RPC().ServerRPC().UpdateServerNamesAuditing(this.UserContext(), &pb.UpdateServerNamesAuditingRequest{
ServerId: params.ServerId,
AuditingResult: &pb.ServerNameAuditingResult{
IsOk: params.AuditingOK,
Reason: params.AuditingReason,
},
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,161 @@
package serverNames
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/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"strings"
)
// IndexAction 域名管理
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.FirstMenu("index")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
serverNamesResp, err := this.RPC().ServerRPC().FindServerNames(this.UserContext(), &pb.FindServerNamesRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var serverNamesConfig = []*serverconfigs.ServerNameConfig{}
this.Data["isAuditing"] = serverNamesResp.IsAuditing
this.Data["auditingResult"] = maps.Map{
"isOk": true,
}
if serverNamesResp.IsAuditing {
serverNamesResp.ServerNamesJSON = serverNamesResp.AuditingServerNamesJSON
} else if serverNamesResp.AuditingResult != nil {
if !serverNamesResp.AuditingResult.IsOk {
serverNamesResp.ServerNamesJSON = serverNamesResp.AuditingServerNamesJSON
}
this.Data["auditingResult"] = maps.Map{
"isOk": serverNamesResp.AuditingResult.IsOk,
"reason": serverNamesResp.AuditingResult.Reason,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", serverNamesResp.AuditingResult.CreatedAt),
}
}
if len(serverNamesResp.ServerNamesJSON) > 0 {
err := json.Unmarshal(serverNamesResp.ServerNamesJSON, &serverNamesConfig)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["serverNames"] = serverNamesConfig
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ServerId int64
ServerNames string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
// 记录日志
defer this.CreateLogInfo(codes.Server_ServerNamesLogUpdateServerNames, params.ServerId)
// 检查用户所在集群
clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()})
if err != nil {
this.ErrorPage(err)
return
}
var clusterId = clusterIdResp.NodeClusterId
var serverNames = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal([]byte(params.ServerNames), &serverNames)
if err != nil {
this.Fail("域名解析失败:" + err.Error())
}
serverconfigs.NormalizeServerNames(serverNames)
// 检查域名是否已经存在
var allServerNames = serverconfigs.PlainServerNames(serverNames)
if len(allServerNames) > 0 {
dupResp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.UserContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
ServerNames: allServerNames,
NodeClusterId: clusterId,
ExcludeServerId: params.ServerId,
})
if err != nil {
this.ErrorPage(err)
return
}
if len(dupResp.DuplicatedServerNames) > 0 {
this.Fail("域名 " + strings.Join(dupResp.DuplicatedServerNames, ", ") + " 已经被其他网站所占用,不能重复使用")
}
}
// 检查套餐限制
userPlanResp, err := this.RPC().ServerRPC().FindServerUserPlan(this.UserContext(), &pb.FindServerUserPlanRequest{ServerId: params.ServerId})
if err != nil {
this.ErrorPage(err)
return
}
var userPlan = userPlanResp.UserPlan
if userPlan != nil && userPlan.Id > 0 {
if userPlan.DayTo < timeutil.Format("Y-m-d") {
this.Fail("当前网站绑定的套餐已过期,请续费或更换套餐后再操作")
return
}
var plan = userPlan.Plan
if plan == nil {
this.Fail("当前网站绑定的套餐已失效,请续费或更换套餐后再操作")
return
}
var countServerNames = len(serverconfigs.PlainServerNames(serverNames))
if countServerNames > 0 {
if plan.TotalServerNames > 0 {
totalServerNamesResp, err := this.RPC().ServerRPC().CountAllServerNamesWithUserId(this.UserContext(), &pb.CountAllServerNamesWithUserIdRequest{
UserId: this.UserId(),
UserPlanId: userPlan.Id,
})
if err != nil {
this.ErrorPage(err)
return
}
var totalServerNames = totalServerNamesResp.Count
if totalServerNames+int64(countServerNames) > int64(plan.TotalServerNames) {
this.Fail("当前域名数量超出当前套餐可以绑定的域名数量(" + types.String(plan.TotalServerNames) + "")
return
}
}
if plan.TotalServerNamesPerServer > 0 {
if countServerNames > types.Int(plan.TotalServerNamesPerServer) {
this.Fail("当前网站域名数量超出套餐限制(" + types.String(plan.TotalServerNamesPerServer) + "")
return
}
}
}
}
_, err = this.RPC().ServerRPC().UpdateServerNames(this.UserContext(), &pb.UpdateServerNamesRequest{
ServerId: params.ServerId,
ServerNamesJSON: []byte(params.ServerNames),
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,21 @@
package serverNames
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Data("mainTab", "setting").
Data("secondMenuItem", "serverName").
Prefix("/servers/server/settings/serverNames").
GetPost("", new(IndexAction)).
Post("/audit", new(AuditAction)).
EndAll()
})
}

View File

@@ -0,0 +1,54 @@
package stat
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"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "setting", "index")
this.SecondMenu("stat")
}
func (this *IndexAction) RunGet(params struct {
ServerId int64
}) {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.UserContext(), params.ServerId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["webId"] = webConfig.Id
this.Data["statConfig"] = webConfig.StatRef
this.Show()
}
func (this *IndexAction) RunPost(params struct {
WebId int64
StatJSON []byte
Must *actions.Must
}) {
defer this.CreateLogInfo(codes.ServerStat_LogUpdateStatSettings, params.WebId)
// TODO 校验配置
_, err := this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.UserContext(), &pb.UpdateHTTPWebStatRequest{
HttpWebId: params.WebId,
StatJSON: params.StatJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,18 @@
package stat
import (
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Helper(serverutils.NewServerHelper()).
Prefix("/servers/server/settings/stat").
GetPost("", new(IndexAction)).
EndAll()
})
}

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