feat: Monitor grath zoom
This commit is contained in:
@@ -1119,6 +1119,26 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"models.MonitorSample": {
|
||||
"properties": {
|
||||
"at": {
|
||||
"type": "string"
|
||||
},
|
||||
"instance_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"latency_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"monitor_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"up": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"models.MonitorState": {
|
||||
"properties": {
|
||||
"cert_expiry_at": {
|
||||
@@ -4345,6 +4365,77 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/monitors/{id}/samples": {
|
||||
"get": {
|
||||
"description": "Raw check results for the last `minutes` minutes, oldest first. Samples expire after 48 hours; use the uptime rollups for longer ranges.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Monitor ID",
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Window in minutes (default 60, max 2880)",
|
||||
"in": "query",
|
||||
"name": "minutes",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/models.MonitorSample"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "OK"
|
||||
},
|
||||
"404": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/api.ErrorResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Not Found"
|
||||
},
|
||||
"500": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/api.ErrorResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"summary": "Get a monitor's individual check results",
|
||||
"tags": [
|
||||
"monitors"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/monitors/{id}/uptime": {
|
||||
"get": {
|
||||
"description": "Hourly rollups for the last 30 days.",
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/auth"
|
||||
@@ -19,6 +20,7 @@ func registerMonitorRoutes(g *gin.RouterGroup) {
|
||||
g.DELETE("/monitors/:id", deleteMonitor)
|
||||
g.GET("/monitors/:id/incidents", getMonitorIncidents)
|
||||
g.GET("/monitors/:id/uptime", getMonitorUptime)
|
||||
g.GET("/monitors/:id/samples", getMonitorSamples)
|
||||
}
|
||||
|
||||
// listMonitors godoc
|
||||
@@ -221,6 +223,49 @@ func getMonitorIncidents(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, incidents)
|
||||
}
|
||||
|
||||
// getMonitorSamples godoc
|
||||
//
|
||||
// @Summary Get a monitor's individual check results
|
||||
// @Description Raw check results for the last `minutes` minutes, oldest first. Samples expire after 48 hours; use the uptime rollups for longer ranges.
|
||||
// @Tags monitors
|
||||
// @Produce json
|
||||
// @Param id path string true "Monitor ID"
|
||||
// @Param minutes query int false "Window in minutes (default 60, max 2880)"
|
||||
// @Success 200 {array} models.MonitorSample
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Security cookieAuth
|
||||
// @Security bearerAuth
|
||||
// @Router /monitors/{id}/samples [get]
|
||||
func getMonitorSamples(c *gin.Context) {
|
||||
m, err := services.GetMonitor(auth.InstanceID(c), c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if m == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "monitor not found"})
|
||||
return
|
||||
}
|
||||
// Clamped rather than rejected: the window is a view setting, and the only
|
||||
// honest answer past the TTL is the shorter window anyway.
|
||||
minutes := 60
|
||||
if raw := c.Query("minutes"); raw != "" {
|
||||
if n, convErr := strconv.Atoi(raw); convErr == nil && n > 0 {
|
||||
minutes = n
|
||||
}
|
||||
}
|
||||
if max := int(services.MonitorSampleTTL.Minutes()); minutes > max {
|
||||
minutes = max
|
||||
}
|
||||
samples, err := services.MonitorSamples(auth.InstanceID(c), c.Param("id"), time.Now().Add(-time.Duration(minutes)*time.Minute))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, samples)
|
||||
}
|
||||
|
||||
// getMonitorUptime godoc
|
||||
//
|
||||
// @Summary Get a monitor's uptime rollups
|
||||
|
||||
@@ -100,6 +100,7 @@ var routeScopes = map[string]string{
|
||||
"DELETE /api/monitors/:id": "monitors:write",
|
||||
"GET /api/monitors/:id/incidents": "monitors:read",
|
||||
"GET /api/monitors/:id/uptime": "monitors:read",
|
||||
"GET /api/monitors/:id/samples": "monitors:read",
|
||||
|
||||
// Channel routes, registered by registerChannelRoutes. Channels exist to
|
||||
// serve alerts, so they share the monitors scope rather than getting their
|
||||
|
||||
Reference in New Issue
Block a user