This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

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