refactor: 更新项目引用路径从perfect-panel/ppanel-server到perfect-panel/server

feat: 添加版本和构建时间变量
fix: 修正短信队列类型注释错误
style: 清理未使用的代码和测试文件
docs: 更新安装文档中的下载链接
chore: 迁移数据库脚本添加日志和订阅配置
This commit is contained in:
2025-10-13 01:33:03 -07:00
parent 393b42f35a
commit c582087c0f
974 changed files with 23609 additions and 23398 deletions
@@ -1,44 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type BatchDeleteNodeGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewBatchDeleteNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchDeleteNodeGroupLogic {
return &BatchDeleteNodeGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *BatchDeleteNodeGroupLogic) BatchDeleteNodeGroup(req *types.BatchDeleteNodeGroupRequest) error {
// Check if the group is empty
count, err := l.svcCtx.ServerModel.QueryServerCountByServerGroups(l.ctx, req.Ids)
if err != nil {
l.Errorw("[BatchDeleteNodeGroup] Query Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query server error: %v", err)
}
if count > 0 {
return errors.Wrapf(xerr.NewErrCode(xerr.NodeGroupNotEmpty), "group is not empty")
}
// Delete the group
err = l.svcCtx.ServerModel.BatchDeleteNodeGroup(l.ctx, req.Ids)
if err != nil {
l.Errorw("[BatchDeleteNodeGroup] Delete Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
}
return nil
}
@@ -1,43 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type BatchDeleteNodeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewBatchDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchDeleteNodeLogic {
return &BatchDeleteNodeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *BatchDeleteNodeLogic) BatchDeleteNode(req *types.BatchDeleteNodeRequest) error {
err := l.svcCtx.DB.Transaction(func(db *gorm.DB) error {
for _, id := range req.Ids {
err := l.svcCtx.ServerModel.Delete(l.ctx, id)
if err != nil {
return err
}
}
return nil
})
if err != nil {
l.Errorw("[BatchDeleteNode] Delete Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
}
return nil
}
+11
View File
@@ -0,0 +1,11 @@
package server
const (
ShadowSocks = "shadowsocks"
Vmess = "vmess"
Vless = "vless"
Trojan = "trojan"
AnyTLS = "anytls"
Tuic = "tuic"
Hysteria2 = "hysteria2"
)
@@ -1,40 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/pkg/errors"
)
type CreateNodeGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateNodeGroupLogic {
return &CreateNodeGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateNodeGroupLogic) CreateNodeGroup(req *types.CreateNodeGroupRequest) error {
groupInfo := &server.Group{
Name: req.Name,
Description: req.Description,
}
err := l.svcCtx.ServerModel.InsertGroup(l.ctx, groupInfo)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), err.Error())
}
return nil
}
+16 -76
View File
@@ -2,18 +2,13 @@ package server
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/hibiken/asynq"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
queue "github.com/perfect-panel/ppanel-server/queue/types"
"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"
"github.com/pkg/errors"
)
@@ -23,6 +18,7 @@ type CreateNodeLogic struct {
svcCtx *svc.ServiceContext
}
// NewCreateNodeLogic Create Node
func NewCreateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateNodeLogic {
return &CreateNodeLogic{
Logger: logger.WithContext(ctx),
@@ -32,75 +28,19 @@ func NewCreateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Create
}
func (l *CreateNodeLogic) CreateNode(req *types.CreateNodeRequest) error {
config, err := json.Marshal(req.Config)
if err != nil {
return err
data := node.Node{
Name: req.Name,
Tags: tool.StringSliceToString(req.Tags),
Port: req.Port,
Address: req.Address,
ServerId: req.ServerId,
Protocol: req.Protocol,
}
var serverInfo server.Server
tool.DeepCopy(&serverInfo, req)
serverInfo.Config = string(config)
nodeRelay, err := json.Marshal(req.RelayNode)
if err != nil {
l.Errorw("[UpdateNode] Marshal RelayNode Error: ", logger.Field("error", err.Error()))
return err
}
if len(req.Tags) > 0 {
serverInfo.Tags = strings.Join(req.Tags, ",")
}
serverInfo.LastReportedAt = time.UnixMicro(1218124800)
serverInfo.City = req.City
serverInfo.Country = req.Country
serverInfo.RelayNode = string(nodeRelay)
if req.Protocol == "vless" {
var cfg types.Vless
if err := json.Unmarshal(config, &cfg); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
}
if cfg.Security == "reality" && cfg.SecurityConfig.RealityPublicKey == "" {
public, private, err := tool.Curve25519Genkey(false, "")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate curve25519 key error")
}
cfg.SecurityConfig.RealityPublicKey = public
cfg.SecurityConfig.RealityPrivateKey = private
cfg.SecurityConfig.RealityShortId = tool.GenerateShortID(private)
}
if cfg.SecurityConfig.RealityServerAddr == "" {
cfg.SecurityConfig.RealityServerAddr = cfg.SecurityConfig.SNI
}
if cfg.SecurityConfig.RealityServerPort == 0 {
cfg.SecurityConfig.RealityServerPort = 443
}
config, _ = json.Marshal(cfg)
serverInfo.Config = string(config)
}
err = l.svcCtx.ServerModel.Insert(l.ctx, &serverInfo)
err := l.svcCtx.NodeModel.InsertNode(l.ctx, &data)
if err != nil {
l.Errorw("[CreateNode] Insert Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create server error: %v", err)
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "[CreateNode] Insert Database Error")
}
// Marshal the task payload
payload, err := json.Marshal(queue.GetNodeCountry{
Protocol: serverInfo.Protocol,
ServerAddr: serverInfo.ServerAddr,
})
if err != nil {
l.Errorw("[GetNodeCountry]: Marshal Error", logger.Field("error", err.Error()))
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to marshal task payload")
}
// Create a queue task
task := asynq.NewTask(queue.ForthwithGetCountry, payload)
// Enqueue the task
taskInfo, err := l.svcCtx.Queue.Enqueue(task)
if err != nil {
l.Errorw("[GetNodeCountry]: Enqueue Error", logger.Field("error", err.Error()), logger.Field("payload", string(payload)))
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to enqueue task")
}
l.Infow("[GetNodeCountry]: Enqueue Success", logger.Field("taskID", taskInfo.ID), logger.Field("payload", string(payload)))
return nil
}
@@ -1,69 +0,0 @@
package server
import (
"context"
"strings"
"github.com/perfect-panel/ppanel-server/pkg/rules"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type CreateRuleGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Create rule group
func NewCreateRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRuleGroupLogic {
return &CreateRuleGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func parseAndValidateRules(ruleText, ruleName string) ([]string, error) {
var rs []string
ruleArr := strings.Split(ruleText, "\n")
if len(ruleArr) == 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "rules is empty")
}
for _, s := range ruleArr {
r := rules.NewRule(s, ruleName)
if r == nil {
continue
}
if err := r.Validate(); err != nil {
continue
}
rs = append(rs, r.String())
}
return rs, nil
}
func (l *CreateRuleGroupLogic) CreateRuleGroup(req *types.CreateRuleGroupRequest) error {
rs, err := parseAndValidateRules(req.Rules, req.Name)
if err != nil {
return err
}
err = l.svcCtx.ServerModel.InsertRuleGroup(l.ctx, &server.RuleGroup{
Name: req.Name,
Icon: req.Icon,
Tags: tool.StringSliceToString(req.Tags),
Rules: strings.Join(rs, "\n"),
Enable: req.Enable,
})
if err != nil {
l.Errorw("[CreateRuleGroup] Insert Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create server rule group error: %v", err)
}
return nil
}
@@ -0,0 +1,110 @@
package server
import (
"context"
"strings"
"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/ip"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type CreateServerLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewCreateServerLogic Create Server
func NewCreateServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateServerLogic {
return &CreateServerLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateServerLogic) CreateServer(req *types.CreateServerRequest) error {
data := node.Server{
Name: req.Name,
Country: req.Country,
City: req.City,
Address: req.Address,
Sort: req.Sort,
Protocols: "",
}
protocols := make([]node.Protocol, 0)
for _, item := range req.Protocols {
if item.Type == "" {
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols type is empty"), "protocols type is empty")
}
var protocol node.Protocol
tool.DeepCopy(&protocol, item)
// VLESS Reality Key Generation
if protocol.Type == "vless" {
if protocol.Security == "reality" {
if protocol.RealityPublicKey == "" {
public, private, err := tool.Curve25519Genkey(false, "")
if err != nil {
l.Errorf("[CreateServer] Generate Reality Key Error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate reality key error: %v", err)
}
protocol.RealityPublicKey = public
protocol.RealityPrivateKey = private
protocol.RealityShortId = tool.GenerateShortID(private)
}
if protocol.RealityServerAddr == "" {
protocol.RealityServerAddr = protocol.SNI
}
if protocol.RealityServerPort == 0 {
protocol.RealityServerPort = 443
}
}
}
// ShadowSocks 2022 Key Generation
if protocol.Type == "shadowsocks" {
if strings.Contains(protocol.Cipher, "2022") {
var length int
switch protocol.Cipher {
case "2022-blake3-aes-128-gcm":
length = 16
default:
length = 32
}
if len(protocol.ServerKey) != length {
protocol.ServerKey = tool.GenerateCipher(protocol.ServerKey, length)
}
}
}
protocols = append(protocols, protocol)
}
err := data.MarshalProtocols(protocols)
if err != nil {
l.Errorf("[CreateServer] Marshal Protocols Error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols marshal error"), "protocols marshal error: %v", err)
}
if data.City == "" && data.Country == "" {
// query server ip location
result, err := ip.GetRegionByIp(req.Address)
if err != nil {
l.Errorf("[CreateServer] GetRegionByIp Error: %v", err.Error())
} else {
data.City = result.City
data.Country = result.Country
}
}
err = l.svcCtx.NodeModel.InsertServer(l.ctx, &data)
if err != nil {
l.Errorf("[CreateServer] Insert Server error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert server error: %v", err)
}
return nil
}
@@ -1,44 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type DeleteNodeGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteNodeGroupLogic {
return &DeleteNodeGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteNodeGroupLogic) DeleteNodeGroup(req *types.DeleteNodeGroupRequest) error {
// Check if the group is empty
count, err := l.svcCtx.ServerModel.QueryServerCountByServerGroups(l.ctx, []int64{req.Id})
if err != nil {
l.Errorw("[DeleteNodeGroup] Query Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query server error: %v", err)
}
if count > 0 {
return errors.Wrapf(xerr.NewErrCode(xerr.NodeGroupNotEmpty), "group is not empty")
}
// Delete the group
err = l.svcCtx.ServerModel.DeleteGroup(l.ctx, req.Id)
if err != nil {
l.Errorw("[DeleteNodeGroup] Delete Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
}
return nil
}
+20 -29
View File
@@ -2,14 +2,14 @@ package server
import (
"context"
"strings"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type DeleteNodeLogic struct {
@@ -18,6 +18,7 @@ type DeleteNodeLogic struct {
svcCtx *svc.ServiceContext
}
// NewDeleteNodeLogic Delete Node
func NewDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteNodeLogic {
return &DeleteNodeLogic{
Logger: logger.WithContext(ctx),
@@ -27,30 +28,20 @@ func NewDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete
}
func (l *DeleteNodeLogic) DeleteNode(req *types.DeleteNodeRequest) error {
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// Delete server
err := l.svcCtx.ServerModel.Delete(l.ctx, req.Id)
if err != nil {
return err
}
// Delete server to subscribe
subs, err := l.svcCtx.SubscribeModel.QuerySubscribeIdsByServerIdAndServerGroupId(l.ctx, req.Id, 0)
if err != nil {
return err
}
for _, sub := range subs {
servers := tool.StringToInt64Slice(sub.Server)
newServers := tool.RemoveElementBySlice(servers, req.Id)
sub.Server = tool.Int64SliceToString(newServers)
if err = l.svcCtx.SubscribeModel.Update(l.ctx, sub); err != nil {
return err
}
}
return nil
})
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
err = l.svcCtx.NodeModel.DeleteNode(l.ctx, req.Id)
if err != nil {
l.Errorw("[DeleteNode] Delete Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete server error: %v", err)
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "[DeleteNode] Delete Database Error")
}
return nil
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
ServerId: []int64{data.ServerId},
Tag: strings.Split(data.Tags, ","),
Search: "",
Protocol: data.Protocol,
})
}
@@ -1,35 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type DeleteRuleGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Delete rule group
func NewDeleteRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRuleGroupLogic {
return &DeleteRuleGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteRuleGroupLogic) DeleteRuleGroup(req *types.DeleteRuleGroupRequest) error {
err := l.svcCtx.ServerModel.DeleteRuleGroup(l.ctx, req.Id)
if err != nil {
l.Errorw("[DeleteRuleGroup] Delete Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete server rule group error: %v", err)
}
return nil
}
@@ -0,0 +1,41 @@
package server
import (
"context"
"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/xerr"
"github.com/pkg/errors"
)
type DeleteServerLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewDeleteServerLogic Delete Server
func NewDeleteServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteServerLogic {
return &DeleteServerLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteServerLogic) DeleteServer(req *types.DeleteServerRequest) error {
err := l.svcCtx.NodeModel.DeleteServer(l.ctx, req.Id)
if err != nil {
l.Errorw("[DeleteServer] Delete Server Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "[DeleteServer] Delete Server Error")
}
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
ServerId: []int64{req.Id},
Search: "",
})
}
@@ -0,0 +1,64 @@
package server
import (
"context"
"strings"
"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"
"github.com/pkg/errors"
)
type FilterNodeListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterNodeListLogic Filter Node List
func NewFilterNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterNodeListLogic {
return &FilterNodeListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterNodeListLogic) FilterNodeList(req *types.FilterNodeListRequest) (resp *types.FilterNodeListResponse, err error) {
total, data, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
Page: req.Page,
Size: req.Size,
Search: req.Search,
})
if err != nil {
l.Errorw("[FilterNodeList] Query Database Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterNodeList] Query Database Error")
}
list := make([]types.Node, 0)
for _, datum := range data {
list = append(list, types.Node{
Id: datum.Id,
Name: datum.Name,
Tags: tool.RemoveDuplicateElements(strings.Split(datum.Tags, ",")...),
Port: datum.Port,
Address: datum.Address,
ServerId: datum.ServerId,
Protocol: datum.Protocol,
Enabled: datum.Enabled,
Sort: datum.Sort,
CreatedAt: datum.CreatedAt.UnixMilli(),
UpdatedAt: datum.UpdatedAt.UnixMilli(),
})
}
return &types.FilterNodeListResponse{
List: list,
Total: total,
}, nil
}
@@ -0,0 +1,164 @@
package server
import (
"context"
"time"
"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"
"github.com/pkg/errors"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
type FilterServerListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterServerListLogic Filter Server List
func NewFilterServerListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterServerListLogic {
return &FilterServerListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterServerListLogic) FilterServerList(req *types.FilterServerListRequest) (resp *types.FilterServerListResponse, err error) {
total, data, err := l.svcCtx.NodeModel.FilterServerList(l.ctx, &node.FilterParams{
Page: req.Page,
Size: req.Size,
Search: req.Search,
})
if err != nil {
l.Errorw("[FilterServerList] Query Database Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterServerList] Query Database Error")
}
list := make([]types.Server, 0)
for _, datum := range data {
var server types.Server
tool.DeepCopy(&server, datum)
// handler protocols
var protocols []types.Protocol
dst, err := datum.UnmarshalProtocols()
if err != nil {
l.Errorf("[FilterServerList] UnmarshalProtocols Error: %s", err.Error())
continue
}
tool.DeepCopy(&protocols, dst)
server.Protocols = protocols
nodeStatus, err := l.svcCtx.NodeModel.StatusCache(l.ctx, datum.Id)
if err != nil {
if !errors.Is(err, redis.Nil) {
l.Errorw("[handlerServerStatus] GetNodeStatus Error: ", logger.Field("error", err.Error()), logger.Field("node_id", datum.Id))
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetNodeStatus Error")
}
server.Status = types.ServerStatus{
Mem: nodeStatus.Mem,
Cpu: nodeStatus.Cpu,
Disk: nodeStatus.Disk,
Online: l.handlerServerStatus(datum.Id, protocols),
Status: l.handlerServerStaus(datum.LastReportedAt),
}
list = append(list, server)
}
return &types.FilterServerListResponse{
List: list,
Total: total,
}, nil
}
func (l *FilterServerListLogic) handlerServerStatus(id int64, protocols []types.Protocol) []types.ServerOnlineUser {
result := make([]types.ServerOnlineUser, 0)
for _, protocol := range protocols {
// query online user
data, err := l.svcCtx.NodeModel.OnlineUserSubscribe(l.ctx, id, protocol.Type)
if err != nil {
if !errors.Is(err, redis.Nil) {
l.Errorw("[handlerServerStatus] OnlineUserSubscribe Error: ", logger.Field("error", err.Error()), logger.Field("node_id", id), logger.Field("protocol", protocol.Type))
}
continue
}
if len(data) > 0 {
for sub, online := range data {
var ips []types.ServerOnlineIP
for _, ip := range online {
ips = append(ips, types.ServerOnlineIP{
IP: ip,
Protocol: protocol.Type,
})
}
result = append(result, types.ServerOnlineUser{
IP: ips,
SubscribeId: sub,
})
}
}
}
// merge same subscribe
var mapResult = make(map[int64]types.ServerOnlineUser)
for _, item := range result {
if exist, ok := mapResult[item.SubscribeId]; ok {
// merge
exist.Traffic += item.Traffic
exist.IP = append(exist.IP, item.IP...)
mapResult[item.SubscribeId] = exist
} else {
// get subscribe info
info, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, item.SubscribeId)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
l.Errorw("[handlerServerStatus] FindOneSubscribe Error: ", logger.Field("error", err.Error()), logger.Field("subscribe_id", item.SubscribeId))
}
continue
}
data := types.ServerOnlineUser{
IP: item.IP,
UserId: info.UserId,
Subscribe: "",
SubscribeId: item.SubscribeId,
Traffic: info.Download + info.Upload,
ExpiredAt: info.ExpireTime.UnixMilli(),
}
if info.Subscribe != nil {
data.Subscribe = info.Subscribe.Name
}
// add new
mapResult[item.SubscribeId] = data
}
}
// convert map to slice
result = make([]types.ServerOnlineUser, 0, len(mapResult))
for _, item := range mapResult {
result = append(result, item)
}
return result
}
func (l *FilterServerListLogic) handlerServerStaus(last *time.Time) string {
if last == nil {
return "offline"
}
if time.Since(*last) > time.Minute*5 {
return "offline"
}
if time.Since(*last) > time.Minute*3 {
return "warning"
}
return "online"
}
@@ -1,43 +0,0 @@
package server
import (
"context"
"encoding/json"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type GetNodeDetailLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetNodeDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeDetailLogic {
return &GetNodeDetailLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetNodeDetailLogic) GetNodeDetail(req *types.GetDetailRequest) (resp *types.Server, err error) {
detail, err := l.svcCtx.ServerModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get server detail error: %v", err.Error())
}
resp = &types.Server{}
tool.DeepCopy(resp, detail)
var cfg map[string]interface{}
err = json.Unmarshal([]byte(detail.Config), &cfg)
if err != nil {
cfg = make(map[string]interface{})
}
resp.Config = cfg
return
}
@@ -1,39 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type GetNodeGroupListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetNodeGroupListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeGroupListLogic {
return &GetNodeGroupListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetNodeGroupListLogic) GetNodeGroupList() (resp *types.GetNodeGroupListResponse, err error) {
nodeGroupList, err := l.svcCtx.ServerModel.QueryAllGroup(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
}
nodeGroups := make([]types.ServerGroup, 0)
tool.DeepCopy(&nodeGroups, nodeGroupList)
return &types.GetNodeGroupListResponse{
Total: int64(len(nodeGroups)),
List: nodeGroups,
}, nil
}
@@ -1,100 +0,0 @@
package server
import (
"context"
"encoding/json"
"strings"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/redis/go-redis/v9"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type GetNodeListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeListLogic {
return &GetNodeListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetNodeListLogic) GetNodeList(req *types.GetNodeServerListRequest) (resp *types.GetNodeServerListResponse, err error) {
total, list, err := l.svcCtx.ServerModel.FindServerListByFilter(l.ctx, &server.ServerFilter{
Page: req.Page,
Size: req.Size,
Search: req.Search,
Tag: req.Tag,
Group: req.GroupId,
})
if err != nil {
l.Errorw("[GetNodeList] Query Database Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
}
nodes := make([]types.Server, 0)
for _, v := range list {
node := types.Server{}
tool.DeepCopy(&node, v)
// default relay mode
if node.RelayMode == "" {
node.RelayMode = "none"
}
if len(v.Tags) > 0 {
if strings.Contains(v.Tags, ",") {
node.Tags = strings.Split(v.Tags, ",")
} else {
node.Tags = []string{v.Tags}
}
}
// parse config
var cfg map[string]interface{}
err = json.Unmarshal([]byte(v.Config), &cfg)
if err != nil {
cfg = make(map[string]interface{})
}
node.Config = cfg
relayNode := make([]types.NodeRelay, 0)
err = json.Unmarshal([]byte(v.RelayNode), &relayNode)
if err != nil {
l.Errorw("[GetNodeList] Unmarshal RelayNode Error: ", logger.Field("error", err.Error()), logger.Field("relayNode", v.RelayNode))
}
node.RelayNode = relayNode
var status types.NodeStatus
nodeStatus, err := l.svcCtx.NodeCache.GetNodeStatus(l.ctx, v.Id)
if err != nil {
// redis nil is not a Error
if !errors.Is(err, redis.Nil) {
l.Errorw("[GetNodeList] Get Node Status Error: ", logger.Field("error", err.Error()))
}
} else {
onlineUser, err := l.svcCtx.NodeCache.GetNodeOnlineUser(l.ctx, v.Id)
if err != nil {
l.Errorw("[GetNodeList] Get Node Online User Error: ", logger.Field("error", err.Error()))
} else {
status.Online = onlineUser
}
status.Cpu = nodeStatus.Cpu
status.Mem = nodeStatus.Mem
status.Disk = nodeStatus.Disk
status.UpdatedAt = nodeStatus.UpdatedAt
}
node.Status = &status
nodes = append(nodes, node)
}
return &types.GetNodeServerListResponse{
Total: total,
List: nodes,
}, nil
}
@@ -1,53 +0,0 @@
package server
import (
"context"
"strings"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type GetNodeTagListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get node tag list
func NewGetNodeTagListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeTagListLogic {
return &GetNodeTagListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetNodeTagListLogic) GetNodeTagList() (resp *types.GetNodeTagListResponse, err error) {
var nodeTags, tags []string
err = l.svcCtx.ServerModel.Transaction(l.ctx, func(db *gorm.DB) error {
return db.Model(&server.Server{}).Select("tags").Pluck("tags", &nodeTags).Error
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get node tag list failed, %s", err.Error())
}
for _, tag := range nodeTags {
tags = append(tags, strings.Split(tag, ",")...)
}
// Remove duplicate tags
tags = tool.RemoveDuplicateElements(tags...)
return &types.GetNodeTagListResponse{
Tags: tags,
}, nil
}
@@ -1,52 +0,0 @@
package server
import (
"context"
"strings"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type GetRuleGroupListLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get rule group list
func NewGetRuleGroupListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRuleGroupListLogic {
return &GetRuleGroupListLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetRuleGroupListLogic) GetRuleGroupList() (resp *types.GetRuleGroupResponse, err error) {
nodeRuleGroupList, err := l.svcCtx.ServerModel.QueryAllRuleGroup(l.ctx)
if err != nil {
l.Errorw("[GetRuleGroupList] Query Database Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
}
nodeRuleGroups := make([]types.ServerRuleGroup, len(nodeRuleGroupList))
for i, v := range nodeRuleGroupList {
nodeRuleGroups[i] = types.ServerRuleGroup{
Id: v.Id,
Icon: v.Icon,
Name: v.Name,
Tags: strings.Split(v.Tags, ","),
Rules: v.Rules,
Enable: v.Enable,
CreatedAt: v.CreatedAt.UnixMilli(),
UpdatedAt: v.UpdatedAt.UnixMilli(),
}
}
return &types.GetRuleGroupResponse{
Total: int64(len(nodeRuleGroups)),
List: nodeRuleGroups,
}, nil
}
@@ -0,0 +1,49 @@
package server
import (
"context"
"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"
"github.com/pkg/errors"
)
type GetServerProtocolsLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get Server Protocols
func NewGetServerProtocolsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetServerProtocolsLogic {
return &GetServerProtocolsLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetServerProtocolsLogic) GetServerProtocols(req *types.GetServerProtocolsRequest) (resp *types.GetServerProtocolsResponse, err error) {
// find server
data, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, req.Id)
if err != nil {
l.Errorf("[GetServerProtocols] FindOneServer Error: %s", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[GetServerProtocols] FindOneServer Error: %s", err.Error())
}
// handler protocols
var protocols []types.Protocol
dst, err := data.UnmarshalProtocols()
if err != nil {
l.Errorf("[FilterServerList] UnmarshalProtocols Error: %s", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterServerList] UnmarshalProtocols Error: %s", err.Error())
}
tool.DeepCopy(&protocols, dst)
return &types.GetServerProtocolsResponse{
Protocols: protocols,
}, nil
}
@@ -0,0 +1,52 @@
package server
import (
"context"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/server"
"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/xerr"
"github.com/pkg/errors"
)
type HasMigrateSeverNodeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewHasMigrateSeverNodeLogic Check if there is any server or node to migrate
func NewHasMigrateSeverNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HasMigrateSeverNodeLogic {
return &HasMigrateSeverNodeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *HasMigrateSeverNodeLogic) HasMigrateSeverNode() (resp *types.HasMigrateSeverNodeResponse, err error) {
var oldCount, newCount int64
query := l.svcCtx.DB.WithContext(l.ctx)
err = query.Model(&server.Server{}).Count(&oldCount).Error
if err != nil {
l.Errorw("[HasMigrateSeverNode] Query Old Server Count Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[HasMigrateSeverNode] Query Old Server Count Error")
}
err = query.Model(&node.Server{}).Count(&newCount).Error
if err != nil {
l.Errorw("[HasMigrateSeverNode] Query New Server Count Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[HasMigrateSeverNode] Query New Server Count Error")
}
var shouldMigrate bool
if oldCount != 0 && newCount == 0 {
shouldMigrate = true
}
return &types.HasMigrateSeverNodeResponse{
HasMigrate: shouldMigrate,
}, nil
}
@@ -0,0 +1,338 @@
package server
import (
"context"
"encoding/json"
"fmt"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/server"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
)
type MigrateServerNodeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewMigrateServerNodeLogic Migrate server and node data to new database
func NewMigrateServerNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MigrateServerNodeLogic {
return &MigrateServerNodeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *MigrateServerNodeLogic) MigrateServerNode() (resp *types.MigrateServerNodeResponse, err error) {
tx := l.svcCtx.DB.WithContext(l.ctx).Begin()
var oldServers []*server.Server
var newServers []*node.Server
var newNodes []*node.Node
err = tx.Model(&server.Server{}).Find(&oldServers).Error
if err != nil {
l.Errorw("[MigrateServerNode] Query Old Server List Error: ", logger.Field("error", err.Error()))
return &types.MigrateServerNodeResponse{
Succee: 0,
Fail: 0,
Message: fmt.Sprintf("Query Old Server List Error: %s", err.Error()),
}, nil
}
for _, oldServer := range oldServers {
data, err := l.adapterServer(oldServer)
if err != nil {
l.Errorw("[MigrateServerNode] Adapter Server Error: ", logger.Field("error", err.Error()))
if resp == nil {
resp = &types.MigrateServerNodeResponse{}
}
resp.Fail++
if resp.Message == "" {
resp.Message = fmt.Sprintf("Adapter Server Error: %s", err.Error())
} else {
resp.Message = fmt.Sprintf("%s; Adapter Server Error: %s", resp.Message, err.Error())
}
continue
}
newServers = append(newServers, data)
newNode, err := l.adapterNode(oldServer)
if err != nil {
l.Errorw("[MigrateServerNode] Adapter Node Error: ", logger.Field("error", err.Error()))
if resp == nil {
resp = &types.MigrateServerNodeResponse{}
}
resp.Fail++
if resp.Message == "" {
resp.Message = fmt.Sprintf("Adapter Node Error: %s", err.Error())
} else {
resp.Message = fmt.Sprintf("%s; Adapter Node Error: %s", resp.Message, err.Error())
}
continue
}
for _, item := range newNode {
if item.Port == 0 {
protocols, _ := data.UnmarshalProtocols()
if len(protocols) > 0 {
item.Port = protocols[0].Port
}
}
newNodes = append(newNodes, item)
}
}
if len(newServers) > 0 {
err = tx.Model(&node.Server{}).CreateInBatches(newServers, 20).Error
if err != nil {
tx.Rollback()
l.Errorw("[MigrateServerNode] Insert New Server List Error: ", logger.Field("error", err.Error()))
return &types.MigrateServerNodeResponse{
Succee: 0,
Fail: uint64(len(newServers)),
Message: fmt.Sprintf("Insert New Server List Error: %s", err.Error()),
}, nil
}
}
if len(newNodes) > 0 {
err = tx.Model(&node.Node{}).CreateInBatches(newNodes, 20).Error
if err != nil {
tx.Rollback()
l.Errorw("[MigrateServerNode] Insert New Node List Error: ", logger.Field("error", err.Error()))
return &types.MigrateServerNodeResponse{
Succee: uint64(len(newServers)),
Fail: uint64(len(newNodes)),
Message: fmt.Sprintf("Insert New Node List Error: %s", err.Error()),
}, nil
}
}
tx.Commit()
return &types.MigrateServerNodeResponse{
Succee: uint64(len(newServers)),
Fail: 0,
Message: fmt.Sprintf("Migrate Success: %d servers and %d nodes", len(newServers), len(newNodes)),
}, nil
}
func (l *MigrateServerNodeLogic) adapterServer(info *server.Server) (*node.Server, error) {
result := &node.Server{
Id: info.Id,
Name: info.Name,
Country: info.Country,
City: info.City,
//Ratio: info.TrafficRatio,
Address: info.ServerAddr,
Sort: int(info.Sort),
Protocols: "",
}
var protocols []node.Protocol
switch info.Protocol {
case ShadowSocks:
var src server.Shadowsocks
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocols = append(protocols, node.Protocol{
Type: "shadowsocks",
Cipher: src.Method,
Port: uint16(src.Port),
ServerKey: src.ServerKey,
Ratio: float64(info.TrafficRatio),
})
case Vmess:
var src server.Vmess
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocol := node.Protocol{
Type: "vmess",
Port: uint16(src.Port),
Security: src.Security,
SNI: src.SecurityConfig.SNI,
AllowInsecure: src.SecurityConfig.AllowInsecure,
Fingerprint: src.SecurityConfig.Fingerprint,
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
RealityServerPort: src.SecurityConfig.RealityServerPort,
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
RealityShortId: src.SecurityConfig.RealityShortId,
Transport: src.Transport,
Host: src.TransportConfig.Host,
Path: src.TransportConfig.Path,
ServiceName: src.TransportConfig.ServiceName,
Flow: src.Flow,
Ratio: float64(info.TrafficRatio),
}
protocols = append(protocols, protocol)
protocols = append(protocols, protocol)
case Vless:
var src server.Vless
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocol := node.Protocol{
Type: "vless",
Port: uint16(src.Port),
Security: src.Security,
SNI: src.SecurityConfig.SNI,
AllowInsecure: src.SecurityConfig.AllowInsecure,
Fingerprint: src.SecurityConfig.Fingerprint,
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
RealityServerPort: src.SecurityConfig.RealityServerPort,
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
RealityShortId: src.SecurityConfig.RealityShortId,
Transport: src.Transport,
Host: src.TransportConfig.Host,
Path: src.TransportConfig.Path,
ServiceName: src.TransportConfig.ServiceName,
Flow: src.Flow,
Ratio: float64(info.TrafficRatio),
}
protocols = append(protocols, protocol)
case Trojan:
var src server.Trojan
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocol := node.Protocol{
Type: "trojan",
Port: uint16(src.Port),
Security: src.Security,
SNI: src.SecurityConfig.SNI,
AllowInsecure: src.SecurityConfig.AllowInsecure,
Fingerprint: src.SecurityConfig.Fingerprint,
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
RealityServerPort: src.SecurityConfig.RealityServerPort,
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
RealityShortId: src.SecurityConfig.RealityShortId,
Transport: src.Transport,
Host: src.TransportConfig.Host,
Path: src.TransportConfig.Path,
ServiceName: src.TransportConfig.ServiceName,
Flow: src.Flow,
Ratio: float64(info.TrafficRatio),
}
protocols = append(protocols, protocol)
case Hysteria2:
var src server.Hysteria2
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocol := node.Protocol{
Type: "hysteria",
Port: uint16(src.Port),
HopPorts: src.HopPorts,
HopInterval: src.HopInterval,
ObfsPassword: src.ObfsPassword,
SNI: src.SecurityConfig.SNI,
AllowInsecure: src.SecurityConfig.AllowInsecure,
Fingerprint: src.SecurityConfig.Fingerprint,
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
RealityServerPort: src.SecurityConfig.RealityServerPort,
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
RealityShortId: src.SecurityConfig.RealityShortId,
Ratio: float64(info.TrafficRatio),
}
protocols = append(protocols, protocol)
case Tuic:
var src server.Tuic
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocol := node.Protocol{
Type: "tuic",
Port: uint16(src.Port),
DisableSNI: src.DisableSNI,
ReduceRtt: src.ReduceRtt,
UDPRelayMode: src.UDPRelayMode,
CongestionController: src.CongestionController,
SNI: src.SecurityConfig.SNI,
AllowInsecure: src.SecurityConfig.AllowInsecure,
Fingerprint: src.SecurityConfig.Fingerprint,
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
RealityServerPort: src.SecurityConfig.RealityServerPort,
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
RealityShortId: src.SecurityConfig.RealityShortId,
Ratio: float64(info.TrafficRatio),
}
protocols = append(protocols, protocol)
case AnyTLS:
var src server.AnyTLS
err := json.Unmarshal([]byte(info.Config), &src)
if err != nil {
return nil, err
}
protocol := node.Protocol{
Type: "anytls",
Port: uint16(src.Port),
SNI: src.SecurityConfig.SNI,
AllowInsecure: src.SecurityConfig.AllowInsecure,
Fingerprint: src.SecurityConfig.Fingerprint,
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
RealityServerPort: src.SecurityConfig.RealityServerPort,
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
RealityShortId: src.SecurityConfig.RealityShortId,
Ratio: float64(info.TrafficRatio),
}
protocols = append(protocols, protocol)
}
if len(protocols) > 0 {
err := result.MarshalProtocols(protocols)
if err != nil {
return nil, err
}
}
return result, nil
}
func (l *MigrateServerNodeLogic) adapterNode(info *server.Server) ([]*node.Node, error) {
var nodes []*node.Node
enable := true
switch info.RelayMode {
case server.RelayModeNone:
nodes = append(nodes, &node.Node{
Name: info.Name,
Tags: "",
Port: 0,
Address: info.ServerAddr,
ServerId: info.Id,
Protocol: info.Protocol,
Enabled: &enable,
})
default:
var relays []server.NodeRelay
err := json.Unmarshal([]byte(info.RelayNode), &relays)
if err != nil {
return nil, err
}
for _, relay := range relays {
nodes = append(nodes, &node.Node{
Name: relay.Prefix + info.Name,
Tags: "",
Port: uint16(relay.Port),
Address: relay.Host,
ServerId: info.Id,
Protocol: info.Protocol,
Enabled: &enable,
})
}
}
return nodes, nil
}
@@ -0,0 +1,46 @@
package server
import (
"context"
"strings"
"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"
"github.com/pkg/errors"
)
type QueryNodeTagLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewQueryNodeTagLogic Query all node tags
func NewQueryNodeTagLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryNodeTagLogic {
return &QueryNodeTagLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QueryNodeTagLogic) QueryNodeTag() (resp *types.QueryNodeTagResponse, err error) {
var nodes []*node.Node
if err = l.svcCtx.DB.WithContext(l.ctx).Model(&node.Node{}).Find(&nodes).Error; err != nil {
l.Errorw("[QueryNodeTag] Query Database Error: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[QueryNodeTag] Query Database Error")
}
var tags []string
for _, item := range nodes {
tags = append(tags, strings.Split(item.Tags, ",")...)
}
return &types.QueryNodeTagResponse{
Tags: tool.RemoveDuplicateElements(tags...),
}, nil
}
@@ -3,36 +3,35 @@ package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
)
type NodeSortLogic struct {
type ResetSortWithNodeLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Node sort
func NewNodeSortLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NodeSortLogic {
return &NodeSortLogic{
// NewResetSortWithNodeLogic Reset node sort
func NewResetSortWithNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetSortWithNodeLogic {
return &ResetSortWithNodeLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
err := l.svcCtx.ServerModel.Transaction(l.ctx, func(db *gorm.DB) error {
func (l *ResetSortWithNodeLogic) ResetSortWithNode(req *types.ResetSortRequest) error {
err := l.svcCtx.NodeModel.Transaction(l.ctx, func(db *gorm.DB) error {
// find all servers id
var existingIDs []int64
db.Model(&server.Server{}).Select("id").Find(&existingIDs)
db.Model(&node.Node{}).Select("id").Find(&existingIDs)
// check if the id is valid
validIDMap := make(map[int64]bool)
for _, id := range existingIDs {
@@ -46,12 +45,12 @@ func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
}
}
// query all servers
var servers []*server.Server
db.Model(&server.Server{}).Order("sort ASC").Find(&servers)
var servers []*node.Node
db.Model(&node.Node{}).Order("sort ASC").Find(&servers)
// create a map of the current sort
currentSortMap := make(map[int64]int64)
for _, item := range servers {
currentSortMap[item.Id] = item.Sort
currentSortMap[item.Id] = int64(item.Sort)
}
// new sort map
@@ -67,7 +66,12 @@ func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
}
}
for _, item := range itemsToUpdate {
if err := db.Model(&server.Server{}).Where("id = ?", item.Id).Update("sort", item.Sort).Error; err != nil {
s, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, item.Id)
if err != nil {
return err
}
s.Sort = int(item.Sort)
if err = l.svcCtx.NodeModel.UpdateNode(l.ctx, s, db); err != nil {
l.Errorw("[NodeSort] Update Database Error: ", logger.Field("error", err.Error()), logger.Field("id", item.Id), logger.Field("sort", item.Sort))
return err
}
@@ -0,0 +1,86 @@
package server
import (
"context"
"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/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type ResetSortWithServerLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewResetSortWithServerLogic Reset server sort
func NewResetSortWithServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetSortWithServerLogic {
return &ResetSortWithServerLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ResetSortWithServerLogic) ResetSortWithServer(req *types.ResetSortRequest) error {
err := l.svcCtx.NodeModel.Transaction(l.ctx, func(db *gorm.DB) error {
// find all servers id
var existingIDs []int64
db.Model(&node.Server{}).Select("id").Find(&existingIDs)
// check if the id is valid
validIDMap := make(map[int64]bool)
for _, id := range existingIDs {
validIDMap[id] = true
}
// check if the sort is valid
var validItems []types.SortItem
for _, item := range req.Sort {
if validIDMap[item.Id] {
validItems = append(validItems, item)
}
}
// query all servers
var servers []*node.Server
db.Model(&node.Server{}).Order("sort ASC").Find(&servers)
// create a map of the current sort
currentSortMap := make(map[int64]int64)
for _, item := range servers {
currentSortMap[item.Id] = int64(item.Sort)
}
// new sort map
newSortMap := make(map[int64]int64)
for _, item := range validItems {
newSortMap[item.Id] = item.Sort
}
var itemsToUpdate []types.SortItem
for _, item := range validItems {
if oldSort, exists := currentSortMap[item.Id]; exists && oldSort != item.Sort {
itemsToUpdate = append(itemsToUpdate, item)
}
}
for _, item := range itemsToUpdate {
s, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, item.Id)
if err != nil {
return err
}
s.Sort = int(item.Sort)
if err = l.svcCtx.NodeModel.UpdateServer(l.ctx, s, db); err != nil {
l.Errorw("[NodeSort] Update Database Error: ", logger.Field("error", err.Error()), logger.Field("id", item.Id), logger.Field("sort", item.Sort))
return err
}
}
return nil
})
if err != nil {
l.Errorw("[NodeSort] Update Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
}
return nil
}
@@ -0,0 +1,51 @@
package server
import (
"context"
"strings"
"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/xerr"
"github.com/pkg/errors"
)
type ToggleNodeStatusLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewToggleNodeStatusLogic Toggle Node Status
func NewToggleNodeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToggleNodeStatusLogic {
return &ToggleNodeStatusLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ToggleNodeStatusLogic) ToggleNodeStatus(req *types.ToggleNodeStatusRequest) error {
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
if err != nil {
l.Errorw("[ToggleNodeStatus] Query Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[ToggleNodeStatus] Query Database Error")
}
data.Enabled = req.Enable
err = l.svcCtx.NodeModel.UpdateNode(l.ctx, data)
if err != nil {
l.Errorw("[ToggleNodeStatus] Update Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "[ToggleNodeStatus] Update Database Error")
}
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
ServerId: []int64{data.ServerId},
Tag: strings.Split(data.Tags, ","),
Search: "",
})
}
@@ -1,40 +0,0 @@
package server
import (
"context"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
)
type UpdateNodeGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUpdateNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateNodeGroupLogic {
return &UpdateNodeGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateNodeGroupLogic) UpdateNodeGroup(req *types.UpdateNodeGroupRequest) error {
// check server group exist
nodeGroup, err := l.svcCtx.ServerModel.FindOneGroup(l.ctx, req.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
}
nodeGroup.Name = req.Name
nodeGroup.Description = req.Description
err = l.svcCtx.ServerModel.UpdateGroup(l.ctx, nodeGroup)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
}
return nil
}
+24 -80
View File
@@ -2,18 +2,13 @@ package server
import (
"context"
"encoding/json"
"strings"
"github.com/perfect-panel/ppanel-server/pkg/device"
"github.com/hibiken/asynq"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
queue "github.com/perfect-panel/ppanel-server/queue/types"
"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"
"github.com/pkg/errors"
)
@@ -23,6 +18,7 @@ type UpdateNodeLogic struct {
svcCtx *svc.ServiceContext
}
// NewUpdateNodeLogic Update Node
func NewUpdateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateNodeLogic {
return &UpdateNodeLogic{
Logger: logger.WithContext(ctx),
@@ -32,79 +28,27 @@ func NewUpdateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Update
}
func (l *UpdateNodeLogic) UpdateNode(req *types.UpdateNodeRequest) error {
// Check server exist
nodeInfo, err := l.svcCtx.ServerModel.FindOne(l.ctx, req.Id)
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find server error: %v", err)
l.Errorw("[UpdateNode] Query Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "[UpdateNode] Query Database Error")
}
tool.DeepCopy(nodeInfo, req)
config, err := json.Marshal(req.Config)
if err != nil {
return err
}
nodeInfo.Config = string(config)
nodeRelay, err := json.Marshal(req.RelayNode)
if err != nil {
l.Errorw("[UpdateNode] Marshal RelayNode Error: ", logger.Field("error", err.Error()))
return err
}
if len(req.Tags) > 0 {
nodeInfo.Tags = strings.Join(req.Tags, ",")
}
nodeInfo.City = req.City
nodeInfo.Country = req.Country
nodeInfo.RelayNode = string(nodeRelay)
if req.Protocol == "vless" {
var cfg types.Vless
if err := json.Unmarshal(config, &cfg); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
}
if cfg.Security == "reality" && cfg.SecurityConfig.RealityPublicKey == "" {
public, private, err := tool.Curve25519Genkey(false, "")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate curve25519 key error")
}
cfg.SecurityConfig.RealityPublicKey = public
cfg.SecurityConfig.RealityPrivateKey = private
cfg.SecurityConfig.RealityShortId = tool.GenerateShortID(private)
}
if cfg.SecurityConfig.RealityServerAddr == "" {
cfg.SecurityConfig.RealityServerAddr = cfg.SecurityConfig.SNI
}
if cfg.SecurityConfig.RealityServerPort == 0 {
cfg.SecurityConfig.RealityServerPort = 443
}
config, _ = json.Marshal(cfg)
nodeInfo.Config = string(config)
}
err = l.svcCtx.ServerModel.Update(l.ctx, nodeInfo)
data.Name = req.Name
data.Tags = tool.StringSliceToString(req.Tags)
data.ServerId = req.ServerId
data.Port = req.Port
data.Address = req.Address
data.Protocol = req.Protocol
data.Enabled = req.Enabled
err = l.svcCtx.NodeModel.UpdateNode(l.ctx, data)
if err != nil {
l.Errorw("[UpdateNode] Update Database Error: ", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create server error: %v", err)
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "[UpdateNode] Update Database Error")
}
// Marshal the task payload
payload, err := json.Marshal(queue.GetNodeCountry{
Protocol: nodeInfo.Protocol,
ServerAddr: nodeInfo.ServerAddr,
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
ServerId: []int64{data.ServerId},
Search: "",
})
if err != nil {
l.Errorw("[GetNodeCountry]: Marshal Error", logger.Field("error", err.Error()))
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to marshal task payload")
}
// Create a queue task
task := asynq.NewTask(queue.ForthwithGetCountry, payload)
// Enqueue the task
taskInfo, err := l.svcCtx.Queue.Enqueue(task)
if err != nil {
l.Errorw("[GetNodeCountry]: Enqueue Error", logger.Field("error", err.Error()), logger.Field("payload", string(payload)))
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to enqueue task")
}
l.Infow("[GetNodeCountry]: Enqueue Success", logger.Field("taskID", taskInfo.ID), logger.Field("payload", string(payload)))
l.svcCtx.DeviceManager.Broadcast(device.SubscribeUpdate)
return nil
}
@@ -1,50 +0,0 @@
package server
import (
"context"
"strings"
"github.com/perfect-panel/ppanel-server/pkg/tool"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/pkg/xerr"
"github.com/pkg/errors"
"github.com/perfect-panel/ppanel-server/internal/svc"
"github.com/perfect-panel/ppanel-server/internal/types"
"github.com/perfect-panel/ppanel-server/pkg/logger"
)
type UpdateRuleGroupLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewUpdateRuleGroupLogic Update rule group
func NewUpdateRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateRuleGroupLogic {
return &UpdateRuleGroupLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateRuleGroupLogic) UpdateRuleGroup(req *types.UpdateRuleGroupRequest) error {
rs, err := parseAndValidateRules(req.Rules, req.Name)
if err != nil {
return err
}
err = l.svcCtx.ServerModel.UpdateRuleGroup(l.ctx, &server.RuleGroup{
Id: req.Id,
Icon: req.Icon,
Name: req.Name,
Tags: tool.StringSliceToString(req.Tags),
Rules: strings.Join(rs, "\n"),
Enable: req.Enable,
})
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
}
return nil
}
@@ -0,0 +1,119 @@
package server
import (
"context"
"strings"
"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/ip"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type UpdateServerLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewUpdateServerLogic Update Server
func NewUpdateServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateServerLogic {
return &UpdateServerLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateServerLogic) UpdateServer(req *types.UpdateServerRequest) error {
data, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, req.Id)
if err != nil {
l.Errorf("[UpdateServer] FindOneServer Error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find server error: %v", err.Error())
}
data.Name = req.Name
data.Country = req.Country
data.City = req.City
// only update address when it's different
if req.Address != data.Address {
// query server ip location
result, err := ip.GetRegionByIp(req.Address)
if err != nil {
l.Errorf("[UpdateServer] GetRegionByIp Error: %v", err.Error())
} else {
data.City = result.City
data.Country = result.Country
}
// update address
data.Address = req.Address
}
protocols := make([]node.Protocol, 0)
for _, item := range req.Protocols {
if item.Type == "" {
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols type is empty"), "protocols type is empty")
}
var protocol node.Protocol
tool.DeepCopy(&protocol, item)
// VLESS Reality Key Generation
if protocol.Type == "vless" {
if protocol.Security == "reality" {
if protocol.RealityPublicKey == "" {
public, private, err := tool.Curve25519Genkey(false, "")
if err != nil {
l.Errorf("[CreateServer] Generate Reality Key Error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate reality key error: %v", err)
}
protocol.RealityPublicKey = public
protocol.RealityPrivateKey = private
protocol.RealityShortId = tool.GenerateShortID(private)
}
if protocol.RealityServerAddr == "" {
protocol.RealityServerAddr = protocol.SNI
}
if protocol.RealityServerPort == 0 {
protocol.RealityServerPort = 443
}
}
}
// ShadowSocks 2022 Key Generation
if protocol.Type == "shadowsocks" {
if strings.Contains(protocol.Cipher, "2022") {
var length int
switch protocol.Cipher {
case "2022-blake3-aes-128-gcm":
length = 16
default:
length = 32
}
if len(protocol.ServerKey) != length {
protocol.ServerKey = tool.GenerateCipher(protocol.ServerKey, length)
}
}
}
protocols = append(protocols, protocol)
}
err = data.MarshalProtocols(protocols)
if err != nil {
l.Errorf("[UpdateServer] Marshal Protocols Error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols marshal error"), "protocols marshal error: %v", err)
}
err = l.svcCtx.NodeModel.UpdateServer(l.ctx, data)
if err != nil {
l.Errorf("[UpdateServer] UpdateServer Error: %v", err.Error())
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update server error: %v", err.Error())
}
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
ServerId: []int64{req.Id},
Search: "",
})
}