107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
package handler
|
||
|
||
import (
|
||
"encoding/json"
|
||
"log/slog"
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"silk-server-go/internal/middleware"
|
||
"silk-server-go/internal/model"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// RegisterAuditRoutes 注册审计日志路由
|
||
func RegisterAuditRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
||
perm := middleware.RequirePermission(db, "audit:read")
|
||
rg.GET("/audit-logs", perm, listAuditLogs(db))
|
||
rg.POST("/audit-logs", perm, createAuditLog(db))
|
||
}
|
||
|
||
// listAuditLogs 审计日志列表(query: limit,默认100,上限500)
|
||
func listAuditLogs(db *gorm.DB) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
limit := 100
|
||
if l := c.Query("limit"); l != "" {
|
||
if n, err := strconv.Atoi(l); err == nil && n > 0 {
|
||
limit = n
|
||
}
|
||
}
|
||
if limit > 500 {
|
||
limit = 500
|
||
}
|
||
|
||
var logs []model.AuditLog
|
||
db.Order("created_at DESC").Limit(limit).Find(&logs)
|
||
c.JSON(http.StatusOK, logs)
|
||
}
|
||
}
|
||
|
||
// createAuditLog 手动写入审计日志,自动补充 userId/username/IP/UA
|
||
func createAuditLog(db *gorm.DB) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
var body struct {
|
||
Action string `json:"action"`
|
||
Resource *string `json:"resource"`
|
||
TargetID *string `json:"targetId"`
|
||
Description *string `json:"description"`
|
||
Metadata json.RawMessage `json:"metadata"`
|
||
}
|
||
if err := c.ShouldBindJSON(&body); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
if body.Action == "" {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "action 不能为空"})
|
||
return
|
||
}
|
||
|
||
// 从 context 获取当前用户信息
|
||
var userID, username *string
|
||
if user, ok := c.Get("user"); ok {
|
||
if userMap, ok := user.(map[string]interface{}); ok {
|
||
if sub, ok := userMap["sub"].(string); ok {
|
||
s := sub
|
||
userID = &s
|
||
}
|
||
if uname, ok := userMap["username"].(string); ok {
|
||
s := uname
|
||
username = &s
|
||
}
|
||
}
|
||
}
|
||
|
||
ip := c.ClientIP()
|
||
ua := c.GetHeader("User-Agent")
|
||
|
||
log := model.AuditLog{
|
||
UserID: userID,
|
||
Username: username,
|
||
Action: body.Action,
|
||
Resource: body.Resource,
|
||
TargetID: body.TargetID,
|
||
Description: body.Description,
|
||
IPAddress: &ip,
|
||
UserAgent: &ua,
|
||
Metadata: body.Metadata,
|
||
}
|
||
if err := db.Create(&log).Error; err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "写入审计日志失败"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusCreated, log)
|
||
}
|
||
}
|
||
|
||
// recordAudit 记录审计日志(供其他 handler 调用)
|
||
func recordAudit(db *gorm.DB, log *model.AuditLog) {
|
||
if log == nil || log.Action == "" {
|
||
return
|
||
}
|
||
if err := db.Create(log).Error; err != nil {
|
||
slog.Warn("记录审计日志失败", "error", err)
|
||
}
|
||
}
|