208 lines
5.7 KiB
Go
208 lines
5.7 KiB
Go
package audit
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.apinb.com/bsm-sdk/core/infra"
|
|
"git.apinb.com/ops/logs/internal/impl"
|
|
"git.apinb.com/ops/logs/internal/models"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func ListAuditLogs(ctx *gin.Context) {
|
|
page, size := pageAndSize(ctx.DefaultQuery("page", "1"), ctx.DefaultQuery("page_size", "50"))
|
|
q := impl.DBService.Model(&models.AuditLog{})
|
|
if v := strings.TrimSpace(ctx.Query("source_service")); v != "" {
|
|
q = q.Where("source_service = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("actor_id")); v != "" {
|
|
q = q.Where("actor_id = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("action")); v != "" {
|
|
q = q.Where("action = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("object_type")); v != "" {
|
|
q = q.Where("object_type = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("object_id")); v != "" {
|
|
q = q.Where("object_id = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("operation_risk")); v != "" {
|
|
q = q.Where("operation_risk = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("result")); v != "" {
|
|
q = q.Where("result = ?", v)
|
|
}
|
|
var total int64
|
|
_ = q.Count(&total).Error
|
|
var rows []models.AuditLog
|
|
if err := q.Order("id desc").Offset((page - 1) * size).Limit(size).Find(&rows).Error; err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
infra.Response.Success(ctx, gin.H{"total": total, "page": page, "page_size": size, "items": rows})
|
|
}
|
|
|
|
func CreateAuditLog(ctx *gin.Context) {
|
|
var req Record
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
req.ClientIP = firstNonEmpty(req.ClientIP, ctx.ClientIP())
|
|
req.RequestMethod = firstNonEmpty(req.RequestMethod, ctx.Request.Method)
|
|
req.RequestPath = firstNonEmpty(req.RequestPath, ctx.FullPath())
|
|
row, err := SaveRecord(req)
|
|
if err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
infra.Response.Success(ctx, row)
|
|
}
|
|
|
|
func ListApprovals(ctx *gin.Context) {
|
|
page, size := pageAndSize(ctx.DefaultQuery("page", "1"), ctx.DefaultQuery("page_size", "50"))
|
|
q := impl.DBService.Model(&models.DangerousOperationApproval{})
|
|
if v := strings.TrimSpace(ctx.Query("source_service")); v != "" {
|
|
q = q.Where("source_service = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("status")); v != "" {
|
|
q = q.Where("status = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("requester_id")); v != "" {
|
|
q = q.Where("requester_id = ?", v)
|
|
}
|
|
if v := strings.TrimSpace(ctx.Query("object_type")); v != "" {
|
|
q = q.Where("object_type = ?", v)
|
|
}
|
|
var total int64
|
|
_ = q.Count(&total).Error
|
|
var rows []models.DangerousOperationApproval
|
|
if err := q.Order("id desc").Offset((page - 1) * size).Limit(size).Find(&rows).Error; err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
infra.Response.Success(ctx, gin.H{"total": total, "page": page, "page_size": size, "items": rows})
|
|
}
|
|
|
|
func CreateApproval(ctx *gin.Context) {
|
|
var req ApprovalRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
req = NormalizeApproval(req)
|
|
if req.RequestID == "" {
|
|
req.RequestID = fmt.Sprintf("apr-%d", time.Now().UnixNano())
|
|
}
|
|
if err := ValidateApprovalRequest(req); err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
row := models.DangerousOperationApproval{
|
|
RequestID: req.RequestID,
|
|
SourceService: req.SourceService,
|
|
Action: req.Action,
|
|
ObjectType: req.ObjectType,
|
|
ObjectID: req.ObjectID,
|
|
RequesterID: req.RequesterID,
|
|
RequesterName: req.RequesterName,
|
|
Reason: req.Reason,
|
|
BeforeJSON: req.BeforeJSON,
|
|
AfterJSON: req.AfterJSON,
|
|
Status: ApprovalPending,
|
|
}
|
|
if err := impl.DBService.Create(&row).Error; err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
infra.Response.Success(ctx, row)
|
|
}
|
|
|
|
func ApproveApproval(ctx *gin.Context) {
|
|
reviewApproval(ctx, ApprovalApproved)
|
|
}
|
|
|
|
func RejectApproval(ctx *gin.Context) {
|
|
reviewApproval(ctx, ApprovalRejected)
|
|
}
|
|
|
|
func reviewApproval(ctx *gin.Context, next string) {
|
|
id, err := parseUintParam(ctx, "id")
|
|
if err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
var body struct {
|
|
ReviewerID string `json:"reviewer_id"`
|
|
ReviewerName string `json:"reviewer_name"`
|
|
ReviewComment string `json:"review_comment"`
|
|
}
|
|
if err := ctx.ShouldBindJSON(&body); err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
var row models.DangerousOperationApproval
|
|
if err := impl.DBService.First(&row, id).Error; err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
req := ApprovalRequest{
|
|
RequestID: row.RequestID,
|
|
SourceService: row.SourceService,
|
|
Action: row.Action,
|
|
ObjectType: row.ObjectType,
|
|
ObjectID: row.ObjectID,
|
|
RequesterID: row.RequesterID,
|
|
Status: row.Status,
|
|
}
|
|
nextReq, err := Transition(req, next, body.ReviewerID, body.ReviewComment)
|
|
if err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
row.Status = nextReq.Status
|
|
row.ReviewerID = nextReq.ReviewerID
|
|
row.ReviewerName = strings.TrimSpace(body.ReviewerName)
|
|
row.ReviewComment = nextReq.ReviewComment
|
|
row.ReviewedAt = nextReq.ReviewedAt
|
|
if err := impl.DBService.Save(&row).Error; err != nil {
|
|
infra.Response.Error(ctx, err)
|
|
return
|
|
}
|
|
infra.Response.Success(ctx, row)
|
|
}
|
|
|
|
func parseUintParam(ctx *gin.Context, name string) (uint, error) {
|
|
v, err := strconv.ParseUint(ctx.Param(name), 10, 32)
|
|
if err != nil || v == 0 {
|
|
return 0, errors.New("invalid id")
|
|
}
|
|
return uint(v), nil
|
|
}
|
|
|
|
func pageAndSize(pageText, sizeText string) (int, int) {
|
|
page, _ := strconv.Atoi(pageText)
|
|
size, _ := strconv.Atoi(sizeText)
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if size < 1 || size > 500 {
|
|
size = 50
|
|
}
|
|
return page, size
|
|
}
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return strings.TrimSpace(value)
|
|
}
|
|
}
|
|
return ""
|
|
}
|