package lb 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/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" "net" "strconv" ) type CreateAction struct { actionutils.ParentAction } func (this *CreateAction) Init() { this.Nav("", "", "create") } func (this *CreateAction) RunGet(params struct{}) { var supportTCP = this.ValidateFeature(userconfigs.UserFeatureCodeServerTCP, 0) var supportUDP = this.ValidateFeature(userconfigs.UserFeatureCodeServerUDP, 0) if !supportTCP && !supportUDP { return } this.Data["supportTCP"] = supportTCP this.Data["supportUDP"] = supportUDP // 服务类型 var serverTypes = []maps.Map{} if supportTCP { serverTypes = append(serverTypes, maps.Map{ "name": "TCP负载均衡", "code": serverconfigs.ServerTypeTCPProxy, }) } if supportUDP { serverTypes = append(serverTypes, maps.Map{ "name": "UDP负载均衡", "code": serverconfigs.ServerTypeUDPProxy, }) } this.Data["serverTypes"] = serverTypes this.Data["canSpecifyTCPPort"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerTCPPort, 0) this.Data["canSpecifyUDPPort"] = this.ValidateFeature(userconfigs.UserFeatureCodeServerUDPPort, 0) this.Show() } func (this *CreateAction) RunPost(params struct { Name string ServerType string Protocols []string CertIdsJSON []byte OriginsJSON []byte TcpPorts []int TlsPorts []int UdpPorts []int Must *actions.Must CSRF *actionutils.CSRF }) { // 检查ServerType var serverType = params.ServerType if !lists.ContainsString([]string{serverconfigs.ServerTypeTCPProxy, serverconfigs.ServerTypeUDPProxy}, serverType) { this.Fail("请选择正确的服务类型") } // 检查用户所在集群 clusterIdResp, err := this.RPC().UserRPC().FindUserNodeClusterId(this.UserContext(), &pb.FindUserNodeClusterIdRequest{UserId: this.UserId()}) if err != nil { this.ErrorPage(err) return } clusterId := clusterIdResp.NodeClusterId if clusterId == 0 { this.Fail("当前用户没有指定集群,不能使用此服务") } // 检查是否有TCP权限 if lists.ContainsString(params.Protocols, "tcp") && !this.ValidateFeature(userconfigs.UserFeatureCodeServerTCP, 0) { this.Fail("你没有权限使用TCP负载均衡功能") } // 检查是否有UDP权限 if lists.ContainsString(params.Protocols, "udp") && !this.ValidateFeature(userconfigs.UserFeatureCodeServerUDP, 0) { this.Fail("你没有权限使用UDP负载均衡功能") } params.Must. Field("name", params.Name). Require("请输入服务名称") // 协议 if len(params.Protocols) == 0 { this.Fail("请选择至少一个协议") } // TCP canSpecifyTCPPort := this.ValidateFeature(userconfigs.UserFeatureCodeServerTCPPort, 0) if serverType == serverconfigs.ServerTypeTCPProxy { // 检查TCP端口 if canSpecifyTCPPort { if lists.Contains(params.Protocols, "tcp") { if len(params.TcpPorts) == 0 { this.Fail("需要至少指定一个TCP监听端口") } for _, port := range params.TcpPorts { if port < 1024 || port > 65534 { this.Fail("端口 '" + strconv.Itoa(port) + "' 范围错误") } // 检查是否被使用 resp, err := this.RPC().NodeClusterRPC().CheckPortIsUsingInNodeCluster(this.UserContext(), &pb.CheckPortIsUsingInNodeClusterRequest{ Port: types.Int32(port), NodeClusterId: clusterId, ProtocolFamily: "tcp", }) if err != nil { this.ErrorPage(err) return } if resp.IsUsing { this.Fail("端口 '" + strconv.Itoa(port) + "' 正在被别的服务使用,请换一个") } } } if lists.Contains(params.Protocols, "tls") { if len(params.TlsPorts) == 0 { this.Fail("需要至少指定一个TLS监听端口") } for _, port := range params.TlsPorts { if port < 1024 || port > 65534 { this.Fail("端口 '" + strconv.Itoa(port) + "' 范围错误") } if lists.ContainsInt(params.TcpPorts, port) { this.Fail("TLS端口 '" + strconv.Itoa(port) + "' 已经被TCP端口使用,不能重复使用") } // 检查是否被使用 resp, err := this.RPC().NodeClusterRPC().CheckPortIsUsingInNodeCluster(this.UserContext(), &pb.CheckPortIsUsingInNodeClusterRequest{ Port: types.Int32(port), NodeClusterId: clusterId, ProtocolFamily: "tcp", }) if err != nil { this.ErrorPage(err) return } if resp.IsUsing { this.Fail("端口 '" + strconv.Itoa(port) + "' 正在被别的服务使用,请换一个") } } } } } // UDP canSpecifyUDPPort := this.ValidateFeature(userconfigs.UserFeatureCodeServerUDPPort, 0) if serverType == serverconfigs.ServerTypeUDPProxy { // 检查UDP端口 if canSpecifyUDPPort { if lists.Contains(params.Protocols, "udp") { if len(params.UdpPorts) == 0 { this.Fail("需要至少指定一个UDP监听端口") } for _, port := range params.UdpPorts { if port < 1024 || port > 65534 { this.Fail("端口 '" + strconv.Itoa(port) + "' 范围错误") } // 检查是否被使用 resp, err := this.RPC().NodeClusterRPC().CheckPortIsUsingInNodeCluster(this.UserContext(), &pb.CheckPortIsUsingInNodeClusterRequest{ Port: types.Int32(port), NodeClusterId: clusterId, ProtocolFamily: "udp", }) if err != nil { this.ErrorPage(err) return } if resp.IsUsing { this.Fail("端口 '" + strconv.Itoa(port) + "' 正在被别的服务使用,请换一个") } } } } } // 先加锁 lockerKey := "create_tcp_server" lockResp, err := this.RPC().SysLockerRPC().SysLockerLock(this.UserContext(), &pb.SysLockerLockRequest{ Key: lockerKey, TimeoutSeconds: 30, }) if err != nil { this.ErrorPage(err) return } if !lockResp.Ok { this.Fail("操作繁忙,请稍后再试") } defer func() { _, err := this.RPC().SysLockerRPC().SysLockerUnlock(this.UserContext(), &pb.SysLockerUnlockRequest{Key: lockerKey}) if err != nil { this.ErrorPage(err) return } }() tcpConfig := &serverconfigs.TCPProtocolConfig{} tlsConfig := &serverconfigs.TLSProtocolConfig{} udpConfig := &serverconfigs.UDPProtocolConfig{} if serverType == serverconfigs.ServerTypeTCPProxy { // TCP ports := []int{} if lists.ContainsString(params.Protocols, "tcp") { tcpConfig.IsOn = true if canSpecifyTCPPort { for _, port := range params.TcpPorts { tcpConfig.Listen = append(tcpConfig.Listen, &serverconfigs.NetworkAddressConfig{ Protocol: serverconfigs.ProtocolTCP, Host: "", PortRange: strconv.Itoa(port), }) } } else { // 获取随机端口 portResp, err := this.RPC().NodeClusterRPC().FindFreePortInNodeCluster(this.UserContext(), &pb.FindFreePortInNodeClusterRequest{ NodeClusterId: clusterId, ProtocolFamily: "tcp", }) if err != nil { this.ErrorPage(err) return } port := int(portResp.Port) ports = append(ports, port) tcpConfig.Listen = []*serverconfigs.NetworkAddressConfig{ { Protocol: serverconfigs.ProtocolTCP, Host: "", PortRange: strconv.Itoa(port), }, } } } // TLS if lists.ContainsString(params.Protocols, "tls") { tlsConfig.IsOn = true if canSpecifyTCPPort { for _, port := range params.TlsPorts { tlsConfig.Listen = append(tlsConfig.Listen, &serverconfigs.NetworkAddressConfig{ Protocol: serverconfigs.ProtocolTLS, Host: "", PortRange: strconv.Itoa(port), }) } } else { var port int // 尝试N次 for i := 0; i < 5; i++ { // 获取随机端口 portResp, err := this.RPC().NodeClusterRPC().FindFreePortInNodeCluster(this.UserContext(), &pb.FindFreePortInNodeClusterRequest{ NodeClusterId: clusterId, ProtocolFamily: "tcp", }) if err != nil { this.ErrorPage(err) return } p := int(portResp.Port) if !lists.ContainsInt(ports, p) { port = p break } } if port == 0 { this.Fail("无法找到可用的端口,请稍后重试") } tlsConfig.Listen = []*serverconfigs.NetworkAddressConfig{ { Protocol: serverconfigs.ProtocolTLS, Host: "", PortRange: strconv.Itoa(port), }, } } if len(params.CertIdsJSON) == 0 { this.Fail("请选择或者上传TLS证书") } certIds := []int64{} err := json.Unmarshal(params.CertIdsJSON, &certIds) if err != nil { this.ErrorPage(err) return } if len(certIds) == 0 { this.Fail("请选择或者上传TLS证书") } 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, ClientAuthType: 0, ClientCACertsJSON: nil, CipherSuites: nil, CipherSuitesIsOn: false, }) if err != nil { this.ErrorPage(err) return } tlsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{ IsOn: true, SSLPolicyId: sslPolicyIdResp.SslPolicyId, } } } // UDP if serverType == serverconfigs.ServerTypeUDPProxy { if lists.ContainsString(params.Protocols, "udp") { udpConfig.IsOn = true if canSpecifyUDPPort { for _, port := range params.UdpPorts { udpConfig.Listen = append(udpConfig.Listen, &serverconfigs.NetworkAddressConfig{ Protocol: serverconfigs.ProtocolUDP, Host: "", PortRange: strconv.Itoa(port), }) } } else { // 获取随机端口 portResp, err := this.RPC().NodeClusterRPC().FindFreePortInNodeCluster(this.UserContext(), &pb.FindFreePortInNodeClusterRequest{ NodeClusterId: clusterId, ProtocolFamily: "udp", }) if err != nil { this.ErrorPage(err) return } port := int(portResp.Port) udpConfig.Listen = []*serverconfigs.NetworkAddressConfig{ { Protocol: serverconfigs.ProtocolUDP, Host: "", PortRange: strconv.Itoa(port), }, } } } } // 源站信息 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("请输入源站信息") } primaryOriginRefs := []*serverconfigs.OriginRef{} backupOriginRefs := []*serverconfigs.OriginRef{} for _, originMap := range originMaps { host := originMap.GetString("host") isPrimary := originMap.GetBool("isPrimary") scheme := originMap.GetString("scheme") if len(host) == 0 { this.Fail("源站地址不能为空") } addrHost, addrPort, err := net.SplitHostPort(host) if err != nil { this.Fail("源站地址'" + host + "'格式错误") } if (serverType == serverconfigs.ServerTypeTCPProxy && scheme != "tcp" && scheme != "tls") || (serverType == serverconfigs.ServerTypeUDPProxy && scheme != "udp") { this.Fail("错误的源站协议") } 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 } 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 } reverseProxyId := reverseProxyResp.ReverseProxyId reverseProxyRef := &serverconfigs.ReverseProxyRef{ IsPrior: false, IsOn: true, ReverseProxyId: reverseProxyId, } reverseProxyRefJSON, err := json.Marshal(reverseProxyRef) if err != nil { this.ErrorPage(err) return } // 开始保存 var tcpJSON []byte var tlsJSON []byte var udpJSON []byte if tcpConfig.IsOn { tcpJSON, err = tcpConfig.AsJSON() if err != nil { this.ErrorPage(err) return } } if tlsConfig.IsOn { tlsJSON, err = tlsConfig.AsJSON() if err != nil { this.ErrorPage(err) return } } if udpConfig.IsOn { udpJSON, err = udpConfig.AsJSON() if err != nil { this.ErrorPage(err) return } } createResp, err := this.RPC().ServerRPC().CreateServer(this.UserContext(), &pb.CreateServerRequest{ UserId: this.UserId(), AdminId: 0, Type: serverType, Name: params.Name, Description: "", ServerNamesJSON: []byte("[]"), HttpJSON: nil, HttpsJSON: nil, TcpJSON: tcpJSON, TlsJSON: tlsJSON, UdpJSON: udpJSON, WebId: 0, ReverseProxyJSON: reverseProxyRefJSON, ServerGroupIds: nil, NodeClusterId: clusterId, IncludeNodesJSON: nil, ExcludeNodesJSON: nil, }) if err != nil { this.ErrorPage(err) return } serverId := createResp.ServerId defer this.CreateLogInfo(codes.Server_LogCreateServer, serverId) this.Success() }