Merge old/master into main, resolving conflicts by preferring old in workflows and removing deprecated adapter files
Build docker and publish / build (20.15.1) (push) Has been cancelled
Build docker and publish / build (20.15.1) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/client"
|
||||
"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 CreateSubscribeApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewCreateSubscribeApplicationLogic Create subscribe application
|
||||
func NewCreateSubscribeApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateSubscribeApplicationLogic {
|
||||
return &CreateSubscribeApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateSubscribeApplicationLogic) CreateSubscribeApplication(req *types.CreateSubscribeApplicationRequest) (resp *types.SubscribeApplication, err error) {
|
||||
var link client.DownloadLink
|
||||
tool.DeepCopy(&link, req.DownloadLink)
|
||||
linkData, err := link.Marshal()
|
||||
if err != nil {
|
||||
l.Errorf("Failed to marshal download link: %v", err)
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.ERROR), " Failed to marshal download link")
|
||||
}
|
||||
data := &client.SubscribeApplication{
|
||||
Name: req.Name,
|
||||
Icon: req.Icon,
|
||||
Description: req.Description,
|
||||
Scheme: req.Scheme,
|
||||
UserAgent: req.UserAgent,
|
||||
IsDefault: req.IsDefault,
|
||||
SubscribeTemplate: req.SubscribeTemplate,
|
||||
OutputFormat: req.OutputFormat,
|
||||
DownloadLink: string(linkData),
|
||||
}
|
||||
|
||||
err = l.svcCtx.ClientModel.Insert(l.ctx, data)
|
||||
if err != nil {
|
||||
l.Errorf("Failed to create subscribe application: %v", err)
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.DatabaseInsertError), "Failed to create subscribe application")
|
||||
}
|
||||
|
||||
resp = &types.SubscribeApplication{}
|
||||
tool.DeepCopy(resp, data)
|
||||
resp.DownloadLink = req.DownloadLink
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package application
|
||||
|
||||
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 DeleteSubscribeApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewDeleteSubscribeApplicationLogic Delete subscribe application
|
||||
func NewDeleteSubscribeApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteSubscribeApplicationLogic {
|
||||
return &DeleteSubscribeApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteSubscribeApplicationLogic) DeleteSubscribeApplication(req *types.DeleteSubscribeApplicationRequest) error {
|
||||
err := l.svcCtx.ClientModel.Delete(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorf("Failed to delete subscribe application with ID %d: %v", req.Id, err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package application
|
||||
|
||||
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/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetSubscribeApplicationListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewGetSubscribeApplicationListLogic Get subscribe application list
|
||||
func NewGetSubscribeApplicationListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSubscribeApplicationListLogic {
|
||||
return &GetSubscribeApplicationListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetSubscribeApplicationListLogic) GetSubscribeApplicationList(req *types.GetSubscribeApplicationListRequest) (resp *types.GetSubscribeApplicationListResponse, err error) {
|
||||
data, err := l.svcCtx.ClientModel.List(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorf("Failed to get subscribe application list: %v", err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Failed to get subscribe application list")
|
||||
}
|
||||
var list []types.SubscribeApplication
|
||||
for _, item := range data {
|
||||
var temp types.DownloadLink
|
||||
if item.DownloadLink != "" {
|
||||
_ = json.Unmarshal([]byte(item.DownloadLink), &temp)
|
||||
}
|
||||
list = append(list, types.SubscribeApplication{
|
||||
Id: item.Id,
|
||||
Name: item.Name,
|
||||
Description: item.Description,
|
||||
Icon: item.Icon,
|
||||
Scheme: item.Scheme,
|
||||
UserAgent: item.UserAgent,
|
||||
IsDefault: item.IsDefault,
|
||||
SubscribeTemplate: item.SubscribeTemplate,
|
||||
OutputFormat: item.OutputFormat,
|
||||
DownloadLink: temp,
|
||||
CreatedAt: item.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: item.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
resp = &types.GetSubscribeApplicationListResponse{
|
||||
Total: int64(len(list)),
|
||||
List: list,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/adapter"
|
||||
"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 PreviewSubscribeTemplateLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Preview Template
|
||||
func NewPreviewSubscribeTemplateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PreviewSubscribeTemplateLogic {
|
||||
return &PreviewSubscribeTemplateLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PreviewSubscribeTemplateLogic) PreviewSubscribeTemplate(req *types.PreviewSubscribeTemplateRequest) (resp *types.PreviewSubscribeTemplateResponse, err error) {
|
||||
_, servers, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
Preload: true,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorf("[PreviewSubscribeTemplateLogic] FindAllServer error: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindAllServer error: %v", err.Error())
|
||||
}
|
||||
|
||||
data, err := l.svcCtx.ClientModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorf("[PreviewSubscribeTemplateLogic] FindOne error: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOneClient error: %v", err.Error())
|
||||
}
|
||||
|
||||
sub := adapter.NewAdapter(data.SubscribeTemplate, adapter.WithServers(servers),
|
||||
adapter.WithSiteName("PerfectPanel"),
|
||||
adapter.WithSubscribeName("Test Subscribe"),
|
||||
adapter.WithOutputFormat(data.OutputFormat),
|
||||
adapter.WithUserInfo(adapter.User{
|
||||
Password: "test-password",
|
||||
ExpiredAt: time.Now().AddDate(1, 0, 0),
|
||||
Download: 0,
|
||||
Upload: 0,
|
||||
Traffic: 1000,
|
||||
SubscribeURL: "https://example.com/subscribe",
|
||||
}))
|
||||
// Get client config
|
||||
a, err := sub.Client()
|
||||
if err != nil {
|
||||
l.Errorf("[PreviewSubscribeTemplateLogic] Client error: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(err.Error()), "Client error: %v", err.Error())
|
||||
}
|
||||
bytes, err := a.Build()
|
||||
if err != nil {
|
||||
l.Errorf("[PreviewSubscribeTemplateLogic] Build error: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(err.Error()), "Build error: %v", err.Error())
|
||||
}
|
||||
return &types.PreviewSubscribeTemplateResponse{
|
||||
Template: string(bytes),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/client"
|
||||
"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 UpdateSubscribeApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewUpdateSubscribeApplicationLogic Update subscribe application
|
||||
func NewUpdateSubscribeApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateSubscribeApplicationLogic {
|
||||
return &UpdateSubscribeApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateSubscribeApplicationLogic) UpdateSubscribeApplication(req *types.UpdateSubscribeApplicationRequest) (resp *types.SubscribeApplication, err error) {
|
||||
data, err := l.svcCtx.ClientModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorf("Failed to find subscribe application with ID %d: %v", req.Id, err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Failed to find subscribe application with ID %d", req.Id)
|
||||
}
|
||||
var link client.DownloadLink
|
||||
tool.DeepCopy(&link, req.DownloadLink)
|
||||
linkData, err := link.Marshal()
|
||||
if err != nil {
|
||||
l.Errorf("Failed to marshal download link: %v", err)
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.ERROR), " Failed to marshal download link")
|
||||
}
|
||||
|
||||
data.Name = req.Name
|
||||
data.Icon = req.Icon
|
||||
data.Description = req.Description
|
||||
data.Scheme = req.Scheme
|
||||
data.UserAgent = req.UserAgent
|
||||
data.IsDefault = req.IsDefault
|
||||
data.SubscribeTemplate = req.SubscribeTemplate
|
||||
data.OutputFormat = req.OutputFormat
|
||||
data.DownloadLink = string(linkData)
|
||||
err = l.svcCtx.ClientModel.Update(l.ctx, data)
|
||||
if err != nil {
|
||||
l.Errorf("Failed to update subscribe application with ID %d: %v", req.Id, err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "Failed to update subscribe application with ID %d", req.Id)
|
||||
}
|
||||
resp = &types.SubscribeApplication{}
|
||||
tool.DeepCopy(&resp, data)
|
||||
resp.DownloadLink = req.DownloadLink
|
||||
return
|
||||
}
|
||||
@@ -50,8 +50,8 @@ func (l *QueryRevenueStatisticsLogic) QueryRevenueStatistics() (resp *types.Reve
|
||||
// Get monthly's revenue statistics
|
||||
monthlyData, err := l.svcCtx.OrderModel.QueryMonthlyOrders(l.ctx, now)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryRevenueStatisticsLogic] QueryDateOrders error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QueryDateOrders error: %v", err)
|
||||
l.Errorw("[QueryRevenueStatisticsLogic] QueryMonthlyOrders error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QueryMonthlyOrders error: %v", err)
|
||||
} else {
|
||||
monthly = types.OrdersStatistics{
|
||||
AmountTotal: monthlyData.AmountTotal,
|
||||
@@ -61,6 +61,24 @@ func (l *QueryRevenueStatisticsLogic) QueryRevenueStatistics() (resp *types.Reve
|
||||
}
|
||||
}
|
||||
|
||||
// Get monthly daily list for the current month (from 1st to current date)
|
||||
monthlyListData, err := l.svcCtx.OrderModel.QueryDailyOrdersList(l.ctx, now)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryRevenueStatisticsLogic] QueryDailyOrdersList error", logger.Field("error", err.Error()))
|
||||
// Don't return error, just log it and continue with empty list
|
||||
} else {
|
||||
monthlyList := make([]types.OrdersStatistics, len(monthlyListData))
|
||||
for i, data := range monthlyListData {
|
||||
monthlyList[i] = types.OrdersStatistics{
|
||||
Date: data.Date,
|
||||
AmountTotal: data.AmountTotal,
|
||||
NewOrderAmount: data.NewOrderAmount,
|
||||
RenewalOrderAmount: data.RenewalOrderAmount,
|
||||
}
|
||||
}
|
||||
monthly.List = monthlyList
|
||||
}
|
||||
|
||||
// Get all revenue statistics
|
||||
allData, err := l.svcCtx.OrderModel.QueryTotalOrders(l.ctx)
|
||||
if err != nil {
|
||||
@@ -74,6 +92,25 @@ func (l *QueryRevenueStatisticsLogic) QueryRevenueStatistics() (resp *types.Reve
|
||||
List: make([]types.OrdersStatistics, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Get all monthly list for the past 6 months
|
||||
allListData, err := l.svcCtx.OrderModel.QueryMonthlyOrdersList(l.ctx, now)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryRevenueStatisticsLogic] QueryMonthlyOrdersList error", logger.Field("error", err.Error()))
|
||||
// Don't return error, just log it and continue with empty list
|
||||
} else {
|
||||
allList := make([]types.OrdersStatistics, len(allListData))
|
||||
for i, data := range allListData {
|
||||
allList[i] = types.OrdersStatistics{
|
||||
Date: data.Date,
|
||||
AmountTotal: data.AmountTotal,
|
||||
NewOrderAmount: data.NewOrderAmount,
|
||||
RenewalOrderAmount: data.RenewalOrderAmount,
|
||||
}
|
||||
}
|
||||
all.List = allList
|
||||
}
|
||||
|
||||
return &types.RevenueStatisticsResponse{
|
||||
Today: today,
|
||||
Monthly: monthly,
|
||||
@@ -85,7 +122,7 @@ func (l *QueryRevenueStatisticsLogic) QueryRevenueStatistics() (resp *types.Reve
|
||||
func (l *QueryRevenueStatisticsLogic) mockRevenueStatistics() *types.RevenueStatisticsResponse {
|
||||
now := time.Now()
|
||||
|
||||
// Generate daily data for the past 7 days (oldest first)
|
||||
// Generate daily data for the current month (from 1st to current date)
|
||||
monthlyList := make([]types.OrdersStatistics, 7)
|
||||
for i := 0; i < 7; i++ {
|
||||
dayDate := now.AddDate(0, 0, -(6 - i))
|
||||
|
||||
@@ -6,12 +6,15 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/model/traffic"
|
||||
"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 QueryServerTotalDataLogic struct {
|
||||
@@ -35,127 +38,194 @@ func (l *QueryServerTotalDataLogic) QueryServerTotalData() (resp *types.ServerTo
|
||||
return l.mockRevenueStatistics(), nil
|
||||
}
|
||||
|
||||
resp = &types.ServerTotalDataResponse{
|
||||
ServerTrafficRankingToday: make([]types.ServerTrafficData, 0),
|
||||
ServerTrafficRankingYesterday: make([]types.ServerTrafficData, 0),
|
||||
UserTrafficRankingToday: make([]types.UserTrafficData, 0),
|
||||
UserTrafficRankingYesterday: make([]types.UserTrafficData, 0),
|
||||
now := time.Now()
|
||||
|
||||
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
todayEnd := todayStart.Add(24 * time.Hour).Add(-time.Second)
|
||||
query := l.svcCtx.DB.WithContext(l.ctx)
|
||||
var todayTop10User []log.UserTraffic
|
||||
|
||||
err = query.Model(&traffic.TrafficLog{}).
|
||||
Select("user_id, subscribe_id, SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
|
||||
Where("timestamp BETWEEN ? AND ?", todayStart, todayEnd).
|
||||
Group("user_id, subscribe_id").
|
||||
Order("total DESC").
|
||||
Limit(10).
|
||||
Scan(&todayTop10User).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Errorf("[Traffic Stat Queue] Query user traffic failed: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " Query user traffic failed: %v", err.Error())
|
||||
}
|
||||
var userTodayTrafficRanking []types.UserTrafficData
|
||||
for _, item := range todayTop10User {
|
||||
userTodayTrafficRanking = append(userTodayTrafficRanking, types.UserTrafficData{
|
||||
SID: item.SubscribeId,
|
||||
Upload: item.Upload,
|
||||
Download: item.Download,
|
||||
})
|
||||
}
|
||||
|
||||
// Query node server status
|
||||
servers, err := l.svcCtx.ServerModel.FindAllServer(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] FindAllServer error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(err, "FindAllServer error: %v", err)
|
||||
}
|
||||
onlineServers, err := l.svcCtx.NodeCache.GetOnlineNodeStatusCount(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] GetOnlineNodeStatusCount error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(err, "GetOnlineNodeStatusCount error: %v", err)
|
||||
}
|
||||
resp.OnlineServers = onlineServers
|
||||
resp.OfflineServers = int64(len(servers) - int(onlineServers))
|
||||
// query yesterday user traffic rank log
|
||||
yesterday := todayStart.Add(-24 * time.Hour).Format(time.DateOnly)
|
||||
|
||||
// 获取所有节点在线用户
|
||||
allNodeOnlineUser, err := l.svcCtx.NodeCache.GetAllNodeOnlineUser(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get all node online user failed", logger.Field("error", err.Error()))
|
||||
var yesterdayLog log.SystemLog
|
||||
err = query.Model(&log.SystemLog{}).Where("`date` = ? AND `type` = ?", yesterday, log.TypeUserTrafficRank).First(&yesterdayLog).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Query yesterday user traffic rank log error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query yesterday user traffic rank log error: %v", err)
|
||||
}
|
||||
resp.OnlineUserIPs = int64(len(allNodeOnlineUser))
|
||||
|
||||
// 获取所有节点今日上传下载流量
|
||||
allNodeUploadTraffic, err := l.svcCtx.NodeCache.GetAllNodeUploadTraffic(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get all node upload traffic failed", logger.Field("error", err.Error()))
|
||||
var yesterdayUserRankData []types.UserTrafficData
|
||||
if yesterdayLog.Id > 0 {
|
||||
var rank log.UserTrafficRank
|
||||
err = rank.Unmarshal([]byte(yesterdayLog.Content))
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Unmarshal yesterday user traffic rank log error", logger.Field("error", err.Error()))
|
||||
}
|
||||
for _, v := range rank.Rank {
|
||||
yesterdayUserRankData = append(yesterdayUserRankData, types.UserTrafficData{
|
||||
SID: v.SubscribeId,
|
||||
Upload: v.Upload,
|
||||
Download: v.Download,
|
||||
})
|
||||
}
|
||||
}
|
||||
resp.TodayUpload = allNodeUploadTraffic
|
||||
allNodeDownloadTraffic, err := l.svcCtx.NodeCache.GetAllNodeDownloadTraffic(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get all node download traffic failed", logger.Field("error", err.Error()))
|
||||
|
||||
// query server traffic rank today
|
||||
var todayTop10Server []log.ServerTraffic
|
||||
err = query.Model(&traffic.TrafficLog{}).Select("server_id, SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
|
||||
Where("timestamp BETWEEN ? AND ?", todayStart, todayEnd).
|
||||
Group("server_id").
|
||||
Order("total DESC").
|
||||
Limit(10).
|
||||
Scan(&todayTop10Server).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Errorf("[Traffic Stat Queue] Query server traffic failed: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " Query server traffic failed: %v", err.Error())
|
||||
}
|
||||
resp.TodayDownload = allNodeDownloadTraffic
|
||||
// 获取节点流量排行榜 前10
|
||||
nodeTrafficRankingToday, err := l.svcCtx.NodeCache.GetNodeTodayTotalTrafficRank(l.ctx, 10)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get node today total traffic rank failed", logger.Field("error", err.Error()))
|
||||
|
||||
var todayServerRanking []types.ServerTrafficData
|
||||
for _, item := range todayTop10Server {
|
||||
info, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, item.ServerId)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] FindOneServer error", logger.Field("error", err.Error()), logger.Field("server_id", item.ServerId))
|
||||
continue
|
||||
}
|
||||
todayServerRanking = append(todayServerRanking, types.ServerTrafficData{
|
||||
ServerId: item.ServerId,
|
||||
Name: info.Name,
|
||||
Upload: item.Upload,
|
||||
Download: item.Download,
|
||||
})
|
||||
}
|
||||
if len(nodeTrafficRankingToday) > 0 {
|
||||
var serverTrafficData []types.ServerTrafficData
|
||||
for _, rank := range nodeTrafficRankingToday {
|
||||
serverInfo, err := l.svcCtx.ServerModel.FindOne(l.ctx, rank.ID)
|
||||
|
||||
// query server traffic rank yesterday
|
||||
var yesterdayTop10Server []types.ServerTrafficData
|
||||
var yesterdayServerTrafficLog log.SystemLog
|
||||
err = query.Model(&log.SystemLog{}).Where("`date` = ? AND `type` = ?", yesterday, log.TypeServerTrafficRank).First(&yesterdayServerTrafficLog).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Query yesterday server traffic rank log error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query yesterday server traffic rank log error: %v", err)
|
||||
}
|
||||
if yesterdayServerTrafficLog.Id > 0 {
|
||||
var rank log.ServerTrafficRank
|
||||
err = rank.Unmarshal([]byte(yesterdayServerTrafficLog.Content))
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Unmarshal yesterday server traffic rank log error", logger.Field("error", err.Error()))
|
||||
}
|
||||
|
||||
for _, v := range rank.Rank {
|
||||
info, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, v.ServerId)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] FindOne error", logger.Field("error", err))
|
||||
l.Errorw("[QueryServerTotalDataLogic] FindOneServer error", logger.Field("error", err.Error()), logger.Field("server_id", v.ServerId))
|
||||
continue
|
||||
}
|
||||
serverTrafficData = append(serverTrafficData, types.ServerTrafficData{
|
||||
ServerId: rank.ID,
|
||||
Name: serverInfo.Name,
|
||||
Upload: rank.Upload,
|
||||
Download: rank.Download,
|
||||
yesterdayTop10Server = append(yesterdayTop10Server, types.ServerTrafficData{
|
||||
ServerId: v.ServerId,
|
||||
Name: info.Name,
|
||||
Upload: v.Upload,
|
||||
Download: v.Download,
|
||||
})
|
||||
}
|
||||
resp.ServerTrafficRankingToday = serverTrafficData
|
||||
}
|
||||
// 获取用户流量排行榜 前10
|
||||
userTrafficRankingToday, err := l.svcCtx.NodeCache.GetUserTodayTotalTrafficRank(l.ctx, 10)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get user today total traffic rank failed", logger.Field("error", err.Error()))
|
||||
}
|
||||
|
||||
if len(userTrafficRankingToday) > 0 {
|
||||
var userTrafficData []types.UserTrafficData
|
||||
for _, rank := range userTrafficRankingToday {
|
||||
userTrafficData = append(userTrafficData, types.UserTrafficData{
|
||||
SID: rank.SID,
|
||||
Upload: rank.Upload,
|
||||
Download: rank.Download,
|
||||
})
|
||||
}
|
||||
resp.UserTrafficRankingToday = userTrafficData
|
||||
}
|
||||
// 获取昨日节点流量排行榜 前10
|
||||
nodeTrafficRankingYesterday, err := l.svcCtx.NodeCache.GetYesterdayNodeTotalTrafficRank(l.ctx)
|
||||
// query online user count
|
||||
onlineUsers, err := l.svcCtx.NodeModel.OnlineUserSubscribeGlobal(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get yesterday node total traffic rank failed", logger.Field("error", err.Error()))
|
||||
}
|
||||
if len(nodeTrafficRankingYesterday) > 0 {
|
||||
var serverTrafficData []types.ServerTrafficData
|
||||
for _, rank := range nodeTrafficRankingYesterday {
|
||||
serverTrafficData = append(serverTrafficData, types.ServerTrafficData{
|
||||
ServerId: rank.ID,
|
||||
Name: rank.Name,
|
||||
Upload: rank.Upload,
|
||||
Download: rank.Download,
|
||||
})
|
||||
}
|
||||
resp.ServerTrafficRankingYesterday = serverTrafficData
|
||||
}
|
||||
// 获取昨日用户流量排行榜 前10
|
||||
userTrafficRankingYesterday, err := l.svcCtx.NodeCache.GetYesterdayUserTotalTrafficRank(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Get yesterday user total traffic rank failed", logger.Field("error", err.Error()))
|
||||
}
|
||||
if len(userTrafficRankingYesterday) > 0 {
|
||||
var userTrafficData []types.UserTrafficData
|
||||
for _, rank := range userTrafficRankingYesterday {
|
||||
userTrafficData = append(userTrafficData, types.UserTrafficData{
|
||||
SID: rank.SID,
|
||||
Upload: rank.Upload,
|
||||
Download: rank.Download,
|
||||
})
|
||||
}
|
||||
resp.UserTrafficRankingYesterday = userTrafficData
|
||||
l.Errorw("[QueryServerTotalDataLogic] OnlineUserSubscribeGlobal error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "OnlineUserSubscribeGlobal error: %v", err)
|
||||
}
|
||||
|
||||
// Query node traffic by monthly
|
||||
nodeTraffic, err := l.svcCtx.TrafficLogModel.QueryTrafficByMonthly(l.ctx, time.Now())
|
||||
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] QueryTrafficByMonthly error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QueryTrafficByMonthly error: %v", err.Error())
|
||||
// query online/offline server count
|
||||
var onlineServers, offlineServers int64
|
||||
err = query.Model(&node.Server{}).Where("`last_reported_at` > ?", now.Add(-5*time.Minute)).Count(&onlineServers).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Count online servers error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Count online servers error: %v", err)
|
||||
}
|
||||
|
||||
err = query.Model(&node.Server{}).Where("`last_reported_at` <= ? OR `last_reported_at` IS NULL", now.Add(-5*time.Minute)).Count(&offlineServers).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Count offline servers error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Count offline servers error: %v", err)
|
||||
}
|
||||
// TodayUpload, TodayDownload, MonthlyUpload, MonthlyDownload
|
||||
var todayUpload, todayDownload, monthlyUpload, monthlyDownload int64
|
||||
|
||||
type trafficSum struct {
|
||||
Upload int64
|
||||
Download int64
|
||||
}
|
||||
var todayTraffic trafficSum
|
||||
// Today
|
||||
err = query.Model(&traffic.TrafficLog{}).Select("SUM(upload) AS upload, SUM(download) AS download").
|
||||
Where("timestamp BETWEEN ? AND ?", todayStart, todayEnd).
|
||||
Scan(&todayTraffic).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Sum today traffic error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Sum today traffic error: %v", err)
|
||||
}
|
||||
todayUpload = todayTraffic.Upload
|
||||
todayDownload = todayTraffic.Download
|
||||
|
||||
// Monthly
|
||||
monthlyUpload += todayUpload
|
||||
monthlyDownload += todayDownload
|
||||
|
||||
for i := now.Day() - 1; i >= 1; i-- {
|
||||
var logInfo log.SystemLog
|
||||
date := time.Date(now.Year(), now.Month(), i, 0, 0, 0, 0, now.Location()).Format(time.DateOnly)
|
||||
err = query.Model(&log.SystemLog{}).Where("`date` = ? AND `type` = ?", date, log.TypeTrafficStat).First(&logInfo).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Query daily traffic stat log error", logger.Field("error", err.Error()), logger.Field("date", date))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query daily traffic stat log error: %v", err)
|
||||
}
|
||||
if logInfo.Id > 0 {
|
||||
var stat log.TrafficStat
|
||||
err = stat.Unmarshal([]byte(logInfo.Content))
|
||||
if err != nil {
|
||||
l.Errorw("[QueryServerTotalDataLogic] Unmarshal daily traffic stat log error", logger.Field("error", err.Error()), logger.Field("date", date))
|
||||
continue
|
||||
}
|
||||
monthlyUpload += stat.Upload
|
||||
monthlyDownload += stat.Download
|
||||
}
|
||||
}
|
||||
|
||||
resp = &types.ServerTotalDataResponse{
|
||||
OnlineUsers: onlineUsers,
|
||||
OnlineServers: onlineServers,
|
||||
OfflineServers: offlineServers,
|
||||
TodayUpload: todayUpload,
|
||||
TodayDownload: todayDownload,
|
||||
MonthlyUpload: monthlyUpload,
|
||||
MonthlyDownload: monthlyDownload,
|
||||
UpdatedAt: now.Unix(),
|
||||
ServerTrafficRankingToday: todayServerRanking,
|
||||
ServerTrafficRankingYesterday: yesterdayTop10Server,
|
||||
UserTrafficRankingToday: userTodayTrafficRanking,
|
||||
UserTrafficRankingYesterday: yesterdayUserRankData,
|
||||
}
|
||||
resp.MonthlyUpload = nodeTraffic.Upload
|
||||
resp.MonthlyDownload = nodeTraffic.Download
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -215,7 +285,7 @@ func (l *QueryServerTotalDataLogic) mockRevenueStatistics() *types.ServerTotalDa
|
||||
//}
|
||||
//
|
||||
return &types.ServerTotalDataResponse{
|
||||
OnlineUserIPs: 1688,
|
||||
OnlineUsers: 1688,
|
||||
OnlineServers: 8,
|
||||
OfflineServers: 2,
|
||||
TodayUpload: 8888888888, // ~8.3GB
|
||||
|
||||
@@ -61,8 +61,24 @@ func (l *QueryUserStatisticsLogic) QueryUserStatistics() (resp *types.UserStatis
|
||||
} else {
|
||||
resp.Monthly.NewOrderUsers = newMonth
|
||||
resp.Monthly.RenewalOrderUsers = renewalMonth
|
||||
// TODO: Check the purchase status in the past seven days
|
||||
resp.Monthly.List = make([]types.UserStatistics, 0)
|
||||
}
|
||||
|
||||
// Get monthly daily user statistics list for the current month (from 1st to current date)
|
||||
monthlyListData, err := l.svcCtx.UserModel.QueryDailyUserStatisticsList(l.ctx, now)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryUserStatisticsLogic] QueryDailyUserStatisticsList error", logger.Field("error", err.Error()))
|
||||
// Don't return error, just log it and continue with empty list
|
||||
} else {
|
||||
monthlyList := make([]types.UserStatistics, len(monthlyListData))
|
||||
for i, data := range monthlyListData {
|
||||
monthlyList[i] = types.UserStatistics{
|
||||
Date: data.Date,
|
||||
Register: data.Register,
|
||||
NewOrderUsers: data.NewOrderUsers,
|
||||
RenewalOrderUsers: data.RenewalOrderUsers,
|
||||
}
|
||||
}
|
||||
resp.Monthly.List = monthlyList
|
||||
}
|
||||
|
||||
// query all user count
|
||||
@@ -81,13 +97,32 @@ func (l *QueryUserStatisticsLogic) QueryUserStatistics() (resp *types.UserStatis
|
||||
resp.All.NewOrderUsers = allNewOrderUsers
|
||||
resp.All.RenewalOrderUsers = allRenewalOrderUsers
|
||||
}
|
||||
|
||||
// Get all monthly user statistics list for the past 6 months
|
||||
allListData, err := l.svcCtx.UserModel.QueryMonthlyUserStatisticsList(l.ctx, now)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryUserStatisticsLogic] QueryMonthlyUserStatisticsList error", logger.Field("error", err.Error()))
|
||||
// Don't return error, just log it and continue with empty list
|
||||
} else {
|
||||
allList := make([]types.UserStatistics, len(allListData))
|
||||
for i, data := range allListData {
|
||||
allList[i] = types.UserStatistics{
|
||||
Date: data.Date,
|
||||
Register: data.Register,
|
||||
NewOrderUsers: data.NewOrderUsers,
|
||||
RenewalOrderUsers: data.RenewalOrderUsers,
|
||||
}
|
||||
}
|
||||
resp.All.List = allList
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *QueryUserStatisticsLogic) mockRevenueStatistics() *types.UserStatisticsResponse {
|
||||
now := time.Now()
|
||||
|
||||
// Generate daily user statistics for the past 7 days (oldest first)
|
||||
// Generate daily user statistics for the current month (from 1st to current date)
|
||||
monthlyList := make([]types.UserStatistics, 7)
|
||||
for i := 0; i < 7; i++ {
|
||||
dayDate := now.AddDate(0, 0, -(6 - i))
|
||||
@@ -100,6 +135,19 @@ func (l *QueryUserStatisticsLogic) mockRevenueStatistics() *types.UserStatistics
|
||||
}
|
||||
}
|
||||
|
||||
// Generate monthly user statistics for the past 6 months (oldest first)
|
||||
allList := make([]types.UserStatistics, 6)
|
||||
for i := 0; i < 6; i++ {
|
||||
monthDate := now.AddDate(0, -(5 - i), 0)
|
||||
baseRegister := int64(1800 + ((5 - i) * 200) + ((5-i)%2)*500)
|
||||
allList[i] = types.UserStatistics{
|
||||
Date: monthDate.Format("2006-01"),
|
||||
Register: baseRegister,
|
||||
NewOrderUsers: int64(float64(baseRegister) * 0.65),
|
||||
RenewalOrderUsers: int64(float64(baseRegister) * 0.35),
|
||||
}
|
||||
}
|
||||
|
||||
return &types.UserStatisticsResponse{
|
||||
Today: types.UserStatistics{
|
||||
Register: 28,
|
||||
@@ -116,6 +164,7 @@ func (l *QueryUserStatisticsLogic) mockRevenueStatistics() *types.UserStatistics
|
||||
Register: 18888,
|
||||
NewOrderUsers: 0, // This field is not used in All statistics
|
||||
RenewalOrderUsers: 0, // This field is not used in All statistics
|
||||
List: allList,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterBalanceLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterBalanceLogLogic Filter balance log
|
||||
func NewFilterBalanceLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterBalanceLogLogic {
|
||||
return &FilterBalanceLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterBalanceLogLogic) FilterBalanceLog(req *types.FilterBalanceLogRequest) (resp *types.FilterBalanceLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeBalance.Uint8(),
|
||||
Data: req.Date,
|
||||
ObjectID: req.UserId,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorw("[FilterBalanceLog] Query User Balance Log Error:", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Balance Log Error")
|
||||
}
|
||||
|
||||
list := make([]types.BalanceLog, 0)
|
||||
for _, datum := range data {
|
||||
var content log.Balance
|
||||
if err = content.Unmarshal([]byte(datum.Content)); err != nil {
|
||||
l.Errorf("[QueryUserBalanceLog] unmarshal balance log content failed: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.BalanceLog{
|
||||
UserId: datum.ObjectID,
|
||||
Amount: content.Amount,
|
||||
Type: content.Type,
|
||||
OrderNo: content.OrderNo,
|
||||
Balance: content.Balance,
|
||||
Timestamp: content.Timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterBalanceLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterCommissionLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterCommissionLogLogic Filter commission log
|
||||
func NewFilterCommissionLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterCommissionLogLogic {
|
||||
return &FilterCommissionLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterCommissionLogLogic) FilterCommissionLog(req *types.FilterCommissionLogRequest) (resp *types.FilterCommissionLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Data: req.Date,
|
||||
Type: log.TypeCommission.Uint8(),
|
||||
ObjectID: req.UserId,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("Query User Commission Log failed", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Commission Log failed")
|
||||
}
|
||||
var list []types.CommissionLog
|
||||
|
||||
for _, datum := range data {
|
||||
var content log.Commission
|
||||
if err = content.Unmarshal([]byte(datum.Content)); err != nil {
|
||||
l.Errorf("unmarshal commission log content failed: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.CommissionLog{
|
||||
UserId: datum.ObjectID,
|
||||
Type: content.Type,
|
||||
Amount: content.Amount,
|
||||
OrderNo: content.OrderNo,
|
||||
Timestamp: content.Timestamp,
|
||||
})
|
||||
}
|
||||
return &types.FilterCommissionLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterEmailLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterEmailLogLogic Filter email log
|
||||
func NewFilterEmailLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterEmailLogLogic {
|
||||
return &FilterEmailLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterEmailLogLogic) FilterEmailLog(req *types.FilterLogParams) (resp *types.FilterEmailLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeEmailMessage.Uint8(),
|
||||
Data: req.Date,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[FilterEmailLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
|
||||
var list []types.MessageLog
|
||||
|
||||
for _, datum := range data {
|
||||
var content log.Message
|
||||
err = content.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterEmailLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.MessageLog{
|
||||
Id: datum.Id,
|
||||
Type: datum.Type,
|
||||
Platform: content.Platform,
|
||||
To: content.To,
|
||||
Subject: content.Subject,
|
||||
Content: content.Content,
|
||||
Status: content.Status,
|
||||
CreatedAt: datum.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterEmailLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterGiftLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Filter gift log
|
||||
func NewFilterGiftLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterGiftLogLogic {
|
||||
return &FilterGiftLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterGiftLogLogic) FilterGiftLog(req *types.FilterGiftLogRequest) (resp *types.FilterGiftLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeGift.Uint8(),
|
||||
ObjectID: req.UserId,
|
||||
Data: req.Date,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[FilterGiftLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
|
||||
var list []types.GiftLog
|
||||
for _, datum := range data {
|
||||
var content log.Gift
|
||||
err = content.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterGiftLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.GiftLog{
|
||||
Type: content.Type,
|
||||
UserId: datum.ObjectID,
|
||||
OrderNo: content.OrderNo,
|
||||
SubscribeId: content.SubscribeId,
|
||||
Amount: content.Amount,
|
||||
Balance: content.Balance,
|
||||
Remark: content.Remark,
|
||||
Timestamp: content.Timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterGiftLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterLoginLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterLoginLogLogic Filter login log
|
||||
func NewFilterLoginLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterLoginLogLogic {
|
||||
return &FilterLoginLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterLoginLogLogic) FilterLoginLog(req *types.FilterLoginLogRequest) (resp *types.FilterLoginLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeLogin.Uint8(),
|
||||
ObjectID: req.UserId,
|
||||
Data: req.Date,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[FilterLoginLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
var list []types.LoginLog
|
||||
for _, datum := range data {
|
||||
var item log.Login
|
||||
err = item.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterLoginLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.LoginLog{
|
||||
UserId: datum.ObjectID,
|
||||
Method: item.Method,
|
||||
LoginIP: item.LoginIP,
|
||||
UserAgent: item.UserAgent,
|
||||
Success: item.Success,
|
||||
Timestamp: datum.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterLoginLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterMobileLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Filter mobile log
|
||||
func NewFilterMobileLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterMobileLogLogic {
|
||||
return &FilterMobileLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterMobileLogLogic) FilterMobileLog(req *types.FilterLogParams) (resp *types.FilterMobileLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeMobileMessage.Uint8(),
|
||||
Data: req.Date,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[FilterMobileLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
|
||||
var list []types.MessageLog
|
||||
|
||||
for _, datum := range data {
|
||||
var content log.Message
|
||||
err = content.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterMobileLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.MessageLog{
|
||||
Id: datum.Id,
|
||||
Type: datum.Type,
|
||||
Platform: content.Platform,
|
||||
To: content.To,
|
||||
Subject: content.Subject,
|
||||
Content: content.Content,
|
||||
Status: content.Status,
|
||||
CreatedAt: datum.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterMobileLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterRegisterLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Filter register log
|
||||
func NewFilterRegisterLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterRegisterLogLogic {
|
||||
return &FilterRegisterLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterRegisterLogLogic) FilterRegisterLog(req *types.FilterRegisterLogRequest) (resp *types.FilterRegisterLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeRegister.Uint8(),
|
||||
ObjectID: req.UserId,
|
||||
Data: req.Date,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[FilterRegisterLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
|
||||
var list []types.RegisterLog
|
||||
for _, datum := range data {
|
||||
var item log.Register
|
||||
err = item.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterLoginLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.RegisterLog{
|
||||
UserId: datum.ObjectID,
|
||||
AuthMethod: item.AuthMethod,
|
||||
Identifier: item.Identifier,
|
||||
RegisterIP: item.RegisterIP,
|
||||
UserAgent: item.UserAgent,
|
||||
Timestamp: item.Timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterRegisterLogResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterResetSubscribeLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterResetSubscribeLogLogic Filter reset subscribe log
|
||||
func NewFilterResetSubscribeLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterResetSubscribeLogLogic {
|
||||
return &FilterResetSubscribeLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterResetSubscribeLogLogic) FilterResetSubscribeLog(req *types.FilterResetSubscribeLogRequest) (resp *types.FilterResetSubscribeLogResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeResetSubscribe.Uint8(),
|
||||
ObjectID: req.UserSubscribeId,
|
||||
Data: req.Date,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("[FilterResetSubscribeLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
|
||||
var list []types.ResetSubscribeLog
|
||||
|
||||
for _, item := range data {
|
||||
var content log.ResetSubscribe
|
||||
err = content.Unmarshal([]byte(item.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterResetSubscribeLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.ResetSubscribeLog{
|
||||
Type: content.Type,
|
||||
UserId: content.UserId,
|
||||
UserSubscribeId: item.ObjectID,
|
||||
OrderNo: content.OrderNo,
|
||||
Timestamp: content.Timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterResetSubscribeLogResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"github.com/perfect-panel/server/internal/model/traffic"
|
||||
"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 FilterServerTrafficLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterServerTrafficLogLogic Filter server traffic log
|
||||
func NewFilterServerTrafficLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterServerTrafficLogLogic {
|
||||
return &FilterServerTrafficLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
func (l *FilterServerTrafficLogLogic) FilterServerTrafficLog(req *types.FilterServerTrafficLogRequest) (resp *types.FilterServerTrafficLogResponse, err error) {
|
||||
today := time.Now().Format("2006-01-02")
|
||||
var list []types.ServerTrafficLog
|
||||
var total int64
|
||||
|
||||
if req.Date == today || req.Date == "" {
|
||||
now := time.Now()
|
||||
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
end := start.Add(24 * time.Hour).Add(-time.Nanosecond)
|
||||
|
||||
var serverTraffic []log.ServerTraffic
|
||||
err = l.svcCtx.DB.WithContext(l.ctx).
|
||||
Model(&traffic.TrafficLog{}).
|
||||
Select("server_id, SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
|
||||
Where("timestamp BETWEEN ? AND ?", start, end).
|
||||
Group("server_id").
|
||||
Order("SUM(download + upload) DESC").
|
||||
Scan(&serverTraffic).Error
|
||||
if err != nil {
|
||||
l.Errorw("[FilterServerTrafficLog] Query Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "today traffic query error: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, v := range serverTraffic {
|
||||
list = append(list, types.ServerTrafficLog{
|
||||
ServerId: v.ServerId,
|
||||
Upload: v.Upload,
|
||||
Download: v.Download,
|
||||
Total: v.Total,
|
||||
Date: today,
|
||||
Details: true,
|
||||
})
|
||||
}
|
||||
|
||||
todayTotal := len(list)
|
||||
|
||||
startIdx := (req.Page - 1) * req.Size
|
||||
endIdx := startIdx + req.Size
|
||||
|
||||
if startIdx < todayTotal {
|
||||
if endIdx > todayTotal {
|
||||
endIdx = todayTotal
|
||||
}
|
||||
pageData := list[startIdx:endIdx]
|
||||
return &types.FilterServerTrafficLogResponse{
|
||||
List: pageData,
|
||||
Total: int64(todayTotal),
|
||||
}, nil
|
||||
}
|
||||
|
||||
need := endIdx - todayTotal
|
||||
historyPage := (need + req.Size - 1) / req.Size // 算出需要的历史页数
|
||||
historyData, historyTotal, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: historyPage,
|
||||
Size: need,
|
||||
Type: log.TypeServerTraffic.Uint8(),
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[FilterServerTrafficLog] Query History Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "history query error: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, item := range historyData {
|
||||
var content log.ServerTraffic
|
||||
if err = content.Unmarshal([]byte(item.Content)); err != nil {
|
||||
l.Errorw("[FilterServerTrafficLog] Unmarshal Error", logger.Field("error", err.Error()), logger.Field("content", item.Content))
|
||||
continue
|
||||
}
|
||||
|
||||
hasDetails := true
|
||||
if l.svcCtx.Config.Log.AutoClear {
|
||||
last := now.AddDate(0, 0, int(-l.svcCtx.Config.Log.ClearDays))
|
||||
dataTime, err := time.Parse(time.DateOnly, item.Date)
|
||||
if err != nil {
|
||||
l.Errorw("[FilterServerTrafficLog] Parse Date Error", logger.Field("error", err.Error()), logger.Field("date", item.Date))
|
||||
} else {
|
||||
if dataTime.Before(last) {
|
||||
hasDetails = false
|
||||
} else {
|
||||
hasDetails = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list = append(list, types.ServerTrafficLog{
|
||||
ServerId: item.ObjectID,
|
||||
Upload: content.Upload,
|
||||
Download: content.Download,
|
||||
Total: content.Total,
|
||||
Date: item.Date,
|
||||
Details: hasDetails,
|
||||
})
|
||||
}
|
||||
|
||||
// 返回最终分页数据
|
||||
if endIdx > len(list) {
|
||||
endIdx = len(list)
|
||||
}
|
||||
pageData := list[startIdx:endIdx]
|
||||
|
||||
return &types.FilterServerTrafficLogResponse{
|
||||
List: pageData,
|
||||
Total: int64(todayTotal) + historyTotal,
|
||||
}, nil
|
||||
}
|
||||
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeServerTraffic.Uint8(),
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[FilterServerTrafficLog] Query Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "history query error: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, item := range data {
|
||||
var content log.ServerTraffic
|
||||
if err = content.Unmarshal([]byte(item.Content)); err != nil {
|
||||
l.Errorw("[FilterServerTrafficLog] Unmarshal Error", logger.Field("error", err.Error()), logger.Field("content", item.Content))
|
||||
continue
|
||||
}
|
||||
list = append(list, types.ServerTrafficLog{
|
||||
ServerId: item.ObjectID,
|
||||
Upload: content.Upload,
|
||||
Download: content.Download,
|
||||
Total: content.Total,
|
||||
Date: item.Date,
|
||||
Details: false,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterServerTrafficLogResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 FilterSubscribeLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterSubscribeLogLogic Filter subscribe log
|
||||
func NewFilterSubscribeLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterSubscribeLogLogic {
|
||||
return &FilterSubscribeLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterSubscribeLogLogic) FilterSubscribeLog(req *types.FilterSubscribeLogRequest) (resp *types.FilterSubscribeLogResponse, err error) {
|
||||
params := &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeSubscribe.Uint8(),
|
||||
Data: req.Date,
|
||||
ObjectID: req.UserId,
|
||||
}
|
||||
|
||||
if req.UserSubscribeId != 0 {
|
||||
params.Search = `"user_subscribe_id":` + strconv.FormatInt(req.UserSubscribeId, 10)
|
||||
}
|
||||
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, params)
|
||||
if err != nil {
|
||||
l.Errorf("[FilterSubscribeLog] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log")
|
||||
}
|
||||
|
||||
var list []types.SubscribeLog
|
||||
for _, datum := range data {
|
||||
var content log.Subscribe
|
||||
err = content.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[FilterSubscribeLog] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.SubscribeLog{
|
||||
UserId: datum.ObjectID,
|
||||
Token: content.Token,
|
||||
UserAgent: content.UserAgent,
|
||||
ClientIP: content.ClientIP,
|
||||
UserSubscribeId: content.UserSubscribeId,
|
||||
Timestamp: datum.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterSubscribeLogResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/traffic"
|
||||
"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 FilterTrafficLogDetailsLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterTrafficLogDetailsLogic Filter traffic log details
|
||||
func NewFilterTrafficLogDetailsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterTrafficLogDetailsLogic {
|
||||
return &FilterTrafficLogDetailsLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterTrafficLogDetailsLogic) FilterTrafficLogDetails(req *types.FilterTrafficLogDetailsRequest) (resp *types.FilterTrafficLogDetailsResponse, err error) {
|
||||
var start, end time.Time
|
||||
if req.Date != "" {
|
||||
day, err := time.ParseInLocation("2006-01-02", req.Date, time.Local)
|
||||
if err != nil {
|
||||
l.Errorw("[FilterTrafficLogDetails] Date Parse Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), " date parse error: %s", err.Error())
|
||||
}
|
||||
start = day
|
||||
end = day.Add(24*time.Hour - time.Nanosecond)
|
||||
} else {
|
||||
// query today
|
||||
now := time.Now()
|
||||
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
end = start.Add(24*time.Hour - time.Nanosecond)
|
||||
}
|
||||
var data []*traffic.TrafficLog
|
||||
tx := l.svcCtx.DB.WithContext(l.ctx).Model(&traffic.TrafficLog{})
|
||||
if req.ServerId != 0 {
|
||||
tx = tx.Where("server_id = ?", req.ServerId)
|
||||
}
|
||||
if !start.IsZero() && !end.IsZero() {
|
||||
tx = tx.Where("timestamp BETWEEN ? AND ?", start, end)
|
||||
}
|
||||
if req.UserId != 0 {
|
||||
tx = tx.Where("user_id = ?", req.UserId)
|
||||
}
|
||||
if req.SubscribeId != 0 {
|
||||
tx = tx.Where("subscribe_id = ?", req.SubscribeId)
|
||||
}
|
||||
var total int64
|
||||
err = tx.Count(&total).Limit(req.Size).Offset((req.Page - 1) * req.Size).Find(&data).Error
|
||||
if err != nil {
|
||||
l.Errorw("[FilterTrafficLogDetails] Query Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " database query error: %s", err.Error())
|
||||
}
|
||||
|
||||
var logs []types.TrafficLogDetails
|
||||
for _, v := range data {
|
||||
logs = append(logs, types.TrafficLogDetails{
|
||||
Id: v.Id,
|
||||
UserId: v.UserId,
|
||||
ServerId: v.ServerId,
|
||||
SubscribeId: v.SubscribeId,
|
||||
Download: v.Download,
|
||||
Upload: v.Upload,
|
||||
Timestamp: v.Timestamp.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterTrafficLogDetailsResponse{
|
||||
List: logs,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"github.com/perfect-panel/server/internal/model/traffic"
|
||||
"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 FilterUserSubscribeTrafficLogLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterUserSubscribeTrafficLogLogic Filter user subscribe traffic log
|
||||
func NewFilterUserSubscribeTrafficLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterUserSubscribeTrafficLogLogic {
|
||||
return &FilterUserSubscribeTrafficLogLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterUserSubscribeTrafficLogLogic) FilterUserSubscribeTrafficLog(req *types.FilterSubscribeTrafficRequest) (resp *types.FilterSubscribeTrafficResponse, err error) {
|
||||
if req.Size <= 0 {
|
||||
req.Size = 10
|
||||
}
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
|
||||
today := time.Now().Format("2006-01-02")
|
||||
var list []types.UserSubscribeTrafficLog
|
||||
var total int64
|
||||
|
||||
if req.Date == today || req.Date == "" {
|
||||
now := time.Now()
|
||||
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
end := start.Add(24 * time.Hour).Add(-time.Nanosecond)
|
||||
|
||||
var userTraffic []types.UserSubscribeTrafficLog
|
||||
err = l.svcCtx.DB.WithContext(l.ctx).
|
||||
Model(&traffic.TrafficLog{}).
|
||||
Select("user_id, subscribe_id, SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
|
||||
Where("timestamp BETWEEN ? AND ?", start, end).
|
||||
Group("user_id, subscribe_id").
|
||||
Order("SUM(download + upload) DESC").
|
||||
Scan(&userTraffic).Error
|
||||
if err != nil {
|
||||
l.Errorw("[FilterUserSubscribeTrafficLog] Query Database Error", logger.Field("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range userTraffic {
|
||||
list = append(list, types.UserSubscribeTrafficLog{
|
||||
UserId: v.UserId,
|
||||
SubscribeId: v.SubscribeId,
|
||||
Upload: v.Upload,
|
||||
Download: v.Download,
|
||||
Total: v.Total,
|
||||
Date: today,
|
||||
Details: true,
|
||||
})
|
||||
}
|
||||
todayTotal := len(list)
|
||||
|
||||
startIdx := (req.Page - 1) * req.Size
|
||||
endIdx := startIdx + req.Size
|
||||
if startIdx < todayTotal {
|
||||
if endIdx > todayTotal {
|
||||
endIdx = todayTotal
|
||||
}
|
||||
pageData := list[startIdx:endIdx]
|
||||
return &types.FilterSubscribeTrafficResponse{
|
||||
List: pageData,
|
||||
Total: int64(todayTotal),
|
||||
}, nil
|
||||
}
|
||||
|
||||
need := endIdx - todayTotal
|
||||
historyPage := (need + req.Size - 1) / req.Size // 算出需要的历史页数
|
||||
historyData, historyTotal, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: historyPage,
|
||||
Size: need,
|
||||
Type: log.TypeSubscribeTraffic.Uint8(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorw("[FilterUserSubscribeTrafficLog] Query Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterUserSubscribeTrafficLog] Query Database Error")
|
||||
}
|
||||
|
||||
for _, datum := range historyData {
|
||||
var item log.UserTraffic
|
||||
err = item.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorw("[FilterUserSubscribeTrafficLog] Unmarshal Content Error", logger.Field("error", err.Error()))
|
||||
continue
|
||||
}
|
||||
list = append(list, types.UserSubscribeTrafficLog{
|
||||
UserId: item.UserId,
|
||||
SubscribeId: item.SubscribeId,
|
||||
Upload: item.Upload,
|
||||
Download: item.Download,
|
||||
Total: item.Total,
|
||||
Date: datum.Date,
|
||||
Details: false,
|
||||
})
|
||||
}
|
||||
// 返回最终分页数据
|
||||
if endIdx > len(list) {
|
||||
endIdx = len(list)
|
||||
}
|
||||
pageData := list[startIdx:endIdx]
|
||||
|
||||
return &types.FilterSubscribeTrafficResponse{
|
||||
List: pageData,
|
||||
Total: int64(todayTotal) + historyTotal,
|
||||
}, nil
|
||||
}
|
||||
var data []*log.SystemLog
|
||||
data, total, err = l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeSubscribeTraffic.Uint8(),
|
||||
Data: req.Date,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[FilterUserSubscribeTrafficLog] Query Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterUserSubscribeTrafficLog] Query Database Error")
|
||||
}
|
||||
for _, datum := range data {
|
||||
var item log.UserTraffic
|
||||
err = item.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorw("[FilterUserSubscribeTrafficLog] Unmarshal Content Error", logger.Field("error", err.Error()))
|
||||
continue
|
||||
}
|
||||
list = append(list, types.UserSubscribeTrafficLog{
|
||||
UserId: item.UserId,
|
||||
SubscribeId: item.SubscribeId,
|
||||
Upload: item.Upload,
|
||||
Download: item.Download,
|
||||
Total: item.Total,
|
||||
Date: datum.Date,
|
||||
Details: false,
|
||||
})
|
||||
}
|
||||
return &types.FilterSubscribeTrafficResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package log
|
||||
|
||||
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 GetLogSettingLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get log setting
|
||||
func NewGetLogSettingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLogSettingLogic {
|
||||
return &GetLogSettingLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetLogSettingLogic) GetLogSetting() (resp *types.LogSetting, err error) {
|
||||
configs, err := l.svcCtx.SystemModel.GetLogConfig(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[GetLogSetting] Database query error", logger.Field("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
resp = &types.LogSetting{}
|
||||
// reflect to response
|
||||
tool.SystemConfigSliceReflectToStruct(configs, resp)
|
||||
return
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"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"
|
||||
)
|
||||
@@ -28,20 +27,39 @@ func NewGetMessageLogListLogic(ctx context.Context, svcCtx *svc.ServiceContext)
|
||||
}
|
||||
|
||||
func (l *GetMessageLogListLogic) GetMessageLogList(req *types.GetMessageLogListRequest) (resp *types.GetMessageLogListResponse, err error) {
|
||||
total, data, err := l.svcCtx.LogModel.FindMessageLogList(l.ctx, req.Page, req.Size, log.MessageLogFilterParams{
|
||||
Type: req.Type,
|
||||
Platform: req.Platform,
|
||||
To: req.To,
|
||||
Subject: req.Subject,
|
||||
Content: req.Content,
|
||||
Status: req.Status,
|
||||
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: req.Type,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorw("[GetMessageLogList] Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[GetMessageLogList] Database Error: %s", err.Error())
|
||||
l.Errorf("[GetMessageLogList] failed to filter system log: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
|
||||
}
|
||||
|
||||
var list []types.MessageLog
|
||||
tool.DeepCopy(&list, data)
|
||||
|
||||
for _, datum := range data {
|
||||
var content log.Message
|
||||
err = content.Unmarshal([]byte(datum.Content))
|
||||
if err != nil {
|
||||
l.Errorf("[GetMessageLogList] failed to unmarshal content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.MessageLog{
|
||||
Id: datum.Id,
|
||||
Type: datum.Type,
|
||||
Platform: content.Platform,
|
||||
To: content.To,
|
||||
Subject: content.Subject,
|
||||
Content: content.Content,
|
||||
Status: content.Status,
|
||||
CreatedAt: datum.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetMessageLogListResponse{
|
||||
Total: total,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/perfect-panel/server/internal/config"
|
||||
"github.com/perfect-panel/server/internal/model/system"
|
||||
"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"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UpdateLogSettingLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewUpdateLogSettingLogic Update log setting
|
||||
func NewUpdateLogSettingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateLogSettingLogic {
|
||||
return &UpdateLogSettingLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateLogSettingLogic) UpdateLogSetting(req *types.LogSetting) error {
|
||||
v := reflect.ValueOf(*req)
|
||||
// Get the reflection type of the structure
|
||||
t := v.Type()
|
||||
err := l.svcCtx.SystemModel.Transaction(l.ctx, func(db *gorm.DB) error {
|
||||
var err error
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
// Get the field name
|
||||
fieldName := t.Field(i).Name
|
||||
// Get the field value to string
|
||||
fieldValue := tool.ConvertValueToString(v.Field(i))
|
||||
// Update the server config
|
||||
err = db.Model(&system.System{}).Where("`category` = 'log' and `key` = ?", fieldName).Update("value", fieldValue).Error
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateLogSetting] update log setting error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), " update log setting error: %v", err)
|
||||
}
|
||||
|
||||
l.svcCtx.Config.Log = config.Log{
|
||||
AutoClear: *req.AutoClear,
|
||||
ClearDays: req.ClearDays,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"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"
|
||||
types2 "github.com/perfect-panel/server/queue/types"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CreateBatchSendEmailTaskLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewCreateBatchSendEmailTaskLogic Create a batch send email task
|
||||
func NewCreateBatchSendEmailTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateBatchSendEmailTaskLogic {
|
||||
return &CreateBatchSendEmailTaskLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
func (l *CreateBatchSendEmailTaskLogic) CreateBatchSendEmailTask(req *types.CreateBatchSendEmailTaskRequest) (err error) {
|
||||
tx := l.svcCtx.DB
|
||||
|
||||
var emails []string
|
||||
|
||||
// 通用查询器(含 user JOIN + 注册时间范围过滤)
|
||||
baseQuery := func() *gorm.DB {
|
||||
query := tx.Model(&user.AuthMethods{}).
|
||||
Select("auth_identifier").
|
||||
Joins("JOIN user ON user.id = user_auth_methods.user_id").
|
||||
Where("auth_type = ?", "email")
|
||||
|
||||
if req.RegisterStartTime != 0 {
|
||||
query = query.Where("user.created_at >= ?", time.UnixMilli(req.RegisterStartTime))
|
||||
}
|
||||
if req.RegisterEndTime != 0 {
|
||||
query = query.Where("user.created_at <= ?", time.UnixMilli(req.RegisterEndTime))
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
var query *gorm.DB
|
||||
|
||||
scope := task.ParseScopeType(req.Scope)
|
||||
|
||||
switch scope {
|
||||
case task.ScopeAll:
|
||||
query = baseQuery()
|
||||
|
||||
case task.ScopeActive:
|
||||
query = baseQuery().
|
||||
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||
Where("user_subscribe.status IN ?", []int64{1, 2})
|
||||
|
||||
case task.ScopeExpired:
|
||||
query = baseQuery().
|
||||
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||
Where("user_subscribe.status = ?", 3)
|
||||
|
||||
case task.ScopeNone:
|
||||
query = baseQuery().
|
||||
Joins("LEFT JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||
Where("user_subscribe.user_id IS NULL")
|
||||
default:
|
||||
|
||||
}
|
||||
if query != nil {
|
||||
// 执行查询
|
||||
err = query.Pluck("auth_identifier", &emails).Error
|
||||
if err != nil {
|
||||
l.Errorf("[CreateBatchSendEmailTask] Failed to fetch email addresses: %v", err.Error())
|
||||
return xerr.NewErrCode(xerr.DatabaseQueryError)
|
||||
}
|
||||
}
|
||||
|
||||
// 邮箱列表为空,返回错误
|
||||
if len(emails) == 0 && scope != task.ScopeSkip {
|
||||
l.Errorf("[CreateBatchSendEmailTask] No email addresses found for the specified scope")
|
||||
return xerr.NewErrMsg("No email addresses found for the specified scope")
|
||||
}
|
||||
|
||||
// 邮箱地址去重
|
||||
emails = tool.RemoveDuplicateElements(emails...)
|
||||
|
||||
var additionalEmails []string
|
||||
// 追加额外的邮箱地址(不覆盖)
|
||||
if req.Additional != "" {
|
||||
additionalEmails = tool.RemoveDuplicateElements(strings.Split(req.Additional, "\n")...)
|
||||
}
|
||||
if len(additionalEmails) == 0 && scope == task.ScopeSkip {
|
||||
l.Errorf("[CreateBatchSendEmailTask] No additional email addresses provided for skip scope")
|
||||
return xerr.NewErrMsg("No additional email addresses provided for skip scope")
|
||||
}
|
||||
|
||||
scheduledAt := time.Now().Add(10 * time.Second) // 默认延迟10秒执行,防止任务创建和执行时间过于接近
|
||||
if req.Scheduled != 0 {
|
||||
scheduledAt = time.Unix(req.Scheduled, 0)
|
||||
if scheduledAt.Before(time.Now()) {
|
||||
scheduledAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
scopeInfo := task.EmailScope{
|
||||
Type: scope.Int8(),
|
||||
RegisterStartTime: req.RegisterStartTime,
|
||||
RegisterEndTime: req.RegisterEndTime,
|
||||
Recipients: emails,
|
||||
Additional: additionalEmails,
|
||||
Scheduled: req.Scheduled,
|
||||
Interval: req.Interval,
|
||||
Limit: req.Limit,
|
||||
}
|
||||
scopeBytes, _ := scopeInfo.Marshal()
|
||||
|
||||
taskContent := task.EmailContent{
|
||||
Subject: req.Subject,
|
||||
Content: req.Content,
|
||||
}
|
||||
|
||||
contentBytes, _ := taskContent.Marshal()
|
||||
|
||||
var total uint64
|
||||
if additionalEmails != nil {
|
||||
list := append(emails, additionalEmails...)
|
||||
total = uint64(len(tool.RemoveDuplicateElements(list...)))
|
||||
} else {
|
||||
total = uint64(len(emails))
|
||||
}
|
||||
|
||||
taskInfo := &task.Task{
|
||||
Type: task.TypeEmail,
|
||||
Scope: string(scopeBytes),
|
||||
Content: string(contentBytes),
|
||||
Status: 0,
|
||||
Errors: "",
|
||||
Total: total,
|
||||
Current: 0,
|
||||
}
|
||||
|
||||
if err = l.svcCtx.DB.Model(&task.Task{}).Create(taskInfo).Error; err != nil {
|
||||
l.Errorf("[CreateBatchSendEmailTask] Failed to create email task: %v", err.Error())
|
||||
return xerr.NewErrCode(xerr.DatabaseInsertError)
|
||||
}
|
||||
// create task
|
||||
l.Infof("[CreateBatchSendEmailTask] Successfully created email task with ID: %d", taskInfo.Id)
|
||||
|
||||
t := asynq.NewTask(types2.ScheduledBatchSendEmail, []byte(strconv.FormatInt(taskInfo.Id, 10)))
|
||||
info, err := l.svcCtx.Queue.EnqueueContext(l.ctx, t, asynq.ProcessAt(scheduledAt))
|
||||
if err != nil {
|
||||
l.Errorf("[CreateBatchSendEmailTask] Failed to enqueue email task: %v", err.Error())
|
||||
return xerr.NewErrCode(xerr.QueueEnqueueError)
|
||||
}
|
||||
l.Infof("[CreateBatchSendEmailTask] Successfully enqueued email task with ID: %s, scheduled at: %s", info.ID, scheduledAt.Format(time.DateTime))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"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"
|
||||
queueType "github.com/perfect-panel/server/queue/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CreateQuotaTaskLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewCreateQuotaTaskLogic Create a quota task
|
||||
func NewCreateQuotaTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateQuotaTaskLogic {
|
||||
return &CreateQuotaTaskLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateQuotaTaskLogic) CreateQuotaTask(req *types.CreateQuotaTaskRequest) error {
|
||||
var subs []*user.Subscribe
|
||||
query := l.svcCtx.DB.WithContext(l.ctx).Model(&user.Subscribe{})
|
||||
if len(req.Subscribers) > 0 {
|
||||
query = query.Where("`subscribe_id` IN ?", req.Subscribers)
|
||||
}
|
||||
|
||||
if req.IsActive != nil && *req.IsActive {
|
||||
query = query.Where("`status` IN ?", []int64{0, 1, 2}) // 0: Pending 1: Active 2: Finished
|
||||
}
|
||||
if req.StartTime != 0 {
|
||||
start := time.UnixMilli(req.StartTime)
|
||||
query = query.Where("`start_time` <= ?", start)
|
||||
}
|
||||
if req.EndTime != 0 {
|
||||
end := time.UnixMilli(req.EndTime)
|
||||
query = query.Where("`expire_time` >= ?", end)
|
||||
}
|
||||
|
||||
if err := query.Find(&subs).Error; err != nil {
|
||||
l.Errorf("[CreateQuotaTask] find subscribers error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribers error")
|
||||
}
|
||||
if len(subs) == 0 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("No subscribers found"), "no subscribers found")
|
||||
}
|
||||
var subIds []int64
|
||||
for _, sub := range subs {
|
||||
subIds = append(subIds, sub.Id)
|
||||
}
|
||||
|
||||
scopeInfo := task.QuotaScope{
|
||||
Subscribers: req.Subscribers,
|
||||
IsActive: req.IsActive,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
Objects: subIds,
|
||||
}
|
||||
scopeBytes, _ := scopeInfo.Marshal()
|
||||
contentInfo := task.QuotaContent{
|
||||
ResetTraffic: req.ResetTraffic,
|
||||
Days: req.Days,
|
||||
GiftType: req.GiftType,
|
||||
GiftValue: req.GiftValue,
|
||||
}
|
||||
contentBytes, _ := contentInfo.Marshal()
|
||||
// create task
|
||||
newTask := &task.Task{
|
||||
Type: task.TypeQuota,
|
||||
Status: 0,
|
||||
Scope: string(scopeBytes),
|
||||
Content: string(contentBytes),
|
||||
Total: uint64(len(subIds)),
|
||||
Current: 0,
|
||||
Errors: "",
|
||||
}
|
||||
|
||||
if err := l.svcCtx.DB.WithContext(l.ctx).Model(&task.Task{}).Create(newTask).Error; err != nil {
|
||||
l.Errorf("[CreateQuotaTask] create task error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create task error")
|
||||
}
|
||||
|
||||
// enqueue task
|
||||
queueTask := asynq.NewTask(queueType.ForthwithQuotaTask, []byte(strconv.FormatInt(newTask.Id, 10)))
|
||||
if _, err := l.svcCtx.Queue.EnqueueContext(l.ctx, queueTask); err != nil {
|
||||
l.Errorf("[CreateQuotaTask] enqueue task error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.QueueEnqueueError), "enqueue task error")
|
||||
}
|
||||
logger.Infof("[CreateQuotaTask] Successfully created task with ID: %d", newTask.Id)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"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"
|
||||
)
|
||||
|
||||
type GetBatchSendEmailTaskListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewGetBatchSendEmailTaskListLogic Get batch send email task list
|
||||
func NewGetBatchSendEmailTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetBatchSendEmailTaskListLogic {
|
||||
return &GetBatchSendEmailTaskListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetBatchSendEmailTaskListLogic) GetBatchSendEmailTaskList(req *types.GetBatchSendEmailTaskListRequest) (resp *types.GetBatchSendEmailTaskListResponse, err error) {
|
||||
|
||||
var tasks []*task.Task
|
||||
tx := l.svcCtx.DB.Model(&task.Task{}).Where("`type` = ?", task.TypeEmail)
|
||||
if req.Status != nil {
|
||||
tx = tx.Where("status = ?", *req.Status)
|
||||
}
|
||||
if req.Scope != nil {
|
||||
tx = tx.Where("scope = ?", req.Scope)
|
||||
}
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size == 0 {
|
||||
req.Size = 10
|
||||
}
|
||||
err = tx.Offset((req.Page - 1) * req.Size).Limit(req.Size).Order("created_at DESC").Find(&tasks).Error
|
||||
if err != nil {
|
||||
l.Errorf("failed to get email tasks: %v", err)
|
||||
return nil, xerr.NewErrCode(xerr.DatabaseQueryError)
|
||||
}
|
||||
|
||||
list := make([]types.BatchSendEmailTask, 0)
|
||||
|
||||
for _, t := range tasks {
|
||||
var scopeInfo task.EmailScope
|
||||
if err = scopeInfo.Unmarshal([]byte(t.Scope)); err != nil {
|
||||
l.Errorf("[GetBatchSendEmailTaskList] failed to unmarshal email task scope: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
var contentInfo task.EmailContent
|
||||
if err = contentInfo.Unmarshal([]byte(t.Content)); err != nil {
|
||||
l.Errorf("[GetBatchSendEmailTaskList] failed to unmarshal email task content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
list = append(list, types.BatchSendEmailTask{
|
||||
Id: t.Id,
|
||||
Subject: contentInfo.Subject,
|
||||
Content: contentInfo.Content,
|
||||
Recipients: strings.Join(scopeInfo.Recipients, "\n"),
|
||||
Scope: scopeInfo.Type,
|
||||
RegisterStartTime: scopeInfo.RegisterStartTime,
|
||||
RegisterEndTime: scopeInfo.RegisterEndTime,
|
||||
Additional: strings.Join(scopeInfo.Additional, "\n"),
|
||||
Scheduled: scopeInfo.Scheduled,
|
||||
Interval: scopeInfo.Interval,
|
||||
Limit: scopeInfo.Limit,
|
||||
Status: uint8(t.Status),
|
||||
Errors: t.Errors,
|
||||
Total: t.Total,
|
||||
Current: t.Current,
|
||||
CreatedAt: t.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: t.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetBatchSendEmailTaskListResponse{
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"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"
|
||||
)
|
||||
|
||||
type GetBatchSendEmailTaskStatusLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewGetBatchSendEmailTaskStatusLogic Get batch send email task status
|
||||
func NewGetBatchSendEmailTaskStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetBatchSendEmailTaskStatusLogic {
|
||||
return &GetBatchSendEmailTaskStatusLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetBatchSendEmailTaskStatusLogic) GetBatchSendEmailTaskStatus(req *types.GetBatchSendEmailTaskStatusRequest) (resp *types.GetBatchSendEmailTaskStatusResponse, err error) {
|
||||
tx := l.svcCtx.DB
|
||||
|
||||
var taskInfo *task.Task
|
||||
err = tx.Model(&task.Task{}).Where("id = ?", req.Id).First(&taskInfo).Error
|
||||
if err != nil {
|
||||
l.Errorf("failed to get email task status, error: %v", err)
|
||||
return nil, xerr.NewErrCode(xerr.DatabaseQueryError)
|
||||
}
|
||||
|
||||
return &types.GetBatchSendEmailTaskStatusResponse{
|
||||
Status: uint8(taskInfo.Status),
|
||||
Total: int64(taskInfo.Total),
|
||||
Current: int64(taskInfo.Current),
|
||||
Errors: taskInfo.Errors,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"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"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GetPreSendEmailCountLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewGetPreSendEmailCountLogic Get pre-send email count
|
||||
func NewGetPreSendEmailCountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPreSendEmailCountLogic {
|
||||
return &GetPreSendEmailCountLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPreSendEmailCountLogic) GetPreSendEmailCount(req *types.GetPreSendEmailCountRequest) (resp *types.GetPreSendEmailCountResponse, err error) {
|
||||
tx := l.svcCtx.DB
|
||||
var count int64
|
||||
// 通用查询器(含 user JOIN + 注册时间范围过滤)
|
||||
baseQuery := func() *gorm.DB {
|
||||
query := tx.Model(&user.AuthMethods{}).
|
||||
Select("auth_identifier").
|
||||
Joins("JOIN user ON user.id = user_auth_methods.user_id").
|
||||
Where("auth_type = ?", "email")
|
||||
|
||||
if req.RegisterStartTime != 0 {
|
||||
|
||||
registerStartTime := time.UnixMilli(req.RegisterStartTime)
|
||||
|
||||
query = query.Where("user.created_at >= ?", registerStartTime)
|
||||
}
|
||||
if req.RegisterEndTime != 0 {
|
||||
registerEndTime := time.UnixMilli(req.RegisterEndTime)
|
||||
query = query.Where("user.created_at <= ?", registerEndTime)
|
||||
}
|
||||
return query
|
||||
}
|
||||
var query *gorm.DB
|
||||
scope := task.ParseScopeType(req.Scope)
|
||||
|
||||
switch scope {
|
||||
case task.ScopeAll:
|
||||
query = baseQuery()
|
||||
|
||||
case task.ScopeActive:
|
||||
query = baseQuery().
|
||||
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||
Where("user_subscribe.status IN ?", []int64{1, 2})
|
||||
|
||||
case task.ScopeExpired:
|
||||
query = baseQuery().
|
||||
Joins("JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||
Where("user_subscribe.status = ?", 3)
|
||||
|
||||
case task.ScopeNone:
|
||||
query = baseQuery().
|
||||
Joins("LEFT JOIN user_subscribe ON user.id = user_subscribe.user_id").
|
||||
Where("user_subscribe.user_id IS NULL")
|
||||
case task.ScopeSkip:
|
||||
// Skip scope does not require a count
|
||||
query = nil
|
||||
default:
|
||||
l.Errorf("[CreateBatchSendEmailTask] Invalid scope: %v", req.Scope)
|
||||
return nil, xerr.NewErrMsg("Invalid email scope")
|
||||
|
||||
}
|
||||
|
||||
if query != nil {
|
||||
if err = query.Count(&count).Error; err != nil {
|
||||
l.Errorf("[GetPreSendEmailCount] Count error: %v", err)
|
||||
return nil, xerr.NewErrMsg("Failed to count emails")
|
||||
}
|
||||
}
|
||||
|
||||
return &types.GetPreSendEmailCountResponse{
|
||||
Count: count,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type QueryQuotaTaskListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewQueryQuotaTaskListLogic Query quota task list
|
||||
func NewQueryQuotaTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryQuotaTaskListLogic {
|
||||
return &QueryQuotaTaskListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *QueryQuotaTaskListLogic) QueryQuotaTaskList(req *types.QueryQuotaTaskListRequest) (resp *types.QueryQuotaTaskListResponse, err error) {
|
||||
var data []*task.Task
|
||||
var count int64
|
||||
query := l.svcCtx.DB.Model(&task.Task{}).Where("`type` = ?", task.TypeQuota)
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size == 0 {
|
||||
req.Size = 20
|
||||
}
|
||||
|
||||
if req.Status != nil {
|
||||
query = query.Where("`status` = ?", *req.Status)
|
||||
}
|
||||
err = query.Count(&count).Offset((req.Page - 1) * req.Size).Limit(req.Size).Order("created_at DESC").Find(&data).Error
|
||||
if err != nil {
|
||||
l.Errorf("[QueryQuotaTaskList] failed to get quota tasks: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var list []types.QuotaTask
|
||||
for _, item := range data {
|
||||
var scopeInfo task.QuotaScope
|
||||
if err = scopeInfo.Unmarshal([]byte(item.Scope)); err != nil {
|
||||
l.Errorf("[QueryQuotaTaskList] failed to unmarshal quota task scope: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
var contentInfo task.QuotaContent
|
||||
if err = contentInfo.Unmarshal([]byte(item.Content)); err != nil {
|
||||
l.Errorf("[QueryQuotaTaskList] failed to unmarshal quota task content: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.QuotaTask{
|
||||
Id: item.Id,
|
||||
Subscribers: scopeInfo.Subscribers,
|
||||
IsActive: scopeInfo.IsActive,
|
||||
StartTime: scopeInfo.StartTime,
|
||||
EndTime: scopeInfo.EndTime,
|
||||
ResetTraffic: contentInfo.ResetTraffic,
|
||||
Days: contentInfo.Days,
|
||||
GiftType: contentInfo.GiftType,
|
||||
GiftValue: contentInfo.GiftValue,
|
||||
Objects: scopeInfo.Objects,
|
||||
Status: uint8(item.Status),
|
||||
Total: int64(item.Total),
|
||||
Current: int64(item.Current),
|
||||
Errors: item.Errors,
|
||||
CreatedAt: item.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: item.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.QueryQuotaTaskListResponse{
|
||||
Total: count,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type QueryQuotaTaskPreCountLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewQueryQuotaTaskPreCountLogic Query quota task pre-count
|
||||
func NewQueryQuotaTaskPreCountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryQuotaTaskPreCountLogic {
|
||||
return &QueryQuotaTaskPreCountLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *QueryQuotaTaskPreCountLogic) QueryQuotaTaskPreCount(req *types.QueryQuotaTaskPreCountRequest) (resp *types.QueryQuotaTaskPreCountResponse, err error) {
|
||||
tx := l.svcCtx.DB.WithContext(l.ctx).Model(&user.Subscribe{})
|
||||
var count int64
|
||||
|
||||
if len(req.Subscribers) > 0 {
|
||||
tx = tx.Where("`subscribe_id` IN ?", req.Subscribers)
|
||||
}
|
||||
|
||||
if req.IsActive != nil && *req.IsActive {
|
||||
tx = tx.Where("`status` IN ?", []int64{0, 1, 2}) // 0: Pending 1: Active 2: Finished
|
||||
}
|
||||
if req.StartTime != 0 {
|
||||
start := time.UnixMilli(req.StartTime)
|
||||
tx = tx.Where("`start_time` <= ?", start)
|
||||
}
|
||||
if req.EndTime != 0 {
|
||||
end := time.UnixMilli(req.EndTime)
|
||||
tx = tx.Where("`expire_time` >= ?", end)
|
||||
}
|
||||
if err = tx.Count(&count).Error; err != nil {
|
||||
l.Errorf("[QueryQuotaTaskPreCount] count error: %v", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.QueryQuotaTaskPreCountResponse{
|
||||
Count: count,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"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 QueryQuotaTaskStatusLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewQueryQuotaTaskStatusLogic Query quota task status
|
||||
func NewQueryQuotaTaskStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryQuotaTaskStatusLogic {
|
||||
return &QueryQuotaTaskStatusLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *QueryQuotaTaskStatusLogic) QueryQuotaTaskStatus(req *types.QueryQuotaTaskStatusRequest) (resp *types.QueryQuotaTaskStatusResponse, err error) {
|
||||
var data *task.Task
|
||||
err = l.svcCtx.DB.Model(&task.Task{}).Where("id = ? AND `type` = ?", req.Id, task.TypeQuota).First(&data).Error
|
||||
if err != nil {
|
||||
l.Errorf("[QueryQuotaTaskStatus] failed to get quota task: %v", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " failed to get quota task: %v", err.Error())
|
||||
}
|
||||
return &types.QueryQuotaTaskStatusResponse{
|
||||
Status: uint8(data.Status),
|
||||
Current: int64(data.Current),
|
||||
Total: int64(data.Total),
|
||||
Errors: data.Errors,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package marketing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/task"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/email"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
)
|
||||
|
||||
type StopBatchSendEmailTaskLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewStopBatchSendEmailTaskLogic Stop a batch send email task
|
||||
func NewStopBatchSendEmailTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StopBatchSendEmailTaskLogic {
|
||||
return &StopBatchSendEmailTaskLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *StopBatchSendEmailTaskLogic) StopBatchSendEmailTask(req *types.StopBatchSendEmailTaskRequest) (err error) {
|
||||
if email.Manager != nil {
|
||||
email.Manager.RemoveWorker(req.Id)
|
||||
} else {
|
||||
logger.Error("[StopBatchSendEmailTaskLogic] email.Manager is nil, cannot stop task")
|
||||
}
|
||||
err = l.svcCtx.DB.Model(&task.Task{}).Where("id = ?", req.Id).Update("status", 2).Error
|
||||
|
||||
if err != nil {
|
||||
l.Errorf("failed to stop email task, error: %v", err)
|
||||
return xerr.NewErrCode(xerr.DatabaseUpdateError)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -55,10 +55,9 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
Token: random.KeyNew(8, 1),
|
||||
}
|
||||
err = l.svcCtx.PaymentModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||
|
||||
if req.Platform == "Stripe" {
|
||||
var cfg paymentModel.StripeConfig
|
||||
if err := cfg.Unmarshal(paymentMethod.Config); err != nil {
|
||||
if err = cfg.Unmarshal([]byte(paymentMethod.Config)); err != nil {
|
||||
l.Errorf("[CreatePaymentMethod] unmarshal stripe config error: %s", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "unmarshal stripe config error: %s", err.Error())
|
||||
}
|
||||
@@ -79,7 +78,8 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "create stripe webhook endpoint error: %s", err.Error())
|
||||
}
|
||||
cfg.WebhookSecret = endpoint.Secret
|
||||
paymentMethod.Config = cfg.Marshal()
|
||||
content, _ := cfg.Marshal()
|
||||
paymentMethod.Config = string(content)
|
||||
}
|
||||
if err = tx.Model(&paymentModel.Payment{}).Create(paymentMethod).Error; err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert payment method error: %s", err.Error())
|
||||
@@ -101,27 +101,36 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
func parsePaymentPlatformConfig(ctx context.Context, platform payment.Platform, config interface{}) string {
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse payment platform config error", logger.Field("platform", platform), logger.Field("config", config), logger.Field("error", err.Error()))
|
||||
logger.WithContext(ctx).Errorw("marshal config error", logger.Field("platform", platform), logger.Field("config", config), logger.Field("error", err.Error()))
|
||||
return ""
|
||||
}
|
||||
|
||||
// 通用处理函数
|
||||
handleConfig := func(name string, target interface {
|
||||
Unmarshal([]byte) error
|
||||
Marshal() ([]byte, error)
|
||||
}) string {
|
||||
if err = target.Unmarshal(data); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse "+name+" config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
return ""
|
||||
}
|
||||
content, err := target.Marshal()
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Errorw("marshal "+name+" config error", logger.Field("error", err.Error()))
|
||||
return ""
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
switch platform {
|
||||
case payment.Stripe:
|
||||
stripe := &paymentModel.StripeConfig{}
|
||||
if err := stripe.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse stripe config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return stripe.Marshal()
|
||||
return handleConfig("Stripe", &paymentModel.StripeConfig{})
|
||||
case payment.AlipayF2F:
|
||||
alipay := &paymentModel.AlipayF2FConfig{}
|
||||
if err := alipay.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse alipay config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return alipay.Marshal()
|
||||
return handleConfig("Alipay", &paymentModel.AlipayF2FConfig{})
|
||||
case payment.EPay:
|
||||
epay := &paymentModel.EPayConfig{}
|
||||
if err := epay.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse epay config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return epay.Marshal()
|
||||
return handleConfig("Epay", &paymentModel.EPayConfig{})
|
||||
case payment.CryptoSaaS:
|
||||
return handleConfig("CryptoSaaS", &paymentModel.CryptoSaaSConfig{})
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -55,17 +55,18 @@ func (l *GetPaymentMethodListLogic) GetPaymentMethodList(req *types.GetPaymentMe
|
||||
}
|
||||
}
|
||||
resp.List[i] = types.PaymentMethodDetail{
|
||||
Id: v.Id,
|
||||
Name: v.Name,
|
||||
Platform: v.Platform,
|
||||
Icon: v.Icon,
|
||||
Domain: v.Domain,
|
||||
Config: config,
|
||||
FeeMode: v.FeeMode,
|
||||
FeePercent: v.FeePercent,
|
||||
FeeAmount: v.FeeAmount,
|
||||
Enable: *v.Enable,
|
||||
NotifyURL: notifyUrl,
|
||||
Id: v.Id,
|
||||
Name: v.Name,
|
||||
Platform: v.Platform,
|
||||
Icon: v.Icon,
|
||||
Domain: v.Domain,
|
||||
Config: config,
|
||||
FeeMode: v.FeeMode,
|
||||
FeePercent: v.FeePercent,
|
||||
FeeAmount: v.FeeAmount,
|
||||
Enable: *v.Enable,
|
||||
NotifyURL: notifyUrl,
|
||||
Description: v.Description,
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -19,7 +19,7 @@ type UpdatePaymentMethodLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Update Payment Method
|
||||
// NewUpdatePaymentMethodLogic Update Payment Method
|
||||
func NewUpdatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePaymentMethodLogic {
|
||||
return &UpdatePaymentMethodLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
|
||||
@@ -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,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/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,14 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"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"
|
||||
"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,33 +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, 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
|
||||
}
|
||||
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
|
||||
|
||||
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
|
||||
|
||||
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/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 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/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,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
|
||||
}
|
||||
+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,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/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,13 +2,8 @@ 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/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
@@ -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,107 +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, 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
|
||||
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
ServerId: []int64{data.ServerId},
|
||||
Search: "",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,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: "",
|
||||
})
|
||||
}
|
||||
@@ -37,6 +37,7 @@ func (l *CreateSubscribeLogic) CreateSubscribe(req *types.CreateSubscribeRequest
|
||||
sub := &subscribe.Subscribe{
|
||||
Id: 0,
|
||||
Name: req.Name,
|
||||
Language: req.Language,
|
||||
Description: req.Description,
|
||||
UnitPrice: req.UnitPrice,
|
||||
UnitTime: req.UnitTime,
|
||||
@@ -47,9 +48,8 @@ func (l *CreateSubscribeLogic) CreateSubscribe(req *types.CreateSubscribeRequest
|
||||
SpeedLimit: req.SpeedLimit,
|
||||
DeviceLimit: req.DeviceLimit,
|
||||
Quota: req.Quota,
|
||||
GroupId: req.GroupId,
|
||||
ServerGroup: tool.Int64SliceToString(req.ServerGroup),
|
||||
Server: tool.Int64SliceToString(req.Server),
|
||||
Nodes: tool.Int64SliceToString(req.Nodes),
|
||||
NodeTags: tool.StringSliceToString(req.NodeTags),
|
||||
Show: req.Show,
|
||||
Sell: req.Sell,
|
||||
Sort: 0,
|
||||
|
||||
@@ -3,6 +3,7 @@ package subscribe
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
@@ -41,7 +42,7 @@ func (l *GetSubscribeDetailsLogic) GetSubscribeDetails(req *types.GetSubscribeDe
|
||||
l.Logger.Error("[GetSubscribeDetailsLogic] JSON unmarshal failed: ", logger.Field("error", err.Error()), logger.Field("discount", sub.Discount))
|
||||
}
|
||||
}
|
||||
resp.Server = tool.StringToInt64Slice(sub.Server)
|
||||
resp.ServerGroup = tool.StringToInt64Slice(sub.ServerGroup)
|
||||
resp.Nodes = tool.StringToInt64Slice(sub.Nodes)
|
||||
resp.NodeTags = strings.Split(sub.NodeTags, ",")
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package subscribe
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/subscribe"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
@@ -28,7 +30,12 @@ func NewGetSubscribeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
|
||||
}
|
||||
|
||||
func (l *GetSubscribeListLogic) GetSubscribeList(req *types.GetSubscribeListRequest) (resp *types.GetSubscribeListResponse, err error) {
|
||||
total, list, err := l.svcCtx.SubscribeModel.QuerySubscribeListByPage(l.ctx, int(req.Page), int(req.Size), req.GroupId, req.Search)
|
||||
total, list, err := l.svcCtx.SubscribeModel.FilterList(l.ctx, &subscribe.FilterParams{
|
||||
Page: int(req.Page),
|
||||
Size: int(req.Size),
|
||||
Language: req.Language,
|
||||
Search: req.Search,
|
||||
})
|
||||
if err != nil {
|
||||
l.Logger.Error("[GetSubscribeListLogic] get subscribe list failed: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get subscribe list failed: %v", err.Error())
|
||||
@@ -47,15 +54,8 @@ func (l *GetSubscribeListLogic) GetSubscribeList(req *types.GetSubscribeListRequ
|
||||
l.Logger.Error("[GetSubscribeListLogic] JSON unmarshal failed: ", logger.Field("error", err.Error()), logger.Field("discount", item.Discount))
|
||||
}
|
||||
}
|
||||
servers, err := l.svcCtx.ServerModel.FindServerListByGroupIds(l.ctx, sub.ServerGroup)
|
||||
if err != nil {
|
||||
l.Errorw("[QuerySubscribeListLogic] FindServerListByGroupIds error", logger.Field("error", err.Error()))
|
||||
sub.ServerCount = 0
|
||||
} else {
|
||||
sub.ServerCount = int64(len(servers))
|
||||
}
|
||||
sub.Server = tool.StringToInt64Slice(item.Server)
|
||||
sub.ServerGroup = tool.StringToInt64Slice(item.ServerGroup)
|
||||
sub.Nodes = tool.StringToInt64Slice(item.Nodes)
|
||||
sub.NodeTags = strings.Split(item.NodeTags, ",")
|
||||
resultList = append(resultList, sub)
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ func (l *GetSubscribeListLogic) GetSubscribeList(req *types.GetSubscribeListRequ
|
||||
}
|
||||
|
||||
for i, item := range resultList {
|
||||
if subscribe, ok := subscribeMaps[item.Id]; ok {
|
||||
resultList[i].Sold = subscribe
|
||||
if sub, ok := subscribeMaps[item.Id]; ok {
|
||||
resultList[i].Sold = sub
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package subscribe
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/subscribe"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
@@ -40,7 +41,11 @@ func (l *SubscribeSortLogic) SubscribeSort(req *types.SubscribeSortRequest) erro
|
||||
l.Logger.Error("[SubscribeSortLogic] query subscribe list by ids error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query subscribe list by ids error: %v", err.Error())
|
||||
}
|
||||
subs, err := l.svcCtx.SubscribeModel.QuerySubscribeListByIds(l.ctx, ids)
|
||||
_, subs, err := l.svcCtx.SubscribeModel.FilterList(l.ctx, &subscribe.FilterParams{
|
||||
Page: 1,
|
||||
Size: 9999,
|
||||
Ids: ids,
|
||||
})
|
||||
if err != nil {
|
||||
l.Logger.Error("[SubscribeSortLogic] query subscribe list by ids error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query subscribe list by ids error: %v", err.Error())
|
||||
|
||||
@@ -45,6 +45,7 @@ func (l *UpdateSubscribeLogic) UpdateSubscribe(req *types.UpdateSubscribeRequest
|
||||
sub := &subscribe.Subscribe{
|
||||
Id: req.Id,
|
||||
Name: req.Name,
|
||||
Language: req.Language,
|
||||
Description: req.Description,
|
||||
UnitPrice: req.UnitPrice,
|
||||
UnitTime: req.UnitTime,
|
||||
@@ -55,9 +56,8 @@ func (l *UpdateSubscribeLogic) UpdateSubscribe(req *types.UpdateSubscribeRequest
|
||||
SpeedLimit: req.SpeedLimit,
|
||||
DeviceLimit: req.DeviceLimit,
|
||||
Quota: req.Quota,
|
||||
GroupId: req.GroupId,
|
||||
ServerGroup: tool.Int64SliceToString(req.ServerGroup),
|
||||
Server: tool.Int64SliceToString(req.Server),
|
||||
Nodes: tool.Int64SliceToString(req.Nodes),
|
||||
NodeTags: tool.StringSliceToString(req.NodeTags),
|
||||
Show: req.Show,
|
||||
Sell: req.Sell,
|
||||
Sort: req.Sort,
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/application"
|
||||
"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 CreateApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewCreateApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateApplicationLogic {
|
||||
return &CreateApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateApplicationLogic) CreateApplication(req *types.CreateApplicationRequest) error {
|
||||
var ios []application.ApplicationVersion
|
||||
if len(req.Platform.IOS) > 0 {
|
||||
for _, ios_ := range req.Platform.IOS {
|
||||
ios = append(ios, application.ApplicationVersion{
|
||||
Url: ios_.Url,
|
||||
Version: ios_.Version,
|
||||
Platform: "ios",
|
||||
IsDefault: ios_.IsDefault,
|
||||
Description: ios_.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var mac []application.ApplicationVersion
|
||||
if len(req.Platform.MacOS) > 0 {
|
||||
for _, mac_ := range req.Platform.MacOS {
|
||||
mac = append(mac, application.ApplicationVersion{
|
||||
Url: mac_.Url,
|
||||
Version: mac_.Version,
|
||||
Platform: "macos",
|
||||
IsDefault: mac_.IsDefault,
|
||||
Description: mac_.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var linux []application.ApplicationVersion
|
||||
if len(req.Platform.Linux) > 0 {
|
||||
for _, linux_ := range req.Platform.Linux {
|
||||
linux = append(linux, application.ApplicationVersion{
|
||||
Url: linux_.Url,
|
||||
Version: linux_.Version,
|
||||
Platform: "linux",
|
||||
IsDefault: linux_.IsDefault,
|
||||
Description: linux_.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var android []application.ApplicationVersion
|
||||
if len(req.Platform.Android) > 0 {
|
||||
for _, android_ := range req.Platform.Android {
|
||||
android = append(android, application.ApplicationVersion{
|
||||
Url: android_.Url,
|
||||
Version: android_.Version,
|
||||
Platform: "android",
|
||||
IsDefault: android_.IsDefault,
|
||||
Description: android_.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var windows []application.ApplicationVersion
|
||||
if len(req.Platform.Windows) > 0 {
|
||||
for _, windows_ := range req.Platform.Windows {
|
||||
windows = append(windows, application.ApplicationVersion{
|
||||
Url: windows_.Url,
|
||||
Version: windows_.Version,
|
||||
Platform: "windows",
|
||||
IsDefault: windows_.IsDefault,
|
||||
Description: windows_.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var harmony []application.ApplicationVersion
|
||||
if len(req.Platform.Harmony) > 0 {
|
||||
for _, harmony_ := range req.Platform.Harmony {
|
||||
harmony = append(harmony, application.ApplicationVersion{
|
||||
Url: harmony_.Url,
|
||||
Version: harmony_.Version,
|
||||
Platform: "harmony",
|
||||
IsDefault: harmony_.IsDefault,
|
||||
Description: harmony_.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
var applicationVersions []application.ApplicationVersion
|
||||
applicationVersions = append(applicationVersions, ios...)
|
||||
applicationVersions = append(applicationVersions, mac...)
|
||||
applicationVersions = append(applicationVersions, linux...)
|
||||
applicationVersions = append(applicationVersions, android...)
|
||||
applicationVersions = append(applicationVersions, windows...)
|
||||
applicationVersions = append(applicationVersions, harmony...)
|
||||
app := application.Application{
|
||||
Name: req.Name,
|
||||
Icon: req.Icon,
|
||||
SubscribeType: req.SubscribeType,
|
||||
ApplicationVersions: applicationVersions,
|
||||
}
|
||||
err := l.svcCtx.ApplicationModel.Insert(l.ctx, &app)
|
||||
if err != nil {
|
||||
l.Errorw("[CreateApplicationLogic] create application error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create application error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/application"
|
||||
"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 CreateApplicationVersionLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Create application version
|
||||
func NewCreateApplicationVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateApplicationVersionLogic {
|
||||
return &CreateApplicationVersionLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateApplicationVersionLogic) CreateApplicationVersion(req *types.CreateApplicationVersionRequest) error {
|
||||
create := &application.ApplicationVersion{
|
||||
Url: req.Url,
|
||||
Platform: req.Platform,
|
||||
Version: req.Version,
|
||||
Description: req.Description,
|
||||
IsDefault: req.IsDefault,
|
||||
ApplicationId: req.ApplicationId,
|
||||
}
|
||||
err := l.svcCtx.ApplicationModel.InsertVersion(l.ctx, create)
|
||||
if err != nil {
|
||||
l.Errorw("[CreateApplicationVersion] create application version error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create application version error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package system
|
||||
|
||||
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 DeleteApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewDeleteApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteApplicationLogic {
|
||||
return &DeleteApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteApplicationLogic) DeleteApplication(req *types.DeleteApplicationRequest) error {
|
||||
// delete application
|
||||
err := l.svcCtx.ApplicationModel.Delete(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteApplicationLogic] delete application error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete application error: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package system
|
||||
|
||||
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 DeleteApplicationVersionLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Delete application
|
||||
func NewDeleteApplicationVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteApplicationVersionLogic {
|
||||
return &DeleteApplicationVersionLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteApplicationVersionLogic) DeleteApplicationVersion(req *types.DeleteApplicationVersionRequest) error {
|
||||
// delete application
|
||||
err := l.svcCtx.ApplicationModel.DeleteVersion(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteApplicationVersion] delete application version error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete application version error: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type GetApplicationConfigLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// get application config
|
||||
func NewGetApplicationConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetApplicationConfigLogic {
|
||||
return &GetApplicationConfigLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetApplicationConfigLogic) GetApplicationConfig() (resp *types.ApplicationConfig, err error) {
|
||||
resp = &types.ApplicationConfig{}
|
||||
appConfig, err := l.svcCtx.ApplicationModel.FindOneConfig(l.ctx, 1)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
l.Errorw("[GetApplicationConfig] Database Error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get app config error: %v", err.Error())
|
||||
}
|
||||
resp.AppId = appConfig.AppId
|
||||
resp.EncryptionKey = appConfig.EncryptionKey
|
||||
resp.EncryptionMethod = appConfig.EncryptionMethod
|
||||
resp.Domains = strings.Split(appConfig.Domains, ";")
|
||||
resp.StartupPicture = appConfig.StartupPicture
|
||||
resp.StartupPictureSkipTime = appConfig.StartupPictureSkipTime
|
||||
return
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/application"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type GetApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get application
|
||||
func NewGetApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetApplicationLogic {
|
||||
return &GetApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetApplicationLogic) GetApplication() (resp *types.ApplicationResponse, err error) {
|
||||
resp = &types.ApplicationResponse{}
|
||||
var applications []*application.Application
|
||||
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(tx *gorm.DB) (err error) {
|
||||
return tx.Model(applications).Preload("ApplicationVersions").Find(&applications).Error
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[GetApplicationLogic] get application error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get application error: %v", err.Error())
|
||||
}
|
||||
|
||||
if len(applications) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
for _, app := range applications {
|
||||
applicationResponse := types.ApplicationResponseInfo{
|
||||
Id: app.Id,
|
||||
Name: app.Name,
|
||||
Icon: app.Icon,
|
||||
Description: app.Description,
|
||||
SubscribeType: app.SubscribeType,
|
||||
}
|
||||
applicationVersions := app.ApplicationVersions
|
||||
if len(applicationVersions) != 0 {
|
||||
for _, applicationVersion := range applicationVersions {
|
||||
switch applicationVersion.Platform {
|
||||
case "ios":
|
||||
applicationResponse.Platform.IOS = append(applicationResponse.Platform.IOS, &types.ApplicationVersion{
|
||||
Id: applicationVersion.Id,
|
||||
Url: applicationVersion.Url,
|
||||
Version: applicationVersion.Version,
|
||||
IsDefault: applicationVersion.IsDefault,
|
||||
Description: applicationVersion.Description,
|
||||
})
|
||||
case "macos":
|
||||
applicationResponse.Platform.MacOS = append(applicationResponse.Platform.MacOS, &types.ApplicationVersion{
|
||||
Id: applicationVersion.Id,
|
||||
Url: applicationVersion.Url,
|
||||
Version: applicationVersion.Version,
|
||||
IsDefault: applicationVersion.IsDefault,
|
||||
Description: applicationVersion.Description,
|
||||
})
|
||||
case "linux":
|
||||
applicationResponse.Platform.Linux = append(applicationResponse.Platform.Linux, &types.ApplicationVersion{
|
||||
Id: applicationVersion.Id,
|
||||
Url: applicationVersion.Url,
|
||||
Version: applicationVersion.Version,
|
||||
IsDefault: applicationVersion.IsDefault,
|
||||
Description: applicationVersion.Description,
|
||||
})
|
||||
case "android":
|
||||
applicationResponse.Platform.Android = append(applicationResponse.Platform.Android, &types.ApplicationVersion{
|
||||
Id: applicationVersion.Id,
|
||||
Url: applicationVersion.Url,
|
||||
Version: applicationVersion.Version,
|
||||
IsDefault: applicationVersion.IsDefault,
|
||||
Description: applicationVersion.Description,
|
||||
})
|
||||
case "windows":
|
||||
applicationResponse.Platform.Windows = append(applicationResponse.Platform.Windows, &types.ApplicationVersion{
|
||||
Id: applicationVersion.Id,
|
||||
Url: applicationVersion.Url,
|
||||
Version: applicationVersion.Version,
|
||||
IsDefault: applicationVersion.IsDefault,
|
||||
Description: applicationVersion.Description,
|
||||
})
|
||||
case "harmony":
|
||||
applicationResponse.Platform.Harmony = append(applicationResponse.Platform.Harmony, &types.ApplicationVersion{
|
||||
Id: applicationVersion.Id,
|
||||
Url: applicationVersion.Url,
|
||||
Version: applicationVersion.Version,
|
||||
IsDefault: applicationVersion.IsDefault,
|
||||
Description: applicationVersion.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
resp.Applications = append(resp.Applications, applicationResponse)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -2,7 +2,8 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/perfect-panel/server/internal/config"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
@@ -26,15 +27,45 @@ func NewGetNodeConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get
|
||||
}
|
||||
|
||||
func (l *GetNodeConfigLogic) GetNodeConfig() (*types.NodeConfig, error) {
|
||||
resp := &types.NodeConfig{}
|
||||
|
||||
// get server config from db
|
||||
configs, err := l.svcCtx.SystemModel.GetNodeConfig(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeConfigLogic] GetNodeConfig get server config error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetNodeConfig get server config error: %v", err.Error())
|
||||
}
|
||||
// reflect to response
|
||||
tool.SystemConfigSliceReflectToStruct(configs, resp)
|
||||
return resp, nil
|
||||
var dbConfig config.NodeDBConfig
|
||||
tool.SystemConfigSliceReflectToStruct(configs, &dbConfig)
|
||||
c := &types.NodeConfig{
|
||||
NodeSecret: dbConfig.NodeSecret,
|
||||
NodePullInterval: dbConfig.NodePullInterval,
|
||||
NodePushInterval: dbConfig.NodePushInterval,
|
||||
IPStrategy: dbConfig.IPStrategy,
|
||||
TrafficReportThreshold: dbConfig.TrafficReportThreshold,
|
||||
}
|
||||
|
||||
if dbConfig.DNS != "" {
|
||||
var dns []types.NodeDNS
|
||||
err = json.Unmarshal([]byte(dbConfig.DNS), &dns)
|
||||
if err != nil {
|
||||
logger.Errorf("[Node] Unmarshal DNS config error: %s", err.Error())
|
||||
panic(err)
|
||||
}
|
||||
c.DNS = dns
|
||||
}
|
||||
if dbConfig.Block != "" {
|
||||
var block []string
|
||||
_ = json.Unmarshal([]byte(dbConfig.Block), &block)
|
||||
c.Block = tool.RemoveDuplicateElements(block...)
|
||||
}
|
||||
if dbConfig.Outbound != "" {
|
||||
var outbound []types.NodeOutbound
|
||||
err = json.Unmarshal([]byte(dbConfig.Outbound), &outbound)
|
||||
if err != nil {
|
||||
logger.Errorf("[Node] Unmarshal Outbound config error: %s", err.Error())
|
||||
panic(err)
|
||||
}
|
||||
c.Outbound = outbound
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/subscribeType"
|
||||
"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 GetSubscribeTypeLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logger.Logger
|
||||
}
|
||||
|
||||
func NewGetSubscribeTypeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSubscribeTypeLogic {
|
||||
return &GetSubscribeTypeLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logger.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetSubscribeTypeLogic) GetSubscribeType() (resp *types.SubscribeType, err error) {
|
||||
var list []*subscribeType.SubscribeType
|
||||
err = l.svcCtx.DB.Model(&subscribeType.SubscribeType{}).Find(&list).Error
|
||||
if err != nil {
|
||||
l.Errorw("[GetSubscribeType] get subscribe type failed", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get subscribe type failed: %v", err)
|
||||
}
|
||||
typeList := make([]string, 0)
|
||||
for _, item := range list {
|
||||
typeList = append(typeList, item.Name)
|
||||
}
|
||||
return &types.SubscribeType{
|
||||
SubscribeTypes: typeList,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PreViewNodeMultiplierLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// PreView Node Multiplier
|
||||
func NewPreViewNodeMultiplierLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PreViewNodeMultiplierLogic {
|
||||
return &PreViewNodeMultiplierLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *PreViewNodeMultiplierLogic) PreViewNodeMultiplier() (resp *types.PreViewNodeMultiplierResponse, err error) {
|
||||
now := time.Now()
|
||||
ratio := l.svcCtx.NodeMultiplierManager.GetMultiplier(now)
|
||||
return &types.PreViewNodeMultiplierResponse{
|
||||
Ratio: ratio,
|
||||
CurrentTime: now.Format("2006-01-02 15:04:05"),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/application"
|
||||
"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 UpdateApplicationConfigLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// update application config
|
||||
func NewUpdateApplicationConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationConfigLogic {
|
||||
return &UpdateApplicationConfigLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateApplicationConfigLogic) UpdateApplicationConfig(req *types.ApplicationConfig) error {
|
||||
err := l.svcCtx.ApplicationModel.UpdateConfig(l.ctx, &application.ApplicationConfig{
|
||||
Id: 1,
|
||||
AppId: req.AppId,
|
||||
EncryptionKey: req.EncryptionKey,
|
||||
EncryptionMethod: req.EncryptionMethod,
|
||||
Domains: strings.Join(req.Domains, ";"),
|
||||
StartupPicture: req.StartupPicture,
|
||||
StartupPictureSkipTime: req.StartupPictureSkipTime,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateApplicationConfig] Database Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update app config error: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/application"
|
||||
"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/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
)
|
||||
|
||||
type UpdateApplicationLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUpdateApplicationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationLogic {
|
||||
return &UpdateApplicationLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateApplicationLogic) UpdateApplication(req *types.UpdateApplicationRequest) error {
|
||||
|
||||
// find application
|
||||
app, err := l.svcCtx.ApplicationModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateApplication] find application error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find application error: %v", err.Error())
|
||||
}
|
||||
app.Name = req.Name
|
||||
app.Icon = req.Icon
|
||||
app.SubscribeType = req.SubscribeType
|
||||
app.Description = req.Description
|
||||
|
||||
var ios []application.ApplicationVersion
|
||||
if len(req.Platform.IOS) > 0 {
|
||||
for _, ios_ := range req.Platform.IOS {
|
||||
ios = append(ios, application.ApplicationVersion{
|
||||
Url: ios_.Url,
|
||||
Version: ios_.Version,
|
||||
Platform: "ios",
|
||||
IsDefault: ios_.IsDefault,
|
||||
Description: ios_.Description,
|
||||
ApplicationId: app.Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var mac []application.ApplicationVersion
|
||||
if len(req.Platform.MacOS) > 0 {
|
||||
for _, mac_ := range req.Platform.MacOS {
|
||||
mac = append(mac, application.ApplicationVersion{
|
||||
Url: mac_.Url,
|
||||
Version: mac_.Version,
|
||||
Platform: "macos",
|
||||
IsDefault: mac_.IsDefault,
|
||||
Description: mac_.Description,
|
||||
ApplicationId: app.Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var linux []application.ApplicationVersion
|
||||
if len(req.Platform.Linux) > 0 {
|
||||
for _, linux_ := range req.Platform.Linux {
|
||||
linux = append(linux, application.ApplicationVersion{
|
||||
Url: linux_.Url,
|
||||
Version: linux_.Version,
|
||||
Platform: "linux",
|
||||
IsDefault: linux_.IsDefault,
|
||||
Description: linux_.Description,
|
||||
ApplicationId: app.Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var android []application.ApplicationVersion
|
||||
if len(req.Platform.Android) > 0 {
|
||||
for _, android_ := range req.Platform.Android {
|
||||
android = append(android, application.ApplicationVersion{
|
||||
Url: android_.Url,
|
||||
Version: android_.Version,
|
||||
Platform: "android",
|
||||
IsDefault: android_.IsDefault,
|
||||
Description: android_.Description,
|
||||
ApplicationId: app.Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var windows []application.ApplicationVersion
|
||||
if len(req.Platform.Windows) > 0 {
|
||||
for _, windows_ := range req.Platform.Windows {
|
||||
windows = append(windows, application.ApplicationVersion{
|
||||
Url: windows_.Url,
|
||||
Version: windows_.Version,
|
||||
Platform: "windows",
|
||||
IsDefault: windows_.IsDefault,
|
||||
Description: windows_.Description,
|
||||
ApplicationId: app.Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var harmony []application.ApplicationVersion
|
||||
if len(req.Platform.Harmony) > 0 {
|
||||
for _, harmony_ := range req.Platform.Harmony {
|
||||
harmony = append(harmony, application.ApplicationVersion{
|
||||
Url: harmony_.Url,
|
||||
Version: harmony_.Version,
|
||||
Platform: "harmony",
|
||||
IsDefault: harmony_.IsDefault,
|
||||
Description: harmony_.Description,
|
||||
ApplicationId: app.Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
var applicationVersions []application.ApplicationVersion
|
||||
applicationVersions = append(applicationVersions, ios...)
|
||||
applicationVersions = append(applicationVersions, mac...)
|
||||
applicationVersions = append(applicationVersions, linux...)
|
||||
applicationVersions = append(applicationVersions, android...)
|
||||
applicationVersions = append(applicationVersions, windows...)
|
||||
applicationVersions = append(applicationVersions, harmony...)
|
||||
app.ApplicationVersions = applicationVersions
|
||||
err = l.svcCtx.ApplicationModel.Transaction(l.ctx, func(db *gorm.DB) error {
|
||||
|
||||
if err = db.Where("application_id = ?", app.Id).Delete(&application.ApplicationVersion{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err = db.Create(&applicationVersions).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Save(app).Error
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateApplication] update application error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update application error: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package system
|
||||
|
||||
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 UpdateApplicationVersionLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Update application version
|
||||
func NewUpdateApplicationVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApplicationVersionLogic {
|
||||
return &UpdateApplicationVersionLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateApplicationVersionLogic) UpdateApplicationVersion(req *types.UpdateApplicationVersionRequest) error {
|
||||
// find application
|
||||
app, err := l.svcCtx.ApplicationModel.FindOneVersion(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateApplicationVersion] find application version error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find application error: %v", err.Error())
|
||||
}
|
||||
app.Url = req.Url
|
||||
app.Version = req.Version
|
||||
app.Description = req.Description
|
||||
app.IsDefault = req.IsDefault
|
||||
err = l.svcCtx.ApplicationModel.UpdateVersion(l.ctx, app)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateApplicationVersion] update application version error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update application version error: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -41,7 +41,9 @@ func (l *UpdateNodeConfigLogic) UpdateNodeConfig(req *types.NodeConfig) error {
|
||||
// Get the field name
|
||||
fieldName := t.Field(i).Name
|
||||
// Get the field value to string
|
||||
fieldValue := tool.ConvertValueToString(v.Field(i))
|
||||
var fieldValue string
|
||||
|
||||
fieldValue = tool.ConvertValueToString(v.Field(i))
|
||||
// Update the server config
|
||||
err = db.Model(&system.System{}).Where("`category` = 'server' and `key` = ?", fieldName).Update("value", fieldValue).Error
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package tool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/constant"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type GetVersionLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewGetVersionLogic Get Version
|
||||
func NewGetVersionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetVersionLogic {
|
||||
return &GetVersionLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetVersionLogic) GetVersion() (resp *types.VersionResponse, err error) {
|
||||
version := constant.Version
|
||||
buildTime := constant.BuildTime
|
||||
|
||||
// Normalize unknown values
|
||||
if version == "unknown version" {
|
||||
version = "unknown"
|
||||
}
|
||||
if buildTime == "unknown time" {
|
||||
buildTime = "unknown"
|
||||
}
|
||||
|
||||
// Format version based on whether it starts with 'v'
|
||||
var formattedVersion string
|
||||
if len(version) > 0 && version[0] == 'v' {
|
||||
formattedVersion = fmt.Sprintf("%s(%s)", version[1:], buildTime)
|
||||
} else {
|
||||
formattedVersion = fmt.Sprintf("%s(%s) Develop", version, buildTime)
|
||||
}
|
||||
|
||||
return &types.VersionResponse{
|
||||
Version: formattedVersion,
|
||||
}, nil
|
||||
}
|
||||
@@ -39,10 +39,12 @@ func (l *CreateUserLogic) CreateUser(req *types.CreateUserRequest) error {
|
||||
}
|
||||
pwd := tool.EncodePassWord(req.Password)
|
||||
newUser := &user.User{
|
||||
Password: pwd,
|
||||
ReferCode: req.ReferCode,
|
||||
Balance: req.Balance,
|
||||
IsAdmin: &req.IsAdmin,
|
||||
Password: pwd,
|
||||
ReferralPercentage: req.ReferralPercentage,
|
||||
OnlyFirstPurchase: &req.OnlyFirstPurchase,
|
||||
ReferCode: req.ReferCode,
|
||||
Balance: req.Balance,
|
||||
IsAdmin: &req.IsAdmin,
|
||||
}
|
||||
var ams []user.AuthMethods
|
||||
|
||||
|
||||
@@ -77,5 +77,9 @@ func (l *CreateUserSubscribeLogic) CreateUserSubscribe(req *types.CreateUserSubs
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "UpdateUserCache error: %v", err.Error())
|
||||
}
|
||||
|
||||
err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSub.SubscribeId)
|
||||
if err != nil {
|
||||
logger.Errorw("ClearSubscribe error", logger.Field("error", err.Error()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,10 +26,27 @@ func NewDeleteUserSubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func (l *DeleteUserSubscribeLogic) DeleteUserSubscribe(req *types.DeleteUserSubscribeRequest) error {
|
||||
err := l.svcCtx.UserModel.DeleteSubscribeById(l.ctx, req.UserSubscribeId)
|
||||
// find user subscribe by ID
|
||||
userSubscribe, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId)
|
||||
if err != nil {
|
||||
l.Errorw("failed to find user subscribe", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to find user subscribe: %v", err.Error())
|
||||
}
|
||||
|
||||
err = l.svcCtx.UserModel.DeleteSubscribeById(l.ctx, req.UserSubscribeId)
|
||||
if err != nil {
|
||||
l.Errorw("failed to delete user subscribe", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "failed to delete user subscribe: %v", err.Error())
|
||||
}
|
||||
// Clear user subscribe cache
|
||||
if err = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, userSubscribe); err != nil {
|
||||
l.Errorw("failed to clear user subscribe cache", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "failed to clear user subscribe cache: %v", err.Error())
|
||||
}
|
||||
// Clear subscribe cache
|
||||
if err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSubscribe.SubscribeId); err != nil {
|
||||
l.Errorw("failed to clear subscribe cache", logger.Field("error", err.Error()), logger.Field("subscribeId", userSubscribe.SubscribeId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "failed to clear subscribe cache: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (*types.Ge
|
||||
Search: req.Search,
|
||||
SubscribeId: req.SubscribeId,
|
||||
UserSubscribeId: req.UserSubscribeId,
|
||||
Order: "DESC",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetUserListLogic failed: %v", err.Error())
|
||||
@@ -40,20 +41,20 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (*types.Ge
|
||||
userRespList := make([]types.User, 0, len(list))
|
||||
|
||||
for _, item := range list {
|
||||
var user types.User
|
||||
tool.DeepCopy(&user, item)
|
||||
var u types.User
|
||||
tool.DeepCopy(&u, item)
|
||||
|
||||
// 处理 AuthMethods
|
||||
authMethods := make([]types.UserAuthMethod, len(user.AuthMethods)) // 直接创建目标 slice
|
||||
for i, method := range user.AuthMethods {
|
||||
authMethods := make([]types.UserAuthMethod, len(u.AuthMethods)) // 直接创建目标 slice
|
||||
for i, method := range u.AuthMethods {
|
||||
tool.DeepCopy(&authMethods[i], method)
|
||||
if method.AuthType == "mobile" {
|
||||
authMethods[i].AuthIdentifier = phone.FormatToInternational(method.AuthIdentifier)
|
||||
}
|
||||
}
|
||||
user.AuthMethods = authMethods
|
||||
u.AuthMethods = authMethods
|
||||
|
||||
userRespList = append(userRespList, user)
|
||||
userRespList = append(userRespList, u)
|
||||
}
|
||||
|
||||
return &types.GetUserListResponse{
|
||||
|
||||
@@ -3,11 +3,10 @@ package user
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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"
|
||||
)
|
||||
@@ -28,15 +27,34 @@ func NewGetUserLoginLogsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
|
||||
}
|
||||
|
||||
func (l *GetUserLoginLogsLogic) GetUserLoginLogs(req *types.GetUserLoginLogsRequest) (resp *types.GetUserLoginLogsResponse, err error) {
|
||||
data, total, err := l.svcCtx.UserModel.FilterLoginLogList(l.ctx, req.Page, req.Size, &user.LoginLogFilterParams{
|
||||
UserId: req.UserId,
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeLogin.Uint8(),
|
||||
ObjectID: req.UserId,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[GetUserLoginLogs] get user login logs failed", logger.Field("error", err.Error()), logger.Field("request", req))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get user login logs failed: %v", err.Error())
|
||||
}
|
||||
var list []types.UserLoginLog
|
||||
tool.DeepCopy(&list, data)
|
||||
|
||||
for _, datum := range data {
|
||||
var content log.Login
|
||||
if err = content.Unmarshal([]byte(datum.Content)); err != nil {
|
||||
l.Errorf("[GetUserLoginLogs] unmarshal login log content failed: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, types.UserLoginLog{
|
||||
Id: datum.Id,
|
||||
UserId: datum.ObjectID,
|
||||
LoginIP: content.LoginIP,
|
||||
UserAgent: content.UserAgent,
|
||||
Success: content.Success,
|
||||
Timestamp: datum.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetUserLoginLogsResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
|
||||
@@ -3,7 +3,7 @@ package user
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
@@ -28,10 +28,7 @@ func NewGetUserSubscribeLogsLogic(ctx context.Context, svcCtx *svc.ServiceContex
|
||||
}
|
||||
|
||||
func (l *GetUserSubscribeLogsLogic) GetUserSubscribeLogs(req *types.GetUserSubscribeLogsRequest) (resp *types.GetUserSubscribeLogsResponse, err error) {
|
||||
data, total, err := l.svcCtx.UserModel.FilterSubscribeLogList(l.ctx, req.Page, req.Size, &user.SubscribeLogFilterParams{
|
||||
UserSubscribeId: req.SubscribeId,
|
||||
UserId: req.UserId,
|
||||
})
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{})
|
||||
|
||||
if err != nil {
|
||||
l.Errorw("[GetUserSubscribeLogs] Get User Subscribe Logs Error:", logger.Field("err", err.Error()))
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"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 GetUserSubscribeResetTrafficLogsLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get user subcribe reset traffic logs
|
||||
func NewGetUserSubscribeResetTrafficLogsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserSubscribeResetTrafficLogsLogic {
|
||||
return &GetUserSubscribeResetTrafficLogsLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetUserSubscribeResetTrafficLogsLogic) GetUserSubscribeResetTrafficLogs(req *types.GetUserSubscribeResetTrafficLogsRequest) (resp *types.GetUserSubscribeResetTrafficLogsResponse, err error) {
|
||||
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Type: log.TypeResetSubscribe.Uint8(),
|
||||
ObjectID: req.UserSubscribeId,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorf("[ResetSubscribeTrafficLog] failed to filter system log: %v", err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FilterSystemLog failed, err: %v", err)
|
||||
}
|
||||
|
||||
var list []types.ResetSubscribeTrafficLog
|
||||
|
||||
for _, item := range data {
|
||||
var content log.ResetSubscribe
|
||||
if err = content.Unmarshal([]byte(item.Content)); err != nil {
|
||||
l.Errorf("[ResetSubscribeTrafficLog] failed to unmarshal log: %v", err)
|
||||
continue
|
||||
}
|
||||
list = append(list, types.ResetSubscribeTrafficLog{
|
||||
Id: item.Id,
|
||||
Type: content.Type,
|
||||
OrderNo: content.OrderNo,
|
||||
Timestamp: content.Timestamp,
|
||||
UserSubscribeId: item.ObjectID,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetUserSubscribeResetTrafficLogsResponse{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -31,11 +31,21 @@ func (l *UpdateUserAuthMethodLogic) UpdateUserAuthMethod(req *types.UpdateUserAu
|
||||
l.Errorw("Get user auth method error", logger.Field("error", err.Error()), logger.Field("userId", req.UserId), logger.Field("authType", req.AuthType))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Get user auth method error: %v", err.Error())
|
||||
}
|
||||
userInfo, err := l.svcCtx.UserModel.FindOne(l.ctx, req.UserId)
|
||||
if err != nil {
|
||||
l.Errorw("Get user info error", logger.Field("error", err.Error()), logger.Field("userId", req.UserId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Get user info error: %v", err.Error())
|
||||
}
|
||||
|
||||
method.AuthType = req.AuthType
|
||||
method.AuthIdentifier = req.AuthIdentifier
|
||||
if err = l.svcCtx.UserModel.UpdateUserAuthMethods(l.ctx, method); err != nil {
|
||||
l.Errorw("Update user auth method error", logger.Field("error", err.Error()), logger.Field("userId", req.UserId), logger.Field("authType", req.AuthType))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "Update user auth method error: %v", err.Error())
|
||||
}
|
||||
if err = l.svcCtx.UserModel.UpdateUserCache(l.ctx, userInfo); err != nil {
|
||||
l.Errorw("Update user cache error", logger.Field("error", err.Error()), logger.Field("userId", req.UserId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Update user cache error: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/log"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
@@ -37,17 +39,90 @@ func (l *UpdateUserBasicInfoLogic) UpdateUserBasicInfo(req *types.UpdateUserBasi
|
||||
|
||||
isDemo := strings.ToLower(os.Getenv("PPANEL_MODE")) == "demo"
|
||||
|
||||
tool.DeepCopy(userInfo, req)
|
||||
if req.Avatar != "" && !tool.IsValidImageSize(req.Avatar, 1024) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Invalid Image Size")
|
||||
}
|
||||
userInfo.Balance = req.Balance
|
||||
userInfo.GiftAmount = req.GiftAmount
|
||||
userInfo.Commission = req.Commission
|
||||
userInfo.Remark = req.Remark
|
||||
// 手动设置 IsAdmin 字段,因为类型不匹配(*bool vs bool)
|
||||
userInfo.IsAdmin = &req.IsAdmin
|
||||
userInfo.Enable = &req.Enable
|
||||
|
||||
if userInfo.Balance != req.Balance {
|
||||
change := req.Balance - userInfo.Balance
|
||||
balanceLog := log.Balance{
|
||||
Type: log.BalanceTypeAdjust,
|
||||
Amount: change,
|
||||
OrderNo: "",
|
||||
Balance: req.Balance,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
content, _ := balanceLog.Marshal()
|
||||
|
||||
err = l.svcCtx.LogModel.Insert(l.ctx, &log.SystemLog{
|
||||
Type: log.TypeBalance.Uint8(),
|
||||
Date: time.Now().Format(time.DateOnly),
|
||||
ObjectID: userInfo.Id,
|
||||
Content: string(content),
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateUserBasicInfoLogic] Insert Balance Log Error:", logger.Field("err", err.Error()), logger.Field("userId", req.UserId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "Insert Balance Log Error")
|
||||
}
|
||||
userInfo.Balance = req.Balance
|
||||
}
|
||||
|
||||
if userInfo.GiftAmount != req.GiftAmount {
|
||||
change := req.GiftAmount - userInfo.GiftAmount
|
||||
if change != 0 {
|
||||
var changeType uint16
|
||||
if userInfo.GiftAmount < req.GiftAmount {
|
||||
changeType = log.GiftTypeIncrease
|
||||
} else {
|
||||
changeType = log.GiftTypeReduce
|
||||
}
|
||||
giftLog := log.Gift{
|
||||
Type: changeType,
|
||||
Amount: change,
|
||||
Balance: req.GiftAmount,
|
||||
Remark: "Admin adjustment",
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
content, _ := giftLog.Marshal()
|
||||
// Add gift amount change log
|
||||
err = l.svcCtx.LogModel.Insert(l.ctx, &log.SystemLog{
|
||||
Type: log.TypeGift.Uint8(),
|
||||
Date: time.Now().Format(time.DateOnly),
|
||||
ObjectID: userInfo.Id,
|
||||
Content: string(content),
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateUserBasicInfoLogic] Insert Balance Log Error:", logger.Field("err", err.Error()), logger.Field("userId", req.UserId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "Insert Balance Log Error")
|
||||
}
|
||||
userInfo.GiftAmount = req.GiftAmount
|
||||
}
|
||||
}
|
||||
|
||||
if req.Commission != userInfo.Commission {
|
||||
|
||||
commentLog := log.Commission{
|
||||
Type: log.CommissionTypeAdjust,
|
||||
Amount: req.Commission - userInfo.Commission,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
content, _ := commentLog.Marshal()
|
||||
err = l.svcCtx.LogModel.Insert(l.ctx, &log.SystemLog{
|
||||
Type: log.TypeCommission.Uint8(),
|
||||
Date: time.Now().Format(time.DateOnly),
|
||||
ObjectID: userInfo.Id,
|
||||
Content: string(content),
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateUserBasicInfoLogic] Insert Commission Log Error:", logger.Field("err", err.Error()), logger.Field("userId", req.UserId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "Insert Commission Log Error")
|
||||
}
|
||||
userInfo.Commission = req.Commission
|
||||
}
|
||||
tool.DeepCopy(userInfo, req)
|
||||
userInfo.OnlyFirstPurchase = &req.OnlyFirstPurchase
|
||||
userInfo.ReferralPercentage = req.ReferralPercentage
|
||||
|
||||
if req.Password != "" {
|
||||
if userInfo.Id == 2 && isDemo {
|
||||
|
||||
@@ -28,7 +28,7 @@ func NewUpdateUserSubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func (l *UpdateUserSubscribeLogic) UpdateUserSubscribe(req *types.UpdateUserSubscribeRequest) error {
|
||||
userSub, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.UserSubscribeId)
|
||||
userSub, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId)
|
||||
if err != nil {
|
||||
l.Errorw("FindOneUserSubscribe failed:", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOneUserSubscribe failed: %v", err.Error())
|
||||
@@ -59,6 +59,15 @@ func (l *UpdateUserSubscribeLogic) UpdateUserSubscribe(req *types.UpdateUserSubs
|
||||
l.Errorw("UpdateSubscribe failed:", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "UpdateSubscribe failed: %v", err.Error())
|
||||
}
|
||||
|
||||
// Clear user subscribe cache
|
||||
if err = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, userSub); err != nil {
|
||||
l.Errorw("ClearSubscribeCache failed:", logger.Field("error", err.Error()), logger.Field("userSubscribeId", userSub.Id))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "ClearSubscribeCache failed: %v", err.Error())
|
||||
}
|
||||
// Clear subscribe cache
|
||||
if err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSub.SubscribeId); err != nil {
|
||||
l.Errorw("failed to clear subscribe cache", logger.Field("error", err.Error()), logger.Field("subscribeId", userSub.SubscribeId))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "failed to clear subscribe cache: %v", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user