All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m20s
修复Redis缓存设置未设置TTL的问题,使用节点拉取间隔加60秒作为TTL 修复ClearServerAllCache中重复添加keys的问题 修复ClearServerCache中未使用cursor参数的问题 优化ClearServerAllCache以支持清除多种前缀的缓存
244 lines
7.6 KiB
Go
244 lines
7.6 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/perfect-panel/server/internal/model/node"
|
|
|
|
"github.com/perfect-panel/server/internal/svc"
|
|
"github.com/perfect-panel/server/internal/types"
|
|
"github.com/perfect-panel/server/pkg/logger"
|
|
"github.com/perfect-panel/server/pkg/tool"
|
|
"github.com/perfect-panel/server/pkg/xerr"
|
|
)
|
|
|
|
type GetServerConfigLogic struct {
|
|
logger.Logger
|
|
ctx *gin.Context
|
|
svcCtx *svc.ServiceContext
|
|
}
|
|
|
|
// NewGetServerConfigLogic Get server config
|
|
func NewGetServerConfigLogic(ctx *gin.Context, svcCtx *svc.ServiceContext) *GetServerConfigLogic {
|
|
return &GetServerConfigLogic{
|
|
Logger: logger.WithContext(ctx.Request.Context()),
|
|
ctx: ctx,
|
|
svcCtx: svcCtx,
|
|
}
|
|
}
|
|
|
|
func (l *GetServerConfigLogic) GetServerConfig(req *types.GetServerConfigRequest) (resp *types.GetServerConfigResponse, err error) {
|
|
cacheKey := fmt.Sprintf("%s%d:%s", node.ServerConfigCacheKey, req.ServerId, req.Protocol)
|
|
cache, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
|
|
if err == nil {
|
|
if cache != "" {
|
|
etag := tool.GenerateETag([]byte(cache))
|
|
// Check If-None-Match header
|
|
match := l.ctx.GetHeader("If-None-Match")
|
|
if match == etag {
|
|
return nil, xerr.StatusNotModified
|
|
}
|
|
l.ctx.Header("ETag", etag)
|
|
resp = &types.GetServerConfigResponse{}
|
|
err = json.Unmarshal([]byte(cache), resp)
|
|
if err != nil {
|
|
l.Errorw("[ServerConfigCacheKey] json unmarshal error", logger.Field("error", err.Error()))
|
|
return nil, err
|
|
}
|
|
return resp, nil
|
|
}
|
|
}
|
|
data, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, req.ServerId)
|
|
if err != nil {
|
|
l.Errorw("[GetServerConfig] FindOne error", logger.Field("error", err.Error()))
|
|
return nil, err
|
|
}
|
|
|
|
// compatible hysteria2, remove in future versions
|
|
protocolRequest := req.Protocol
|
|
if protocolRequest == Hysteria2 {
|
|
protocolRequest = Hysteria
|
|
}
|
|
|
|
protocols, err := data.UnmarshalProtocols()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var cfg map[string]interface{}
|
|
for _, protocol := range protocols {
|
|
if protocol.Type == protocolRequest {
|
|
cfg = l.compatible(protocol)
|
|
break
|
|
}
|
|
}
|
|
|
|
resp = &types.GetServerConfigResponse{
|
|
Basic: types.ServerBasic{
|
|
PullInterval: l.svcCtx.Config.Node.NodePullInterval,
|
|
PushInterval: l.svcCtx.Config.Node.NodePushInterval,
|
|
},
|
|
Protocol: req.Protocol,
|
|
Config: cfg,
|
|
}
|
|
c, err := json.Marshal(resp)
|
|
if err != nil {
|
|
l.Errorw("[GetServerConfig] json marshal error", logger.Field("error", err.Error()))
|
|
return nil, err
|
|
}
|
|
etag := tool.GenerateETag(c)
|
|
l.ctx.Header("ETag", etag)
|
|
ttl := time.Second * time.Duration(l.svcCtx.Config.Node.NodePullInterval+60)
|
|
if err = l.svcCtx.Redis.Set(l.ctx, cacheKey, c, ttl).Err(); err != nil {
|
|
l.Errorw("[GetServerConfig] redis set error", logger.Field("error", err.Error()))
|
|
}
|
|
// Check If-None-Match header
|
|
match := l.ctx.GetHeader("If-None-Match")
|
|
if match == etag {
|
|
return nil, xerr.StatusNotModified
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (l *GetServerConfigLogic) compatible(config node.Protocol) map[string]interface{} {
|
|
var result interface{}
|
|
switch config.Type {
|
|
case ShadowSocks:
|
|
result = ShadowsocksNode{
|
|
Port: config.Port,
|
|
Cipher: config.Cipher,
|
|
ServerKey: base64.StdEncoding.EncodeToString([]byte(config.ServerKey)),
|
|
}
|
|
case Vless:
|
|
result = VlessNode{
|
|
Port: config.Port,
|
|
Flow: config.Flow,
|
|
Network: config.Transport,
|
|
TransportConfig: &TransportConfig{
|
|
Path: config.Path,
|
|
Host: config.Host,
|
|
ServiceName: config.ServiceName,
|
|
DisableSNI: config.DisableSNI,
|
|
ReduceRtt: config.ReduceRtt,
|
|
UDPRelayMode: config.UDPRelayMode,
|
|
CongestionController: config.CongestionController,
|
|
},
|
|
Security: config.Security,
|
|
SecurityConfig: &SecurityConfig{
|
|
SNI: config.SNI,
|
|
AllowInsecure: &config.AllowInsecure,
|
|
Fingerprint: config.Fingerprint,
|
|
RealityServerAddress: config.RealityServerAddr,
|
|
RealityServerPort: config.RealityServerPort,
|
|
RealityPrivateKey: config.RealityPrivateKey,
|
|
RealityPublicKey: config.RealityPublicKey,
|
|
RealityShortId: config.RealityShortId,
|
|
},
|
|
}
|
|
case Vmess:
|
|
result = VmessNode{
|
|
Port: config.Port,
|
|
Network: config.Transport,
|
|
TransportConfig: &TransportConfig{
|
|
Path: config.Path,
|
|
Host: config.Host,
|
|
ServiceName: config.ServiceName,
|
|
DisableSNI: config.DisableSNI,
|
|
ReduceRtt: config.ReduceRtt,
|
|
UDPRelayMode: config.UDPRelayMode,
|
|
CongestionController: config.CongestionController,
|
|
},
|
|
Security: config.Security,
|
|
SecurityConfig: &SecurityConfig{
|
|
SNI: config.SNI,
|
|
AllowInsecure: &config.AllowInsecure,
|
|
Fingerprint: config.Fingerprint,
|
|
RealityServerAddress: config.RealityServerAddr,
|
|
RealityServerPort: config.RealityServerPort,
|
|
RealityPrivateKey: config.RealityPrivateKey,
|
|
RealityPublicKey: config.RealityPublicKey,
|
|
RealityShortId: config.RealityShortId,
|
|
},
|
|
}
|
|
case Trojan:
|
|
result = TrojanNode{
|
|
Port: config.Port,
|
|
Network: config.Transport,
|
|
TransportConfig: &TransportConfig{
|
|
Path: config.Path,
|
|
Host: config.Host,
|
|
ServiceName: config.ServiceName,
|
|
DisableSNI: config.DisableSNI,
|
|
ReduceRtt: config.ReduceRtt,
|
|
UDPRelayMode: config.UDPRelayMode,
|
|
CongestionController: config.CongestionController,
|
|
},
|
|
Security: config.Security,
|
|
SecurityConfig: &SecurityConfig{
|
|
SNI: config.SNI,
|
|
AllowInsecure: &config.AllowInsecure,
|
|
Fingerprint: config.Fingerprint,
|
|
RealityServerAddress: config.RealityServerAddr,
|
|
RealityServerPort: config.RealityServerPort,
|
|
RealityPrivateKey: config.RealityPrivateKey,
|
|
RealityPublicKey: config.RealityPublicKey,
|
|
RealityShortId: config.RealityShortId,
|
|
},
|
|
}
|
|
case AnyTLS:
|
|
result = AnyTLSNode{
|
|
Port: config.Port,
|
|
SecurityConfig: &SecurityConfig{
|
|
SNI: config.SNI,
|
|
AllowInsecure: &config.AllowInsecure,
|
|
Fingerprint: config.Fingerprint,
|
|
RealityServerAddress: config.RealityServerAddr,
|
|
RealityServerPort: config.RealityServerPort,
|
|
RealityPrivateKey: config.RealityPrivateKey,
|
|
RealityPublicKey: config.RealityPublicKey,
|
|
RealityShortId: config.RealityShortId,
|
|
},
|
|
}
|
|
case Tuic:
|
|
result = TuicNode{
|
|
Port: config.Port,
|
|
SecurityConfig: &SecurityConfig{
|
|
SNI: config.SNI,
|
|
AllowInsecure: &config.AllowInsecure,
|
|
Fingerprint: config.Fingerprint,
|
|
RealityServerAddress: config.RealityServerAddr,
|
|
RealityServerPort: config.RealityServerPort,
|
|
RealityPrivateKey: config.RealityPrivateKey,
|
|
RealityPublicKey: config.RealityPublicKey,
|
|
RealityShortId: config.RealityShortId,
|
|
},
|
|
}
|
|
case Hysteria:
|
|
result = Hysteria2Node{
|
|
Port: config.Port,
|
|
HopPorts: config.HopPorts,
|
|
HopInterval: config.HopInterval,
|
|
ObfsPassword: config.ObfsPassword,
|
|
SecurityConfig: &SecurityConfig{
|
|
SNI: config.SNI,
|
|
AllowInsecure: &config.AllowInsecure,
|
|
Fingerprint: config.Fingerprint,
|
|
RealityServerAddress: config.RealityServerAddr,
|
|
RealityServerPort: config.RealityServerPort,
|
|
RealityPrivateKey: config.RealityPrivateKey,
|
|
RealityPublicKey: config.RealityPublicKey,
|
|
RealityShortId: config.RealityShortId,
|
|
},
|
|
}
|
|
|
|
}
|
|
var resp map[string]interface{}
|
|
s, _ := json.Marshal(result)
|
|
_ = json.Unmarshal(s, &resp)
|
|
return resp
|
|
}
|