From 73d132eb4c098ae8c0dff2eabd69f98766fd8eaa Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 28 Dec 2023 14:28:59 +0100 Subject: [PATCH] feat: add GET /v2/admin/audit-log --- backend/routes/v2/admin/audit_log.go | 65 ++++++++++++++++++++++++++++ backend/routes/v2/admin/routes.go | 1 + backend/server/errors.go | 1 - 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 backend/routes/v2/admin/audit_log.go diff --git a/backend/routes/v2/admin/audit_log.go b/backend/routes/v2/admin/audit_log.go new file mode 100644 index 0000000..40ccfbc --- /dev/null +++ b/backend/routes/v2/admin/audit_log.go @@ -0,0 +1,65 @@ +package admin + +import ( + "net/http" + + "codeberg.org/pronounscc/pronouns.cc/backend/common" + "codeberg.org/pronounscc/pronouns.cc/backend/db" + "codeberg.org/pronounscc/pronouns.cc/backend/log" + "codeberg.org/pronounscc/pronouns.cc/backend/server" + "emperror.dev/errors" + "github.com/go-chi/render" +) + +type AuditLogEntryResponse struct { + ID common.AuditLogID `json:"id"` + TargetUserID common.UserID `json:"target_user_id"` + TargetMemberID *common.MemberID `json:"target_member_id"` + ModeratorID common.UserID `json:"moderator_id"` + ReportID *int64 `json:"report_id"` + + Reason string `json:"reason"` + ActionTaken string `json:"action_taken"` + ClearedData *db.AuditLogClearedData `json:"cleared_data"` +} + +func dbAuditLogToResponse(e db.AuditLogEntry) AuditLogEntryResponse { + return AuditLogEntryResponse{ + ID: e.ID, + TargetUserID: e.TargetUserID, + TargetMemberID: e.TargetMemberID, + ModeratorID: e.ModeratorID, + ReportID: e.ReportID, + Reason: e.Reason, + ActionTaken: e.ActionTaken, + ClearedData: e.ClearedData, + } +} + +func dbAuditLogsToResponse(es []db.AuditLogEntry) []AuditLogEntryResponse { + resps := make([]AuditLogEntryResponse, len(es)) + for i := range es { + resps[i] = dbAuditLogToResponse(es[i]) + } + return resps +} + +func (s *Server) AuditLog(w http.ResponseWriter, r *http.Request) (err error) { + beforeID := common.AuditLogID(0) + if s := r.FormValue("before"); s != "" { + sf, err := common.ParseSnowflake(s) + if err != nil { + return server.NewV2Error(server.ErrBadRequest, "", server.NewModelParseError("query", "Invalid snowflake")) + } + beforeID = common.AuditLogID(sf) + } + + es, err := s.DB.AuditLog(r.Context(), beforeID) + if err != nil { + log.Errorf("getting audit logs before %v: %v", beforeID, err) + return errors.Wrap(err, "getting audit logs") + } + + render.JSON(w, r, dbAuditLogsToResponse(es)) + return nil +} diff --git a/backend/routes/v2/admin/routes.go b/backend/routes/v2/admin/routes.go index fac47a6..cfc8bcd 100644 --- a/backend/routes/v2/admin/routes.go +++ b/backend/routes/v2/admin/routes.go @@ -22,6 +22,7 @@ func Mount(srv *server.Server, r chi.Router) { s := &Server{Server: srv} r.With(MustAdmin).Route("/admin", func(r chi.Router) { + r.Get("/audit-log", server.WrapHandler(s.AuditLog)) r.Post("/users/{id}/actions", server.WrapHandler(s.UserAction)) }) } diff --git a/backend/server/errors.go b/backend/server/errors.go index 4e264f0..076213b 100644 --- a/backend/server/errors.go +++ b/backend/server/errors.go @@ -47,7 +47,6 @@ func WrapHandler(hn func(w http.ResponseWriter, r *http.Request) error) http.Han if isExpectedError(err) { log.Infof("expected error in handler for %v %v, ignoring", rctx.RouteMethod, rctx.RoutePattern()) } else { - log.Errorf("error in handler for %v %v: %v", rctx.RouteMethod, rctx.RoutePattern(), err) eventID = hub.CaptureException(err) } apiErr := APIError{ID: eventID, Code: ErrInternalServerError}