refactor: 更新项目引用路径从perfect-panel/ppanel-server到perfect-panel/server
Build docker and publish / build (20.15.1) (push) Failing after 6m27s
Build docker and publish / build (20.15.1) (push) Failing after 6m27s
feat: 添加版本和构建时间变量 fix: 修正短信队列类型注释错误 style: 清理未使用的代码和测试文件 docs: 更新安装文档中的下载链接 chore: 迁移数据库脚本添加日志和订阅配置
This commit is contained in:
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/ads"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package ads
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package ads
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ package ads
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/ads"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/ads"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package announcement
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/announcement"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/announcement"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package announcement
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ package announcement
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/announcement"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/announcement"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type GetAnnouncementListLogic struct {
|
||||
|
||||
@@ -3,11 +3,11 @@ package announcement
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package announcement
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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,76 @@
|
||||
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) {
|
||||
enable := true
|
||||
_, servers, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
Preload: true,
|
||||
Enabled: &enable,
|
||||
})
|
||||
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
|
||||
}
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ type GetAuthMethodConfigLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get auth method config
|
||||
// NewGetAuthMethodConfigLogic Get auth method config
|
||||
func NewGetAuthMethodConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAuthMethodConfigLogic {
|
||||
return &GetAuthMethodConfigLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package authMethod
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/email"
|
||||
"github.com/perfect-panel/server/pkg/email"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type GetEmailPlatformLogic struct {
|
||||
|
||||
@@ -3,11 +3,11 @@ package authMethod
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/sms"
|
||||
"github.com/perfect-panel/server/pkg/sms"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type GetSmsPlatformLogic struct {
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/email"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"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"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/sms"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"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/sms"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/initialize"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/auth"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/email"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/sms"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/initialize"
|
||||
"github.com/perfect-panel/server/internal/model/auth"
|
||||
"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/sms"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -40,34 +40,32 @@ func (l *UpdateAuthMethodConfigLogic) UpdateAuthMethodConfig(req *types.UpdateAu
|
||||
|
||||
tool.DeepCopy(method, req)
|
||||
if req.Config != nil {
|
||||
if value, ok := req.Config.(map[string]interface{}); ok {
|
||||
if req.Method == "email" && value["verify_email_template"] == "" {
|
||||
value["verify_email_template"] = email.DefaultEmailVerifyTemplate
|
||||
}
|
||||
if req.Method == "email" && value["expiration_email_template"] == "" {
|
||||
value["expiration_email_template"] = email.DefaultExpirationEmailTemplate
|
||||
}
|
||||
if req.Method == "email" && value["maintenance_email_template"] == "" {
|
||||
value["maintenance_email_template"] = email.DefaultMaintenanceEmailTemplate
|
||||
}
|
||||
if req.Method == "email" && value["traffic_exceed_email_template"] == "" {
|
||||
value["traffic_exceed_email_template"] = email.DefaultTrafficExceedEmailTemplate
|
||||
}
|
||||
|
||||
if value["platform_config"] != nil {
|
||||
platformConfig, err := validatePlatformConfig(value["platform"].(string), value["platform_config"].(map[string]interface{}))
|
||||
if err != nil {
|
||||
l.Errorw("validate platform config failed", logger.Field("config", req.Config), logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "validate platform config failed: %v", err.Error())
|
||||
}
|
||||
req.Config.(map[string]interface{})["platform_config"] = platformConfig
|
||||
}
|
||||
_, exist := req.Config.(map[string]interface{})
|
||||
if !exist {
|
||||
req.Config = initializePlatformConfig(req.Method).(string)
|
||||
}
|
||||
if req.Method == "email" {
|
||||
configs, _ := json.Marshal(req.Config)
|
||||
emailConfig := new(auth.EmailAuthConfig)
|
||||
emailConfig.Unmarshal(string(configs))
|
||||
req.Config = emailConfig
|
||||
}
|
||||
|
||||
if req.Method == "mobile" {
|
||||
configs, _ := json.Marshal(req.Config)
|
||||
mobileConfig := new(auth.MobileAuthConfig)
|
||||
mobileConfig.Unmarshal(string(configs))
|
||||
req.Config = mobileConfig
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(req.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "marshal config failed: %v", err.Error())
|
||||
}
|
||||
method.Config = string(bytes)
|
||||
} else {
|
||||
// initialize platform config
|
||||
method.Config = initializePlatformConfig(req.Method).(string)
|
||||
}
|
||||
err = l.svcCtx.AuthModel.Update(l.ctx, method)
|
||||
if err != nil {
|
||||
@@ -94,6 +92,9 @@ func (l *UpdateAuthMethodConfigLogic) UpdateGlobal(method string) {
|
||||
if method == "mobile" {
|
||||
initialize.Mobile(l.svcCtx)
|
||||
}
|
||||
if method == "device" {
|
||||
initialize.Device(l.svcCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func validatePlatformConfig(platform string, cfg map[string]interface{}) (interface{}, error) {
|
||||
@@ -124,3 +125,26 @@ func validatePlatformConfig(platform string, cfg map[string]interface{}) (interf
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func initializePlatformConfig(platform string) interface{} {
|
||||
var result interface{}
|
||||
switch platform {
|
||||
case "email":
|
||||
result = new(auth.EmailAuthConfig).Marshal()
|
||||
case "mobile":
|
||||
result = new(auth.MobileAuthConfig).Marshal()
|
||||
case "apple":
|
||||
result = new(auth.AppleAuthConfig).Marshal()
|
||||
case "google":
|
||||
result = new(auth.GoogleAuthConfig).Marshal()
|
||||
case "github":
|
||||
result = new(auth.GithubAuthConfig).Marshal()
|
||||
case "facebook":
|
||||
result = new(auth.FacebookAuthConfig).Marshal()
|
||||
case "telegram":
|
||||
result = new(auth.TelegramAuthConfig).Marshal()
|
||||
case "device":
|
||||
result = new(auth.DeviceConfig).Marshal()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/sms"
|
||||
"github.com/perfect-panel/server/pkg/sms"
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
|
||||
@@ -2,12 +2,14 @@ package console
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
@@ -17,7 +19,7 @@ type QueryRevenueStatisticsLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Query revenue statistics
|
||||
// NewQueryRevenueStatisticsLogic Query revenue statistics
|
||||
func NewQueryRevenueStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryRevenueStatisticsLogic {
|
||||
return &QueryRevenueStatisticsLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
@@ -27,6 +29,9 @@ func NewQueryRevenueStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceCont
|
||||
}
|
||||
|
||||
func (l *QueryRevenueStatisticsLogic) QueryRevenueStatistics() (resp *types.RevenueStatisticsResponse, err error) {
|
||||
if strings.ToLower(os.Getenv("PPANEL_MODE")) == "demo" {
|
||||
return l.mockRevenueStatistics(), nil
|
||||
}
|
||||
|
||||
var today, monthly, all types.OrdersStatistics
|
||||
now := time.Now()
|
||||
@@ -45,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,
|
||||
@@ -56,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 {
|
||||
@@ -69,9 +92,79 @@ 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,
|
||||
All: all,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// mockRevenueStatistics is a mock function to simulate revenue statistics data.
|
||||
func (l *QueryRevenueStatisticsLogic) mockRevenueStatistics() *types.RevenueStatisticsResponse {
|
||||
now := time.Now()
|
||||
|
||||
// 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))
|
||||
baseAmount := int64(25000 + ((6 - i) * 3000) + ((6-i)%3)*8000)
|
||||
monthlyList[i] = types.OrdersStatistics{
|
||||
Date: dayDate.Format("2006-01-02"),
|
||||
AmountTotal: baseAmount,
|
||||
NewOrderAmount: int64(float64(baseAmount) * 0.68),
|
||||
RenewalOrderAmount: int64(float64(baseAmount) * 0.32),
|
||||
}
|
||||
}
|
||||
|
||||
// Generate monthly data for the past 6 months (oldest first)
|
||||
allList := make([]types.OrdersStatistics, 6)
|
||||
for i := 0; i < 6; i++ {
|
||||
monthDate := now.AddDate(0, -(5 - i), 0)
|
||||
baseAmount := int64(1800000 + ((5 - i) * 200000) + ((5-i)%2)*500000)
|
||||
allList[i] = types.OrdersStatistics{
|
||||
Date: monthDate.Format("2006-01"),
|
||||
AmountTotal: baseAmount,
|
||||
NewOrderAmount: int64(float64(baseAmount) * 0.68),
|
||||
RenewalOrderAmount: int64(float64(baseAmount) * 0.32),
|
||||
}
|
||||
}
|
||||
|
||||
return &types.RevenueStatisticsResponse{
|
||||
Today: types.OrdersStatistics{
|
||||
AmountTotal: 35888,
|
||||
NewOrderAmount: 22888,
|
||||
RenewalOrderAmount: 13000,
|
||||
},
|
||||
Monthly: types.OrdersStatistics{
|
||||
AmountTotal: 888888,
|
||||
NewOrderAmount: 588888,
|
||||
RenewalOrderAmount: 300000,
|
||||
List: monthlyList,
|
||||
},
|
||||
All: types.OrdersStatistics{
|
||||
AmountTotal: 12888888,
|
||||
NewOrderAmount: 8588888,
|
||||
RenewalOrderAmount: 4300000,
|
||||
List: allList,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,19 @@ package console
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/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 {
|
||||
@@ -25,116 +33,269 @@ func NewQueryServerTotalDataLogic(ctx context.Context, svcCtx *svc.ServiceContex
|
||||
}
|
||||
|
||||
func (l *QueryServerTotalDataLogic) QueryServerTotalData() (resp *types.ServerTotalDataResponse, err error) {
|
||||
resp = &types.ServerTotalDataResponse{
|
||||
ServerTrafficRankingToday: make([]types.ServerTrafficData, 0),
|
||||
ServerTrafficRankingYesterday: make([]types.ServerTrafficData, 0),
|
||||
UserTrafficRankingToday: make([]types.UserTrafficData, 0),
|
||||
UserTrafficRankingYesterday: make([]types.UserTrafficData, 0),
|
||||
|
||||
if strings.ToLower(os.Getenv("PPANEL_MODE")) == "demo" {
|
||||
return l.mockRevenueStatistics(), nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
now := time.Now()
|
||||
|
||||
// 获取所有节点在线用户
|
||||
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()))
|
||||
}
|
||||
resp.OnlineUserIPs = int64(len(allNodeOnlineUser))
|
||||
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
|
||||
|
||||
// 获取所有节点今日上传下载流量
|
||||
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()))
|
||||
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())
|
||||
}
|
||||
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()))
|
||||
var userTodayTrafficRanking []types.UserTrafficData
|
||||
for _, item := range todayTop10User {
|
||||
userTodayTrafficRanking = append(userTodayTrafficRanking, types.UserTrafficData{
|
||||
SID: item.SubscribeId,
|
||||
Upload: item.Upload,
|
||||
Download: item.Download,
|
||||
})
|
||||
}
|
||||
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()))
|
||||
|
||||
// query yesterday user traffic rank log
|
||||
yesterday := todayStart.Add(-24 * time.Hour).Format(time.DateOnly)
|
||||
|
||||
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)
|
||||
}
|
||||
if len(nodeTrafficRankingToday) > 0 {
|
||||
var serverTrafficData []types.ServerTrafficData
|
||||
for _, rank := range nodeTrafficRankingToday {
|
||||
serverInfo, err := l.svcCtx.ServerModel.FindOne(l.ctx, rank.ID)
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
// 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()))
|
||||
l.Errorw("[QueryServerTotalDataLogic] OnlineUserSubscribeGlobal error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "OnlineUserSubscribeGlobal error: %v", err)
|
||||
}
|
||||
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,
|
||||
})
|
||||
|
||||
// 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)
|
||||
}
|
||||
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,
|
||||
})
|
||||
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.UserTrafficRankingYesterday = userTrafficData
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (l *QueryServerTotalDataLogic) mockRevenueStatistics() *types.ServerTotalDataResponse {
|
||||
now := time.Now()
|
||||
|
||||
// Generate server traffic ranking data for today (top 10)
|
||||
serverTrafficToday := make([]types.ServerTrafficData, 10)
|
||||
serverNames := []string{"香港-01", "美国-洛杉矶", "日本-东京", "新加坡-01", "韩国-首尔", "台湾-01", "德国-法兰克福", "英国-伦敦", "加拿大-多伦多", "澳洲-悉尼"}
|
||||
for i := 0; i < 10; i++ {
|
||||
upload := int64(500000000 + (i * 100000000) + (i%3)*200000000) // 500MB - 1.5GB
|
||||
download := int64(2000000000 + (i * 300000000) + (i%4)*500000000) // 2GB - 8GB
|
||||
serverTrafficToday[i] = types.ServerTrafficData{
|
||||
ServerId: int64(i + 1),
|
||||
Name: serverNames[i],
|
||||
Upload: upload,
|
||||
Download: download,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate server traffic ranking data for yesterday (top 10)
|
||||
serverTrafficYesterday := make([]types.ServerTrafficData, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
upload := int64(480000000 + (i * 95000000) + (i%3)*180000000)
|
||||
download := int64(1900000000 + (i * 280000000) + (i%4)*450000000)
|
||||
serverTrafficYesterday[i] = types.ServerTrafficData{
|
||||
ServerId: int64(i + 1),
|
||||
Name: serverNames[i],
|
||||
Upload: upload,
|
||||
Download: download,
|
||||
}
|
||||
}
|
||||
|
||||
//// Generate user traffic ranking data for today (top 10)
|
||||
//userTrafficToday := make([]types.UserTrafficData, 10)
|
||||
//for i := 0; i < 10; i++ {
|
||||
// upload := int64(100000000 + (i*20000000) + (i%5)*50000000) // 100MB - 400MB
|
||||
// download := int64(800000000 + (i*150000000) + (i%3)*300000000) // 800MB - 3GB
|
||||
// userTrafficToday[i] = types.UserTrafficData{
|
||||
// SID: int64(10001 + i),
|
||||
// Upload: upload,
|
||||
// Download: download,
|
||||
// }
|
||||
//}
|
||||
|
||||
//// Generate user traffic ranking data for yesterday (top 10)
|
||||
//userTrafficYesterday := make([]types.UserTrafficData, 10)
|
||||
//for i := 0; i < 10; i++ {
|
||||
// upload := int64(95000000 + (i*18000000) + (i%5)*45000000)
|
||||
// download := int64(750000000 + (i*140000000) + (i%3)*280000000)
|
||||
// userTrafficYesterday[i] = types.UserTrafficData{
|
||||
// SID: int64(10001 + i),
|
||||
// Upload: upload,
|
||||
// Download: download,
|
||||
// }
|
||||
//}
|
||||
//
|
||||
return &types.ServerTotalDataResponse{
|
||||
OnlineUsers: 1688,
|
||||
OnlineServers: 8,
|
||||
OfflineServers: 2,
|
||||
TodayUpload: 8888888888, // ~8.3GB
|
||||
TodayDownload: 28888888888, // ~26.9GB
|
||||
MonthlyUpload: 288888888888, // ~269GB
|
||||
MonthlyDownload: 888888888888, // ~828GB
|
||||
UpdatedAt: now.Unix(),
|
||||
ServerTrafficRankingToday: serverTrafficToday,
|
||||
ServerTrafficRankingYesterday: serverTrafficYesterday,
|
||||
//UserTrafficRankingToday: userTrafficToday,
|
||||
//UserTrafficRankingYesterday: userTrafficYesterday,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package console
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type QueryTicketWaitReplyLogic struct {
|
||||
|
||||
@@ -2,11 +2,13 @@ package console
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type QueryUserStatisticsLogic struct {
|
||||
@@ -25,6 +27,9 @@ func NewQueryUserStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func (l *QueryUserStatisticsLogic) QueryUserStatistics() (resp *types.UserStatisticsResponse, err error) {
|
||||
if strings.ToLower(os.Getenv("PPANEL_MODE")) == "demo" {
|
||||
return l.mockRevenueStatistics(), nil
|
||||
}
|
||||
resp = &types.UserStatisticsResponse{}
|
||||
now := time.Now()
|
||||
// query today user register count
|
||||
@@ -56,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
|
||||
@@ -67,5 +88,83 @@ func (l *QueryUserStatisticsLogic) QueryUserStatistics() (resp *types.UserStatis
|
||||
} else {
|
||||
resp.All.Register = allUserCount
|
||||
}
|
||||
|
||||
// query all user order counts
|
||||
allNewOrderUsers, allRenewalOrderUsers, err := l.svcCtx.OrderModel.QueryTotalUserCounts(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[QueryUserStatisticsLogic] QueryTotalUserCounts error", logger.Field("error", err.Error()))
|
||||
} else {
|
||||
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 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))
|
||||
baseRegister := int64(18 + ((6 - i) * 3) + ((6-i)%3)*8)
|
||||
monthlyList[i] = types.UserStatistics{
|
||||
Date: dayDate.Format("2006-01-02"),
|
||||
Register: baseRegister,
|
||||
NewOrderUsers: int64(float64(baseRegister) * 0.65),
|
||||
RenewalOrderUsers: int64(float64(baseRegister) * 0.35),
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
NewOrderUsers: 18,
|
||||
RenewalOrderUsers: 10,
|
||||
},
|
||||
Monthly: types.UserStatistics{
|
||||
Register: 888,
|
||||
NewOrderUsers: 588,
|
||||
RenewalOrderUsers: 300,
|
||||
List: monthlyList,
|
||||
},
|
||||
All: 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package coupon
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/coupon"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/random"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/snowflake"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/coupon"
|
||||
"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/random"
|
||||
"github.com/perfect-panel/server/pkg/snowflake"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package coupon
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package coupon
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/coupon"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/coupon"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package document
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/document"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/document"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package document
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package document
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package document
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/document"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/document"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -3,12 +3,11 @@ package log
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/log"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -3,12 +3,12 @@ package order
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/order"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/order"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package order
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
queue "github.com/perfect-panel/ppanel-server/queue/types"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
queue "github.com/perfect-panel/server/queue/types"
|
||||
)
|
||||
|
||||
type UpdateOrderStatusLogic struct {
|
||||
|
||||
@@ -3,16 +3,20 @@ package payment
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/random"
|
||||
"github.com/perfect-panel/server/pkg/payment/stripe"
|
||||
"gorm.io/gorm"
|
||||
|
||||
paymentModel "github.com/perfect-panel/ppanel-server/internal/model/payment"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/payment"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/pkg/random"
|
||||
|
||||
paymentModel "github.com/perfect-panel/server/internal/model/payment"
|
||||
"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/payment"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -50,9 +54,42 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
Enable: req.Enable,
|
||||
Token: random.KeyNew(8, 1),
|
||||
}
|
||||
if err := l.svcCtx.PaymentModel.Insert(l.ctx, paymentMethod); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert payment method error: %s", err.Error())
|
||||
err = l.svcCtx.PaymentModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||
if req.Platform == "Stripe" {
|
||||
var cfg paymentModel.StripeConfig
|
||||
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())
|
||||
}
|
||||
if cfg.SecretKey == "" {
|
||||
l.Error("[CreatePaymentMethod] stripe secret key is empty")
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "stripe secret key is empty")
|
||||
}
|
||||
|
||||
// Create Stripe webhook endpoint
|
||||
client := stripe.NewClient(stripe.Config{
|
||||
SecretKey: cfg.SecretKey,
|
||||
PublicKey: cfg.PublicKey,
|
||||
})
|
||||
url := fmt.Sprintf("%s/v1/notify/Stripe/%s", req.Domain, paymentMethod.Token)
|
||||
endpoint, err := client.CreateWebhookEndpoint(url)
|
||||
if err != nil {
|
||||
l.Errorw("[CreatePaymentMethod] create stripe webhook endpoint error", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "create stripe webhook endpoint error: %s", err.Error())
|
||||
}
|
||||
cfg.WebhookSecret = endpoint.Secret
|
||||
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())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = &types.PaymentConfig{}
|
||||
tool.DeepCopy(resp, paymentMethod)
|
||||
var configMap map[string]interface{}
|
||||
@@ -64,33 +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()
|
||||
case payment.Payssion:
|
||||
payssion := &paymentModel.PayssionConfig{}
|
||||
if err := payssion.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse payssion config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return payssion.Marshal()
|
||||
return handleConfig("Epay", &paymentModel.EPayConfig{})
|
||||
case payment.CryptoSaaS:
|
||||
return handleConfig("CryptoSaaS", &paymentModel.CryptoSaaSConfig{})
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package payment
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
paymentPlatform "github.com/perfect-panel/ppanel-server/pkg/payment"
|
||||
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/payment"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/payment"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,10 +3,10 @@ package payment
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/payment"
|
||||
"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/payment"
|
||||
)
|
||||
|
||||
type GetPaymentPlatformLogic struct {
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/payment"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"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/payment"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
@@ -39,7 +39,7 @@ func (l *UpdatePaymentMethodLogic) UpdatePaymentMethod(req *types.UpdatePaymentM
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find payment method error: %s", err.Error())
|
||||
}
|
||||
config := parsePaymentPlatformConfig(l.ctx, payment.ParsePlatform(req.Platform), req.Config)
|
||||
tool.DeepCopy(method, req)
|
||||
tool.DeepCopy(method, req, tool.CopyWithIgnoreEmpty(false))
|
||||
method.Config = config
|
||||
if err := l.svcCtx.PaymentModel.Update(l.ctx, method); err != nil {
|
||||
l.Errorw("update payment method error", logger.Field("id", req.Id), logger.Field("error", err.Error()))
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type BatchDeleteNodeGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewBatchDeleteNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchDeleteNodeGroupLogic {
|
||||
return &BatchDeleteNodeGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchDeleteNodeGroupLogic) BatchDeleteNodeGroup(req *types.BatchDeleteNodeGroupRequest) error {
|
||||
// Check if the group is empty
|
||||
count, err := l.svcCtx.ServerModel.QueryServerCountByServerGroups(l.ctx, req.Ids)
|
||||
if err != nil {
|
||||
l.Errorw("[BatchDeleteNodeGroup] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query server error: %v", err)
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.NodeGroupNotEmpty), "group is not empty")
|
||||
}
|
||||
// Delete the group
|
||||
err = l.svcCtx.ServerModel.BatchDeleteNodeGroup(l.ctx, req.Ids)
|
||||
if err != nil {
|
||||
l.Errorw("[BatchDeleteNodeGroup] Delete Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type BatchDeleteNodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewBatchDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchDeleteNodeLogic {
|
||||
return &BatchDeleteNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchDeleteNodeLogic) BatchDeleteNode(req *types.BatchDeleteNodeRequest) error {
|
||||
err := l.svcCtx.DB.Transaction(func(db *gorm.DB) error {
|
||||
for _, id := range req.Ids {
|
||||
err := l.svcCtx.ServerModel.Delete(l.ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[BatchDeleteNode] Delete Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package server
|
||||
|
||||
const (
|
||||
ShadowSocks = "shadowsocks"
|
||||
Vmess = "vmess"
|
||||
Vless = "vless"
|
||||
Trojan = "trojan"
|
||||
AnyTLS = "anytls"
|
||||
Tuic = "tuic"
|
||||
Hysteria2 = "hysteria2"
|
||||
)
|
||||
@@ -1,40 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CreateNodeGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewCreateNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateNodeGroupLogic {
|
||||
return &CreateNodeGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateNodeGroupLogic) CreateNodeGroup(req *types.CreateNodeGroupRequest) error {
|
||||
groupInfo := &server.Group{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
}
|
||||
err := l.svcCtx.ServerModel.InsertGroup(l.ctx, groupInfo)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,18 +2,13 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
queue "github.com/perfect-panel/ppanel-server/queue/types"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -23,6 +18,7 @@ type CreateNodeLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewCreateNodeLogic Create Node
|
||||
func NewCreateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateNodeLogic {
|
||||
return &CreateNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
@@ -32,75 +28,19 @@ func NewCreateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Create
|
||||
}
|
||||
|
||||
func (l *CreateNodeLogic) CreateNode(req *types.CreateNodeRequest) error {
|
||||
config, err := json.Marshal(req.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
data := node.Node{
|
||||
Name: req.Name,
|
||||
Tags: tool.StringSliceToString(req.Tags),
|
||||
Port: req.Port,
|
||||
Address: req.Address,
|
||||
ServerId: req.ServerId,
|
||||
Protocol: req.Protocol,
|
||||
}
|
||||
var serverInfo server.Server
|
||||
tool.DeepCopy(&serverInfo, req)
|
||||
serverInfo.Config = string(config)
|
||||
nodeRelay, err := json.Marshal(req.RelayNode)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateNode] Marshal RelayNode Error: ", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
if len(req.Tags) > 0 {
|
||||
serverInfo.Tags = strings.Join(req.Tags, ",")
|
||||
}
|
||||
|
||||
serverInfo.LastReportedAt = time.UnixMicro(1218124800)
|
||||
|
||||
serverInfo.City = req.City
|
||||
serverInfo.Country = req.Country
|
||||
|
||||
serverInfo.RelayNode = string(nodeRelay)
|
||||
if req.Protocol == "vless" {
|
||||
var cfg types.Vless
|
||||
if err := json.Unmarshal(config, &cfg); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
|
||||
}
|
||||
if cfg.Security == "reality" && cfg.SecurityConfig.RealityPublicKey == "" {
|
||||
public, private, err := tool.Curve25519Genkey(false, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate curve25519 key error")
|
||||
}
|
||||
cfg.SecurityConfig.RealityPublicKey = public
|
||||
cfg.SecurityConfig.RealityPrivateKey = private
|
||||
cfg.SecurityConfig.RealityShortId = tool.GenerateShortID(private)
|
||||
}
|
||||
if cfg.SecurityConfig.RealityServerAddr == "" {
|
||||
cfg.SecurityConfig.RealityServerAddr = cfg.SecurityConfig.SNI
|
||||
}
|
||||
if cfg.SecurityConfig.RealityServerPort == 0 {
|
||||
cfg.SecurityConfig.RealityServerPort = 443
|
||||
}
|
||||
config, _ = json.Marshal(cfg)
|
||||
serverInfo.Config = string(config)
|
||||
}
|
||||
|
||||
err = l.svcCtx.ServerModel.Insert(l.ctx, &serverInfo)
|
||||
err := l.svcCtx.NodeModel.InsertNode(l.ctx, &data)
|
||||
if err != nil {
|
||||
l.Errorw("[CreateNode] Insert Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create server error: %v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "[CreateNode] Insert Database Error")
|
||||
}
|
||||
|
||||
// Marshal the task payload
|
||||
payload, err := json.Marshal(queue.GetNodeCountry{
|
||||
Protocol: serverInfo.Protocol,
|
||||
ServerAddr: serverInfo.ServerAddr,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeCountry]: Marshal Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to marshal task payload")
|
||||
}
|
||||
// Create a queue task
|
||||
task := asynq.NewTask(queue.ForthwithGetCountry, payload)
|
||||
// Enqueue the task
|
||||
taskInfo, err := l.svcCtx.Queue.Enqueue(task)
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeCountry]: Enqueue Error", logger.Field("error", err.Error()), logger.Field("payload", string(payload)))
|
||||
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to enqueue task")
|
||||
}
|
||||
l.Infow("[GetNodeCountry]: Enqueue Success", logger.Field("taskID", taskInfo.ID), logger.Field("payload", string(payload)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/rules"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CreateRuleGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Create rule group
|
||||
func NewCreateRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRuleGroupLogic {
|
||||
return &CreateRuleGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
func parseAndValidateRules(ruleText, ruleName string) ([]string, error) {
|
||||
var rs []string
|
||||
ruleArr := strings.Split(ruleText, "\n")
|
||||
if len(ruleArr) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "rules is empty")
|
||||
}
|
||||
|
||||
for _, s := range ruleArr {
|
||||
r := rules.NewRule(s, ruleName)
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
if err := r.Validate(); err != nil {
|
||||
continue
|
||||
}
|
||||
rs = append(rs, r.String())
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
func (l *CreateRuleGroupLogic) CreateRuleGroup(req *types.CreateRuleGroupRequest) error {
|
||||
rs, err := parseAndValidateRules(req.Rules, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = l.svcCtx.ServerModel.InsertRuleGroup(l.ctx, &server.RuleGroup{
|
||||
Name: req.Name,
|
||||
Icon: req.Icon,
|
||||
Tags: tool.StringSliceToString(req.Tags),
|
||||
Rules: strings.Join(rs, "\n"),
|
||||
Enable: req.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[CreateRuleGroup] Insert Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create server rule group error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/ip"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CreateServerLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewCreateServerLogic Create Server
|
||||
func NewCreateServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateServerLogic {
|
||||
return &CreateServerLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateServerLogic) CreateServer(req *types.CreateServerRequest) error {
|
||||
data := node.Server{
|
||||
Name: req.Name,
|
||||
Country: req.Country,
|
||||
City: req.City,
|
||||
Address: req.Address,
|
||||
Sort: req.Sort,
|
||||
Protocols: "",
|
||||
}
|
||||
protocols := make([]node.Protocol, 0)
|
||||
for _, item := range req.Protocols {
|
||||
if item.Type == "" {
|
||||
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols type is empty"), "protocols type is empty")
|
||||
}
|
||||
var protocol node.Protocol
|
||||
tool.DeepCopy(&protocol, item)
|
||||
|
||||
// VLESS Reality Key Generation
|
||||
if protocol.Type == "vless" {
|
||||
if protocol.Security == "reality" {
|
||||
if protocol.RealityPublicKey == "" {
|
||||
public, private, err := tool.Curve25519Genkey(false, "")
|
||||
if err != nil {
|
||||
l.Errorf("[CreateServer] Generate Reality Key Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate reality key error: %v", err)
|
||||
}
|
||||
protocol.RealityPublicKey = public
|
||||
protocol.RealityPrivateKey = private
|
||||
protocol.RealityShortId = tool.GenerateShortID(private)
|
||||
}
|
||||
if protocol.RealityServerAddr == "" {
|
||||
protocol.RealityServerAddr = protocol.SNI
|
||||
}
|
||||
if protocol.RealityServerPort == 0 {
|
||||
protocol.RealityServerPort = 443
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// ShadowSocks 2022 Key Generation
|
||||
if protocol.Type == "shadowsocks" {
|
||||
if strings.Contains(protocol.Cipher, "2022") {
|
||||
var length int
|
||||
switch protocol.Cipher {
|
||||
case "2022-blake3-aes-128-gcm":
|
||||
length = 16
|
||||
default:
|
||||
length = 32
|
||||
}
|
||||
if len(protocol.ServerKey) != length {
|
||||
protocol.ServerKey = tool.GenerateCipher(protocol.ServerKey, length)
|
||||
}
|
||||
}
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
}
|
||||
|
||||
err := data.MarshalProtocols(protocols)
|
||||
if err != nil {
|
||||
l.Errorf("[CreateServer] Marshal Protocols Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols marshal error"), "protocols marshal error: %v", err)
|
||||
}
|
||||
if data.City == "" && data.Country == "" {
|
||||
// query server ip location
|
||||
result, err := ip.GetRegionByIp(req.Address)
|
||||
if err != nil {
|
||||
l.Errorf("[CreateServer] GetRegionByIp Error: %v", err.Error())
|
||||
} else {
|
||||
data.City = result.City
|
||||
data.Country = result.Country
|
||||
}
|
||||
}
|
||||
err = l.svcCtx.NodeModel.InsertServer(l.ctx, &data)
|
||||
if err != nil {
|
||||
l.Errorf("[CreateServer] Insert Server error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert server error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DeleteNodeGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewDeleteNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteNodeGroupLogic {
|
||||
return &DeleteNodeGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteNodeGroupLogic) DeleteNodeGroup(req *types.DeleteNodeGroupRequest) error {
|
||||
// Check if the group is empty
|
||||
count, err := l.svcCtx.ServerModel.QueryServerCountByServerGroups(l.ctx, []int64{req.Id})
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteNodeGroup] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query server error: %v", err)
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.NodeGroupNotEmpty), "group is not empty")
|
||||
}
|
||||
// Delete the group
|
||||
err = l.svcCtx.ServerModel.DeleteGroup(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteNodeGroup] Delete Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,14 +2,14 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DeleteNodeLogic struct {
|
||||
@@ -18,6 +18,7 @@ type DeleteNodeLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewDeleteNodeLogic Delete Node
|
||||
func NewDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteNodeLogic {
|
||||
return &DeleteNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
@@ -27,30 +28,20 @@ func NewDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete
|
||||
}
|
||||
|
||||
func (l *DeleteNodeLogic) DeleteNode(req *types.DeleteNodeRequest) error {
|
||||
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
|
||||
// Delete server
|
||||
err := l.svcCtx.ServerModel.Delete(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete server to subscribe
|
||||
subs, err := l.svcCtx.SubscribeModel.QuerySubscribeIdsByServerIdAndServerGroupId(l.ctx, req.Id, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sub := range subs {
|
||||
servers := tool.StringToInt64Slice(sub.Server)
|
||||
newServers := tool.RemoveElementBySlice(servers, req.Id)
|
||||
sub.Server = tool.Int64SliceToString(newServers)
|
||||
if err = l.svcCtx.SubscribeModel.Update(l.ctx, sub); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
|
||||
|
||||
err = l.svcCtx.NodeModel.DeleteNode(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteNode] Delete Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete server error: %v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "[DeleteNode] Delete Database Error")
|
||||
}
|
||||
return nil
|
||||
|
||||
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
ServerId: []int64{data.ServerId},
|
||||
Tag: strings.Split(data.Tags, ","),
|
||||
Search: "",
|
||||
Protocol: data.Protocol,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DeleteRuleGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Delete rule group
|
||||
func NewDeleteRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRuleGroupLogic {
|
||||
return &DeleteRuleGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteRuleGroupLogic) DeleteRuleGroup(req *types.DeleteRuleGroupRequest) error {
|
||||
err := l.svcCtx.ServerModel.DeleteRuleGroup(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteRuleGroup] Delete Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete server rule group error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DeleteServerLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewDeleteServerLogic Delete Server
|
||||
func NewDeleteServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteServerLogic {
|
||||
return &DeleteServerLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteServerLogic) DeleteServer(req *types.DeleteServerRequest) error {
|
||||
err := l.svcCtx.NodeModel.DeleteServer(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[DeleteServer] Delete Server Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "[DeleteServer] Delete Server Error")
|
||||
}
|
||||
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
ServerId: []int64{req.Id},
|
||||
Search: "",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FilterNodeListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterNodeListLogic Filter Node List
|
||||
func NewFilterNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterNodeListLogic {
|
||||
return &FilterNodeListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterNodeListLogic) FilterNodeList(req *types.FilterNodeListRequest) (resp *types.FilterNodeListResponse, err error) {
|
||||
total, data, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Search: req.Search,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Errorw("[FilterNodeList] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterNodeList] Query Database Error")
|
||||
}
|
||||
|
||||
list := make([]types.Node, 0)
|
||||
for _, datum := range data {
|
||||
list = append(list, types.Node{
|
||||
Id: datum.Id,
|
||||
Name: datum.Name,
|
||||
Tags: tool.RemoveDuplicateElements(strings.Split(datum.Tags, ",")...),
|
||||
Port: datum.Port,
|
||||
Address: datum.Address,
|
||||
ServerId: datum.ServerId,
|
||||
Protocol: datum.Protocol,
|
||||
Enabled: datum.Enabled,
|
||||
Sort: datum.Sort,
|
||||
CreatedAt: datum.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: datum.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.FilterNodeListResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FilterServerListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewFilterServerListLogic Filter Server List
|
||||
func NewFilterServerListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterServerListLogic {
|
||||
return &FilterServerListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *FilterServerListLogic) FilterServerList(req *types.FilterServerListRequest) (resp *types.FilterServerListResponse, err error) {
|
||||
total, data, err := l.svcCtx.NodeModel.FilterServerList(l.ctx, &node.FilterParams{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Search: req.Search,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[FilterServerList] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterServerList] Query Database Error")
|
||||
}
|
||||
|
||||
list := make([]types.Server, 0)
|
||||
|
||||
for _, datum := range data {
|
||||
var server types.Server
|
||||
tool.DeepCopy(&server, datum)
|
||||
|
||||
// handler protocols
|
||||
var protocols []types.Protocol
|
||||
dst, err := datum.UnmarshalProtocols()
|
||||
if err != nil {
|
||||
l.Errorf("[FilterServerList] UnmarshalProtocols Error: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
tool.DeepCopy(&protocols, dst)
|
||||
server.Protocols = protocols
|
||||
|
||||
nodeStatus, err := l.svcCtx.NodeModel.StatusCache(l.ctx, datum.Id)
|
||||
if err != nil {
|
||||
if !errors.Is(err, redis.Nil) {
|
||||
l.Errorw("[handlerServerStatus] GetNodeStatus Error: ", logger.Field("error", err.Error()), logger.Field("node_id", datum.Id))
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "GetNodeStatus Error")
|
||||
}
|
||||
server.Status = types.ServerStatus{
|
||||
Mem: nodeStatus.Mem,
|
||||
Cpu: nodeStatus.Cpu,
|
||||
Disk: nodeStatus.Disk,
|
||||
Online: l.handlerServerStatus(datum.Id, protocols),
|
||||
Status: l.handlerServerStaus(datum.LastReportedAt),
|
||||
}
|
||||
list = append(list, server)
|
||||
}
|
||||
|
||||
return &types.FilterServerListResponse{
|
||||
List: list,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *FilterServerListLogic) handlerServerStatus(id int64, protocols []types.Protocol) []types.ServerOnlineUser {
|
||||
result := make([]types.ServerOnlineUser, 0)
|
||||
|
||||
for _, protocol := range protocols {
|
||||
// query online user
|
||||
data, err := l.svcCtx.NodeModel.OnlineUserSubscribe(l.ctx, id, protocol.Type)
|
||||
if err != nil {
|
||||
if !errors.Is(err, redis.Nil) {
|
||||
l.Errorw("[handlerServerStatus] OnlineUserSubscribe Error: ", logger.Field("error", err.Error()), logger.Field("node_id", id), logger.Field("protocol", protocol.Type))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(data) > 0 {
|
||||
for sub, online := range data {
|
||||
var ips []types.ServerOnlineIP
|
||||
for _, ip := range online {
|
||||
ips = append(ips, types.ServerOnlineIP{
|
||||
IP: ip,
|
||||
Protocol: protocol.Type,
|
||||
})
|
||||
}
|
||||
|
||||
result = append(result, types.ServerOnlineUser{
|
||||
IP: ips,
|
||||
SubscribeId: sub,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// merge same subscribe
|
||||
var mapResult = make(map[int64]types.ServerOnlineUser)
|
||||
for _, item := range result {
|
||||
if exist, ok := mapResult[item.SubscribeId]; ok {
|
||||
// merge
|
||||
exist.Traffic += item.Traffic
|
||||
exist.IP = append(exist.IP, item.IP...)
|
||||
mapResult[item.SubscribeId] = exist
|
||||
} else {
|
||||
// get subscribe info
|
||||
info, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, item.SubscribeId)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
l.Errorw("[handlerServerStatus] FindOneSubscribe Error: ", logger.Field("error", err.Error()), logger.Field("subscribe_id", item.SubscribeId))
|
||||
}
|
||||
continue
|
||||
}
|
||||
data := types.ServerOnlineUser{
|
||||
IP: item.IP,
|
||||
UserId: info.UserId,
|
||||
Subscribe: "",
|
||||
SubscribeId: item.SubscribeId,
|
||||
Traffic: info.Download + info.Upload,
|
||||
ExpiredAt: info.ExpireTime.UnixMilli(),
|
||||
}
|
||||
if info.Subscribe != nil {
|
||||
data.Subscribe = info.Subscribe.Name
|
||||
}
|
||||
// add new
|
||||
mapResult[item.SubscribeId] = data
|
||||
}
|
||||
}
|
||||
// convert map to slice
|
||||
result = make([]types.ServerOnlineUser, 0, len(mapResult))
|
||||
for _, item := range mapResult {
|
||||
result = append(result, item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (l *FilterServerListLogic) handlerServerStaus(last *time.Time) string {
|
||||
if last == nil {
|
||||
return "offline"
|
||||
}
|
||||
if time.Since(*last) > time.Minute*5 {
|
||||
return "offline"
|
||||
}
|
||||
if time.Since(*last) > time.Minute*3 {
|
||||
return "warning"
|
||||
}
|
||||
return "online"
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetNodeDetailLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetNodeDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeDetailLogic {
|
||||
return &GetNodeDetailLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetNodeDetailLogic) GetNodeDetail(req *types.GetDetailRequest) (resp *types.Server, err error) {
|
||||
detail, err := l.svcCtx.ServerModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get server detail error: %v", err.Error())
|
||||
}
|
||||
resp = &types.Server{}
|
||||
tool.DeepCopy(resp, detail)
|
||||
var cfg map[string]interface{}
|
||||
err = json.Unmarshal([]byte(detail.Config), &cfg)
|
||||
if err != nil {
|
||||
cfg = make(map[string]interface{})
|
||||
}
|
||||
resp.Config = cfg
|
||||
return
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetNodeGroupListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetNodeGroupListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeGroupListLogic {
|
||||
return &GetNodeGroupListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetNodeGroupListLogic) GetNodeGroupList() (resp *types.GetNodeGroupListResponse, err error) {
|
||||
nodeGroupList, err := l.svcCtx.ServerModel.QueryAllGroup(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
|
||||
}
|
||||
nodeGroups := make([]types.ServerGroup, 0)
|
||||
tool.DeepCopy(&nodeGroups, nodeGroupList)
|
||||
return &types.GetNodeGroupListResponse{
|
||||
Total: int64(len(nodeGroups)),
|
||||
List: nodeGroups,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetNodeListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeListLogic {
|
||||
return &GetNodeListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetNodeListLogic) GetNodeList(req *types.GetNodeServerListRequest) (resp *types.GetNodeServerListResponse, err error) {
|
||||
total, list, err := l.svcCtx.ServerModel.FindServerListByFilter(l.ctx, &server.ServerFilter{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Search: req.Search,
|
||||
Tag: req.Tag,
|
||||
Group: req.GroupId,
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeList] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
|
||||
}
|
||||
nodes := make([]types.Server, 0)
|
||||
for _, v := range list {
|
||||
node := types.Server{}
|
||||
tool.DeepCopy(&node, v)
|
||||
// default relay mode
|
||||
if node.RelayMode == "" {
|
||||
node.RelayMode = "none"
|
||||
}
|
||||
if len(v.Tags) > 0 {
|
||||
if strings.Contains(v.Tags, ",") {
|
||||
node.Tags = strings.Split(v.Tags, ",")
|
||||
} else {
|
||||
node.Tags = []string{v.Tags}
|
||||
}
|
||||
}
|
||||
// parse config
|
||||
var cfg map[string]interface{}
|
||||
err = json.Unmarshal([]byte(v.Config), &cfg)
|
||||
if err != nil {
|
||||
cfg = make(map[string]interface{})
|
||||
}
|
||||
node.Config = cfg
|
||||
relayNode := make([]types.NodeRelay, 0)
|
||||
err = json.Unmarshal([]byte(v.RelayNode), &relayNode)
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeList] Unmarshal RelayNode Error: ", logger.Field("error", err.Error()), logger.Field("relayNode", v.RelayNode))
|
||||
}
|
||||
node.RelayNode = relayNode
|
||||
var status types.NodeStatus
|
||||
nodeStatus, err := l.svcCtx.NodeCache.GetNodeStatus(l.ctx, v.Id)
|
||||
if err != nil {
|
||||
// redis nil is not a Error
|
||||
if !errors.Is(err, redis.Nil) {
|
||||
l.Errorw("[GetNodeList] Get Node Status Error: ", logger.Field("error", err.Error()))
|
||||
}
|
||||
} else {
|
||||
onlineUser, err := l.svcCtx.NodeCache.GetNodeOnlineUser(l.ctx, v.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeList] Get Node Online User Error: ", logger.Field("error", err.Error()))
|
||||
} else {
|
||||
status.Online = onlineUser
|
||||
}
|
||||
status.Cpu = nodeStatus.Cpu
|
||||
status.Mem = nodeStatus.Mem
|
||||
status.Disk = nodeStatus.Disk
|
||||
status.UpdatedAt = nodeStatus.UpdatedAt
|
||||
}
|
||||
node.Status = &status
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
return &types.GetNodeServerListResponse{
|
||||
Total: total,
|
||||
List: nodes,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GetNodeTagListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get node tag list
|
||||
func NewGetNodeTagListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNodeTagListLogic {
|
||||
return &GetNodeTagListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetNodeTagListLogic) GetNodeTagList() (resp *types.GetNodeTagListResponse, err error) {
|
||||
var nodeTags, tags []string
|
||||
err = l.svcCtx.ServerModel.Transaction(l.ctx, func(db *gorm.DB) error {
|
||||
|
||||
return db.Model(&server.Server{}).Select("tags").Pluck("tags", &nodeTags).Error
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get node tag list failed, %s", err.Error())
|
||||
}
|
||||
|
||||
for _, tag := range nodeTags {
|
||||
tags = append(tags, strings.Split(tag, ",")...)
|
||||
}
|
||||
|
||||
// Remove duplicate tags
|
||||
tags = tool.RemoveDuplicateElements(tags...)
|
||||
|
||||
return &types.GetNodeTagListResponse{
|
||||
Tags: tags,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetRuleGroupListLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get rule group list
|
||||
func NewGetRuleGroupListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRuleGroupListLogic {
|
||||
return &GetRuleGroupListLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetRuleGroupListLogic) GetRuleGroupList() (resp *types.GetRuleGroupResponse, err error) {
|
||||
nodeRuleGroupList, err := l.svcCtx.ServerModel.QueryAllRuleGroup(l.ctx)
|
||||
if err != nil {
|
||||
l.Errorw("[GetRuleGroupList] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
|
||||
}
|
||||
nodeRuleGroups := make([]types.ServerRuleGroup, len(nodeRuleGroupList))
|
||||
for i, v := range nodeRuleGroupList {
|
||||
nodeRuleGroups[i] = types.ServerRuleGroup{
|
||||
Id: v.Id,
|
||||
Icon: v.Icon,
|
||||
Name: v.Name,
|
||||
Tags: strings.Split(v.Tags, ","),
|
||||
Rules: v.Rules,
|
||||
Enable: v.Enable,
|
||||
CreatedAt: v.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: v.UpdatedAt.UnixMilli(),
|
||||
}
|
||||
}
|
||||
return &types.GetRuleGroupResponse{
|
||||
Total: int64(len(nodeRuleGroups)),
|
||||
List: nodeRuleGroups,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GetServerProtocolsLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Get Server Protocols
|
||||
func NewGetServerProtocolsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetServerProtocolsLogic {
|
||||
return &GetServerProtocolsLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetServerProtocolsLogic) GetServerProtocols(req *types.GetServerProtocolsRequest) (resp *types.GetServerProtocolsResponse, err error) {
|
||||
// find server
|
||||
data, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorf("[GetServerProtocols] FindOneServer Error: %s", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[GetServerProtocols] FindOneServer Error: %s", err.Error())
|
||||
}
|
||||
|
||||
// handler protocols
|
||||
var protocols []types.Protocol
|
||||
dst, err := data.UnmarshalProtocols()
|
||||
if err != nil {
|
||||
l.Errorf("[FilterServerList] UnmarshalProtocols Error: %s", err.Error())
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterServerList] UnmarshalProtocols Error: %s", err.Error())
|
||||
}
|
||||
tool.DeepCopy(&protocols, dst)
|
||||
|
||||
return &types.GetServerProtocolsResponse{
|
||||
Protocols: protocols,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type HasMigrateSeverNodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewHasMigrateSeverNodeLogic Check if there is any server or node to migrate
|
||||
func NewHasMigrateSeverNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HasMigrateSeverNodeLogic {
|
||||
return &HasMigrateSeverNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *HasMigrateSeverNodeLogic) HasMigrateSeverNode() (resp *types.HasMigrateSeverNodeResponse, err error) {
|
||||
var oldCount, newCount int64
|
||||
query := l.svcCtx.DB.WithContext(l.ctx)
|
||||
|
||||
err = query.Model(&server.Server{}).Count(&oldCount).Error
|
||||
if err != nil {
|
||||
l.Errorw("[HasMigrateSeverNode] Query Old Server Count Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[HasMigrateSeverNode] Query Old Server Count Error")
|
||||
}
|
||||
err = query.Model(&node.Server{}).Count(&newCount).Error
|
||||
if err != nil {
|
||||
l.Errorw("[HasMigrateSeverNode] Query New Server Count Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[HasMigrateSeverNode] Query New Server Count Error")
|
||||
}
|
||||
var shouldMigrate bool
|
||||
if oldCount != 0 && newCount == 0 {
|
||||
shouldMigrate = true
|
||||
}
|
||||
|
||||
return &types.HasMigrateSeverNodeResponse{
|
||||
HasMigrate: shouldMigrate,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/model/server"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
)
|
||||
|
||||
type MigrateServerNodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewMigrateServerNodeLogic Migrate server and node data to new database
|
||||
func NewMigrateServerNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MigrateServerNodeLogic {
|
||||
return &MigrateServerNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *MigrateServerNodeLogic) MigrateServerNode() (resp *types.MigrateServerNodeResponse, err error) {
|
||||
tx := l.svcCtx.DB.WithContext(l.ctx).Begin()
|
||||
var oldServers []*server.Server
|
||||
var newServers []*node.Server
|
||||
var newNodes []*node.Node
|
||||
|
||||
err = tx.Model(&server.Server{}).Find(&oldServers).Error
|
||||
if err != nil {
|
||||
l.Errorw("[MigrateServerNode] Query Old Server List Error: ", logger.Field("error", err.Error()))
|
||||
return &types.MigrateServerNodeResponse{
|
||||
Succee: 0,
|
||||
Fail: 0,
|
||||
Message: fmt.Sprintf("Query Old Server List Error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
for _, oldServer := range oldServers {
|
||||
data, err := l.adapterServer(oldServer)
|
||||
if err != nil {
|
||||
l.Errorw("[MigrateServerNode] Adapter Server Error: ", logger.Field("error", err.Error()))
|
||||
if resp == nil {
|
||||
resp = &types.MigrateServerNodeResponse{}
|
||||
}
|
||||
resp.Fail++
|
||||
if resp.Message == "" {
|
||||
resp.Message = fmt.Sprintf("Adapter Server Error: %s", err.Error())
|
||||
} else {
|
||||
resp.Message = fmt.Sprintf("%s; Adapter Server Error: %s", resp.Message, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
newServers = append(newServers, data)
|
||||
|
||||
newNode, err := l.adapterNode(oldServer)
|
||||
if err != nil {
|
||||
l.Errorw("[MigrateServerNode] Adapter Node Error: ", logger.Field("error", err.Error()))
|
||||
if resp == nil {
|
||||
resp = &types.MigrateServerNodeResponse{}
|
||||
}
|
||||
resp.Fail++
|
||||
if resp.Message == "" {
|
||||
resp.Message = fmt.Sprintf("Adapter Node Error: %s", err.Error())
|
||||
} else {
|
||||
resp.Message = fmt.Sprintf("%s; Adapter Node Error: %s", resp.Message, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, item := range newNode {
|
||||
if item.Port == 0 {
|
||||
protocols, _ := data.UnmarshalProtocols()
|
||||
if len(protocols) > 0 {
|
||||
item.Port = protocols[0].Port
|
||||
}
|
||||
}
|
||||
newNodes = append(newNodes, item)
|
||||
}
|
||||
}
|
||||
|
||||
if len(newServers) > 0 {
|
||||
err = tx.Model(&node.Server{}).CreateInBatches(newServers, 20).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
l.Errorw("[MigrateServerNode] Insert New Server List Error: ", logger.Field("error", err.Error()))
|
||||
return &types.MigrateServerNodeResponse{
|
||||
Succee: 0,
|
||||
Fail: uint64(len(newServers)),
|
||||
Message: fmt.Sprintf("Insert New Server List Error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
if len(newNodes) > 0 {
|
||||
err = tx.Model(&node.Node{}).CreateInBatches(newNodes, 20).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
l.Errorw("[MigrateServerNode] Insert New Node List Error: ", logger.Field("error", err.Error()))
|
||||
return &types.MigrateServerNodeResponse{
|
||||
Succee: uint64(len(newServers)),
|
||||
Fail: uint64(len(newNodes)),
|
||||
Message: fmt.Sprintf("Insert New Node List Error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
|
||||
return &types.MigrateServerNodeResponse{
|
||||
Succee: uint64(len(newServers)),
|
||||
Fail: 0,
|
||||
Message: fmt.Sprintf("Migrate Success: %d servers and %d nodes", len(newServers), len(newNodes)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *MigrateServerNodeLogic) adapterServer(info *server.Server) (*node.Server, error) {
|
||||
result := &node.Server{
|
||||
Id: info.Id,
|
||||
Name: info.Name,
|
||||
Country: info.Country,
|
||||
City: info.City,
|
||||
//Ratio: info.TrafficRatio,
|
||||
Address: info.ServerAddr,
|
||||
Sort: int(info.Sort),
|
||||
Protocols: "",
|
||||
}
|
||||
var protocols []node.Protocol
|
||||
|
||||
switch info.Protocol {
|
||||
case ShadowSocks:
|
||||
var src server.Shadowsocks
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocols = append(protocols, node.Protocol{
|
||||
Type: "shadowsocks",
|
||||
Cipher: src.Method,
|
||||
Port: uint16(src.Port),
|
||||
ServerKey: src.ServerKey,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
})
|
||||
case Vmess:
|
||||
var src server.Vmess
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocol := node.Protocol{
|
||||
Type: "vmess",
|
||||
Port: uint16(src.Port),
|
||||
Security: src.Security,
|
||||
SNI: src.SecurityConfig.SNI,
|
||||
AllowInsecure: src.SecurityConfig.AllowInsecure,
|
||||
Fingerprint: src.SecurityConfig.Fingerprint,
|
||||
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
|
||||
RealityServerPort: src.SecurityConfig.RealityServerPort,
|
||||
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
|
||||
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
|
||||
RealityShortId: src.SecurityConfig.RealityShortId,
|
||||
Transport: src.Transport,
|
||||
Host: src.TransportConfig.Host,
|
||||
Path: src.TransportConfig.Path,
|
||||
ServiceName: src.TransportConfig.ServiceName,
|
||||
Flow: src.Flow,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
protocols = append(protocols, protocol)
|
||||
case Vless:
|
||||
var src server.Vless
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocol := node.Protocol{
|
||||
Type: "vless",
|
||||
Port: uint16(src.Port),
|
||||
Security: src.Security,
|
||||
SNI: src.SecurityConfig.SNI,
|
||||
AllowInsecure: src.SecurityConfig.AllowInsecure,
|
||||
Fingerprint: src.SecurityConfig.Fingerprint,
|
||||
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
|
||||
RealityServerPort: src.SecurityConfig.RealityServerPort,
|
||||
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
|
||||
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
|
||||
RealityShortId: src.SecurityConfig.RealityShortId,
|
||||
Transport: src.Transport,
|
||||
Host: src.TransportConfig.Host,
|
||||
Path: src.TransportConfig.Path,
|
||||
ServiceName: src.TransportConfig.ServiceName,
|
||||
Flow: src.Flow,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
case Trojan:
|
||||
var src server.Trojan
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocol := node.Protocol{
|
||||
Type: "trojan",
|
||||
Port: uint16(src.Port),
|
||||
Security: src.Security,
|
||||
SNI: src.SecurityConfig.SNI,
|
||||
AllowInsecure: src.SecurityConfig.AllowInsecure,
|
||||
Fingerprint: src.SecurityConfig.Fingerprint,
|
||||
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
|
||||
RealityServerPort: src.SecurityConfig.RealityServerPort,
|
||||
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
|
||||
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
|
||||
RealityShortId: src.SecurityConfig.RealityShortId,
|
||||
Transport: src.Transport,
|
||||
Host: src.TransportConfig.Host,
|
||||
Path: src.TransportConfig.Path,
|
||||
ServiceName: src.TransportConfig.ServiceName,
|
||||
Flow: src.Flow,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
case Hysteria2:
|
||||
var src server.Hysteria2
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocol := node.Protocol{
|
||||
Type: "hysteria",
|
||||
Port: uint16(src.Port),
|
||||
HopPorts: src.HopPorts,
|
||||
HopInterval: src.HopInterval,
|
||||
ObfsPassword: src.ObfsPassword,
|
||||
SNI: src.SecurityConfig.SNI,
|
||||
AllowInsecure: src.SecurityConfig.AllowInsecure,
|
||||
Fingerprint: src.SecurityConfig.Fingerprint,
|
||||
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
|
||||
RealityServerPort: src.SecurityConfig.RealityServerPort,
|
||||
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
|
||||
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
|
||||
RealityShortId: src.SecurityConfig.RealityShortId,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
case Tuic:
|
||||
var src server.Tuic
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocol := node.Protocol{
|
||||
Type: "tuic",
|
||||
Port: uint16(src.Port),
|
||||
DisableSNI: src.DisableSNI,
|
||||
ReduceRtt: src.ReduceRtt,
|
||||
UDPRelayMode: src.UDPRelayMode,
|
||||
CongestionController: src.CongestionController,
|
||||
SNI: src.SecurityConfig.SNI,
|
||||
AllowInsecure: src.SecurityConfig.AllowInsecure,
|
||||
Fingerprint: src.SecurityConfig.Fingerprint,
|
||||
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
|
||||
RealityServerPort: src.SecurityConfig.RealityServerPort,
|
||||
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
|
||||
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
|
||||
RealityShortId: src.SecurityConfig.RealityShortId,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
case AnyTLS:
|
||||
var src server.AnyTLS
|
||||
err := json.Unmarshal([]byte(info.Config), &src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protocol := node.Protocol{
|
||||
Type: "anytls",
|
||||
Port: uint16(src.Port),
|
||||
SNI: src.SecurityConfig.SNI,
|
||||
AllowInsecure: src.SecurityConfig.AllowInsecure,
|
||||
Fingerprint: src.SecurityConfig.Fingerprint,
|
||||
RealityServerAddr: src.SecurityConfig.RealityServerAddr,
|
||||
RealityServerPort: src.SecurityConfig.RealityServerPort,
|
||||
RealityPrivateKey: src.SecurityConfig.RealityPrivateKey,
|
||||
RealityPublicKey: src.SecurityConfig.RealityPublicKey,
|
||||
RealityShortId: src.SecurityConfig.RealityShortId,
|
||||
Ratio: float64(info.TrafficRatio),
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
}
|
||||
if len(protocols) > 0 {
|
||||
err := result.MarshalProtocols(protocols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *MigrateServerNodeLogic) adapterNode(info *server.Server) ([]*node.Node, error) {
|
||||
var nodes []*node.Node
|
||||
enable := true
|
||||
switch info.RelayMode {
|
||||
case server.RelayModeNone:
|
||||
nodes = append(nodes, &node.Node{
|
||||
Name: info.Name,
|
||||
Tags: "",
|
||||
Port: 0,
|
||||
Address: info.ServerAddr,
|
||||
ServerId: info.Id,
|
||||
Protocol: info.Protocol,
|
||||
Enabled: &enable,
|
||||
})
|
||||
default:
|
||||
var relays []server.NodeRelay
|
||||
err := json.Unmarshal([]byte(info.RelayNode), &relays)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, relay := range relays {
|
||||
nodes = append(nodes, &node.Node{
|
||||
Name: relay.Prefix + info.Name,
|
||||
Tags: "",
|
||||
Port: uint16(relay.Port),
|
||||
Address: relay.Host,
|
||||
ServerId: info.Id,
|
||||
Protocol: info.Protocol,
|
||||
Enabled: &enable,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type QueryNodeTagLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewQueryNodeTagLogic Query all node tags
|
||||
func NewQueryNodeTagLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryNodeTagLogic {
|
||||
return &QueryNodeTagLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *QueryNodeTagLogic) QueryNodeTag() (resp *types.QueryNodeTagResponse, err error) {
|
||||
|
||||
var nodes []*node.Node
|
||||
if err = l.svcCtx.DB.WithContext(l.ctx).Model(&node.Node{}).Find(&nodes).Error; err != nil {
|
||||
l.Errorw("[QueryNodeTag] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[QueryNodeTag] Query Database Error")
|
||||
}
|
||||
var tags []string
|
||||
for _, item := range nodes {
|
||||
tags = append(tags, strings.Split(item.Tags, ",")...)
|
||||
}
|
||||
|
||||
return &types.QueryNodeTagResponse{
|
||||
Tags: tool.RemoveDuplicateElements(tags...),
|
||||
}, nil
|
||||
}
|
||||
+21
-17
@@ -3,36 +3,35 @@ package server
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
)
|
||||
|
||||
type NodeSortLogic struct {
|
||||
type ResetSortWithNodeLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Node sort
|
||||
func NewNodeSortLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NodeSortLogic {
|
||||
return &NodeSortLogic{
|
||||
// NewResetSortWithNodeLogic Reset node sort
|
||||
func NewResetSortWithNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetSortWithNodeLogic {
|
||||
return &ResetSortWithNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
|
||||
err := l.svcCtx.ServerModel.Transaction(l.ctx, func(db *gorm.DB) error {
|
||||
func (l *ResetSortWithNodeLogic) ResetSortWithNode(req *types.ResetSortRequest) error {
|
||||
err := l.svcCtx.NodeModel.Transaction(l.ctx, func(db *gorm.DB) error {
|
||||
// find all servers id
|
||||
var existingIDs []int64
|
||||
db.Model(&server.Server{}).Select("id").Find(&existingIDs)
|
||||
db.Model(&node.Node{}).Select("id").Find(&existingIDs)
|
||||
// check if the id is valid
|
||||
validIDMap := make(map[int64]bool)
|
||||
for _, id := range existingIDs {
|
||||
@@ -46,12 +45,12 @@ func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
|
||||
}
|
||||
}
|
||||
// query all servers
|
||||
var servers []*server.Server
|
||||
db.Model(&server.Server{}).Order("sort ASC").Find(&servers)
|
||||
var servers []*node.Node
|
||||
db.Model(&node.Node{}).Order("sort ASC").Find(&servers)
|
||||
// create a map of the current sort
|
||||
currentSortMap := make(map[int64]int64)
|
||||
for _, item := range servers {
|
||||
currentSortMap[item.Id] = item.Sort
|
||||
currentSortMap[item.Id] = int64(item.Sort)
|
||||
}
|
||||
|
||||
// new sort map
|
||||
@@ -67,7 +66,12 @@ func (l *NodeSortLogic) NodeSort(req *types.NodeSortRequest) error {
|
||||
}
|
||||
}
|
||||
for _, item := range itemsToUpdate {
|
||||
if err := db.Model(&server.Server{}).Where("id = ?", item.Id).Update("sort", item.Sort).Error; err != nil {
|
||||
s, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, item.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Sort = int(item.Sort)
|
||||
if err = l.svcCtx.NodeModel.UpdateNode(l.ctx, s, db); err != nil {
|
||||
l.Errorw("[NodeSort] Update Database Error: ", logger.Field("error", err.Error()), logger.Field("id", item.Id), logger.Field("sort", item.Sort))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ResetSortWithServerLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewResetSortWithServerLogic Reset server sort
|
||||
func NewResetSortWithServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetSortWithServerLogic {
|
||||
return &ResetSortWithServerLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ResetSortWithServerLogic) ResetSortWithServer(req *types.ResetSortRequest) error {
|
||||
err := l.svcCtx.NodeModel.Transaction(l.ctx, func(db *gorm.DB) error {
|
||||
// find all servers id
|
||||
var existingIDs []int64
|
||||
db.Model(&node.Server{}).Select("id").Find(&existingIDs)
|
||||
// check if the id is valid
|
||||
validIDMap := make(map[int64]bool)
|
||||
for _, id := range existingIDs {
|
||||
validIDMap[id] = true
|
||||
}
|
||||
// check if the sort is valid
|
||||
var validItems []types.SortItem
|
||||
for _, item := range req.Sort {
|
||||
if validIDMap[item.Id] {
|
||||
validItems = append(validItems, item)
|
||||
}
|
||||
}
|
||||
// query all servers
|
||||
var servers []*node.Server
|
||||
db.Model(&node.Server{}).Order("sort ASC").Find(&servers)
|
||||
// create a map of the current sort
|
||||
currentSortMap := make(map[int64]int64)
|
||||
for _, item := range servers {
|
||||
currentSortMap[item.Id] = int64(item.Sort)
|
||||
}
|
||||
|
||||
// new sort map
|
||||
newSortMap := make(map[int64]int64)
|
||||
for _, item := range validItems {
|
||||
newSortMap[item.Id] = item.Sort
|
||||
}
|
||||
|
||||
var itemsToUpdate []types.SortItem
|
||||
for _, item := range validItems {
|
||||
if oldSort, exists := currentSortMap[item.Id]; exists && oldSort != item.Sort {
|
||||
itemsToUpdate = append(itemsToUpdate, item)
|
||||
}
|
||||
}
|
||||
for _, item := range itemsToUpdate {
|
||||
s, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, item.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Sort = int(item.Sort)
|
||||
if err = l.svcCtx.NodeModel.UpdateServer(l.ctx, s, db); err != nil {
|
||||
l.Errorw("[NodeSort] Update Database Error: ", logger.Field("error", err.Error()), logger.Field("id", item.Id), logger.Field("sort", item.Sort))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[NodeSort] Update Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ToggleNodeStatusLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewToggleNodeStatusLogic Toggle Node Status
|
||||
func NewToggleNodeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToggleNodeStatusLogic {
|
||||
return &ToggleNodeStatusLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ToggleNodeStatusLogic) ToggleNodeStatus(req *types.ToggleNodeStatusRequest) error {
|
||||
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorw("[ToggleNodeStatus] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[ToggleNodeStatus] Query Database Error")
|
||||
}
|
||||
data.Enabled = req.Enable
|
||||
|
||||
err = l.svcCtx.NodeModel.UpdateNode(l.ctx, data)
|
||||
if err != nil {
|
||||
l.Errorw("[ToggleNodeStatus] Update Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "[ToggleNodeStatus] Update Database Error")
|
||||
}
|
||||
|
||||
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
ServerId: []int64{data.ServerId},
|
||||
Tag: strings.Split(data.Tags, ","),
|
||||
Search: "",
|
||||
})
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type UpdateNodeGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUpdateNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateNodeGroupLogic {
|
||||
return &UpdateNodeGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateNodeGroupLogic) UpdateNodeGroup(req *types.UpdateNodeGroupRequest) error {
|
||||
// check server group exist
|
||||
nodeGroup, err := l.svcCtx.ServerModel.FindOneGroup(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
|
||||
}
|
||||
nodeGroup.Name = req.Name
|
||||
nodeGroup.Description = req.Description
|
||||
err = l.svcCtx.ServerModel.UpdateGroup(l.ctx, nodeGroup)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,18 +2,13 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/device"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
queue "github.com/perfect-panel/ppanel-server/queue/types"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -23,6 +18,7 @@ type UpdateNodeLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewUpdateNodeLogic Update Node
|
||||
func NewUpdateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateNodeLogic {
|
||||
return &UpdateNodeLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
@@ -32,79 +28,27 @@ func NewUpdateNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Update
|
||||
}
|
||||
|
||||
func (l *UpdateNodeLogic) UpdateNode(req *types.UpdateNodeRequest) error {
|
||||
// Check server exist
|
||||
nodeInfo, err := l.svcCtx.ServerModel.FindOne(l.ctx, req.Id)
|
||||
data, err := l.svcCtx.NodeModel.FindOneNode(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find server error: %v", err)
|
||||
l.Errorw("[UpdateNode] Query Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "[UpdateNode] Query Database Error")
|
||||
}
|
||||
tool.DeepCopy(nodeInfo, req)
|
||||
config, err := json.Marshal(req.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodeInfo.Config = string(config)
|
||||
nodeRelay, err := json.Marshal(req.RelayNode)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateNode] Marshal RelayNode Error: ", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
if len(req.Tags) > 0 {
|
||||
nodeInfo.Tags = strings.Join(req.Tags, ",")
|
||||
}
|
||||
|
||||
nodeInfo.City = req.City
|
||||
nodeInfo.Country = req.Country
|
||||
|
||||
nodeInfo.RelayNode = string(nodeRelay)
|
||||
if req.Protocol == "vless" {
|
||||
var cfg types.Vless
|
||||
if err := json.Unmarshal(config, &cfg); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "json.Unmarshal error: %v", err.Error())
|
||||
}
|
||||
if cfg.Security == "reality" && cfg.SecurityConfig.RealityPublicKey == "" {
|
||||
public, private, err := tool.Curve25519Genkey(false, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate curve25519 key error")
|
||||
}
|
||||
cfg.SecurityConfig.RealityPublicKey = public
|
||||
cfg.SecurityConfig.RealityPrivateKey = private
|
||||
cfg.SecurityConfig.RealityShortId = tool.GenerateShortID(private)
|
||||
}
|
||||
if cfg.SecurityConfig.RealityServerAddr == "" {
|
||||
cfg.SecurityConfig.RealityServerAddr = cfg.SecurityConfig.SNI
|
||||
}
|
||||
if cfg.SecurityConfig.RealityServerPort == 0 {
|
||||
cfg.SecurityConfig.RealityServerPort = 443
|
||||
}
|
||||
config, _ = json.Marshal(cfg)
|
||||
nodeInfo.Config = string(config)
|
||||
}
|
||||
err = l.svcCtx.ServerModel.Update(l.ctx, nodeInfo)
|
||||
data.Name = req.Name
|
||||
data.Tags = tool.StringSliceToString(req.Tags)
|
||||
data.ServerId = req.ServerId
|
||||
data.Port = req.Port
|
||||
data.Address = req.Address
|
||||
data.Protocol = req.Protocol
|
||||
data.Enabled = req.Enabled
|
||||
err = l.svcCtx.NodeModel.UpdateNode(l.ctx, data)
|
||||
if err != nil {
|
||||
l.Errorw("[UpdateNode] Update Database Error: ", logger.Field("error", err.Error()))
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create server error: %v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "[UpdateNode] Update Database Error")
|
||||
}
|
||||
|
||||
// Marshal the task payload
|
||||
payload, err := json.Marshal(queue.GetNodeCountry{
|
||||
Protocol: nodeInfo.Protocol,
|
||||
ServerAddr: nodeInfo.ServerAddr,
|
||||
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
ServerId: []int64{data.ServerId},
|
||||
Search: "",
|
||||
})
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeCountry]: Marshal Error", logger.Field("error", err.Error()))
|
||||
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to marshal task payload")
|
||||
}
|
||||
// Create a queue task
|
||||
task := asynq.NewTask(queue.ForthwithGetCountry, payload)
|
||||
// Enqueue the task
|
||||
taskInfo, err := l.svcCtx.Queue.Enqueue(task)
|
||||
if err != nil {
|
||||
l.Errorw("[GetNodeCountry]: Enqueue Error", logger.Field("error", err.Error()), logger.Field("payload", string(payload)))
|
||||
return errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to enqueue task")
|
||||
}
|
||||
l.Infow("[GetNodeCountry]: Enqueue Success", logger.Field("taskID", taskInfo.ID), logger.Field("payload", string(payload)))
|
||||
l.svcCtx.DeviceManager.Broadcast(device.SubscribeUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/server"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
)
|
||||
|
||||
type UpdateRuleGroupLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewUpdateRuleGroupLogic Update rule group
|
||||
func NewUpdateRuleGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateRuleGroupLogic {
|
||||
return &UpdateRuleGroupLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateRuleGroupLogic) UpdateRuleGroup(req *types.UpdateRuleGroupRequest) error {
|
||||
rs, err := parseAndValidateRules(req.Rules, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = l.svcCtx.ServerModel.UpdateRuleGroup(l.ctx, &server.RuleGroup{
|
||||
Id: req.Id,
|
||||
Icon: req.Icon,
|
||||
Name: req.Name,
|
||||
Tags: tool.StringSliceToString(req.Tags),
|
||||
Rules: strings.Join(rs, "\n"),
|
||||
Enable: req.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/internal/types"
|
||||
"github.com/perfect-panel/server/pkg/ip"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type UpdateServerLogic struct {
|
||||
logger.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// NewUpdateServerLogic Update Server
|
||||
func NewUpdateServerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateServerLogic {
|
||||
return &UpdateServerLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdateServerLogic) UpdateServer(req *types.UpdateServerRequest) error {
|
||||
data, err := l.svcCtx.NodeModel.FindOneServer(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
l.Errorf("[UpdateServer] FindOneServer Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find server error: %v", err.Error())
|
||||
}
|
||||
data.Name = req.Name
|
||||
data.Country = req.Country
|
||||
data.City = req.City
|
||||
// only update address when it's different
|
||||
if req.Address != data.Address {
|
||||
// query server ip location
|
||||
result, err := ip.GetRegionByIp(req.Address)
|
||||
if err != nil {
|
||||
l.Errorf("[UpdateServer] GetRegionByIp Error: %v", err.Error())
|
||||
} else {
|
||||
data.City = result.City
|
||||
data.Country = result.Country
|
||||
}
|
||||
// update address
|
||||
data.Address = req.Address
|
||||
}
|
||||
protocols := make([]node.Protocol, 0)
|
||||
for _, item := range req.Protocols {
|
||||
if item.Type == "" {
|
||||
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols type is empty"), "protocols type is empty")
|
||||
}
|
||||
var protocol node.Protocol
|
||||
tool.DeepCopy(&protocol, item)
|
||||
|
||||
// VLESS Reality Key Generation
|
||||
if protocol.Type == "vless" {
|
||||
if protocol.Security == "reality" {
|
||||
if protocol.RealityPublicKey == "" {
|
||||
public, private, err := tool.Curve25519Genkey(false, "")
|
||||
if err != nil {
|
||||
l.Errorf("[CreateServer] Generate Reality Key Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "generate reality key error: %v", err)
|
||||
}
|
||||
protocol.RealityPublicKey = public
|
||||
protocol.RealityPrivateKey = private
|
||||
protocol.RealityShortId = tool.GenerateShortID(private)
|
||||
}
|
||||
if protocol.RealityServerAddr == "" {
|
||||
protocol.RealityServerAddr = protocol.SNI
|
||||
}
|
||||
if protocol.RealityServerPort == 0 {
|
||||
protocol.RealityServerPort = 443
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// ShadowSocks 2022 Key Generation
|
||||
if protocol.Type == "shadowsocks" {
|
||||
if strings.Contains(protocol.Cipher, "2022") {
|
||||
var length int
|
||||
switch protocol.Cipher {
|
||||
case "2022-blake3-aes-128-gcm":
|
||||
length = 16
|
||||
default:
|
||||
length = 32
|
||||
}
|
||||
if len(protocol.ServerKey) != length {
|
||||
protocol.ServerKey = tool.GenerateCipher(protocol.ServerKey, length)
|
||||
}
|
||||
}
|
||||
}
|
||||
protocols = append(protocols, protocol)
|
||||
}
|
||||
err = data.MarshalProtocols(protocols)
|
||||
if err != nil {
|
||||
l.Errorf("[UpdateServer] Marshal Protocols Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "protocols marshal error"), "protocols marshal error: %v", err)
|
||||
}
|
||||
|
||||
err = l.svcCtx.NodeModel.UpdateServer(l.ctx, data)
|
||||
if err != nil {
|
||||
l.Errorf("[UpdateServer] UpdateServer Error: %v", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update server error: %v", err.Error())
|
||||
}
|
||||
|
||||
return l.svcCtx.NodeModel.ClearNodeCache(l.ctx, &node.FilterNodeParams{
|
||||
Page: 1,
|
||||
Size: 1000,
|
||||
ServerId: []int64{req.Id},
|
||||
Search: "",
|
||||
})
|
||||
}
|
||||
@@ -3,11 +3,11 @@ package subscribe
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/internal/model/subscribe"
|
||||
"github.com/perfect-panel/ppanel-server/internal/svc"
|
||||
"github.com/perfect-panel/ppanel-server/internal/types"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/xerr"
|
||||
"github.com/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"
|
||||
"github.com/perfect-panel/server/pkg/xerr"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user