e6956aa001
- React frontend with route-level code splitting - Backend rebranded from Baihu to TaskPool - DB brand migration script and local compatibility
129 lines
3.6 KiB
Go
129 lines
3.6 KiB
Go
package services
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/engigu/taskpool/internal/constant"
|
|
"github.com/engigu/taskpool/internal/database"
|
|
"github.com/engigu/taskpool/internal/eventbus"
|
|
"github.com/engigu/taskpool/internal/models"
|
|
"github.com/engigu/taskpool/internal/utils"
|
|
)
|
|
|
|
type LoginLogService struct{}
|
|
|
|
func NewLoginLogService() *LoginLogService {
|
|
return &LoginLogService{}
|
|
}
|
|
|
|
// Create 创建登录日志
|
|
func (s *LoginLogService) Create(username, ip, userAgent, status, message string) error {
|
|
level := constant.LogLevelInfo
|
|
if status != "success" {
|
|
level = constant.LogLevelWarning
|
|
}
|
|
log := &models.AppLog{
|
|
ID: utils.GenerateID(),
|
|
Category: constant.LogCategoryLoginLog,
|
|
Title: username,
|
|
Content: models.BigText(userAgent),
|
|
Level: level,
|
|
Status: status,
|
|
RefID: ip,
|
|
ErrorMsg: models.BigText(message),
|
|
}
|
|
err := database.DB.Create(log).Error
|
|
if err == nil {
|
|
eventbus.DefaultBus.Publish(eventbus.Event{
|
|
Type: constant.EventAppLogAdded,
|
|
Payload: log,
|
|
})
|
|
}
|
|
return err
|
|
}
|
|
|
|
// SubscribeEvents 注册订阅事件
|
|
func (s *LoginLogService) SubscribeEvents(bus *eventbus.EventBus) {
|
|
// 用户登录事件
|
|
bus.Subscribe(constant.EventUserLogin, func(e eventbus.Event) {
|
|
payload, ok := e.Payload.(map[string]interface{})
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
username, _ := payload["username"].(string)
|
|
ip, _ := payload["ip"].(string)
|
|
userAgent, _ := payload["userAgent"].(string)
|
|
status, _ := payload["status"].(string)
|
|
message, _ := payload["message"].(string)
|
|
|
|
s.Create(username, ip, userAgent, status, message)
|
|
|
|
// 如果登录成功,触发系统通知
|
|
if status == "success" {
|
|
bus.Publish(eventbus.Event{
|
|
Type: constant.EventSystemNotice,
|
|
Payload: map[string]interface{}{
|
|
"title": "登录提醒",
|
|
"content": fmt.Sprintf("用户 %s 已从 IP %s 登录系统", username, ip),
|
|
"level": constant.LogLevelWarning,
|
|
},
|
|
})
|
|
}
|
|
})
|
|
|
|
// 暴力破解防御触发事件
|
|
bus.Subscribe(constant.EventBruteForceLogin, func(e eventbus.Event) {
|
|
payload, ok := e.Payload.(map[string]interface{})
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
username, _ := payload["username"].(string)
|
|
ip, _ := payload["ip"].(string)
|
|
userAgent, _ := payload["userAgent"].(string)
|
|
|
|
s.Create(username, ip, userAgent, "failed", "尝试次数过多,由于暴力破解防御机制已锁定")
|
|
|
|
// 触发系统通知
|
|
bus.Publish(eventbus.Event{
|
|
Type: constant.EventSystemNotice,
|
|
Payload: map[string]interface{}{
|
|
"title": "系统安全警告",
|
|
"content": fmt.Sprintf("检测到 IP %s 正在尝试暴力破解用户 %s", ip, username),
|
|
"level": constant.LogLevelError,
|
|
},
|
|
})
|
|
})
|
|
}
|
|
|
|
// List 获取登录日志列表
|
|
func (s *LoginLogService) List(page, pageSize int, username string) ([]models.AppLog, int64, error) {
|
|
var logs []models.AppLog
|
|
var total int64
|
|
|
|
query := database.DB.Model(&models.AppLog{}).Where("category = ?", constant.LogCategoryLoginLog)
|
|
if username != "" {
|
|
query = query.Where("title LIKE ?", "%"+username+"%")
|
|
}
|
|
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
offset := (page - 1) * pageSize
|
|
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&logs).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return logs, total, nil
|
|
}
|
|
|
|
// CleanOldLogs 清理指定天数前的日志
|
|
func (s *LoginLogService) CleanOldLogs(days int) (int64, error) {
|
|
deadline := time.Now().AddDate(0, 0, -days)
|
|
result := database.DB.Unscoped().Where("category = ? AND created_at < ?", constant.LogCategoryLoginLog, deadline).Delete(&models.AppLog{})
|
|
return result.RowsAffected, result.Error
|
|
}
|