added admin page and audit log viewing
This commit is contained in:
@@ -41,29 +41,48 @@ func CreateAuditLog(ctx context.Context, tx bun.Tx, log *AuditLog) error {
|
||||
|
||||
type AuditLogFilter struct {
|
||||
*ListFilter
|
||||
customWhere []whereClause
|
||||
}
|
||||
|
||||
type whereClause struct {
|
||||
query string
|
||||
args []any
|
||||
}
|
||||
|
||||
func NewAuditLogFilter() *AuditLogFilter {
|
||||
return &AuditLogFilter{NewListFilter()}
|
||||
return &AuditLogFilter{
|
||||
ListFilter: NewListFilter(),
|
||||
customWhere: []whereClause{},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AuditLogFilter) UserID(id int) *AuditLogFilter {
|
||||
a.Add("al.user_id", id)
|
||||
a.Add("al.user_id", "=", id)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AuditLogFilter) Action(action string) *AuditLogFilter {
|
||||
a.Add("al.action", action)
|
||||
a.Add("al.action", "=", action)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AuditLogFilter) ResourceType(resourceType string) *AuditLogFilter {
|
||||
a.Add("al.resource_type", resourceType)
|
||||
a.Add("al.resource_type", "=", resourceType)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AuditLogFilter) Result(result string) *AuditLogFilter {
|
||||
a.Add("al.result", result)
|
||||
a.Add("al.result", "=", result)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AuditLogFilter) DateRange(start, end int64) *AuditLogFilter {
|
||||
if start > 0 {
|
||||
a.Add("al.created_at", ">=", start)
|
||||
}
|
||||
if end > 0 {
|
||||
a.Add("al.created_at", "<=", end)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -75,10 +94,17 @@ func GetAuditLogs(ctx context.Context, tx bun.Tx, pageOpts *PageOpts, filters *A
|
||||
Order: bun.OrderDesc,
|
||||
OrderBy: "created_at",
|
||||
}
|
||||
return GetList[AuditLog](tx).
|
||||
|
||||
lg := GetList[AuditLog](tx).
|
||||
Relation("User").
|
||||
Filter(filters.filters...).
|
||||
GetPaged(ctx, pageOpts, defaultPageOpts)
|
||||
Filter(filters.filters...)
|
||||
|
||||
// Apply custom where clauses (e.g., date range)
|
||||
for _, clause := range filters.customWhere {
|
||||
lg = lg.Where(clause.query, clause.args...)
|
||||
}
|
||||
|
||||
return lg.GetPaged(ctx, pageOpts, defaultPageOpts)
|
||||
}
|
||||
|
||||
// GetAuditLogsByUser retrieves audit logs for a specific user
|
||||
@@ -101,6 +127,57 @@ func GetAuditLogsByAction(ctx context.Context, tx bun.Tx, action string, pageOpt
|
||||
return GetAuditLogs(ctx, tx, pageOpts, filters)
|
||||
}
|
||||
|
||||
// GetAuditLogByID retrieves a single audit log by ID
|
||||
func GetAuditLogByID(ctx context.Context, tx bun.Tx, id int) (*AuditLog, error) {
|
||||
if id <= 0 {
|
||||
return nil, errors.New("id must be positive")
|
||||
}
|
||||
|
||||
log := new(AuditLog)
|
||||
err := tx.NewSelect().
|
||||
Model(log).
|
||||
Relation("User").
|
||||
Where("al.id = ?", id).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
if err.Error() == "sql: no rows in result set" {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return log, nil
|
||||
}
|
||||
|
||||
// GetUniqueActions retrieves a list of all unique actions in the audit log
|
||||
func GetUniqueActions(ctx context.Context, tx bun.Tx) ([]string, error) {
|
||||
var actions []string
|
||||
err := tx.NewSelect().
|
||||
Model((*AuditLog)(nil)).
|
||||
Column("action").
|
||||
Distinct().
|
||||
Order("action ASC").
|
||||
Scan(ctx, &actions)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
// GetUniqueResourceTypes retrieves a list of all unique resource types in the audit log
|
||||
func GetUniqueResourceTypes(ctx context.Context, tx bun.Tx) ([]string, error) {
|
||||
var resourceTypes []string
|
||||
err := tx.NewSelect().
|
||||
Model((*AuditLog)(nil)).
|
||||
Column("resource_type").
|
||||
Distinct().
|
||||
Order("resource_type ASC").
|
||||
Scan(ctx, &resourceTypes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return resourceTypes, nil
|
||||
}
|
||||
|
||||
// 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().
|
||||
|
||||
@@ -20,8 +20,9 @@ type List[T any] struct {
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
Field string
|
||||
Value any
|
||||
Field string
|
||||
Value any
|
||||
Operator string
|
||||
}
|
||||
|
||||
type ListFilter struct {
|
||||
@@ -32,8 +33,8 @@ func NewListFilter() *ListFilter {
|
||||
return &ListFilter{[]Filter{}}
|
||||
}
|
||||
|
||||
func (f *ListFilter) Add(field string, value any) {
|
||||
f.filters = append(f.filters, Filter{field, value})
|
||||
func (f *ListFilter) Add(field, operator string, value any) {
|
||||
f.filters = append(f.filters, Filter{field, value, "="})
|
||||
}
|
||||
|
||||
func GetList[T any](tx bun.Tx) *listgetter[T] {
|
||||
@@ -62,7 +63,7 @@ func (l *listgetter[T]) Relation(name string, apply ...func(*bun.SelectQuery) *b
|
||||
|
||||
func (l *listgetter[T]) Filter(filters ...Filter) *listgetter[T] {
|
||||
for _, filter := range filters {
|
||||
l.q = l.q.Where("? = ?", bun.Ident(filter.Field), filter.Value)
|
||||
l.q = l.q.Where("? ? ?", bun.Ident(filter.Field), bun.Safe(filter.Operator), filter.Value)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
@@ -141,3 +141,9 @@ func GetUsers(ctx context.Context, tx bun.Tx, pageOpts *PageOpts) (*List[User],
|
||||
defaults := &PageOpts{1, 50, bun.OrderAsc, "id"}
|
||||
return GetList[User](tx).GetPaged(ctx, pageOpts, defaults)
|
||||
}
|
||||
|
||||
// GetUsersWithRoles queries the database for users with their roles preloaded
|
||||
func GetUsersWithRoles(ctx context.Context, tx bun.Tx, pageOpts *PageOpts) (*List[User], error) {
|
||||
defaults := &PageOpts{1, 25, bun.OrderAsc, "id"}
|
||||
return GetList[User](tx).Relation("Roles").GetPaged(ctx, pageOpts, defaults)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user