Files
Beszel/internal/hub/notifications/api.go
T

278 lines
8.1 KiB
Go

package notifications
import (
"encoding/json"
"net/http"
"github.com/henrygd/beszel/internal/entities/notification"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
)
// RegisterRoutes registers notification API routes
func RegisterRoutes(app core.App, se *core.ServeEvent) {
api := &NotificationAPI{app: app}
group := se.Router.Group("/api/beszel/notifications")
group.Bind(apis.RequireAuth())
group.GET("/", api.listNotifications)
group.POST("/", api.createNotification)
group.GET("/{id}", api.getNotification)
group.PATCH("/{id}", api.updateNotification)
group.DELETE("/{id}", api.deleteNotification)
group.POST("/{id}/test", api.testNotification)
}
// NotificationAPI handles notification API requests
type NotificationAPI struct {
app core.App
}
// CreateNotificationRequest represents a notification creation request
type CreateNotificationRequest struct {
Name string `json:"name"`
Type string `json:"type"`
Settings map[string]interface{} `json:"settings"`
IsDefault bool `json:"is_default"`
}
// UpdateNotificationRequest represents a notification update request
type UpdateNotificationRequest struct {
Name string `json:"name,omitempty"`
Settings map[string]interface{} `json:"settings,omitempty"`
IsDefault *bool `json:"is_default,omitempty"`
Active *bool `json:"active,omitempty"`
}
// NotificationResponse represents a notification response
type NotificationResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
IsDefault bool `json:"is_default"`
Active bool `json:"active"`
Settings map[string]interface{} `json:"settings"`
Created string `json:"created"`
Updated string `json:"updated"`
}
// listNotifications lists all notifications for the authenticated user
func (api *NotificationAPI) listNotifications(e *core.RequestEvent) error {
authRecord := e.Auth
if authRecord == nil {
return e.UnauthorizedError("unauthorized", nil)
}
records, err := api.app.FindAllRecords("notifications",
dbx.NewExp("user = {:user}", dbx.Params{"user": authRecord.Id}),
)
if err != nil {
return e.InternalServerError("failed to fetch notifications", err)
}
notifications := make([]NotificationResponse, 0, len(records))
for _, record := range records {
notifications = append(notifications, api.recordToResponse(record))
}
return e.JSON(http.StatusOK, notifications)
}
// createNotification creates a new notification
func (api *NotificationAPI) createNotification(e *core.RequestEvent) error {
authRecord := e.Auth
if authRecord == nil {
return e.UnauthorizedError("unauthorized", nil)
}
var req CreateNotificationRequest
if err := json.NewDecoder(e.Request.Body).Decode(&req); err != nil {
return e.BadRequestError("invalid request body", err)
}
if req.Name == "" || req.Type == "" {
return e.BadRequestError("name and type are required", nil)
}
collection, err := api.app.FindCollectionByNameOrId("notifications")
if err != nil {
return e.InternalServerError("failed to find collection", err)
}
settingsJSON, _ := json.Marshal(req.Settings)
record := core.NewRecord(collection)
record.Set("name", req.Name)
record.Set("type", req.Type)
record.Set("settings", string(settingsJSON))
record.Set("is_default", req.IsDefault)
record.Set("active", true)
record.Set("user", authRecord.Id)
if err := api.app.Save(record); err != nil {
return e.InternalServerError("failed to create notification", err)
}
return e.JSON(http.StatusCreated, api.recordToResponse(record))
}
// getNotification gets a single notification
func (api *NotificationAPI) getNotification(e *core.RequestEvent) error {
authRecord := e.Auth
if authRecord == nil {
return e.UnauthorizedError("unauthorized", nil)
}
id := e.Request.PathValue("id")
record, err := api.app.FindRecordById("notifications", id)
if err != nil {
return e.NotFoundError("notification not found", err)
}
if record.GetString("user") != authRecord.Id {
return e.ForbiddenError("not authorized", nil)
}
return e.JSON(http.StatusOK, api.recordToResponse(record))
}
// updateNotification updates a notification
func (api *NotificationAPI) updateNotification(e *core.RequestEvent) error {
authRecord := e.Auth
if authRecord == nil {
return e.UnauthorizedError("unauthorized", nil)
}
id := e.Request.PathValue("id")
record, err := api.app.FindRecordById("notifications", id)
if err != nil {
return e.NotFoundError("notification not found", err)
}
if record.GetString("user") != authRecord.Id {
return e.ForbiddenError("not authorized", nil)
}
var req UpdateNotificationRequest
if err := json.NewDecoder(e.Request.Body).Decode(&req); err != nil {
return e.BadRequestError("invalid request body", err)
}
if req.Name != "" {
record.Set("name", req.Name)
}
if req.Settings != nil {
settingsJSON, _ := json.Marshal(req.Settings)
record.Set("settings", string(settingsJSON))
}
if req.IsDefault != nil {
record.Set("is_default", *req.IsDefault)
}
if req.Active != nil {
record.Set("active", *req.Active)
}
if err := api.app.Save(record); err != nil {
return e.InternalServerError("failed to update notification", err)
}
return e.JSON(http.StatusOK, api.recordToResponse(record))
}
// deleteNotification deletes a notification
func (api *NotificationAPI) deleteNotification(e *core.RequestEvent) error {
authRecord := e.Auth
if authRecord == nil {
return e.UnauthorizedError("unauthorized", nil)
}
id := e.Request.PathValue("id")
record, err := api.app.FindRecordById("notifications", id)
if err != nil {
return e.NotFoundError("notification not found", err)
}
if record.GetString("user") != authRecord.Id {
return e.ForbiddenError("not authorized", nil)
}
if err := api.app.Delete(record); err != nil {
return e.InternalServerError("failed to delete notification", err)
}
return e.NoContent(http.StatusNoContent)
}
// testNotification sends a test notification
func (api *NotificationAPI) testNotification(e *core.RequestEvent) error {
authRecord := e.Auth
if authRecord == nil {
return e.UnauthorizedError("unauthorized", nil)
}
id := e.Request.PathValue("id")
record, err := api.app.FindRecordById("notifications", id)
if err != nil {
return e.NotFoundError("notification not found", err)
}
if record.GetString("user") != authRecord.Id {
return e.ForbiddenError("not authorized", nil)
}
notif := &notification.Notification{
ID: record.Id,
Name: record.GetString("name"),
Type: record.GetString("type"),
Active: record.GetBool("active"),
}
if settingsJSON := record.GetString("settings"); settingsJSON != "" {
var settings map[string]interface{}
if err := json.Unmarshal([]byte(settingsJSON), &settings); err == nil {
notif.Settings = settings
}
}
dispatcher := NewDispatcher(api.app)
msg := &notification.NotificationMessage{
Title: "Test Notification",
Body: "This is a test notification from Beszel.",
MonitorName: "Test Monitor",
Status: "UP",
Message: "Test message",
}
provider, err := dispatcher.getProvider(notif)
if err != nil {
return e.InternalServerError("failed to create provider", err)
}
if err := provider.Send(msg); err != nil {
return e.InternalServerError("failed to send test notification", err)
}
return e.JSON(http.StatusOK, map[string]string{"status": "sent"})
}
// recordToResponse converts a record to a response
func (api *NotificationAPI) recordToResponse(record *core.Record) NotificationResponse {
var settings map[string]interface{}
if settingsJSON := record.GetString("settings"); settingsJSON != "" {
json.Unmarshal([]byte(settingsJSON), &settings)
}
return NotificationResponse{
ID: record.Id,
Name: record.GetString("name"),
Type: record.GetString("type"),
IsDefault: record.GetBool("is_default"),
Active: record.GetBool("active"),
Settings: settings,
Created: record.GetDateTime("created").String(),
Updated: record.GetDateTime("updated").String(),
}
}