mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-21 17:07:47 +02:00
Implement container health alerts (#1679)
* Implement container health alerts, including a database migration for alert names and UI updates. * Add bulk container alerts and improve alerting logic * Implement container-specific alerts with new database collections, API endpoints, and UI components.
This commit is contained in:
@@ -117,3 +117,119 @@ func DeleteUserAlerts(e *core.RequestEvent) error {
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{"success": true, "count": numDeleted})
|
||||
}
|
||||
|
||||
// UpsertUserContainerAlerts handles API request to create or update container alerts for a user
|
||||
// across multiple containers (POST /api/beszel/user-container-alerts)
|
||||
func UpsertUserContainerAlerts(e *core.RequestEvent) error {
|
||||
userID := e.Auth.Id
|
||||
|
||||
reqData := struct {
|
||||
Min uint8 `json:"min"`
|
||||
Value float64 `json:"value"`
|
||||
Name string `json:"name"`
|
||||
Systems []string `json:"systems"`
|
||||
Containers []string `json:"containers"`
|
||||
Overwrite bool `json:"overwrite"`
|
||||
}{}
|
||||
err := e.BindBody(&reqData)
|
||||
if err != nil || userID == "" || reqData.Name == "" || len(reqData.Systems) == 0 || len(reqData.Containers) == 0 {
|
||||
return e.BadRequestError("Bad data", err)
|
||||
}
|
||||
|
||||
containerAlertsCollection, err := e.App.FindCachedCollectionByNameOrId("container_alerts")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = e.App.RunInTransaction(func(txApp core.App) error {
|
||||
for _, systemId := range reqData.Systems {
|
||||
for _, containerId := range reqData.Containers {
|
||||
// find existing matching alert
|
||||
alertRecord, err := txApp.FindFirstRecordByFilter(containerAlertsCollection,
|
||||
"system={:system} && container={:container} && name={:name} && user={:user}",
|
||||
dbx.Params{"system": systemId, "container": containerId, "name": reqData.Name, "user": userID})
|
||||
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip if alert already exists and overwrite is not set
|
||||
if !reqData.Overwrite && alertRecord != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// create new alert if it doesn't exist
|
||||
if alertRecord == nil {
|
||||
alertRecord = core.NewRecord(containerAlertsCollection)
|
||||
alertRecord.Set("user", userID)
|
||||
alertRecord.Set("system", systemId)
|
||||
alertRecord.Set("container", containerId)
|
||||
alertRecord.Set("name", reqData.Name)
|
||||
}
|
||||
|
||||
alertRecord.Set("value", reqData.Value)
|
||||
alertRecord.Set("min", reqData.Min)
|
||||
|
||||
if err := txApp.SaveNoValidate(alertRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{"success": true})
|
||||
}
|
||||
|
||||
// DeleteUserContainerAlerts handles API request to delete container alerts for a user
|
||||
// across multiple containers (DELETE /api/beszel/user-container-alerts)
|
||||
func DeleteUserContainerAlerts(e *core.RequestEvent) error {
|
||||
userID := e.Auth.Id
|
||||
|
||||
reqData := struct {
|
||||
AlertName string `json:"name"`
|
||||
Systems []string `json:"systems"`
|
||||
Containers []string `json:"containers"`
|
||||
}{}
|
||||
err := e.BindBody(&reqData)
|
||||
if err != nil || userID == "" || reqData.AlertName == "" || len(reqData.Systems) == 0 || len(reqData.Containers) == 0 {
|
||||
return e.BadRequestError("Bad data", err)
|
||||
}
|
||||
|
||||
var numDeleted uint16
|
||||
|
||||
err = e.App.RunInTransaction(func(txApp core.App) error {
|
||||
for _, systemId := range reqData.Systems {
|
||||
for _, containerId := range reqData.Containers {
|
||||
// Find existing alert to delete
|
||||
alertRecord, err := txApp.FindFirstRecordByFilter("container_alerts",
|
||||
"system={:system} && container={:container} && name={:name} && user={:user}",
|
||||
dbx.Params{"system": systemId, "container": containerId, "name": reqData.AlertName, "user": userID})
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// alert doesn't exist, continue to next container
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err := txApp.Delete(alertRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
numDeleted++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{"success": true, "count": numDeleted})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user