Develop (#76)
* refactor: rename queryannouncementhandler.go to queryAnnouncementLogic.go for clarity
* feat(panDomain): update subscription logic to use V2 handler for improved functionality
* refactor(subscribe): replace V2 handler with a unified Handler method for subscription logic
* feat(subscribe): implement user agent limit feature with configurable list
* fix(subscribe): improve error handling and logging for subscription requests
* feat(subscribe): add user agent limit configuration to system settings
* refactor(api): remove deprecated application-related endpoints and types
* refactor(swagger): remove deprecated app.json generation from swagger configuration
* refactor(swagger): remove deprecated app.json check from swagger configuration
* fix(subscribe): update delete method to use Where clause for improved query accuracy
* fix(subscribe): update Id field tag to use primaryKey and improve save method query
* fix(subscribe): update Id field tag to use primaryKey and improve model queries
* fix(subscribe): rename variable for clarity and add special handling for Stash user agent
* fix(email): convert RegisterStartTime and RegisterEndTime to time.Time for accurate query filtering
* refactor(log): consolidate logging models and update related logic for improved clarity and functionality
* fix(types): change Content field type in MessageLog to interface{} for improved flexibility
* fix(log): change MessageLog list to use value type for improved performance and memory efficiency
* fix(email): set EmailTypeVerify in task payload and update content type conversion for verification email
* fix(log): remove unused Id field from SystemLog during login log insertion
* fix(login): remove debug logs and error logging during user login process
* fix(log): add traffic reset logging for subscription resets
* fix(log): insert reset traffic log during subscription activation
* feat(log): add endpoints for retrieving and resetting subscribe traffic logs
* refactor(log): remove Reset Subscribe Traffic Log endpoint and related types
* feat(traffic): add traffic statistics logging and scheduling
* fix(subscribe): ensure active status and reset timestamps during traffic resets
* feat(api): enhance server and node management with new request/response structures
* refactor(api): rename OnlineUser to ServerOnlineUser for clarity
* feat(api): define OnlineUser type with SID and IP fields
* feat(server): implement server management handlers and database schema
* feat(api): add traffic log details filtering and enhance traffic log structures
* feat(api): migrate server and node data handling, update related structures and logic
* feat(server): implement server deletion logic with error handling
* feat(api): update log filtering to use ResetSubscribe type for subscription logs
* feat(api): standardize timestamp field across log structures
* feat(api): refactor cache key handling for server and user lists
* feat(api): enhance server status handling with protocol support and refactor related logic
* fix(traffic): adjust start date for traffic statistics and improve log deletion comment
* feat(api): implement daily traffic ranking for users and servers with error handling
* feat(api): update server total data response to use 'OnlineUsers' and implement daily traffic statistics logging
* feat(api): add log settings management with auto-clear and clear days configuration
* fix(log): correct category in log settings update query
* feat(routes): add handler for scheduled traffic statistics
* feat(model): add user counts struct and update queries for new and renewal users
* feat(api): add referral percentage and only first purchase fields to user model and requests
* feat(database): update user table to add referral percentage and only first purchase fields
* feat(api): add reset sort endpoints for server and node
* feat(api): add sort field to server model
* feat(api): implement sorting functionality for nodes and servers
* fix(database): add sort column to nodes table
* fix(model): enhance user statistics queries with new order and renewal order counts
* fix(log): update timestamp handling in login and registration logs
* fix(log): update sorting logic for server and user subscribe traffic logs
* fix(server): add server status handling based on last reported time
* fix(model): correct filter condition to use 'date' instead of 'data'
* fix(migration): add index for traffic log on timestamp, user_id, and subscribe_id
* fix(log): optimize user traffic rank data handling by using append instead of index assignment
* fix(filter): refactor node list creation to use append and remove duplicates from tags
* fix(node): add ServerId and Enabled fields to node update logic
* feat(tags): add endpoint to query all node tags
* fix(preview): add Preload parameter to FilterNodeList for improved data retrieval
* fix(log): date is empty
* feat(subscribe): add Language field to subscription models and update query logic
* feat(subscription): add Language parameter to GetSubscription request and update query logic
* fix(server): encode ServerKey in base64 and update last reported time for nodes
* feat: delete common GetSubscription
* feat(subscription): implement FilterList method for subscription queries and update related logic
* fix(subscribe): remove duplicate user agents in SubscribeHandler
* fix(push): initialize onlineUsers as a map in pushOnlineUsersLogic
* fix(reset): initialize subs as a map in clearCache method
* refactor(query): simplify node and tag filtering using InSet function
* feat(userlist): enhance GetServerUserListLogic with improved node and tag handling
* fix(userlist): correct node ID assignment and update query logic for tag filtering
* fix(userlist): correct node ID assignment in getServerUserListLogic
* refactor(query): streamline query construction for tag filtering
* fix(statistics): optimize server ranking data handling in QueryServerTotalDataLogic
* refactor(statistics): simplify server ranking data construction in QueryServerTotalDataLogic
* fix(statistics): correct server traffic data assignment in QueryServerTotalDataLogic
* fix(statistics): optimize yesterday's top 10 server traffic data assignment in QueryServerTotalDataLogic
* fix(middleware): remove duplicate elements from user agent list in PanDomainMiddleware
* feat(middleware): enhance user agent handling by querying client list in PanDomainMiddleware
* feat(client): subscribe_template
* feat(oauth): add user agent and IP logging to registration and login processes
* fix(balance): add timestamp to balance logs for payment, refund, and recharge transactions
* fix(log): correct comment for CommissionTypeRefund to improve clarity
* fix(log): replace magic number with constant for gift type in purchase checkout logic
* fix(log): rename OrderId to OrderNo for consistency in balance logging
* feat(log): add logging for balance, gift amount, and commission adjustments
* fix(user): correct placement of DeepCopy for user info update logic
* feat(log): add UserSubscribeId to FilterSubscribeLogRequest for enhanced filtering
* fix(purchase): streamline error handling and improve JSON marshaling for temporary orders
* fix(order): simplify commission handling and improve payload parsing logic
* fix(order): update commission calculation to actual payment amount minus gateway handling fee
* feat(payment): add support for CryptoSaaS payment platform and enhance configuration handling
* fix(balance): update QueryUserBalanceLog response structure to include balance log list
* fix(email): update task progress handling to use specific task ID for updates
* feat(quota): add quota task creation and querying endpoints with updated data structures
* fix(email): update task handling to use generic task model and improve error logging
* fix(order): improve error logging for database transaction and user cache updates
* feat(quota): enhance quota task management with new request structures and processing logic
* fix(quota): remove redundant quota task status endpoint from admin marketing routes
* fix(worker): update task completion status handling in worker logic
* fix(quota): update taskInfo to include current subscription count in quota logic
* doc(log): rename function for clarity and add cache cleanup comment
* fix(quota): update time handling in quota logic and correct subscriber ID query
* fix(quota): update time handling to use UnixMilli for start time in quota logic
* feat(protocol): add server protocol configuration query and enhance protocol options
* fix(quota): correct time range queries for start and expire times in quota logic
* fix(types): update plugin options to include 'none' in the plugin field
---------
Co-authored-by: Chang lue Tsen <tension@ppanel.dev>
This commit is contained in:
@@ -1,44 +0,0 @@
|
||||
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/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/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 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
|
||||
}
|
||||
@@ -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/server/internal/model/server"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/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
|
||||
}
|
||||
@@ -2,18 +2,13 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
"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"
|
||||
queue "github.com/perfect-panel/server/queue/types"
|
||||
"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,97 +28,18 @@ 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)
|
||||
} else if req.Protocol == "shadowsocks" {
|
||||
var cfg types.Shadowsocks
|
||||
if err = json.Unmarshal(config, &cfg); err != nil {
|
||||
l.Errorf("[CreateNode] Unmarshal Shadowsocks Config Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
|
||||
}
|
||||
if strings.Contains(cfg.Method, "2022") {
|
||||
var length int
|
||||
switch cfg.Method {
|
||||
case "2022-blake3-aes-128-gcm":
|
||||
length = 16
|
||||
default:
|
||||
length = 32
|
||||
}
|
||||
if len(cfg.ServerKey) != length {
|
||||
cfg.ServerKey = tool.GenerateCipher(cfg.ServerKey, length)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
if req.City == "" || req.Country == "" {
|
||||
// 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 errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "[CreateNode] Insert Database Error")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/rules"
|
||||
|
||||
"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/tool"
|
||||
"github.com/perfect-panel/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
|
||||
}
|
||||
info := &server.RuleGroup{
|
||||
Name: req.Name,
|
||||
Icon: req.Icon,
|
||||
Type: req.Type,
|
||||
Tags: tool.StringSliceToString(req.Tags),
|
||||
Rules: strings.Join(rs, "\n"),
|
||||
Default: req.Default,
|
||||
Enable: req.Enable,
|
||||
}
|
||||
err = l.svcCtx.ServerModel.InsertRuleGroup(l.ctx, info)
|
||||
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)
|
||||
}
|
||||
if req.Default {
|
||||
if err = l.svcCtx.ServerModel.SetDefaultRuleGroup(l.ctx, info.Id); err != nil {
|
||||
l.Errorw("[CreateRuleGroup] Set Default Rule Group Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "set default rule group error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
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,
|
||||
Ratio: req.Ratio,
|
||||
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/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 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
|
||||
}
|
||||
@@ -2,14 +2,12 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
|
||||
"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 +16,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,33 +26,10 @@ 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, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete server to subscribe
|
||||
subs, err := l.svcCtx.SubscribeModel.QuerySubscribeIdsByServerIdAndServerGroupId(l.ctx, req.Id, 0)
|
||||
if err != nil {
|
||||
l.Logger.Errorf("[DeleteNode] QuerySubscribeIdsByServerIdAndServerGroupId error: %v", err.Error())
|
||||
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, tx); err != nil {
|
||||
l.Logger.Errorf("[DeleteNode] UpdateSubscribe error: %v", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
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
|
||||
}
|
||||
|
||||
+8
-8
@@ -10,26 +10,26 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DeleteRuleGroupLogic struct {
|
||||
type DeleteServerLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Delete rule group
|
||||
func NewDeleteRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRuleGroupLogic {
|
||||
return &DeleteRuleGroupLogic{
|
||||
// NewDeleteServerLogic Delete Server
|
||||
func NewDeleteServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteServerLogic {
|
||||
return &DeleteServerLogic{
|
||||
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)
|
||||
func (l *DeleteServerLogic) DeleteServer(req *types.DeleteServerRequest) error {
|
||||
err := l.svcCtx.NodeModel.DeleteServer(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)
|
||||
l.Errorw("[DeleteServer] Delete Server Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "[DeleteServer] Delete Server Error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -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/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 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/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 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,104 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"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 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) {
|
||||
tags := make([]string, 0)
|
||||
if req.Tags != "" {
|
||||
tags = strings.Split(req.Tags, ",")
|
||||
}
|
||||
total, list, err := l.svcCtx.ServerModel.FindServerListByFilter(l.ctx, &server.ServerFilter{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Search: req.Search,
|
||||
Tags: tags,
|
||||
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,31 +0,0 @@
|
||||
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"
|
||||
)
|
||||
|
||||
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) {
|
||||
tags, err := l.svcCtx.ServerModel.FindServerTags(l.ctx)
|
||||
return &types.GetNodeTagListResponse{
|
||||
Tags: tool.RemoveDuplicateElements(tags...),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"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 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,
|
||||
Type: v.Type,
|
||||
Tags: strings.Split(v.Tags, ","),
|
||||
Rules: v.Rules,
|
||||
Enable: v.Enable,
|
||||
Default: v.Default,
|
||||
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,331 @@
|
||||
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,
|
||||
})
|
||||
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,
|
||||
}
|
||||
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,
|
||||
}
|
||||
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,
|
||||
}
|
||||
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: "hysteria2",
|
||||
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,
|
||||
}
|
||||
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,
|
||||
}
|
||||
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,
|
||||
}
|
||||
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
|
||||
}
|
||||
+17
-18
@@ -3,36 +3,35 @@ package server
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"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 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,12 +66,12 @@ func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
|
||||
}
|
||||
}
|
||||
for _, item := range itemsToUpdate {
|
||||
s, err := l.svcCtx.ServerModel.FindOne(l.ctx, item.Id)
|
||||
s, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, item.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Sort = item.Sort
|
||||
if err := l.svcCtx.ServerModel.Update(l.ctx, s, db); err != nil {
|
||||
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,43 @@
|
||||
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/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 nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
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/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
|
||||
}
|
||||
@@ -2,12 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/pkg/device"
|
||||
queue "github.com/perfect-panel/server/queue/types"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
@@ -23,6 +17,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,108 +27,22 @@ 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, tool.CopyWithIgnoreEmpty(false))
|
||||
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
|
||||
}
|
||||
|
||||
// 处理Tags字段
|
||||
switch {
|
||||
case len(req.Tags) > 0:
|
||||
// 有Tags,进行连接
|
||||
nodeInfo.Tags = strings.Join(req.Tags, ",")
|
||||
default:
|
||||
// 空数组,清空Tags
|
||||
nodeInfo.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)
|
||||
} else if req.Protocol == "shadowsocks" {
|
||||
var cfg types.Shadowsocks
|
||||
if err = json.Unmarshal(config, &cfg); err != nil {
|
||||
l.Errorf("[CreateNode] Unmarshal Shadowsocks Config Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
|
||||
}
|
||||
if strings.Contains(cfg.Method, "2022") {
|
||||
var length int
|
||||
switch cfg.Method {
|
||||
case "2022-blake3-aes-128-gcm":
|
||||
length = 16
|
||||
default:
|
||||
length = 32
|
||||
}
|
||||
if len(cfg.ServerKey) != length {
|
||||
cfg.ServerKey = tool.GenerateCipher(cfg.ServerKey, length)
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
if req.City == "" || req.Country == "" {
|
||||
// Marshal the task payload
|
||||
payload, err := json.Marshal(queue.GetNodeCountry{
|
||||
Protocol: nodeInfo.Protocol,
|
||||
ServerAddr: nodeInfo.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)))
|
||||
}
|
||||
|
||||
l.svcCtx.DeviceManager.Broadcast(device.SubscribeUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/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,
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
Tags: tool.StringSliceToString(req.Tags),
|
||||
Rules: strings.Join(rs, "\n"),
|
||||
Default: req.Default,
|
||||
Enable: req.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
|
||||
}
|
||||
if req.Default {
|
||||
if err = l.svcCtx.ServerModel.SetDefaultRuleGroup(l.ctx, req.Id); err != nil {
|
||||
l.Errorf("SetDefaultRuleGroup error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
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
|
||||
data.Ratio = req.Ratio
|
||||
// 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 nil
|
||||
}
|
||||
Reference in New Issue
Block a user