feat: expanded health checks, deploy filtering, per-project notifications, error sanitization, and audit trail

- Expand health endpoint to check DB, Docker, and NPM connectivity (FUNC-M4)
- Add project_id, stage_id, offset query params to deploys endpoint (FUNC-M5, FUNC-M6)
- Add notification_url field to Stage model for per-project overrides (FUNC-M2)
- Add NPM Ping method for health checking
- Sanitize all internal error messages in API handlers (SEC-M4)
- Add audit trail events for admin actions (FUNC-M3)
- Add EventLog event type to event bus
This commit is contained in:
2026-04-04 13:10:10 +03:00
parent 04c1411f5d
commit 91b49cb5ed
14 changed files with 280 additions and 170 deletions
+20 -10
View File
@@ -26,7 +26,8 @@ type registryRequest struct {
func (s *Server) listRegistries(w http.ResponseWriter, r *http.Request) {
registries, err := s.store.GetAllRegistries()
if err != nil {
respondError(w, http.StatusInternalServerError, "failed to list registries: "+err.Error())
slog.Error("failed to list registries", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
@@ -80,7 +81,8 @@ func (s *Server) createRegistry(w http.ResponseWriter, r *http.Request) {
// Encrypt the token if provided.
encToken, err := crypto.EncryptIfNotEmpty(s.encKey, req.Token)
if err != nil {
respondError(w, http.StatusInternalServerError, "failed to encrypt token: "+err.Error())
slog.Error("failed to encrypt token", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
@@ -92,7 +94,8 @@ func (s *Server) createRegistry(w http.ResponseWriter, r *http.Request) {
Owner: req.Owner,
})
if err != nil {
respondError(w, http.StatusInternalServerError, "failed to create registry: "+err.Error())
slog.Error("failed to create registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
@@ -112,7 +115,8 @@ func (s *Server) updateRegistry(w http.ResponseWriter, r *http.Request) {
respondNotFound(w, "registry")
return
}
respondError(w, http.StatusInternalServerError, "failed to get registry: "+err.Error())
slog.Error("failed to get registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
@@ -138,14 +142,16 @@ func (s *Server) updateRegistry(w http.ResponseWriter, r *http.Request) {
if req.Token != "" {
encToken, err := crypto.EncryptIfNotEmpty(s.encKey, req.Token)
if err != nil {
respondError(w, http.StatusInternalServerError, "failed to encrypt token: "+err.Error())
slog.Error("failed to encrypt token", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
updated.Token = encToken
}
if err := s.store.UpdateRegistry(updated); err != nil {
respondError(w, http.StatusInternalServerError, "failed to update registry: "+err.Error())
slog.Error("failed to update registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
respondJSON(w, http.StatusOK, map[string]string{
@@ -162,7 +168,8 @@ func (s *Server) deleteRegistry(w http.ResponseWriter, r *http.Request) {
respondNotFound(w, "registry")
return
}
respondError(w, http.StatusInternalServerError, "failed to delete registry: "+err.Error())
slog.Error("failed to delete registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
respondJSON(w, http.StatusOK, map[string]string{"deleted": id})
@@ -184,7 +191,8 @@ func (s *Server) testRegistry(w http.ResponseWriter, r *http.Request) {
respondNotFound(w, "registry")
return
}
respondError(w, http.StatusInternalServerError, "failed to get registry: "+err.Error())
slog.Error("failed to get registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
@@ -244,7 +252,8 @@ func (s *Server) listRegistryTags(w http.ResponseWriter, r *http.Request) {
respondNotFound(w, "registry")
return
}
respondError(w, http.StatusInternalServerError, "failed to get registry: "+err.Error())
slog.Error("failed to get registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}
@@ -285,7 +294,8 @@ func (s *Server) listRegistryImages(w http.ResponseWriter, r *http.Request) {
respondNotFound(w, "registry")
return
}
respondError(w, http.StatusInternalServerError, "failed to get registry: "+err.Error())
slog.Error("failed to get registry", "error", err)
respondError(w, http.StatusInternalServerError, "internal server error")
return
}