feat: add proxy and statistics features
This commit is contained in:
@ -124,7 +124,7 @@ func (h *Handler) ListContainers(w http.ResponseWriter, r *http.Request) {
|
||||
IPAddress string `json:"ip_address"`
|
||||
Container *agentv1.ContainerInfo `json:"container"`
|
||||
}
|
||||
var out []containerDTO
|
||||
out := make([]containerDTO, 0)
|
||||
for _, agent := range h.registry.List() {
|
||||
for _, c := range agent.Containers {
|
||||
out = append(out, containerDTO{
|
||||
@ -193,7 +193,7 @@ func (h *Handler) ListImages(w http.ResponseWriter, r *http.Request) {
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
IsOrphan bool `json:"is_orphan"`
|
||||
}
|
||||
var out []imageDTO
|
||||
out := make([]imageDTO, 0)
|
||||
for _, agent := range h.registry.List() {
|
||||
for _, img := range agent.Images {
|
||||
out = append(out, imageDTO{
|
||||
@ -247,7 +247,7 @@ func (h *Handler) ListVolumes(w http.ResponseWriter, r *http.Request) {
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
IsOrphan bool `json:"is_orphan"`
|
||||
}
|
||||
var out []volumeDTO
|
||||
out := make([]volumeDTO, 0)
|
||||
for _, agent := range h.registry.List() {
|
||||
for _, vol := range agent.Volumes {
|
||||
out = append(out, volumeDTO{
|
||||
@ -301,7 +301,7 @@ func (h *Handler) ListNetworks(w http.ResponseWriter, r *http.Request) {
|
||||
Scope string `json:"scope"`
|
||||
IsOrphan bool `json:"is_orphan"`
|
||||
}
|
||||
var out []networkDTO
|
||||
out := make([]networkDTO, 0)
|
||||
for _, agent := range h.registry.List() {
|
||||
for _, net := range agent.Networks {
|
||||
out = append(out, networkDTO{
|
||||
@ -753,6 +753,129 @@ func (h *Handler) UpdateNow(w http.ResponseWriter, r *http.Request) {
|
||||
jsonOK(w, map[string]string{"command_id": cmdID})
|
||||
}
|
||||
|
||||
// ── Stats ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
type processDTO struct {
|
||||
Pid uint32 `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
Cmd string `json:"cmd"`
|
||||
CpuPct float64 `json:"cpu_pct"`
|
||||
MemRss uint64 `json:"mem_rss"`
|
||||
}
|
||||
|
||||
type netIfaceDTO struct {
|
||||
Name string `json:"name"`
|
||||
BytesRecv uint64 `json:"bytes_recv"`
|
||||
BytesSent uint64 `json:"bytes_sent"`
|
||||
BytesRecvRate uint64 `json:"bytes_recv_rate"`
|
||||
BytesSentRate uint64 `json:"bytes_sent_rate"`
|
||||
}
|
||||
|
||||
type diskDTO struct {
|
||||
Path string `json:"path"`
|
||||
Total uint64 `json:"total"`
|
||||
Used uint64 `json:"used"`
|
||||
Free uint64 `json:"free"`
|
||||
}
|
||||
|
||||
type statsSnapshotDTO struct {
|
||||
CpuPct float64 `json:"cpu_pct"`
|
||||
CpuPerCore []float64 `json:"cpu_per_core"`
|
||||
MemTotal uint64 `json:"mem_total"`
|
||||
MemUsed uint64 `json:"mem_used"`
|
||||
MemAvailable uint64 `json:"mem_available"`
|
||||
NetInterfaces []netIfaceDTO `json:"net_interfaces"`
|
||||
Processes []processDTO `json:"processes"`
|
||||
Disks []diskDTO `json:"disks"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
func protoStatsToDTO(s *agentv1.StatsSnapshot) *statsSnapshotDTO {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
dto := &statsSnapshotDTO{
|
||||
CpuPct: s.CpuPct,
|
||||
CpuPerCore: s.CpuPerCore,
|
||||
MemTotal: s.MemTotal,
|
||||
MemUsed: s.MemUsed,
|
||||
MemAvailable: s.MemAvailable,
|
||||
Timestamp: s.Timestamp,
|
||||
}
|
||||
if dto.CpuPerCore == nil {
|
||||
dto.CpuPerCore = []float64{}
|
||||
}
|
||||
for _, iface := range s.NetInterfaces {
|
||||
dto.NetInterfaces = append(dto.NetInterfaces, netIfaceDTO{
|
||||
Name: iface.Name,
|
||||
BytesRecv: iface.BytesRecv,
|
||||
BytesSent: iface.BytesSent,
|
||||
BytesRecvRate: iface.BytesRecvRate,
|
||||
BytesSentRate: iface.BytesSentRate,
|
||||
})
|
||||
}
|
||||
if dto.NetInterfaces == nil {
|
||||
dto.NetInterfaces = []netIfaceDTO{}
|
||||
}
|
||||
for _, p := range s.Processes {
|
||||
dto.Processes = append(dto.Processes, processDTO{
|
||||
Pid: p.Pid,
|
||||
Name: p.Name,
|
||||
Cmd: p.Cmd,
|
||||
CpuPct: p.CpuPct,
|
||||
MemRss: p.MemRss,
|
||||
})
|
||||
}
|
||||
if dto.Processes == nil {
|
||||
dto.Processes = []processDTO{}
|
||||
}
|
||||
for _, d := range s.Disks {
|
||||
dto.Disks = append(dto.Disks, diskDTO{
|
||||
Path: d.Path,
|
||||
Total: d.Total,
|
||||
Used: d.Used,
|
||||
Free: d.Free,
|
||||
})
|
||||
}
|
||||
if dto.Disks == nil {
|
||||
dto.Disks = []diskDTO{}
|
||||
}
|
||||
return dto
|
||||
}
|
||||
|
||||
// ListStats handles GET /api/v1/stats — returns system stats for all connected agents.
|
||||
func (h *Handler) ListStats(w http.ResponseWriter, r *http.Request) {
|
||||
type statsDTO struct {
|
||||
AgentID string `json:"agent_id"`
|
||||
Hostname string `json:"hostname"`
|
||||
Alias string `json:"alias"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Stats *statsSnapshotDTO `json:"stats"`
|
||||
}
|
||||
out := make([]statsDTO, 0)
|
||||
for _, agent := range h.registry.List() {
|
||||
out = append(out, statsDTO{
|
||||
AgentID: agent.ID,
|
||||
Hostname: agent.Hostname,
|
||||
Alias: agent.Alias,
|
||||
IPAddress: agent.IPAddress,
|
||||
Stats: protoStatsToDTO(agent.Stats),
|
||||
})
|
||||
}
|
||||
jsonOK(w, out)
|
||||
}
|
||||
|
||||
// GetAgentStats handles GET /api/v1/agents/{agentID}/stats — returns stats for a single agent.
|
||||
func (h *Handler) GetAgentStats(w http.ResponseWriter, r *http.Request) {
|
||||
agentID := chi.URLParam(r, "agentID")
|
||||
state, ok := h.registry.Get(agentID)
|
||||
if !ok {
|
||||
http.Error(w, "agent not connected", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
jsonOK(w, protoStatsToDTO(state.Stats))
|
||||
}
|
||||
|
||||
func jsonOK(w http.ResponseWriter, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(v)
|
||||
|
||||
@ -63,6 +63,8 @@ func NewRouter(h *Handler) http.Handler {
|
||||
r.Get("/agents/{agentID}/containers/{containerID}/auto-update", h.GetAutoUpdatePolicy)
|
||||
r.Put("/agents/{agentID}/containers/{containerID}/auto-update", h.PutAutoUpdatePolicy)
|
||||
r.Post("/agents/{agentID}/containers/{containerID}/update-now", h.UpdateNow)
|
||||
r.Get("/stats", h.ListStats)
|
||||
r.Get("/agents/{agentID}/stats", h.GetAgentStats)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user