feat(observability): phase 8 - container stats, notifications & dashboard

Add container monitoring and notification system:
- Docker Stats API: real-time CPU/memory for running containers
- Webhook notifications for errors (deploy failures, stale, proxy unhealthy)
- Event log auto-pruning (daily, 30-day retention)
- ContainerStats component with auto-polling progress bars
- SystemHealthCard dashboard widget with running/proxy/error counts
- Full EN/RU i18n for stats and system health
This commit is contained in:
2026-03-30 11:37:25 +03:00
parent 79a40f3d9c
commit 7c57c740b4
13 changed files with 436 additions and 0 deletions
+1
View File
@@ -136,6 +136,7 @@ func (s *Server) Router() chi.Router {
r.Get("/", s.getProject)
r.Get("/stages/{stage}/env", s.listStageEnv)
r.Get("/stages/{stage}/instances", s.listInstances)
r.Get("/stages/{stage}/instances/{iid}/stats", s.getInstanceStats)
r.Get("/volumes", s.listVolumes)
})
r.Get("/deploys", s.listDeploys)
+39
View File
@@ -0,0 +1,39 @@
package api
import (
"errors"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/alexei/docker-watcher/internal/store"
)
// getInstanceStats handles GET /api/projects/{id}/stages/{stage}/instances/{iid}/stats.
// Returns CPU and memory stats for the container backing the given instance.
func (s *Server) getInstanceStats(w http.ResponseWriter, r *http.Request) {
instanceID := chi.URLParam(r, "iid")
inst, err := s.store.GetInstanceByID(instanceID)
if err != nil {
if errors.Is(err, store.ErrNotFound) {
respondNotFound(w, "instance")
return
}
respondError(w, http.StatusInternalServerError, "failed to get instance: "+err.Error())
return
}
if inst.ContainerID == "" {
respondError(w, http.StatusBadRequest, "instance has no container")
return
}
stats, err := s.docker.GetContainerStats(r.Context(), inst.ContainerID)
if err != nil {
respondError(w, http.StatusInternalServerError, "failed to get container stats: "+err.Error())
return
}
respondJSON(w, http.StatusOK, stats)
}