152 lines
3.8 KiB
Go
152 lines
3.8 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
type AuditLog struct {
|
|
bun.BaseModel `bun:"table:audit_log,alias:al"`
|
|
|
|
ID int `bun:"id,pk,autoincrement"`
|
|
UserID int `bun:"user_id,notnull"`
|
|
Action string `bun:"action,notnull"`
|
|
ResourceType string `bun:"resource_type,notnull"`
|
|
ResourceID *string `bun:"resource_id"`
|
|
Details json.RawMessage `bun:"details,type:jsonb"`
|
|
IPAddress string `bun:"ip_address"`
|
|
UserAgent string `bun:"user_agent"`
|
|
Result string `bun:"result,notnull"` // success, denied, error
|
|
ErrorMessage *string `bun:"error_message"`
|
|
CreatedAt int64 `bun:"created_at,notnull"`
|
|
|
|
// Relations
|
|
User *User `bun:"rel:belongs-to,join:user_id=id"`
|
|
}
|
|
|
|
type AuditLogs struct {
|
|
AuditLogs []*AuditLog
|
|
Total int
|
|
PageOpts PageOpts
|
|
}
|
|
|
|
// CreateAuditLog creates a new audit log entry
|
|
func CreateAuditLog(ctx context.Context, tx bun.Tx, log *AuditLog) error {
|
|
if log == nil {
|
|
return errors.New("log cannot be nil")
|
|
}
|
|
|
|
_, err := tx.NewInsert().
|
|
Model(log).
|
|
Exec(ctx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "tx.NewInsert")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type AuditLogFilters struct {
|
|
UserID *int
|
|
Action *string
|
|
ResourceType *string
|
|
Result *string
|
|
}
|
|
|
|
// GetAuditLogs retrieves audit logs with optional filters and pagination
|
|
func GetAuditLogs(ctx context.Context, tx bun.Tx, pageOpts *PageOpts, filters *AuditLogFilters) (*AuditLogs, error) {
|
|
pageOpts = setDefaultPageOpts(pageOpts, 1, 50, bun.OrderDesc, "created_at")
|
|
query := tx.NewSelect().
|
|
Model((*AuditLog)(nil)).
|
|
Relation("User").
|
|
OrderBy(pageOpts.OrderBy, pageOpts.Order)
|
|
|
|
// Apply filters if provided
|
|
if filters != nil {
|
|
if filters.UserID != nil {
|
|
query = query.Where("al.user_id = ?", *filters.UserID)
|
|
}
|
|
if filters.Action != nil {
|
|
query = query.Where("al.action = ?", *filters.Action)
|
|
}
|
|
if filters.ResourceType != nil {
|
|
query = query.Where("al.resource_type = ?", *filters.ResourceType)
|
|
}
|
|
if filters.Result != nil {
|
|
query = query.Where("al.result = ?", *filters.Result)
|
|
}
|
|
}
|
|
|
|
// Get total count
|
|
total, err := query.Count(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "query.Count")
|
|
}
|
|
|
|
// Get paginated results
|
|
logs := new([]*AuditLog)
|
|
err = query.
|
|
Offset(pageOpts.PerPage*(pageOpts.Page-1)).
|
|
Limit(pageOpts.PerPage).
|
|
Scan(ctx, &logs)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return nil, errors.Wrap(err, "query.Scan")
|
|
}
|
|
|
|
list := &AuditLogs{
|
|
AuditLogs: *logs,
|
|
Total: total,
|
|
PageOpts: *pageOpts,
|
|
}
|
|
|
|
return list, nil
|
|
}
|
|
|
|
// GetAuditLogsByUser retrieves audit logs for a specific user
|
|
func GetAuditLogsByUser(ctx context.Context, tx bun.Tx, userID int, pageOpts *PageOpts) (*AuditLogs, error) {
|
|
if userID <= 0 {
|
|
return nil, errors.New("userID must be positive")
|
|
}
|
|
|
|
filters := &AuditLogFilters{
|
|
UserID: &userID,
|
|
}
|
|
|
|
return GetAuditLogs(ctx, tx, pageOpts, filters)
|
|
}
|
|
|
|
// GetAuditLogsByAction retrieves audit logs for a specific action
|
|
func GetAuditLogsByAction(ctx context.Context, tx bun.Tx, action string, pageOpts *PageOpts) (*AuditLogs, error) {
|
|
if action == "" {
|
|
return nil, errors.New("action cannot be empty")
|
|
}
|
|
|
|
filters := &AuditLogFilters{
|
|
Action: &action,
|
|
}
|
|
|
|
return GetAuditLogs(ctx, tx, pageOpts, filters)
|
|
}
|
|
|
|
// CleanupOldAuditLogs deletes audit logs older than the specified timestamp
|
|
func CleanupOldAuditLogs(ctx context.Context, tx bun.Tx, olderThan int64) (int, error) {
|
|
result, err := tx.NewDelete().
|
|
Model((*AuditLog)(nil)).
|
|
Where("created_at < ?", olderThan).
|
|
Exec(ctx)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "tx.NewDelete")
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "result.RowsAffected")
|
|
}
|
|
|
|
return int(rowsAffected), nil
|
|
}
|