Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/alerts/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ func NewAlertManager(app hubLike) *AlertManager {
func (am *AlertManager) bindEvents() {
am.hub.OnRecordAfterUpdateSuccess("alerts").BindFunc(updateHistoryOnAlertUpdate)
am.hub.OnRecordAfterDeleteSuccess("alerts").BindFunc(resolveHistoryOnAlertDelete)
am.hub.OnRecordAfterUpdateSuccess("container_alerts").BindFunc(updateHistoryOnContainerAlertUpdate)
am.hub.OnRecordAfterDeleteSuccess("container_alerts").BindFunc(resolveHistoryOnContainerAlertDelete)
am.hub.OnRecordAfterUpdateSuccess("smart_devices").BindFunc(am.handleSmartDeviceAlert)
}

Expand Down
116 changes: 116 additions & 0 deletions internal/alerts/alerts_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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})
}
Loading