Files
ttstd-infinite-canvas/backend/internal/app/admin_login_logs.go
T

88 lines
2.7 KiB
Go

package app
import (
"errors"
"net"
"strings"
"time"
"yingce/backend/internal/kernel"
"yingce/backend/internal/model"
"yingce/backend/internal/repository"
"gorm.io/gorm"
)
type LoginLogQuery struct{ Keyword, Event, Method, Result, IP, From, To string }
type LoginLogPage struct {
Logs []model.LoginLog `json:"logs"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
func (s *Service) AdminLoginLogs(actor *model.User, query LoginLogQuery, page, pageSize int) (*LoginLogPage, error) {
if err := s.RequireAdmin(actor); err != nil {
return nil, err
}
filter := repository.LoginLogFilter{Keyword: strings.TrimSpace(query.Keyword), Event: strings.TrimSpace(query.Event), Method: strings.TrimSpace(query.Method), Result: strings.TrimSpace(query.Result), IP: strings.TrimSpace(query.IP)}
for _, enum := range []struct {
value string
options []string
}{
{filter.Event, []string{"", "login", "logout"}},
{filter.Result, []string{"", "success", "failed"}},
{filter.Method, []string{"", "password", "email", "sms", "linuxdo", "verification", "unknown"}},
} {
valid := false
for _, option := range enum.options {
if option == enum.value {
valid = true
break
}
}
if !valid {
return nil, kernel.BadAuthRequest("登录日志筛选参数无效")
}
}
if len([]rune(filter.Keyword)) > 160 || (filter.IP != "" && net.ParseIP(filter.IP) == nil) {
return nil, kernel.BadAuthRequest("关键词或 IP 参数无效")
}
for _, bound := range []struct {
raw string
value **time.Time
}{{query.From, &filter.From}, {query.To, &filter.To}} {
if bound.raw == "" {
continue
}
parsed, err := time.Parse(time.RFC3339, bound.raw)
if err != nil {
return nil, kernel.BadAuthRequest("时间必须使用 RFC3339 格式")
}
*bound.value = &parsed
}
if filter.From != nil && filter.To != nil && filter.From.After(*filter.To) {
return nil, kernel.BadAuthRequest("开始时间不能晚于结束时间")
}
page, pageSize = normalizeAdminPage(page, pageSize)
logs, total, err := s.repo.LoginLogs(filter, pageSize, (page-1)*pageSize)
if err != nil {
return nil, err
}
return &LoginLogPage{Logs: logs, Total: total, Page: page, PageSize: pageSize}, nil
}
func (s *Service) AdminLoginLog(actor *model.User, id string) (*model.LoginLog, error) {
if err := s.RequireAdmin(actor); err != nil {
return nil, err
}
event, err := s.repo.LoginLog(strings.TrimSpace(id))
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, kernel.NotFound("登录日志不存在")
}
return event, err
}
func (s *Service) RecordRejectedLogin(method string, context LoginContext, status int) error {
return s.authDomain().RecordRejectedLogin(method, context, status)
}