590 lines
14 KiB
Go
590 lines
14 KiB
Go
package servers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
|
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
|
"github.com/TeaOSLab/EdgeUser/internal/utils/domainutils"
|
|
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
|
|
"github.com/iwind/TeaGo/actions"
|
|
"github.com/iwind/TeaGo/lists"
|
|
"github.com/iwind/TeaGo/maps"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
type CreateAction struct {
|
|
actionutils.ParentAction
|
|
}
|
|
|
|
func (this *CreateAction) Init() {
|
|
this.Nav("", "", "create")
|
|
}
|
|
|
|
func (this *CreateAction) RunGet(params struct{}) {
|
|
// 检查用户状态
|
|
this.CheckUserStatus()
|
|
|
|
clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
this.Data["clusterId"] = clusterIdResp.NodeClusterId
|
|
|
|
// 套餐
|
|
userPlansResp, err := this.RPC().UserPlanRPC().FindAllEnabledUserPlansForServer(this.UserContext(), &pb.FindAllEnabledUserPlansForServerRequest{
|
|
UserId: this.UserId(),
|
|
ServerId: 0,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var userPlanMaps = []maps.Map{}
|
|
for _, userPlan := range userPlansResp.UserPlans {
|
|
if userPlan.Plan == nil {
|
|
continue
|
|
}
|
|
|
|
var name = userPlan.Plan.Name
|
|
if len(userPlan.Name) > 0 {
|
|
name += "-" + userPlan.Name
|
|
}
|
|
|
|
userPlanMaps = append(userPlanMaps, maps.Map{
|
|
"id": userPlan.Id,
|
|
"name": name,
|
|
"dayTo": userPlan.DayTo,
|
|
})
|
|
}
|
|
this.Data["userPlans"] = userPlanMaps
|
|
|
|
// 是否必须使用套餐
|
|
this.Data["requirePlan"] = false
|
|
userServerConfig, err := configloaders.LoadServerConfig()
|
|
if err == nil && userServerConfig.RequirePlan {
|
|
this.Data["requirePlan"] = true
|
|
}
|
|
|
|
// OSS
|
|
this.Data["ossTypes"] = ossconfigs.FindAllOSSTypes()
|
|
this.Data["ossBucketParams"] = ossconfigs.FindAllOSSBucketParamDefinitions()
|
|
|
|
this.Show()
|
|
}
|
|
|
|
func (this *CreateAction) RunPost(params struct {
|
|
ServerNames []byte
|
|
Protocols []string
|
|
CertIdsJSON []byte
|
|
OriginsJSON []byte
|
|
RequestHostType int32
|
|
RequestHost string
|
|
CacheCondsJSON []byte
|
|
|
|
GroupIds []int64
|
|
UserPlanId int64
|
|
|
|
Must *actions.Must
|
|
CSRF *actionutils.CSRF
|
|
}) {
|
|
// 检查用户所在集群
|
|
clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var clusterId = clusterIdResp.NodeClusterId
|
|
|
|
if len(params.ServerNames) == 0 {
|
|
this.Data["requireServerNames"] = true
|
|
this.Fail("请添加要加速的域名")
|
|
}
|
|
|
|
var serverNames = []*serverconfigs.ServerNameConfig{}
|
|
err = json.Unmarshal(params.ServerNames, &serverNames)
|
|
if err != nil {
|
|
this.Fail("域名参数解析错误:" + err.Error())
|
|
}
|
|
serverconfigs.NormalizeServerNames(serverNames)
|
|
|
|
var allDomainNames = serverconfigs.PlainServerNames(serverNames)
|
|
if len(allDomainNames) == 0 {
|
|
this.Data["requireServerNames"] = true
|
|
this.Fail("请添加要加速的域名")
|
|
}
|
|
|
|
for _, domainName := range allDomainNames {
|
|
if !domainutils.ValidateDomainFormat(strings.ReplaceAll(domainName, "*.", "") /** 支持泛域名 **/) {
|
|
this.Fail("域名'" + domainName + "'输入错误")
|
|
}
|
|
}
|
|
|
|
// 检查域名是否已经存在
|
|
dupResp, err := this.RPC().ServerRPC().CheckServerNameDuplicationInNodeCluster(this.UserContext(), &pb.CheckServerNameDuplicationInNodeClusterRequest{
|
|
ServerNames: allDomainNames,
|
|
NodeClusterId: clusterId,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
if len(dupResp.DuplicatedServerNames) > 0 {
|
|
this.Fail("域名 " + strings.Join(dupResp.DuplicatedServerNames, ", ") + " 已经被其他网站所占用,不能重复使用")
|
|
}
|
|
|
|
serverNamesJSON, err := json.Marshal(serverNames)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 协议
|
|
if len(params.Protocols) == 0 {
|
|
this.Fail("请选择至少一个域名协议")
|
|
}
|
|
|
|
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
|
httpsConfig := &serverconfigs.HTTPSProtocolConfig{}
|
|
|
|
// HTTP
|
|
if lists.ContainsString(params.Protocols, "http") {
|
|
httpConfig.IsOn = true
|
|
httpConfig.Listen = []*serverconfigs.NetworkAddressConfig{
|
|
{
|
|
Protocol: serverconfigs.ProtocolHTTP,
|
|
Host: "",
|
|
PortRange: "80",
|
|
},
|
|
}
|
|
}
|
|
|
|
// HTTPS
|
|
if lists.ContainsString(params.Protocols, "https") {
|
|
httpsConfig.IsOn = true
|
|
httpsConfig.Listen = []*serverconfigs.NetworkAddressConfig{
|
|
{
|
|
Protocol: serverconfigs.ProtocolHTTPS,
|
|
Host: "",
|
|
PortRange: "443",
|
|
},
|
|
}
|
|
|
|
if len(params.CertIdsJSON) == 0 {
|
|
this.Fail("请选择或者上传HTTPS证书")
|
|
}
|
|
certIds := []int64{}
|
|
err := json.Unmarshal(params.CertIdsJSON, &certIds)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
if len(certIds) == 0 {
|
|
this.Fail("请选择或者上传HTTPS证书")
|
|
}
|
|
|
|
certRefs := []*sslconfigs.SSLCertRef{}
|
|
for _, certId := range certIds {
|
|
certRefs = append(certRefs, &sslconfigs.SSLCertRef{
|
|
IsOn: true,
|
|
CertId: certId,
|
|
})
|
|
}
|
|
certRefsJSON, err := json.Marshal(certRefs)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 创建策略
|
|
sslPolicyIdResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.UserContext(), &pb.CreateSSLPolicyRequest{
|
|
Http2Enabled: false,
|
|
Http3Enabled: false,
|
|
MinVersion: "TLS 1.1",
|
|
SslCertsJSON: certRefsJSON,
|
|
HstsJSON: nil,
|
|
OcspIsOn: false,
|
|
ClientAuthType: 0,
|
|
ClientCACertsJSON: nil,
|
|
CipherSuites: nil,
|
|
CipherSuitesIsOn: false,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{
|
|
IsOn: true,
|
|
SSLPolicyId: sslPolicyIdResp.SslPolicyId,
|
|
}
|
|
}
|
|
|
|
// 源站信息
|
|
var originMaps = []maps.Map{}
|
|
if len(params.OriginsJSON) == 0 {
|
|
this.Fail("请输入源站信息")
|
|
}
|
|
err = json.Unmarshal(params.OriginsJSON, &originMaps)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
if len(originMaps) == 0 {
|
|
this.Fail("请输入源站信息")
|
|
}
|
|
var primaryOriginRefs = []*serverconfigs.OriginRef{}
|
|
var backupOriginRefs = []*serverconfigs.OriginRef{}
|
|
|
|
var hasOSS = false
|
|
|
|
for _, originMap := range originMaps {
|
|
var host = originMap.GetString("host")
|
|
var isPrimary = originMap.GetBool("isPrimary")
|
|
var scheme = originMap.GetString("scheme")
|
|
|
|
if ossconfigs.IsOSSProtocol(scheme) {
|
|
hasOSS = true
|
|
continue
|
|
}
|
|
|
|
if len(host) == 0 {
|
|
this.Fail("源站地址不能为空")
|
|
}
|
|
if strings.Index(host, ":") > 0 {
|
|
_, _, err := net.SplitHostPort(host)
|
|
if err != nil {
|
|
this.Fail("源站地址'" + host + "'格式错误")
|
|
}
|
|
} else if !domainutils.ValidateDomainFormat(host) {
|
|
this.Fail("源站地址'" + host + "'格式错误")
|
|
}
|
|
|
|
if scheme != "http" && scheme != "https" {
|
|
this.Fail("错误的源站协议")
|
|
}
|
|
|
|
addrHost, addrPort, err := net.SplitHostPort(host)
|
|
if err != nil {
|
|
addrHost = host
|
|
if scheme == "http" {
|
|
addrPort = "80"
|
|
} else if scheme == "https" {
|
|
addrPort = "443"
|
|
}
|
|
}
|
|
|
|
originIdResp, err := this.RPC().OriginRPC().CreateOrigin(this.UserContext(), &pb.CreateOriginRequest{
|
|
Name: "",
|
|
Addr: &pb.NetworkAddress{
|
|
Protocol: scheme,
|
|
Host: addrHost,
|
|
PortRange: addrPort,
|
|
},
|
|
Description: "",
|
|
Weight: 10,
|
|
IsOn: true,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
if isPrimary {
|
|
primaryOriginRefs = append(primaryOriginRefs, &serverconfigs.OriginRef{
|
|
IsOn: true,
|
|
OriginId: originIdResp.OriginId,
|
|
})
|
|
} else {
|
|
backupOriginRefs = append(backupOriginRefs, &serverconfigs.OriginRef{
|
|
IsOn: true,
|
|
OriginId: originIdResp.OriginId,
|
|
})
|
|
}
|
|
}
|
|
primaryOriginsJSON, err := json.Marshal(primaryOriginRefs)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
backupOriginsJSON, err := json.Marshal(backupOriginRefs)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
var scheduling = &serverconfigs.SchedulingConfig{
|
|
Code: "random",
|
|
Options: nil,
|
|
}
|
|
schedulingJSON, err := json.Marshal(scheduling)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 反向代理
|
|
reverseProxyResp, err := this.RPC().ReverseProxyRPC().CreateReverseProxy(this.UserContext(), &pb.CreateReverseProxyRequest{
|
|
SchedulingJSON: schedulingJSON,
|
|
PrimaryOriginsJSON: primaryOriginsJSON,
|
|
BackupOriginsJSON: backupOriginsJSON,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var reverseProxyId = reverseProxyResp.ReverseProxyId
|
|
var reverseProxyRef = &serverconfigs.ReverseProxyRef{
|
|
IsPrior: false,
|
|
IsOn: true,
|
|
ReverseProxyId: reverseProxyId,
|
|
}
|
|
reverseProxyRefJSON, err := json.Marshal(reverseProxyRef)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
if params.RequestHostType > 0 {
|
|
_, err = this.RPC().ReverseProxyRPC().UpdateReverseProxy(this.UserContext(), &pb.UpdateReverseProxyRequest{
|
|
ReverseProxyId: reverseProxyId,
|
|
RequestHostType: params.RequestHostType,
|
|
RequestHost: params.RequestHost,
|
|
RequestURI: "",
|
|
StripPrefix: "",
|
|
AutoFlush: false,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
}
|
|
}
|
|
|
|
// 缓存设置
|
|
var cacheCondMaps = []maps.Map{}
|
|
if len(params.CacheCondsJSON) > 0 {
|
|
err = json.Unmarshal(params.CacheCondsJSON, &cacheCondMaps)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
var cacheRefs = []*serverconfigs.HTTPCacheRef{}
|
|
if len(cacheCondMaps) > 0 {
|
|
for _, condMap := range cacheCondMaps {
|
|
var durationMap = condMap.GetMap("duration")
|
|
if durationMap == nil {
|
|
continue
|
|
}
|
|
|
|
var duration = &shared.TimeDuration{
|
|
Count: durationMap.GetInt64("count"),
|
|
Unit: durationMap.GetString("unit"),
|
|
}
|
|
|
|
var value string
|
|
var param string
|
|
var operator string
|
|
var condType = condMap.GetString("type")
|
|
switch condType {
|
|
case "url-extension":
|
|
param = "${requestPathExtension}"
|
|
operator = shared.RequestCondOperatorIn
|
|
result := []string{}
|
|
v := condMap.GetString("value")
|
|
if len(v) > 0 {
|
|
err = json.Unmarshal([]byte(v), &result)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
value = v
|
|
}
|
|
case "url-prefix":
|
|
param = "${requestPath}"
|
|
operator = shared.RequestCondOperatorHasPrefix
|
|
value = condMap.GetString("value")
|
|
default:
|
|
continue
|
|
}
|
|
|
|
var conds = &shared.HTTPRequestCondsConfig{
|
|
IsOn: true,
|
|
Connector: "or",
|
|
Groups: []*shared.HTTPRequestCondGroup{
|
|
{
|
|
IsOn: true,
|
|
Connector: "or",
|
|
Conds: []*shared.HTTPRequestCond{
|
|
{
|
|
IsRequest: true,
|
|
Type: condType,
|
|
Param: param,
|
|
Operator: operator,
|
|
Value: value,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var cacheRef = &serverconfigs.HTTPCacheRef{
|
|
IsOn: true,
|
|
CachePolicyId: 0,
|
|
Key: "${scheme}://${host}${requestURI}",
|
|
Life: duration,
|
|
Status: []int{200},
|
|
MaxSize: &shared.SizeCapacity{
|
|
Count: 128,
|
|
Unit: shared.SizeCapacityUnitMB,
|
|
},
|
|
SkipResponseCacheControlValues: nil,
|
|
SkipResponseSetCookie: true,
|
|
EnableRequestCachePragma: false,
|
|
Conds: conds,
|
|
CachePolicy: nil,
|
|
AllowChunkedEncoding: true,
|
|
AllowPartialContent: false,
|
|
}
|
|
cacheRefs = append(cacheRefs, cacheRef)
|
|
}
|
|
}
|
|
|
|
var cacheConfig = &serverconfigs.HTTPCacheConfig{
|
|
IsPrior: false,
|
|
IsOn: true,
|
|
CacheRefs: cacheRefs,
|
|
AddStatusHeader: true,
|
|
}
|
|
cacheConfigJSON, err := cacheConfig.AsJSON()
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 检查是否必须使用套餐
|
|
userServerConfig, err := configloaders.LoadServerConfig()
|
|
if err == nil {
|
|
if params.UserPlanId <= 0 && userServerConfig.RequirePlan {
|
|
this.Fail("请选择套餐")
|
|
}
|
|
}
|
|
|
|
// 检查套餐
|
|
if params.UserPlanId > 0 {
|
|
userPlanResp, err := this.RPC().UserPlanRPC().FindEnabledUserPlan(this.UserContext(), &pb.FindEnabledUserPlanRequest{UserPlanId: params.UserPlanId})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var userPlan = userPlanResp.UserPlan
|
|
if userPlan == nil || userPlan.UserId != this.UserId() {
|
|
this.Fail("找不到要使用的套餐")
|
|
}
|
|
}
|
|
|
|
// 开始保存
|
|
httpJSON, err := httpConfig.AsJSON()
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
httpsJSON, err := httpsConfig.AsJSON()
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
createResp, err := this.RPC().ServerRPC().CreateServer(this.UserContext(), &pb.CreateServerRequest{
|
|
UserId: this.UserId(),
|
|
AdminId: 0,
|
|
Type: serverconfigs.ServerTypeHTTPProxy,
|
|
Name: serverNames[0].Name,
|
|
Description: "",
|
|
ServerNamesJSON: serverNamesJSON,
|
|
HttpJSON: httpJSON,
|
|
HttpsJSON: httpsJSON,
|
|
TcpJSON: nil,
|
|
TlsJSON: nil,
|
|
UdpJSON: nil,
|
|
WebId: 0,
|
|
ReverseProxyJSON: reverseProxyRefJSON,
|
|
ServerGroupIds: params.GroupIds,
|
|
NodeClusterId: clusterId,
|
|
IncludeNodesJSON: nil,
|
|
ExcludeNodesJSON: nil,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var serverId = createResp.ServerId
|
|
|
|
defer this.CreateLogInfo(codes.Server_LogCreateServer, serverId)
|
|
|
|
// 保存缓存设置
|
|
webIdResp, err := this.RPC().HTTPWebRPC().CreateHTTPWeb(this.UserContext(), &pb.CreateHTTPWebRequest{RootJSON: nil})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var webId = webIdResp.HttpWebId
|
|
_, err = this.RPC().ServerRPC().UpdateServerWeb(this.UserContext(), &pb.UpdateServerWebRequest{
|
|
ServerId: serverId,
|
|
WebId: webId,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.UserContext(), &pb.UpdateHTTPWebCacheRequest{
|
|
HttpWebId: webId,
|
|
CacheJSON: cacheConfigJSON,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 统计
|
|
if userServerConfig != nil && userServerConfig.EnableStat {
|
|
var statConfig = &serverconfigs.HTTPStatRef{
|
|
IsPrior: false,
|
|
IsOn: true,
|
|
}
|
|
statJSON, err := json.Marshal(statConfig)
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.UserContext(), &pb.UpdateHTTPWebStatRequest{
|
|
HttpWebId: webId,
|
|
StatJSON: statJSON,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// 绑定套餐
|
|
_, err = this.RPC().ServerRPC().UpdateServerUserPlan(this.UserContext(), &pb.UpdateServerUserPlanRequest{
|
|
ServerId: serverId,
|
|
UserPlanId: params.UserPlanId,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
this.Data["serverId"] = serverId
|
|
this.Data["hasOSS"] = hasOSS
|
|
|
|
this.Success()
|
|
}
|