115 lines
3.0 KiB
Go
115 lines
3.0 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
)
|
|
|
|
type AuditLogEntry struct {
|
|
ID int64
|
|
AdminID int64
|
|
Action string
|
|
EntityType string
|
|
EntityID sql.NullInt64
|
|
OldValue sql.NullString
|
|
NewValue sql.NullString
|
|
IPAddress string
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// Audit action constants
|
|
const (
|
|
AuditCreatePanel = "create_panel"
|
|
AuditUpdatePanel = "update_panel"
|
|
AuditDeletePanel = "delete_panel"
|
|
AuditCreateType = "create_type"
|
|
AuditUpdateType = "update_type"
|
|
AuditDeleteType = "delete_type"
|
|
AuditReorderTypes = "reorder_types"
|
|
AuditUpdateConvoc = "update_convocation"
|
|
AuditSendPanelDiscord = "send_panel_discord"
|
|
AuditDeletePanelMsg = "delete_panel_discord"
|
|
AuditRevokeSession = "revoke_session"
|
|
AuditRevokeAllSess = "revoke_all_sessions"
|
|
AuditAdminLogin = "admin_login"
|
|
AuditAdminLoginFail = "admin_login_failed"
|
|
AuditAdminLocked = "admin_locked"
|
|
)
|
|
|
|
type AuditFilter struct {
|
|
AdminID int64
|
|
Action string
|
|
EntityType string
|
|
From, To time.Time
|
|
}
|
|
|
|
type AuditLogRepo struct{ db *sql.DB }
|
|
|
|
func NewAuditLogRepo(db *sql.DB) *AuditLogRepo { return &AuditLogRepo{db: db} }
|
|
|
|
func (r *AuditLogRepo) Insert(ctx context.Context, e *AuditLogEntry) error {
|
|
e.CreatedAt = time.Now().UTC()
|
|
_, err := r.db.ExecContext(ctx,
|
|
`INSERT INTO audit_log(admin_id,action,entity_type,entity_id,old_value,new_value,ip_address,created_at)
|
|
VALUES(?,?,?,?,?,?,?,?)`,
|
|
e.AdminID, e.Action, e.EntityType, e.EntityID, e.OldValue, e.NewValue, e.IPAddress, e.CreatedAt)
|
|
return err
|
|
}
|
|
|
|
func (r *AuditLogRepo) List(ctx context.Context, f AuditFilter, page, pageSize int) ([]*AuditLogEntry, int, error) {
|
|
where := "WHERE 1=1"
|
|
args := []any{}
|
|
if f.AdminID != 0 {
|
|
where += " AND admin_id=?"
|
|
args = append(args, f.AdminID)
|
|
}
|
|
if f.Action != "" {
|
|
where += " AND action=?"
|
|
args = append(args, f.Action)
|
|
}
|
|
if f.EntityType != "" {
|
|
where += " AND entity_type=?"
|
|
args = append(args, f.EntityType)
|
|
}
|
|
if !f.From.IsZero() {
|
|
where += " AND created_at>=?"
|
|
args = append(args, f.From)
|
|
}
|
|
if !f.To.IsZero() {
|
|
where += " AND created_at<=?"
|
|
args = append(args, f.To)
|
|
}
|
|
|
|
var total int
|
|
countArgs := make([]any, len(args))
|
|
copy(countArgs, args)
|
|
if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM audit_log `+where, countArgs...).Scan(&total); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
offset := (page - 1) * pageSize
|
|
args = append(args, pageSize, offset)
|
|
rows, err := r.db.QueryContext(ctx,
|
|
`SELECT id,admin_id,action,entity_type,entity_id,old_value,new_value,ip_address,created_at
|
|
FROM audit_log `+where+` ORDER BY created_at DESC LIMIT ? OFFSET ?`, args...)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []*AuditLogEntry
|
|
for rows.Next() {
|
|
var e AuditLogEntry
|
|
if err := rows.Scan(&e.ID, &e.AdminID, &e.Action, &e.EntityType, &e.EntityID,
|
|
&e.OldValue, &e.NewValue, &e.IPAddress, &e.CreatedAt); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
out = append(out, &e)
|
|
}
|
|
return out, total, rows.Err()
|
|
}
|