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:
+30
-35
@@ -4,53 +4,48 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/alexei/docker-watcher/internal/docker"
|
||||
)
|
||||
|
||||
// getHealth handles GET /api/health.
|
||||
// Returns connectivity status for Docker with diagnostic hints on failure.
|
||||
func (s *Server) getHealth(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
result := map[string]any{
|
||||
"checked_at": now,
|
||||
}
|
||||
|
||||
// Check database connectivity.
|
||||
if err := s.store.DB().PingContext(ctx); err != nil {
|
||||
result["database"] = map[string]any{"connected": false, "error": "database unreachable"}
|
||||
} else {
|
||||
result["database"] = map[string]any{"connected": true}
|
||||
}
|
||||
|
||||
// Check Docker connectivity.
|
||||
if s.docker == nil {
|
||||
diag := docker.Diagnose(nil, "")
|
||||
respondJSON(w, http.StatusOK, map[string]any{
|
||||
"docker": map[string]any{
|
||||
"connected": false,
|
||||
"error": "docker client not initialized",
|
||||
"category": diag.Category,
|
||||
"hints": diag.Hints,
|
||||
"platform": diag.Platform,
|
||||
"checked_at": now,
|
||||
},
|
||||
})
|
||||
return
|
||||
result["docker"] = map[string]any{
|
||||
"connected": false,
|
||||
"error": "docker client not initialized",
|
||||
}
|
||||
} else if err := s.docker.Ping(ctx); err != nil {
|
||||
result["docker"] = map[string]any{
|
||||
"connected": false,
|
||||
"error": err.Error(),
|
||||
}
|
||||
} else {
|
||||
result["docker"] = map[string]any{"connected": true}
|
||||
}
|
||||
|
||||
err := s.docker.Ping(ctx)
|
||||
if err == nil {
|
||||
respondJSON(w, http.StatusOK, map[string]any{
|
||||
"docker": map[string]any{
|
||||
"connected": true,
|
||||
"checked_at": now,
|
||||
},
|
||||
})
|
||||
return
|
||||
// Check NPM connectivity if configured.
|
||||
if s.npm != nil {
|
||||
if err := s.npm.Ping(ctx); err != nil {
|
||||
result["npm"] = map[string]any{"connected": false, "error": "NPM unreachable"}
|
||||
} else {
|
||||
result["npm"] = map[string]any{"connected": true}
|
||||
}
|
||||
}
|
||||
|
||||
diag := docker.Diagnose(err, "")
|
||||
respondJSON(w, http.StatusOK, map[string]any{
|
||||
"docker": map[string]any{
|
||||
"connected": false,
|
||||
"error": err.Error(),
|
||||
"category": diag.Category,
|
||||
"hints": diag.Hints,
|
||||
"platform": diag.Platform,
|
||||
"checked_at": now,
|
||||
},
|
||||
})
|
||||
respondJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user