package servers import ( "encoding/json" "github.com/TeaOSLab/EdgeCommon/pkg/langs/codes" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs" "github.com/TeaOSLab/EdgeUser/internal/configloaders" "github.com/TeaOSLab/EdgeUser/internal/utils/domainutils" "github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils" "github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/types" "net" "strings" ) type CreateAction struct { actionutils.ParentAction } func (this *CreateAction) Init() { this.Nav("", "", "create") } func (this *CreateAction) RunGet(params struct{}) { // 检查用户状态 this.CheckUserStatus() clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()}) if err != nil { this.ErrorPage(err) return } this.Data["clusterId"] = clusterIdResp.NodeClusterId // 套餐 userPlansResp, err := this.RPC().UserPlanRPC().FindAllEnabledUserPlansForServer(this.UserContext(), &pb.FindAllEnabledUserPlansForServerRequest{ UserId: this.UserId(), ServerId: 0, }) if err != nil { this.ErrorPage(err) return } var userPlanMaps = []maps.Map{} for _, userPlan := range userPlansResp.UserPlans { if userPlan.Plan == nil { continue } var name = userPlan.Plan.Name if len(userPlan.Name) > 0 { name += "-" + userPlan.Name } userPlanMaps = append(userPlanMaps, maps.Map{ "id": userPlan.Id, "name": name, "dayTo": userPlan.DayTo, }) } this.Data["userPlans"] = userPlanMaps // 是否必须使用套餐 this.Data["requirePlan"] = false userServerConfig, err := configloaders.LoadServerConfig() if err == nil && userServerConfig.RequirePlan { this.Data["requirePlan"] = true } // OSS this.Data["ossTypes"] = ossconfigs.FindAllOSSTypes() this.Data["ossBucketParams"] = ossconfigs.FindAllOSSBucketParamDefinitions() this.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 } // 协议:未选择时默认 HTTP if len(params.Protocols) == 0 { params.Protocols = []string{"http"} } httpConfig := &serverconfigs.HTTPProtocolConfig{} httpsConfig := &serverconfigs.HTTPSProtocolConfig{} // HTTP if lists.ContainsString(params.Protocols, "http") { httpConfig.IsOn = true httpConfig.Listen = []*serverconfigs.NetworkAddressConfig{ { Protocol: serverconfigs.ProtocolHTTP, Host: "", PortRange: "80", }, } } // HTTPS if lists.ContainsString(params.Protocols, "https") { httpsConfig.IsOn = true httpsConfig.Listen = []*serverconfigs.NetworkAddressConfig{ { Protocol: serverconfigs.ProtocolHTTPS, Host: "", PortRange: "443", }, } if len(params.CertIdsJSON) == 0 { this.Fail("请选择或者上传HTTPS证书") } certIds := []int64{} err := json.Unmarshal(params.CertIdsJSON, &certIds) if err != nil { this.ErrorPage(err) return } if len(certIds) == 0 { this.Fail("请选择或者上传HTTPS证书") } certRefs := []*sslconfigs.SSLCertRef{} for _, certId := range certIds { certRefs = append(certRefs, &sslconfigs.SSLCertRef{ IsOn: true, CertId: certId, }) } certRefsJSON, err := json.Marshal(certRefs) if err != nil { this.ErrorPage(err) return } // 创建策略 sslPolicyIdResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.UserContext(), &pb.CreateSSLPolicyRequest{ Http2Enabled: false, Http3Enabled: false, MinVersion: "TLS 1.1", SslCertsJSON: certRefsJSON, HstsJSON: nil, OcspIsOn: false, ClientAuthType: 0, ClientCACertsJSON: nil, CipherSuites: nil, CipherSuitesIsOn: false, }) if err != nil { this.ErrorPage(err) return } httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{ IsOn: true, SSLPolicyId: sslPolicyIdResp.SslPolicyId, } } // 源站信息 var originMaps = []maps.Map{} if len(params.OriginsJSON) == 0 { this.Fail("请输入源站信息") } err = json.Unmarshal(params.OriginsJSON, &originMaps) if err != nil { this.ErrorPage(err) return } if len(originMaps) == 0 { this.Fail("请输入源站信息") } var primaryOriginRefs = []*serverconfigs.OriginRef{} var backupOriginRefs = []*serverconfigs.OriginRef{} var hasOSS = false for _, originMap := range originMaps { var host = originMap.GetString("host") var isPrimary = originMap.GetBool("isPrimary") var scheme = originMap.GetString("scheme") if ossconfigs.IsOSSProtocol(scheme) { hasOSS = true continue } if len(host) == 0 { this.Fail("源站地址不能为空") } if strings.Index(host, ":") > 0 { _, _, err := net.SplitHostPort(host) if err != nil { this.Fail("源站地址'" + host + "'格式错误") } } else if !domainutils.ValidateDomainFormat(host) { this.Fail("源站地址'" + host + "'格式错误") } if scheme != "http" && scheme != "https" { this.Fail("错误的源站协议") } addrHost, addrPort, err := net.SplitHostPort(host) if err != nil { addrHost = host if scheme == "http" { addrPort = "80" } else if scheme == "https" { addrPort = "443" } } originIdResp, err := this.RPC().OriginRPC().CreateOrigin(this.UserContext(), &pb.CreateOriginRequest{ Name: "", Addr: &pb.NetworkAddress{ Protocol: scheme, Host: addrHost, PortRange: addrPort, }, Description: "", Weight: 10, IsOn: true, }) if err != nil { this.ErrorPage(err) return } if isPrimary { primaryOriginRefs = append(primaryOriginRefs, &serverconfigs.OriginRef{ IsOn: true, OriginId: originIdResp.OriginId, }) } else { backupOriginRefs = append(backupOriginRefs, &serverconfigs.OriginRef{ IsOn: true, OriginId: originIdResp.OriginId, }) } } primaryOriginsJSON, err := json.Marshal(primaryOriginRefs) if err != nil { this.ErrorPage(err) return } backupOriginsJSON, err := json.Marshal(backupOriginRefs) if err != nil { this.ErrorPage(err) return } var scheduling = &serverconfigs.SchedulingConfig{ Code: "random", Options: nil, } schedulingJSON, err := json.Marshal(scheduling) if err != nil { this.ErrorPage(err) return } // 反向代理 reverseProxyResp, err := this.RPC().ReverseProxyRPC().CreateReverseProxy(this.UserContext(), &pb.CreateReverseProxyRequest{ SchedulingJSON: schedulingJSON, PrimaryOriginsJSON: primaryOriginsJSON, BackupOriginsJSON: backupOriginsJSON, }) if err != nil { this.ErrorPage(err) return } var reverseProxyId = reverseProxyResp.ReverseProxyId var reverseProxyRef = &serverconfigs.ReverseProxyRef{ IsPrior: false, IsOn: true, ReverseProxyId: reverseProxyId, } reverseProxyRefJSON, err := json.Marshal(reverseProxyRef) if err != nil { this.ErrorPage(err) return } if params.RequestHostType > 0 { _, err = this.RPC().ReverseProxyRPC().UpdateReverseProxy(this.UserContext(), &pb.UpdateReverseProxyRequest{ ReverseProxyId: reverseProxyId, RequestHostType: params.RequestHostType, RequestHost: params.RequestHost, RequestURI: "", StripPrefix: "", AutoFlush: false, }) if err != nil { this.ErrorPage(err) } } // 缓存设置 var cacheCondMaps = []maps.Map{} if len(params.CacheCondsJSON) > 0 { err = json.Unmarshal(params.CacheCondsJSON, &cacheCondMaps) if err != nil { this.ErrorPage(err) return } } var cacheRefs = []*serverconfigs.HTTPCacheRef{} if len(cacheCondMaps) > 0 { for _, condMap := range cacheCondMaps { var durationMap = condMap.GetMap("duration") if durationMap == nil { continue } var duration = &shared.TimeDuration{ Count: durationMap.GetInt64("count"), Unit: durationMap.GetString("unit"), } var value string var param string var operator string var condType = condMap.GetString("type") switch condType { case "url-extension": param = "${requestPathExtension}" operator = shared.RequestCondOperatorIn result := []string{} v := condMap.GetString("value") if len(v) > 0 { err = json.Unmarshal([]byte(v), &result) if err != nil { this.ErrorPage(err) return } value = v } case "url-prefix": param = "${requestPath}" operator = shared.RequestCondOperatorHasPrefix value = condMap.GetString("value") default: continue } var conds = &shared.HTTPRequestCondsConfig{ IsOn: true, Connector: "or", Groups: []*shared.HTTPRequestCondGroup{ { IsOn: true, Connector: "or", Conds: []*shared.HTTPRequestCond{ { IsRequest: true, Type: condType, Param: param, Operator: operator, Value: value, }, }, }, }, } var cacheRef = &serverconfigs.HTTPCacheRef{ IsOn: true, CachePolicyId: 0, Key: "${scheme}://${host}${requestURI}", Life: duration, Status: []int{200}, MaxSize: &shared.SizeCapacity{ Count: 128, Unit: shared.SizeCapacityUnitMB, }, SkipResponseCacheControlValues: nil, SkipResponseSetCookie: true, EnableRequestCachePragma: false, Conds: conds, CachePolicy: nil, AllowChunkedEncoding: true, AllowPartialContent: false, } cacheRefs = append(cacheRefs, cacheRef) } } var cacheConfig = &serverconfigs.HTTPCacheConfig{ IsPrior: false, IsOn: true, CacheRefs: cacheRefs, AddStatusHeader: true, } cacheConfigJSON, err := cacheConfig.AsJSON() if err != nil { this.ErrorPage(err) return } // 检查是否必须使用套餐 userServerConfig, err := configloaders.LoadServerConfig() if err == nil { if params.UserPlanId <= 0 && userServerConfig.RequirePlan { this.Fail("请选择套餐") } } // 检查套餐 if params.UserPlanId > 0 { userPlanResp, err := this.RPC().UserPlanRPC().FindEnabledUserPlan(this.UserContext(), &pb.FindEnabledUserPlanRequest{UserPlanId: params.UserPlanId}) if err != nil { this.ErrorPage(err) return } var userPlan = userPlanResp.UserPlan if userPlan == nil || userPlan.UserId != this.UserId() { this.Fail("找不到要使用的套餐") } } // 开始保存 httpJSON, err := httpConfig.AsJSON() if err != nil { this.ErrorPage(err) return } httpsJSON, err := httpsConfig.AsJSON() if err != nil { this.ErrorPage(err) return } createResp, err := this.RPC().ServerRPC().CreateServer(this.UserContext(), &pb.CreateServerRequest{ UserId: this.UserId(), AdminId: 0, Type: serverconfigs.ServerTypeHTTPProxy, Name: serverNames[0].Name, Description: "", ServerNamesJSON: serverNamesJSON, HttpJSON: httpJSON, HttpsJSON: httpsJSON, TcpJSON: nil, TlsJSON: nil, UdpJSON: nil, WebId: 0, ReverseProxyJSON: reverseProxyRefJSON, ServerGroupIds: params.GroupIds, NodeClusterId: clusterId, IncludeNodesJSON: nil, ExcludeNodesJSON: nil, }) if err != nil { this.ErrorPage(err) return } var serverId = createResp.ServerId defer this.CreateLogInfo(codes.Server_LogCreateServer, serverId) // 保存缓存设置 webIdResp, err := this.RPC().HTTPWebRPC().CreateHTTPWeb(this.UserContext(), &pb.CreateHTTPWebRequest{RootJSON: nil}) if err != nil { this.ErrorPage(err) return } var webId = webIdResp.HttpWebId _, err = this.RPC().ServerRPC().UpdateServerWeb(this.UserContext(), &pb.UpdateServerWebRequest{ ServerId: serverId, WebId: webId, }) if err != nil { this.ErrorPage(err) return } _, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.UserContext(), &pb.UpdateHTTPWebCacheRequest{ HttpWebId: webId, CacheJSON: cacheConfigJSON, }) if err != nil { this.ErrorPage(err) return } // ========== 默认开启的功能 ========== // 1. 访问日志 _, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.UserContext(), &pb.UpdateHTTPWebAccessLogRequest{ HttpWebId: webId, AccessLogJSON: []byte(`{ "isPrior": false, "isOn": true, "fields": [1, 2, 6, 7], "status1": true, "status2": true, "status3": true, "status4": true, "status5": true, "storageOnly": false, "storagePolicies": [], "firewallOnly": false }`), }) if err != nil { this.ErrorPage(err) return } // 2. WebSocket createWebSocketResp, err := this.RPC().HTTPWebsocketRPC().CreateHTTPWebsocket(this.UserContext(), &pb.CreateHTTPWebsocketRequest{ HandshakeTimeoutJSON: []byte(`{"count": 30, "unit": "second"}`), AllowAllOrigins: true, AllowedOrigins: nil, RequestSameOrigin: true, RequestOrigin: "", }) if err != nil { this.ErrorPage(err) return } _, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.UserContext(), &pb.UpdateHTTPWebWebsocketRequest{ HttpWebId: webId, WebsocketJSON: []byte(`{"isPrior": false, "isOn": true, "websocketId": ` + types.String(createWebSocketResp.WebsocketId) + `}`), }) if err != nil { this.ErrorPage(err) return } // 3. WAF 防火墙 var firewallRef = &firewallconfigs.HTTPFirewallRef{ IsPrior: false, IsOn: true, FirewallPolicyId: 0, } firewallRefJSON, err := json.Marshal(firewallRef) if err != nil { this.ErrorPage(err) return } _, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.UserContext(), &pb.UpdateHTTPWebFirewallRequest{ HttpWebId: webId, FirewallJSON: firewallRefJSON, }) if err != nil { this.ErrorPage(err) return } // 4. 从上级代理中读取IP var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{ IsOn: true, Value: "${remoteAddr}", Type: serverconfigs.HTTPRemoteAddrTypeProxy, } remoteAddrConfigJSON, err := json.Marshal(remoteAddrConfig) if err != nil { this.ErrorPage(err) return } _, err = this.RPC().HTTPWebRPC().UpdateHTTPWebRemoteAddr(this.UserContext(), &pb.UpdateHTTPWebRemoteAddrRequest{ HttpWebId: webId, RemoteAddrJSON: remoteAddrConfigJSON, }) if err != nil { this.ErrorPage(err) return } // 5. 统计 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() }