mirror of
https://github.com/Dvorinka/beszel.git
synced 2026-06-03 21:02:56 +00:00
feat(site): implement subdomain discovery and enhanced monitoring dashboard
This commit introduces a comprehensive subdomain discovery system and significantly upgrades the monitoring and domain management user interfaces.
Key changes include:
- **Subdomain Discovery**: Added a new service in the hub that performs advanced subdomain discovery using DNS brute forcing, Certificate Transparency (CT) log searches, pattern enumeration, and HTTP probing.
- **Enhanced Domain Management**:
- Added API endpoints for retrieving, discovering, and deleting subdomains.
- Implemented a new `SubdomainList` component in the UI to manage discovered subdomains.
- Improved WHOIS lookup robustness by supporting a wider range of registry field variations.
- **Advanced Monitoring UI**:
- Introduced `GroupedMonitorsTable` to organize monitors by root domain and their respective subdomains.
- Added visual uptime timelines (heartbeat dots) and response time statistics (Avg, Min, Max, P95, P99) to the monitor detail view.
- Implemented "Uptime Pills" for high-visibility status indicators in the monitors table.
- **Status Page Management**: Replaced the static status pages table with a full `StatusPageManager` capable of managing status pages and incidents.
- **Refactoring & Cleanup**:
- Cleaned up `.gitignore` and removed unused reference submodules.
- Improved domain extraction and grouping logic in the frontend.
- Enhanced the `SystemsTable` with better sorting and layout.
This commit is contained in:
@@ -52,6 +52,13 @@ func (h *APIHandler) RegisterRoutes(se *core.ServeEvent) {
|
||||
api.GET("/:id/heartbeats", h.getHeartbeats)
|
||||
}
|
||||
|
||||
// HeartbeatSummary represents a minimal heartbeat for the monitor list
|
||||
type HeartbeatSummary struct {
|
||||
Status string `json:"status"`
|
||||
Ping int `json:"ping"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// MonitorResponse represents a monitor in API responses
|
||||
type MonitorResponse struct {
|
||||
ID string `json:"id"`
|
||||
@@ -69,6 +76,7 @@ type MonitorResponse struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
LastCheck *time.Time `json:"last_check,omitempty"`
|
||||
UptimeStats map[string]float64 `json:"uptime_stats,omitempty"`
|
||||
RecentHeartbeats []HeartbeatSummary `json:"recent_heartbeats,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Keyword string `json:"keyword,omitempty"`
|
||||
JSONQuery string `json:"json_query,omitempty"`
|
||||
@@ -165,6 +173,35 @@ func (h *APIHandler) listMonitors(e *core.RequestEvent) error {
|
||||
})
|
||||
}
|
||||
|
||||
// getRecentHeartbeats fetches the last N heartbeats for a monitor
|
||||
func (h *APIHandler) getRecentHeartbeats(monitorID string, limit int) []HeartbeatSummary {
|
||||
records, err := h.app.FindRecordsByFilter(
|
||||
"monitor_heartbeats",
|
||||
"monitor = {:monitorId}",
|
||||
"-time",
|
||||
limit,
|
||||
0,
|
||||
map[string]any{"monitorId": monitorID},
|
||||
)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
heartbeats := make([]HeartbeatSummary, 0, len(records))
|
||||
for _, hb := range records {
|
||||
var t time.Time
|
||||
if ts := hb.GetDateTime("time"); !ts.IsZero() {
|
||||
t = ts.Time()
|
||||
}
|
||||
heartbeats = append(heartbeats, HeartbeatSummary{
|
||||
Status: hb.GetString("status"),
|
||||
Ping: hb.GetInt("ping"),
|
||||
Time: t,
|
||||
})
|
||||
}
|
||||
return heartbeats
|
||||
}
|
||||
|
||||
// getMonitor returns a single monitor by ID
|
||||
func (h *APIHandler) getMonitor(e *core.RequestEvent) error {
|
||||
id := e.Request.PathValue("id")
|
||||
@@ -182,7 +219,10 @@ func (h *APIHandler) getMonitor(e *core.RequestEvent) error {
|
||||
return e.ForbiddenError("Access denied", nil)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, recordToResponse(record))
|
||||
resp := recordToResponse(record)
|
||||
resp.RecentHeartbeats = h.getRecentHeartbeats(id, 12)
|
||||
|
||||
return e.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// createMonitor creates a new monitor
|
||||
|
||||
Reference in New Issue
Block a user