548 lines
14 KiB
Go
548 lines
14 KiB
Go
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()
|
||
}
|