* refactor: rename queryannouncementhandler.go to queryAnnouncementLogic.go for clarity

* feat(panDomain): update subscription logic to use V2 handler for improved functionality

* refactor(subscribe): replace V2 handler with a unified Handler method for subscription logic

* feat(subscribe): implement user agent limit feature with configurable list

* fix(subscribe): improve error handling and logging for subscription requests

* feat(subscribe): add user agent limit configuration to system settings

* refactor(api): remove deprecated application-related endpoints and types

* refactor(swagger): remove deprecated app.json generation from swagger configuration

* refactor(swagger): remove deprecated app.json check from swagger configuration

* fix(subscribe): update delete method to use Where clause for improved query accuracy

* fix(subscribe): update Id field tag to use primaryKey and improve save method query

* fix(subscribe): update Id field tag to use primaryKey and improve model queries

* fix(subscribe): rename variable for clarity and add special handling for Stash user agent

* fix(email): convert RegisterStartTime and RegisterEndTime to time.Time for accurate query filtering

* refactor(log): consolidate logging models and update related logic for improved clarity and functionality

* fix(types): change Content field type in MessageLog to interface{} for improved flexibility

* fix(log): change MessageLog list to use value type for improved performance and memory efficiency

* fix(email): set EmailTypeVerify in task payload and update content type conversion for verification email

* fix(log): remove unused Id field from SystemLog during login log insertion

* fix(login): remove debug logs and error logging during user login process

* fix(log): add traffic reset logging for subscription resets

* fix(log): insert reset traffic log during subscription activation

* feat(log): add endpoints for retrieving and resetting subscribe traffic logs

* refactor(log): remove Reset Subscribe Traffic Log endpoint and related types

* feat(traffic): add traffic statistics logging and scheduling

* fix(subscribe): ensure active status and reset timestamps during traffic resets

* feat(api): enhance server and node management with new request/response structures

* refactor(api): rename OnlineUser to ServerOnlineUser for clarity

* feat(api): define OnlineUser type with SID and IP fields

* feat(server): implement server management handlers and database schema

* feat(api): add traffic log details filtering and enhance traffic log structures

* feat(api): migrate server and node data handling, update related structures and logic

* feat(server): implement server deletion logic with error handling

* feat(api): update log filtering to use ResetSubscribe type for subscription logs

* feat(api): standardize timestamp field across log structures

* feat(api): refactor cache key handling for server and user lists

* feat(api): enhance server status handling with protocol support and refactor related logic

* fix(traffic): adjust start date for traffic statistics and improve log deletion comment

* feat(api): implement daily traffic ranking for users and servers with error handling

* feat(api): update server total data response to use 'OnlineUsers' and implement daily traffic statistics logging

* feat(api): add log settings management with auto-clear and clear days configuration

* fix(log): correct category in log settings update query

* feat(routes): add handler for scheduled traffic statistics

* feat(model): add user counts struct and update queries for new and renewal users

* feat(api): add referral percentage and only first purchase fields to user model and requests

* feat(database): update user table to add referral percentage and only first purchase fields

* feat(api): add reset sort endpoints for server and node

* feat(api): add sort field to server model

* feat(api): implement sorting functionality for nodes and servers

* fix(database): add sort column to nodes table

* fix(model): enhance user statistics queries with new order and renewal order counts

* fix(log): update timestamp handling in login and registration logs

* fix(log): update sorting logic for server and user subscribe traffic logs

* fix(server): add server status handling based on last reported time

* fix(model): correct filter condition to use 'date' instead of 'data'

* fix(migration): add index for traffic log on timestamp, user_id, and subscribe_id

* fix(log): optimize user traffic rank data handling by using append instead of index assignment

* fix(filter): refactor node list creation to use append and remove duplicates from tags

* fix(node): add ServerId and Enabled fields to node update logic

* feat(tags): add endpoint to query all node tags

* fix(preview): add Preload parameter to FilterNodeList for improved data retrieval

* fix(log): date is empty

* feat(subscribe): add Language field to subscription models and update query logic

* feat(subscription): add Language parameter to GetSubscription request and update query logic

* fix(server): encode ServerKey in base64 and update last reported time for nodes

* feat: delete common GetSubscription

* feat(subscription): implement FilterList method for subscription queries and update related logic

* fix(subscribe): remove duplicate user agents in SubscribeHandler

* fix(push): initialize onlineUsers as a map in pushOnlineUsersLogic

* fix(reset): initialize subs as a map in clearCache method

* refactor(query): simplify node and tag filtering using InSet function

* feat(userlist): enhance GetServerUserListLogic with improved node and tag handling

* fix(userlist): correct node ID assignment and update query logic for tag filtering

* fix(userlist): correct node ID assignment in getServerUserListLogic

* refactor(query): streamline query construction for tag filtering

* fix(statistics): optimize server ranking data handling in QueryServerTotalDataLogic

* refactor(statistics): simplify server ranking data construction in QueryServerTotalDataLogic

* fix(statistics): correct server traffic data assignment in QueryServerTotalDataLogic

* fix(statistics): optimize yesterday's top 10 server traffic data assignment in QueryServerTotalDataLogic

* fix(middleware): remove duplicate elements from user agent list in PanDomainMiddleware

* feat(middleware): enhance user agent handling by querying client list in PanDomainMiddleware

* feat(client): subscribe_template

* feat(oauth): add user agent and IP logging to registration and login processes

* fix(balance): add timestamp to balance logs for payment, refund, and recharge transactions

* fix(log): correct comment for CommissionTypeRefund to improve clarity

* fix(log): replace magic number with constant for gift type in purchase checkout logic

* fix(log): rename OrderId to OrderNo for consistency in balance logging

* feat(log): add logging for balance, gift amount, and commission adjustments

* fix(user): correct placement of DeepCopy for user info update logic

* feat(log): add UserSubscribeId to FilterSubscribeLogRequest for enhanced filtering

* fix(purchase): streamline error handling and improve JSON marshaling for temporary orders

* fix(order): simplify commission handling and improve payload parsing logic

* fix(order): update commission calculation to actual payment amount minus gateway handling fee

* feat(payment): add support for CryptoSaaS payment platform and enhance configuration handling

* fix(balance): update QueryUserBalanceLog response structure to include balance log list

* fix(email): update task progress handling to use specific task ID for updates

* feat(quota): add quota task creation and querying endpoints with updated data structures

* fix(email): update task handling to use generic task model and improve error logging

* fix(order): improve error logging for database transaction and user cache updates

* feat(quota): enhance quota task management with new request structures and processing logic

* fix(quota): remove redundant quota task status endpoint from admin marketing routes

* fix(worker): update task completion status handling in worker logic

* fix(quota): update taskInfo to include current subscription count in quota logic

* doc(log): rename function for clarity and add cache cleanup comment

* fix(quota): update time handling in quota logic and correct subscriber ID query

* fix(quota): update time handling to use UnixMilli for start time in quota logic

* feat(protocol): add server protocol configuration query and enhance protocol options

* fix(quota): correct time range queries for start and expire times in quota logic

* fix(types): update plugin options to include 'none' in the plugin field

---------

Co-authored-by: Chang lue Tsen <tension@ppanel.dev>
This commit is contained in:
Leif Draven
2025-09-14 22:50:22 +09:00
committed by GitHub
parent b5e244d83d
commit e895180388
359 changed files with 9213 additions and 12125 deletions
@@ -0,0 +1,64 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterBalanceLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterBalanceLogLogic Filter balance log
func NewFilterBalanceLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterBalanceLogLogic {
return &FilterBalanceLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterBalanceLogLogic) FilterBalanceLog(req *types.FilterBalanceLogRequest) (resp *types.FilterBalanceLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeBalance.Uint8(),
Data: req.Date,
ObjectID: req.UserId,
})
if err != nil {
l.Errorw("[FilterBalanceLog] Query User Balance Log Error:", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Balance Log Error")
}
list := make([]types.BalanceLog, 0)
for _, datum := range data {
var content log.Balance
if err = content.Unmarshal([]byte(datum.Content)); err != nil {
l.Errorf("[QueryUserBalanceLog] unmarshal balance log content failed: %v", err.Error())
continue
}
list = append(list, types.BalanceLog{
UserId: datum.ObjectID,
Amount: content.Amount,
Type: content.Type,
OrderNo: content.OrderNo,
Balance: content.Balance,
Timestamp: content.Timestamp,
})
}
return &types.FilterBalanceLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,61 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterCommissionLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterCommissionLogLogic Filter commission log
func NewFilterCommissionLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterCommissionLogLogic {
return &FilterCommissionLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterCommissionLogLogic) FilterCommissionLog(req *types.FilterCommissionLogRequest) (resp *types.FilterCommissionLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Data: req.Date,
Type: log.TypeCommission.Uint8(),
ObjectID: req.UserId,
})
if err != nil {
l.Errorw("Query User Commission Log failed", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Query User Commission Log failed")
}
var list []types.CommissionLog
for _, datum := range data {
var content log.Commission
if err = content.Unmarshal([]byte(datum.Content)); err != nil {
l.Errorf("unmarshal commission log content failed: %v", err.Error())
continue
}
list = append(list, types.CommissionLog{
UserId: datum.ObjectID,
Type: content.Type,
Amount: content.Amount,
OrderNo: content.OrderNo,
Timestamp: content.Timestamp,
})
}
return &types.FilterCommissionLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,68 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterEmailLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterEmailLogLogic Filter email log
func NewFilterEmailLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterEmailLogLogic {
return &FilterEmailLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterEmailLogLogic) FilterEmailLog(req *types.FilterLogParams) (resp *types.FilterEmailLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeEmailMessage.Uint8(),
Data: req.Date,
Search: req.Search,
})
if err != nil {
l.Errorf("[FilterEmailLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.MessageLog
for _, datum := range data {
var content log.Message
err = content.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[FilterEmailLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.MessageLog{
Id: datum.Id,
Type: datum.Type,
Platform: content.Platform,
To: content.To,
Subject: content.Subject,
Content: content.Content,
Status: content.Status,
CreatedAt: datum.CreatedAt.UnixMilli(),
})
}
return &types.FilterEmailLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,68 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterGiftLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Filter gift log
func NewFilterGiftLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterGiftLogLogic {
return &FilterGiftLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterGiftLogLogic) FilterGiftLog(req *types.FilterGiftLogRequest) (resp *types.FilterGiftLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeGift.Uint8(),
ObjectID: req.UserId,
Data: req.Date,
Search: req.Search,
})
if err != nil {
l.Errorf("[FilterGiftLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.GiftLog
for _, datum := range data {
var content log.Gift
err = content.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[FilterGiftLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.GiftLog{
Type: content.Type,
UserId: datum.ObjectID,
OrderNo: content.OrderNo,
SubscribeId: content.SubscribeId,
Amount: content.Amount,
Balance: content.Balance,
Remark: content.Remark,
Timestamp: content.Timestamp,
})
}
return &types.FilterGiftLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,65 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterLoginLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterLoginLogLogic Filter login log
func NewFilterLoginLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterLoginLogLogic {
return &FilterLoginLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterLoginLogLogic) FilterLoginLog(req *types.FilterLoginLogRequest) (resp *types.FilterLoginLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeLogin.Uint8(),
ObjectID: req.UserId,
Data: req.Date,
Search: req.Search,
})
if err != nil {
l.Errorf("[FilterLoginLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.LoginLog
for _, datum := range data {
var item log.Login
err = item.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[FilterLoginLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.LoginLog{
UserId: datum.ObjectID,
Method: item.Method,
LoginIP: item.LoginIP,
UserAgent: item.UserAgent,
Success: item.Success,
Timestamp: datum.CreatedAt.UnixMilli(),
})
}
return &types.FilterLoginLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,68 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterMobileLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Filter mobile log
func NewFilterMobileLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterMobileLogLogic {
return &FilterMobileLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterMobileLogLogic) FilterMobileLog(req *types.FilterLogParams) (resp *types.FilterMobileLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeMobileMessage.Uint8(),
Data: req.Date,
Search: req.Search,
})
if err != nil {
l.Errorf("[FilterMobileLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.MessageLog
for _, datum := range data {
var content log.Message
err = content.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[FilterMobileLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.MessageLog{
Id: datum.Id,
Type: datum.Type,
Platform: content.Platform,
To: content.To,
Subject: content.Subject,
Content: content.Content,
Status: content.Status,
CreatedAt: datum.CreatedAt.UnixMilli(),
})
}
return &types.FilterMobileLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,66 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterRegisterLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Filter register log
func NewFilterRegisterLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterRegisterLogLogic {
return &FilterRegisterLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterRegisterLogLogic) FilterRegisterLog(req *types.FilterRegisterLogRequest) (resp *types.FilterRegisterLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeRegister.Uint8(),
ObjectID: req.UserId,
Data: req.Date,
Search: req.Search,
})
if err != nil {
l.Errorf("[FilterRegisterLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.RegisterLog
for _, datum := range data {
var item log.Register
err = item.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[FilterLoginLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.RegisterLog{
UserId: datum.ObjectID,
AuthMethod: item.AuthMethod,
Identifier: item.Identifier,
RegisterIP: item.RegisterIP,
UserAgent: item.UserAgent,
Timestamp: item.Timestamp,
})
}
return &types.FilterRegisterLogResponse{
List: list,
Total: total,
}, nil
}
@@ -0,0 +1,66 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterResetSubscribeLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterResetSubscribeLogLogic Filter reset subscribe log
func NewFilterResetSubscribeLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterResetSubscribeLogLogic {
return &FilterResetSubscribeLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterResetSubscribeLogLogic) FilterResetSubscribeLog(req *types.FilterResetSubscribeLogRequest) (resp *types.FilterResetSubscribeLogResponse, err error) {
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeResetSubscribe.Uint8(),
ObjectID: req.UserSubscribeId,
Data: req.Date,
Search: req.Search,
})
if err != nil {
l.Errorf("[FilterResetSubscribeLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.ResetSubscribeLog
for _, item := range data {
var content log.ResetSubscribe
err = content.Unmarshal([]byte(item.Content))
if err != nil {
l.Errorf("[FilterResetSubscribeLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.ResetSubscribeLog{
Type: content.Type,
UserId: content.UserId,
UserSubscribeId: item.ObjectID,
OrderNo: content.OrderNo,
Timestamp: content.Timestamp,
})
}
return &types.FilterResetSubscribeLogResponse{
List: list,
Total: total,
}, nil
}
@@ -0,0 +1,166 @@
package log
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/model/traffic"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterServerTrafficLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterServerTrafficLogLogic Filter server traffic log
func NewFilterServerTrafficLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterServerTrafficLogLogic {
return &FilterServerTrafficLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterServerTrafficLogLogic) FilterServerTrafficLog(req *types.FilterServerTrafficLogRequest) (resp *types.FilterServerTrafficLogResponse, err error) {
today := time.Now().Format("2006-01-02")
var list []types.ServerTrafficLog
var total int64
if req.Date == today || req.Date == "" {
now := time.Now()
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
end := start.Add(24 * time.Hour).Add(-time.Nanosecond)
var serverTraffic []log.ServerTraffic
err = l.svcCtx.DB.WithContext(l.ctx).
Model(&traffic.TrafficLog{}).
Select("server_id, SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
Where("timestamp BETWEEN ? AND ?", start, end).
Group("server_id").
Order("SUM(download + upload) DESC").
Scan(&serverTraffic).Error
if err != nil {
l.Errorw("[FilterServerTrafficLog] Query Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "today traffic query error: %s", err.Error())
}
for _, v := range serverTraffic {
list = append(list, types.ServerTrafficLog{
ServerId: v.ServerId,
Upload: v.Upload,
Download: v.Download,
Total: v.Total,
Date: today,
Details: true,
})
}
todayTotal := len(list)
startIdx := (req.Page - 1) * req.Size
endIdx := startIdx + req.Size
if startIdx < todayTotal {
if endIdx > todayTotal {
endIdx = todayTotal
}
pageData := list[startIdx:endIdx]
return &types.FilterServerTrafficLogResponse{
List: pageData,
Total: int64(todayTotal),
}, nil
}
need := endIdx - todayTotal
historyPage := (need + req.Size - 1) / req.Size // 算出需要的历史页数
historyData, historyTotal, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: historyPage,
Size: need,
Type: log.TypeServerTraffic.Uint8(),
})
if err != nil {
l.Errorw("[FilterServerTrafficLog] Query History Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "history query error: %s", err.Error())
}
for _, item := range historyData {
var content log.ServerTraffic
if err = content.Unmarshal([]byte(item.Content)); err != nil {
l.Errorw("[FilterServerTrafficLog] Unmarshal Error", logger.Field("error", err.Error()), logger.Field("content", item.Content))
continue
}
hasDetails := true
if l.svcCtx.Config.Log.AutoClear {
last := now.AddDate(0, 0, int(-l.svcCtx.Config.Log.ClearDays))
dataTime, err := time.Parse(time.DateOnly, item.Date)
if err != nil {
l.Errorw("[FilterServerTrafficLog] Parse Date Error", logger.Field("error", err.Error()), logger.Field("date", item.Date))
} else {
if dataTime.Before(last) {
hasDetails = false
} else {
hasDetails = true
}
}
}
list = append(list, types.ServerTrafficLog{
ServerId: item.ObjectID,
Upload: content.Upload,
Download: content.Download,
Total: content.Total,
Date: item.Date,
Details: hasDetails,
})
}
// 返回最终分页数据
if endIdx > len(list) {
endIdx = len(list)
}
pageData := list[startIdx:endIdx]
return &types.FilterServerTrafficLogResponse{
List: pageData,
Total: int64(todayTotal) + historyTotal,
}, nil
}
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeServerTraffic.Uint8(),
})
if err != nil {
l.Errorw("[FilterServerTrafficLog] Query Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "history query error: %s", err.Error())
}
for _, item := range data {
var content log.ServerTraffic
if err = content.Unmarshal([]byte(item.Content)); err != nil {
l.Errorw("[FilterServerTrafficLog] Unmarshal Error", logger.Field("error", err.Error()), logger.Field("content", item.Content))
continue
}
list = append(list, types.ServerTrafficLog{
ServerId: item.ObjectID,
Upload: content.Upload,
Download: content.Download,
Total: content.Total,
Date: item.Date,
Details: false,
})
}
return &types.FilterServerTrafficLogResponse{
List: list,
Total: total,
}, nil
}
@@ -0,0 +1,71 @@
package log
import (
"context"
"strconv"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterSubscribeLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterSubscribeLogLogic Filter subscribe log
func NewFilterSubscribeLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterSubscribeLogLogic {
return &FilterSubscribeLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterSubscribeLogLogic) FilterSubscribeLog(req *types.FilterSubscribeLogRequest) (resp *types.FilterSubscribeLogResponse, err error) {
params := &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeSubscribe.Uint8(),
Data: req.Date,
ObjectID: req.UserId,
}
if req.UserSubscribeId != 0 {
params.Search = `"user_subscribe_id":` + strconv.FormatInt(req.UserSubscribeId, 10)
}
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, params)
if err != nil {
l.Errorf("[FilterSubscribeLog] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log")
}
var list []types.SubscribeLog
for _, datum := range data {
var content log.Subscribe
err = content.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[FilterSubscribeLog] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.SubscribeLog{
UserId: datum.ObjectID,
Token: content.Token,
UserAgent: content.UserAgent,
ClientIP: content.ClientIP,
UserSubscribeId: content.UserSubscribeId,
Timestamp: datum.CreatedAt.UnixMilli(),
})
}
return &types.FilterSubscribeLogResponse{
Total: total,
List: list,
}, nil
}
@@ -0,0 +1,84 @@
package log
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/traffic"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterTrafficLogDetailsLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterTrafficLogDetailsLogic Filter traffic log details
func NewFilterTrafficLogDetailsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterTrafficLogDetailsLogic {
return &FilterTrafficLogDetailsLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterTrafficLogDetailsLogic) FilterTrafficLogDetails(req *types.FilterTrafficLogDetailsRequest) (resp *types.FilterTrafficLogDetailsResponse, err error) {
var start, end time.Time
if req.Date != "" {
day, err := time.ParseInLocation("2006-01-02", req.Date, time.Local)
if err != nil {
l.Errorw("[FilterTrafficLogDetails] Date Parse Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), " date parse error: %s", err.Error())
}
start = day
end = day.Add(24*time.Hour - time.Nanosecond)
} else {
// query today
now := time.Now()
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
end = start.Add(24*time.Hour - time.Nanosecond)
}
var data []*traffic.TrafficLog
tx := l.svcCtx.DB.WithContext(l.ctx).Model(&traffic.TrafficLog{})
if req.ServerId != 0 {
tx = tx.Where("server_id = ?", req.ServerId)
}
if !start.IsZero() && !end.IsZero() {
tx = tx.Where("timestamp BETWEEN ? AND ?", start, end)
}
if req.UserId != 0 {
tx = tx.Where("user_id = ?", req.UserId)
}
if req.SubscribeId != 0 {
tx = tx.Where("subscribe_id = ?", req.SubscribeId)
}
var total int64
err = tx.Count(&total).Limit(req.Size).Offset((req.Page - 1) * req.Size).Find(&data).Error
if err != nil {
l.Errorw("[FilterTrafficLogDetails] Query Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " database query error: %s", err.Error())
}
var logs []types.TrafficLogDetails
for _, v := range data {
logs = append(logs, types.TrafficLogDetails{
Id: v.Id,
UserId: v.UserId,
ServerId: v.ServerId,
SubscribeId: v.SubscribeId,
Download: v.Download,
Upload: v.Upload,
Timestamp: v.Timestamp.UnixMilli(),
})
}
return &types.FilterTrafficLogDetailsResponse{
List: logs,
Total: total,
}, nil
}
@@ -0,0 +1,160 @@
package log
import (
"context"
"time"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/model/traffic"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
type FilterUserSubscribeTrafficLogLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewFilterUserSubscribeTrafficLogLogic Filter user subscribe traffic log
func NewFilterUserSubscribeTrafficLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FilterUserSubscribeTrafficLogLogic {
return &FilterUserSubscribeTrafficLogLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *FilterUserSubscribeTrafficLogLogic) FilterUserSubscribeTrafficLog(req *types.FilterSubscribeTrafficRequest) (resp *types.FilterSubscribeTrafficResponse, err error) {
if req.Size <= 0 {
req.Size = 10
}
if req.Page <= 0 {
req.Page = 1
}
today := time.Now().Format("2006-01-02")
var list []types.UserSubscribeTrafficLog
var total int64
if req.Date == today || req.Date == "" {
now := time.Now()
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
end := start.Add(24 * time.Hour).Add(-time.Nanosecond)
var userTraffic []types.UserSubscribeTrafficLog
err = l.svcCtx.DB.WithContext(l.ctx).
Model(&traffic.TrafficLog{}).
Select("user_id, subscribe_id, SUM(download + upload) AS total, SUM(download) AS download, SUM(upload) AS upload").
Where("timestamp BETWEEN ? AND ?", start, end).
Group("user_id, subscribe_id").
Order("SUM(download + upload) DESC").
Scan(&userTraffic).Error
if err != nil {
l.Errorw("[FilterUserSubscribeTrafficLog] Query Database Error", logger.Field("error", err.Error()))
return nil, err
}
for _, v := range userTraffic {
list = append(list, types.UserSubscribeTrafficLog{
UserId: v.UserId,
SubscribeId: v.SubscribeId,
Upload: v.Upload,
Download: v.Download,
Total: v.Total,
Date: today,
Details: true,
})
}
todayTotal := len(list)
startIdx := (req.Page - 1) * req.Size
endIdx := startIdx + req.Size
if startIdx < todayTotal {
if endIdx > todayTotal {
endIdx = todayTotal
}
pageData := list[startIdx:endIdx]
return &types.FilterSubscribeTrafficResponse{
List: pageData,
Total: int64(todayTotal),
}, nil
}
need := endIdx - todayTotal
historyPage := (need + req.Size - 1) / req.Size // 算出需要的历史页数
historyData, historyTotal, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: historyPage,
Size: need,
Type: log.TypeSubscribeTraffic.Uint8(),
})
if err != nil {
l.Errorw("[FilterUserSubscribeTrafficLog] Query Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterUserSubscribeTrafficLog] Query Database Error")
}
for _, datum := range historyData {
var item log.UserTraffic
err = item.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorw("[FilterUserSubscribeTrafficLog] Unmarshal Content Error", logger.Field("error", err.Error()))
continue
}
list = append(list, types.UserSubscribeTrafficLog{
UserId: item.UserId,
SubscribeId: item.SubscribeId,
Upload: item.Upload,
Download: item.Download,
Total: item.Total,
Date: datum.Date,
Details: false,
})
}
// 返回最终分页数据
if endIdx > len(list) {
endIdx = len(list)
}
pageData := list[startIdx:endIdx]
return &types.FilterSubscribeTrafficResponse{
List: pageData,
Total: int64(todayTotal) + historyTotal,
}, nil
}
var data []*log.SystemLog
data, total, err = l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: log.TypeSubscribeTraffic.Uint8(),
Data: req.Date,
})
if err != nil {
l.Errorw("[FilterUserSubscribeTrafficLog] Query Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[FilterUserSubscribeTrafficLog] Query Database Error")
}
for _, datum := range data {
var item log.UserTraffic
err = item.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorw("[FilterUserSubscribeTrafficLog] Unmarshal Content Error", logger.Field("error", err.Error()))
continue
}
list = append(list, types.UserSubscribeTrafficLog{
UserId: item.UserId,
SubscribeId: item.SubscribeId,
Upload: item.Upload,
Download: item.Download,
Total: item.Total,
Date: datum.Date,
Details: false,
})
}
return &types.FilterSubscribeTrafficResponse{
List: list,
Total: total,
}, nil
}
@@ -0,0 +1,37 @@
package log
import (
"context"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
)
type GetLogSettingLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// Get log setting
func NewGetLogSettingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLogSettingLogic {
return &GetLogSettingLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetLogSettingLogic) GetLogSetting() (resp *types.LogSetting, err error) {
configs, err := l.svcCtx.SystemModel.GetLogConfig(l.ctx)
if err != nil {
l.Errorw("[GetLogSetting] Database query error", logger.Field("error", err.Error()))
return nil, err
}
resp = &types.LogSetting{}
// reflect to response
tool.SystemConfigSliceReflectToStruct(configs, resp)
return
}
@@ -7,7 +7,6 @@ import (
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
@@ -28,20 +27,39 @@ func NewGetMessageLogListLogic(ctx context.Context, svcCtx *svc.ServiceContext)
}
func (l *GetMessageLogListLogic) GetMessageLogList(req *types.GetMessageLogListRequest) (resp *types.GetMessageLogListResponse, err error) {
total, data, err := l.svcCtx.LogModel.FindMessageLogList(l.ctx, req.Page, req.Size, log.MessageLogFilterParams{
Type: req.Type,
Platform: req.Platform,
To: req.To,
Subject: req.Subject,
Content: req.Content,
Status: req.Status,
data, total, err := l.svcCtx.LogModel.FilterSystemLog(l.ctx, &log.FilterParams{
Page: req.Page,
Size: req.Size,
Type: req.Type,
Search: req.Search,
})
if err != nil {
l.Errorw("[GetMessageLogList] Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "[GetMessageLogList] Database Error: %s", err.Error())
l.Errorf("[GetMessageLogList] failed to filter system log: %v", err.Error())
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "failed to filter system log: %v", err.Error())
}
var list []types.MessageLog
tool.DeepCopy(&list, data)
for _, datum := range data {
var content log.Message
err = content.Unmarshal([]byte(datum.Content))
if err != nil {
l.Errorf("[GetMessageLogList] failed to unmarshal content: %v", err.Error())
continue
}
list = append(list, types.MessageLog{
Id: datum.Id,
Type: datum.Type,
Platform: content.Platform,
To: content.To,
Subject: content.Subject,
Content: content.Content,
Status: content.Status,
CreatedAt: datum.CreatedAt.UnixMilli(),
})
}
return &types.GetMessageLogListResponse{
Total: total,
@@ -0,0 +1,63 @@
package log
import (
"context"
"reflect"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/model/system"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type UpdateLogSettingLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewUpdateLogSettingLogic Update log setting
func NewUpdateLogSettingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateLogSettingLogic {
return &UpdateLogSettingLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UpdateLogSettingLogic) UpdateLogSetting(req *types.LogSetting) error {
v := reflect.ValueOf(*req)
// Get the reflection type of the structure
t := v.Type()
err := l.svcCtx.SystemModel.Transaction(l.ctx, func(db *gorm.DB) error {
var err error
for i := 0; i < v.NumField(); i++ {
// Get the field name
fieldName := t.Field(i).Name
// Get the field value to string
fieldValue := tool.ConvertValueToString(v.Field(i))
// Update the server config
err = db.Model(&system.System{}).Where("`category` = 'log' and `key` = ?", fieldName).Update("value", fieldValue).Error
if err != nil {
break
}
}
return err
})
if err != nil {
l.Errorw("[UpdateLogSetting] update log setting error", logger.Field("error", err.Error()))
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), " update log setting error: %v", err)
}
l.svcCtx.Config.Log = config.Log{
AutoClear: *req.AutoClear,
ClearDays: req.ClearDays,
}
return nil
}