mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
- Introduced a new struct for NotificationService within the notification form data, encompassing various configuration fields for services like Pushbullet, Ntfy, Gotify, and Pushover. - This addition enhances the form's capability to manage detailed service configurations, improving user experience and flexibility in notification management.
1579 lines
51 KiB
Go
1579 lines
51 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/starfleetcptn/gomft/components"
|
|
"github.com/starfleetcptn/gomft/internal/db"
|
|
)
|
|
|
|
// HandleSettings handles GET /settings
|
|
func (h *Handlers) HandleSettings(c *gin.Context) {
|
|
// Check if the user has permission to view settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
var notificationServices []db.NotificationService
|
|
if err := h.DB.Find(¬ificationServices).Error; err != nil {
|
|
log.Printf("Error fetching notification services: %v", err)
|
|
}
|
|
|
|
// Convert to components.NotificationService
|
|
var componentServices []components.NotificationService
|
|
for _, service := range notificationServices {
|
|
componentServices = append(componentServices, components.NotificationService{
|
|
ID: service.ID,
|
|
Name: service.Name,
|
|
Type: service.Type,
|
|
IsEnabled: service.IsEnabled,
|
|
Config: service.Config,
|
|
Description: service.Description,
|
|
EventTriggers: service.EventTriggers,
|
|
PayloadTemplate: service.PayloadTemplate,
|
|
SecretKey: service.SecretKey,
|
|
RetryPolicy: service.RetryPolicy,
|
|
SuccessCount: service.SuccessCount,
|
|
FailureCount: service.FailureCount,
|
|
})
|
|
}
|
|
|
|
data := components.SettingsData{
|
|
NotificationServices: componentServices,
|
|
}
|
|
|
|
ctx := h.CreateTemplateContext(c)
|
|
components.Settings(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// HandleCreateNotificationService handles POST /admin/settings/notifications
|
|
func (h *Handlers) HandleCreateNotificationService(c *gin.Context) {
|
|
// Check if the user has permission to manage settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
// Parse form data
|
|
name := c.PostForm("name")
|
|
serviceType := c.PostForm("type")
|
|
description := c.PostForm("description")
|
|
isEnabled := c.PostForm("is_enabled") == "on"
|
|
|
|
// Validate required fields
|
|
if name == "" || serviceType == "" {
|
|
// Return to notifications page with error message
|
|
h.handleNotificationsWithError(c, "Name and type are required fields.")
|
|
return
|
|
}
|
|
|
|
// Create config map based on service type
|
|
config := make(map[string]string)
|
|
eventTriggers := make([]string, 0)
|
|
fmt.Println("serviceType", serviceType)
|
|
switch serviceType {
|
|
case "email":
|
|
config["smtp_host"] = c.PostForm("smtp_host")
|
|
config["smtp_port"] = c.PostForm("smtp_port")
|
|
config["smtp_username"] = c.PostForm("smtp_username")
|
|
config["smtp_password"] = c.PostForm("smtp_password")
|
|
config["from_email"] = c.PostForm("from_email")
|
|
|
|
case "pushbullet":
|
|
config["api_key"] = c.PostForm("pushbullet_api_key")
|
|
config["device_iden"] = c.PostForm("pushbullet_device_iden")
|
|
config["title_template"] = c.PostForm("pushbullet_title_template")
|
|
config["body_template"] = c.PostForm("pushbullet_body_template")
|
|
|
|
// Build event triggers
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
|
|
// Validate required fields
|
|
if config["api_key"] == "" {
|
|
h.handleNotificationsWithError(c, "Pushbullet API Key is required.")
|
|
return
|
|
}
|
|
|
|
// Create new notification service
|
|
service := db.NotificationService{
|
|
Name: name,
|
|
Type: serviceType,
|
|
IsEnabled: isEnabled,
|
|
Config: config,
|
|
Description: description,
|
|
EventTriggers: eventTriggers,
|
|
CreatedBy: c.GetUint("userID"),
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Create(&service).Error; err != nil {
|
|
log.Printf("Error creating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to create notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
"event_triggers": eventTriggers,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "create",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Pushbullet notification service created successfully.")
|
|
return
|
|
|
|
case "ntfy":
|
|
config["server"] = c.PostForm("ntfy_server")
|
|
config["topic"] = c.PostForm("ntfy_topic")
|
|
config["priority"] = c.PostForm("ntfy_priority")
|
|
config["username"] = c.PostForm("ntfy_username")
|
|
config["password"] = c.PostForm("ntfy_password")
|
|
config["title_template"] = c.PostForm("ntfy_title_template")
|
|
config["message_template"] = c.PostForm("ntfy_message_template")
|
|
|
|
// Build event triggers
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
|
|
// Validate required fields
|
|
if config["server"] == "" || config["topic"] == "" {
|
|
h.handleNotificationsWithError(c, "Ntfy Server and Topic are required.")
|
|
return
|
|
}
|
|
|
|
// Create new notification service
|
|
service := db.NotificationService{
|
|
Name: name,
|
|
Type: serviceType,
|
|
IsEnabled: isEnabled,
|
|
Config: config,
|
|
Description: description,
|
|
EventTriggers: eventTriggers,
|
|
CreatedBy: c.GetUint("userID"),
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Create(&service).Error; err != nil {
|
|
log.Printf("Error creating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to create notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
"event_triggers": eventTriggers,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "create",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Ntfy notification service created successfully.")
|
|
return
|
|
|
|
case "gotify":
|
|
config["url"] = c.PostForm("gotify_url")
|
|
config["token"] = c.PostForm("gotify_token")
|
|
config["priority"] = c.PostForm("gotify_priority")
|
|
config["title_template"] = c.PostForm("gotify_title_template")
|
|
config["message_template"] = c.PostForm("gotify_message_template")
|
|
|
|
// Build event triggers
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
|
|
// Validate required fields
|
|
if config["url"] == "" || config["token"] == "" {
|
|
h.handleNotificationsWithError(c, "Gotify Server URL and Application Token are required.")
|
|
return
|
|
}
|
|
|
|
// Create new notification service
|
|
service := db.NotificationService{
|
|
Name: name,
|
|
Type: serviceType,
|
|
IsEnabled: isEnabled,
|
|
Config: config,
|
|
Description: description,
|
|
EventTriggers: eventTriggers,
|
|
CreatedBy: c.GetUint("userID"),
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Create(&service).Error; err != nil {
|
|
log.Printf("Error creating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to create notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
"event_triggers": eventTriggers,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "create",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Gotify notification service created successfully.")
|
|
return
|
|
|
|
case "pushover":
|
|
config["app_token"] = c.PostForm("pushover_app_token")
|
|
config["user_key"] = c.PostForm("pushover_user_key")
|
|
config["device"] = c.PostForm("pushover_device")
|
|
config["priority"] = c.PostForm("pushover_priority")
|
|
config["sound"] = c.PostForm("pushover_sound")
|
|
config["title_template"] = c.PostForm("pushover_title_template")
|
|
config["message_template"] = c.PostForm("pushover_message_template")
|
|
|
|
// Build event triggers
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
|
|
// Validate required fields
|
|
if config["app_token"] == "" || config["user_key"] == "" {
|
|
h.handleNotificationsWithError(c, "Pushover API Token and User Key are required.")
|
|
return
|
|
}
|
|
|
|
// Create new notification service
|
|
service := db.NotificationService{
|
|
Name: name,
|
|
Type: serviceType,
|
|
IsEnabled: isEnabled,
|
|
Config: config,
|
|
Description: description,
|
|
EventTriggers: eventTriggers,
|
|
CreatedBy: c.GetUint("userID"),
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Create(&service).Error; err != nil {
|
|
log.Printf("Error creating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to create notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
"event_triggers": eventTriggers,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "create",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Pushover notification service created successfully.")
|
|
return
|
|
|
|
case "webhook":
|
|
config["webhook_url"] = c.PostForm("webhook_url")
|
|
config["method"] = c.PostForm("method")
|
|
config["headers"] = c.PostForm("headers")
|
|
|
|
// Add the new webhook fields
|
|
// Create event triggers array
|
|
// print all event triggers
|
|
log.Printf("Event triggers: %v", c.PostForm("trigger_job_start"))
|
|
log.Printf("Event triggers: %v", c.PostForm("trigger_job_complete"))
|
|
log.Printf("Event triggers: %v", c.PostForm("trigger_job_error"))
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
|
|
// Create new notification service with additional fields
|
|
service := db.NotificationService{
|
|
Name: name,
|
|
Type: serviceType,
|
|
IsEnabled: isEnabled,
|
|
Config: config,
|
|
Description: description,
|
|
EventTriggers: eventTriggers,
|
|
PayloadTemplate: c.PostForm("payload_template"),
|
|
SecretKey: c.PostForm("secret_key"),
|
|
RetryPolicy: c.PostForm("retry_policy"),
|
|
CreatedBy: c.GetUint("userID"),
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Create(&service).Error; err != nil {
|
|
log.Printf("Error creating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to create notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
"event_triggers": eventTriggers,
|
|
"retry_policy": service.RetryPolicy,
|
|
"has_secret_key": service.SecretKey != "",
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "create",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Notification service created successfully.")
|
|
return
|
|
default:
|
|
h.handleNotificationsWithError(c, "Invalid notification service type.")
|
|
return
|
|
}
|
|
|
|
// Create new notification service
|
|
service := db.NotificationService{
|
|
Name: name,
|
|
Type: serviceType,
|
|
IsEnabled: isEnabled,
|
|
Config: config,
|
|
Description: description,
|
|
CreatedBy: c.GetUint("userID"),
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Create(&service).Error; err != nil {
|
|
log.Printf("Error creating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to create notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "create",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Notification service created successfully.")
|
|
}
|
|
|
|
// HandleDeleteNotificationService handles DELETE /admin/settings/notifications/:id
|
|
func (h *Handlers) HandleDeleteNotificationService(c *gin.Context) {
|
|
// Check if the user has permission to manage settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
// Get service ID from path
|
|
serviceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
h.handleNotificationsWithError(c, "Invalid notification service ID.")
|
|
return
|
|
}
|
|
|
|
// Find service to delete (for audit log)
|
|
var service db.NotificationService
|
|
if err := h.DB.First(&service, serviceID).Error; err != nil {
|
|
h.handleNotificationsWithError(c, "Notification service not found.")
|
|
return
|
|
}
|
|
|
|
// Delete the service
|
|
if err := h.DB.Delete(&db.NotificationService{}, serviceID).Error; err != nil {
|
|
log.Printf("Error deleting notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to delete notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "delete",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Notification service deleted successfully.")
|
|
}
|
|
|
|
// HandleTestNotification handles POST /settings/notifications/test
|
|
// This endpoint tests a notification configuration without saving it
|
|
func (h *Handlers) HandleTestNotification(c *gin.Context) {
|
|
// Check that the user has permissions to view settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.JSON(http.StatusForbidden, gin.H{"success": false, "message": "You do not have permission to manage settings"})
|
|
return
|
|
}
|
|
|
|
// Get notification service configuration
|
|
serviceType := c.PostForm("type")
|
|
config := make(map[string]string)
|
|
|
|
// print the form data
|
|
fmt.Println("serviceType", serviceType)
|
|
|
|
// Process based on service type
|
|
switch serviceType {
|
|
case "email":
|
|
config["smtp_host"] = c.PostForm("smtp_host")
|
|
config["smtp_port"] = c.PostForm("smtp_port")
|
|
config["smtp_username"] = c.PostForm("smtp_username")
|
|
config["smtp_password"] = c.PostForm("smtp_password")
|
|
config["from_email"] = c.PostForm("from_email")
|
|
config["to_email"] = c.PostForm("to_email")
|
|
config["use_tls"] = c.PostForm("use_tls")
|
|
|
|
// Validate required fields
|
|
if config["smtp_host"] == "" || config["smtp_port"] == "" || config["from_email"] == "" || config["to_email"] == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "SMTP Host, Port, From Email, and To Email are required"})
|
|
return
|
|
}
|
|
|
|
// For email, we'll simply simulate a successful test
|
|
// In a real implementation, you would want to actually try to send an email
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Test email notification would be sent successfully"})
|
|
return
|
|
|
|
case "webhook":
|
|
config["webhook_url"] = c.PostForm("webhook_url")
|
|
config["webhook_method"] = c.PostForm("webhook_method")
|
|
config["webhook_headers"] = c.PostForm("webhook_headers")
|
|
config["webhook_body_template"] = c.PostForm("webhook_body_template")
|
|
|
|
// Validate required fields
|
|
if config["webhook_url"] == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Webhook URL is required"})
|
|
return
|
|
}
|
|
|
|
// Send a test webhook
|
|
// This is a simplified example - in a real implementation, you would want to use the templates
|
|
// and properly format the request based on the configured method, headers, etc.
|
|
resp, err := http.Post(config["webhook_url"], "application/json", bytes.NewBuffer([]byte(`{"message":"This is a test notification from GoMFT"}`)))
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to send test webhook: " + err.Error()})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Test webhook sent successfully"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Test webhook returned non-success status code: " + resp.Status})
|
|
}
|
|
return
|
|
|
|
case "pushbullet":
|
|
config["api_key"] = c.PostForm("pushbullet_api_key")
|
|
config["device_iden"] = c.PostForm("pushbullet_device_iden")
|
|
|
|
// Validate required fields
|
|
if config["api_key"] == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Pushbullet API Key is required"})
|
|
return
|
|
}
|
|
|
|
// Create a test push request
|
|
pushURL := "https://api.pushbullet.com/v2/pushes"
|
|
pushData := map[string]interface{}{
|
|
"type": "note",
|
|
"title": "GoMFT Test Notification",
|
|
"body": "This is a test notification from GoMFT",
|
|
}
|
|
|
|
// Add device identifier if provided
|
|
if config["device_iden"] != "" {
|
|
pushData["device_iden"] = config["device_iden"]
|
|
}
|
|
|
|
jsonData, err := json.Marshal(pushData)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to prepare test push: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Create the request
|
|
req, err := http.NewRequest("POST", pushURL, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to create request: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Set headers
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Access-Token", config["api_key"])
|
|
|
|
// Send the request
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to send test push: " + err.Error()})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read response body for error details if needed
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Test push sent successfully"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Test push returned error: " + resp.Status + " - " + string(respBody)})
|
|
}
|
|
return
|
|
|
|
case "ntfy":
|
|
config["topic"] = c.PostForm("ntfy_topic")
|
|
config["server"] = c.PostForm("ntfy_server")
|
|
config["priority"] = c.PostForm("ntfy_priority")
|
|
config["title"] = c.PostForm("ntfy_title")
|
|
|
|
// Validate required fields
|
|
if config["topic"] == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Ntfy Topic is required"})
|
|
return
|
|
}
|
|
|
|
// Set default server if not provided
|
|
if config["server"] == "" {
|
|
config["server"] = "https://ntfy.sh"
|
|
}
|
|
|
|
// Create the URL for the notification
|
|
ntfyURL := fmt.Sprintf("%s/%s", strings.TrimRight(config["server"], "/"), config["topic"])
|
|
|
|
// Create the notification data
|
|
ntfyData := map[string]interface{}{
|
|
"topic": config["topic"],
|
|
"title": "GoMFT Test Notification",
|
|
"message": "This is a test notification from GoMFT",
|
|
}
|
|
|
|
// Add priority if provided
|
|
if config["priority"] != "" {
|
|
ntfyData["priority"] = config["priority"]
|
|
}
|
|
|
|
// Add title if provided
|
|
if config["title"] != "" {
|
|
ntfyData["title"] = config["title"]
|
|
}
|
|
|
|
jsonData, err := json.Marshal(ntfyData)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to prepare test notification: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Send the notification
|
|
resp, err := http.Post(ntfyURL, "application/json", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to send test notification: " + err.Error()})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Test notification sent successfully"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Test notification returned non-success status code: " + resp.Status})
|
|
}
|
|
return
|
|
|
|
case "gotify":
|
|
config["url"] = c.PostForm("gotify_url")
|
|
config["token"] = c.PostForm("gotify_token")
|
|
config["priority"] = c.PostForm("gotify_priority")
|
|
config["title"] = c.PostForm("gotify_title_template")
|
|
|
|
fmt.Println("config", config)
|
|
|
|
// Validate required fields
|
|
if config["url"] == "" || config["token"] == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Gotify Server URL and Application Token are required"})
|
|
return
|
|
}
|
|
|
|
// Create the URL for the notification
|
|
gotifyURL := fmt.Sprintf("%s/message", strings.TrimRight(config["url"], "/"))
|
|
|
|
// Set default values for any missing fields
|
|
title := "GoMFT Test Notification"
|
|
if config["title"] != "" {
|
|
title = config["title"]
|
|
}
|
|
|
|
priority := 5 // Default priority
|
|
if config["priority"] != "" {
|
|
priorityInt, err := strconv.Atoi(config["priority"])
|
|
if err == nil {
|
|
priority = priorityInt
|
|
}
|
|
}
|
|
|
|
// Create the notification data
|
|
gotifyData := map[string]interface{}{
|
|
"title": title,
|
|
"message": "This is a test notification from GoMFT",
|
|
"priority": priority,
|
|
}
|
|
|
|
jsonData, err := json.Marshal(gotifyData)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to prepare test notification: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Create the request
|
|
req, err := http.NewRequest("POST", gotifyURL, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to create request: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Set headers
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("X-Gotify-Key", config["token"])
|
|
|
|
// Send the request
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to send test notification: " + err.Error()})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read response body for error details if needed
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Test notification sent successfully"})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Test notification returned error: " + resp.Status + " - " + string(respBody)})
|
|
}
|
|
return
|
|
|
|
case "pushover":
|
|
config["app_token"] = c.PostForm("pushover_app_token")
|
|
config["user_key"] = c.PostForm("pushover_user_key")
|
|
config["device"] = c.PostForm("pushover_device")
|
|
config["priority"] = c.PostForm("pushover_priority")
|
|
config["sound"] = c.PostForm("pushover_sound")
|
|
|
|
// Validate required fields
|
|
if config["app_token"] == "" || config["user_key"] == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Pushover API Token and User Key are required"})
|
|
return
|
|
}
|
|
|
|
// Create the URL for the notification
|
|
pushoverURL := "https://api.pushover.net/1/messages.json"
|
|
|
|
// Create the form data for Pushover
|
|
formData := url.Values{}
|
|
formData.Set("token", config["app_token"])
|
|
formData.Set("user", config["user_key"])
|
|
formData.Set("title", "GoMFT Test Notification")
|
|
formData.Set("message", "This is a test notification from GoMFT")
|
|
|
|
// Add optional fields if provided
|
|
if config["device"] != "" {
|
|
formData.Set("device", config["device"])
|
|
}
|
|
if config["priority"] != "" {
|
|
formData.Set("priority", config["priority"])
|
|
}
|
|
if config["sound"] != "" {
|
|
formData.Set("sound", config["sound"])
|
|
}
|
|
|
|
// Send the request
|
|
resp, err := http.PostForm(pushoverURL, formData)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "Failed to send test notification: " + err.Error()})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read response body for error details if needed
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
var pushoverResp map[string]interface{}
|
|
json.Unmarshal(respBody, &pushoverResp)
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 && pushoverResp["status"].(float64) == 1 {
|
|
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Test notification sent successfully"})
|
|
} else {
|
|
errorMsg := "Test notification returned error"
|
|
if errStr, ok := pushoverResp["errors"]; ok {
|
|
errorMsg = errorMsg + ": " + fmt.Sprintf("%v", errStr)
|
|
}
|
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": errorMsg})
|
|
}
|
|
return
|
|
|
|
default:
|
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Unknown service type: " + serviceType})
|
|
}
|
|
}
|
|
|
|
// replacePlaceholders replaces placeholders in templates with sample values
|
|
func replacePlaceholders(template string) string {
|
|
// Replace common placeholders
|
|
replacements := map[string]string{
|
|
"{{job.id}}": "sample-job-123",
|
|
"{{job.name}}": "Test Job",
|
|
"{{job.status}}": "completed",
|
|
"{{job.message}}": "This is a test notification",
|
|
"{{job.event}}": "job_complete",
|
|
"{{job.started_at}}": time.Now().Add(-5 * time.Minute).Format(time.RFC3339),
|
|
"{{job.completed_at}}": time.Now().Format(time.RFC3339),
|
|
"{{job.duration_seconds}}": "300",
|
|
"{{job.config_id}}": "config-456",
|
|
"{{job.config_name}}": "Test Config",
|
|
"{{job.transfer_bytes}}": "1024",
|
|
"{{job.file_count}}": "5",
|
|
"{{instance.id}}": "gomft-instance-1",
|
|
"{{instance.name}}": "GoMFT Test Instance",
|
|
"{{instance.version}}": "1.0.0",
|
|
"{{instance.environment}}": "testing",
|
|
"{{timestamp}}": time.Now().Format(time.RFC3339),
|
|
"{{notification.id}}": "test-notification",
|
|
}
|
|
|
|
result := template
|
|
for placeholder, value := range replacements {
|
|
result = strings.Replace(result, placeholder, value, -1)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// sendTestPushbulletNotification sends a test Pushbullet notification
|
|
func sendTestPushbulletNotification(apiKey, deviceIden, title, body string) error {
|
|
const pushbulletAPI = "https://api.pushbullet.com/v2/pushes"
|
|
|
|
// Create payload
|
|
payload := map[string]interface{}{
|
|
"type": "note",
|
|
"title": title,
|
|
"body": body,
|
|
}
|
|
|
|
// Add device_iden if provided
|
|
if deviceIden != "" {
|
|
payload["device_iden"] = deviceIden
|
|
}
|
|
|
|
// Convert payload to JSON
|
|
jsonPayload, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating JSON payload: %v", err)
|
|
}
|
|
|
|
// Create request
|
|
req, err := http.NewRequest("POST", pushbulletAPI, bytes.NewBuffer(jsonPayload))
|
|
if err != nil {
|
|
return fmt.Errorf("error creating request: %v", err)
|
|
}
|
|
|
|
// Set headers
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Access-Token", apiKey)
|
|
|
|
// Send request
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("error sending request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Check response
|
|
if resp.StatusCode >= 400 {
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("pushbullet API error %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// sendTestNtfyNotification sends a test Ntfy notification
|
|
func sendTestNtfyNotification(server, topic, title, message string, priority int, username, password string) error {
|
|
// Ensure server doesn't end with a slash
|
|
server = strings.TrimSuffix(server, "/")
|
|
|
|
// Build URL
|
|
ntfyURL := fmt.Sprintf("%s/%s", server, topic)
|
|
|
|
// Create request
|
|
req, err := http.NewRequest("POST", ntfyURL, bytes.NewBufferString(message))
|
|
if err != nil {
|
|
return fmt.Errorf("error creating request: %v", err)
|
|
}
|
|
|
|
// Set headers
|
|
req.Header.Set("Title", title)
|
|
req.Header.Set("Priority", strconv.Itoa(priority))
|
|
|
|
// Set authentication if provided
|
|
if username != "" && password != "" {
|
|
req.SetBasicAuth(username, password)
|
|
}
|
|
|
|
// Send request
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("error sending request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Check response
|
|
if resp.StatusCode >= 400 {
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("ntfy API error %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// generateSamplePayload creates a sample payload for testing
|
|
func generateSamplePayload(template string) string {
|
|
// If no template provided, use a default sample
|
|
if template == "" {
|
|
return `{
|
|
"event": "job_complete",
|
|
"job": {
|
|
"id": "sample-job-123",
|
|
"name": "Test Job",
|
|
"status": "completed",
|
|
"message": "This is a test notification",
|
|
"started_at": "` + time.Now().Add(-5*time.Minute).Format(time.RFC3339) + `",
|
|
"completed_at": "` + time.Now().Format(time.RFC3339) + `",
|
|
"duration_seconds": 300,
|
|
"config_id": "config-456",
|
|
"config_name": "Test Config",
|
|
"transfer_bytes": 1024,
|
|
"file_count": 5
|
|
},
|
|
"instance": {
|
|
"id": "gomft-instance-1",
|
|
"name": "GoMFT Test Instance",
|
|
"version": "1.0.0",
|
|
"environment": "testing"
|
|
},
|
|
"timestamp": "` + time.Now().Format(time.RFC3339) + `",
|
|
"notification_id": "test-notification"
|
|
}`
|
|
}
|
|
|
|
// Replace placeholders in the template with sample values
|
|
samplePayload := template
|
|
// Replace common placeholders
|
|
replacements := map[string]string{
|
|
"{{job.id}}": "sample-job-123",
|
|
"{{job.name}}": "Test Job",
|
|
"{{job.status}}": "completed",
|
|
"{{job.message}}": "This is a test notification",
|
|
"{{job.event}}": "job_complete",
|
|
"{{job.started_at}}": time.Now().Add(-5 * time.Minute).Format(time.RFC3339),
|
|
"{{job.completed_at}}": time.Now().Format(time.RFC3339),
|
|
"{{job.duration_seconds}}": "300",
|
|
"{{job.config_id}}": "config-456",
|
|
"{{job.config_name}}": "Test Config",
|
|
"{{job.transfer_bytes}}": "1024",
|
|
"{{job.file_count}}": "5",
|
|
"{{instance.id}}": "gomft-instance-1",
|
|
"{{instance.name}}": "GoMFT Test Instance",
|
|
"{{instance.version}}": "1.0.0",
|
|
"{{instance.environment}}": "testing",
|
|
"{{timestamp}}": time.Now().Format(time.RFC3339),
|
|
"{{notification.id}}": "test-notification",
|
|
}
|
|
|
|
for placeholder, value := range replacements {
|
|
samplePayload = strings.Replace(samplePayload, placeholder, value, -1)
|
|
}
|
|
|
|
return samplePayload
|
|
}
|
|
|
|
// sendTestWebhook sends a test webhook to the specified URL
|
|
func sendTestWebhook(config map[string]string, payload string, secretKey string) error {
|
|
webhookURL := config["webhook_url"]
|
|
method := config["method"]
|
|
if method == "" {
|
|
method = "POST"
|
|
}
|
|
|
|
// Create the request
|
|
req, err := http.NewRequest(method, webhookURL, bytes.NewBufferString(payload))
|
|
if err != nil {
|
|
return fmt.Errorf("error creating request: %v", err)
|
|
}
|
|
|
|
// Set default Content-Type if not specified
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// Parse and set custom headers
|
|
if config["headers"] != "" {
|
|
var headers map[string]string
|
|
if err := json.Unmarshal([]byte(config["headers"]), &headers); err == nil {
|
|
for key, value := range headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add signature if secret key is provided
|
|
if secretKey != "" {
|
|
signature := calculateSignature(payload, secretKey)
|
|
req.Header.Set("X-GoMFT-Signature", signature)
|
|
}
|
|
|
|
// Send the request
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("error sending webhook: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Check the response
|
|
if resp.StatusCode >= 400 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("webhook returned error %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// calculateSignature generates an HMAC signature for webhook payloads
|
|
func calculateSignature(payload string, secretKey string) string {
|
|
h := hmac.New(sha256.New, []byte(secretKey))
|
|
h.Write([]byte(payload))
|
|
return fmt.Sprintf("sha256=%x", h.Sum(nil))
|
|
}
|
|
|
|
// Helper function to check if user has a specific permission
|
|
func (h *Handlers) checkPermission(c *gin.Context, permission string) bool {
|
|
// If user is admin, they have all permissions
|
|
isAdmin, exists := c.Get("isAdmin")
|
|
if exists && isAdmin.(bool) {
|
|
return true
|
|
}
|
|
|
|
// Get user from context
|
|
userID := c.GetUint("userID")
|
|
if userID == 0 {
|
|
return false
|
|
}
|
|
|
|
// Check if user has the required permission
|
|
// h.DB.UserHasPermission undefined (type *db.DB has no field or method UserHasPermission)
|
|
// Load user with roles and check permission
|
|
var user db.User
|
|
if err := h.DB.Preload("Roles").First(&user, userID).Error; err != nil {
|
|
log.Printf("Error loading user: %v", err)
|
|
return false
|
|
}
|
|
|
|
return user.HasPermission(permission)
|
|
}
|
|
|
|
// HandleNotificationsPage handles GET /admin/settings/notifications
|
|
func (h *Handlers) HandleNotificationsPage(c *gin.Context) {
|
|
// Check if the user has permission to view settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
var notificationServices []db.NotificationService
|
|
if err := h.DB.Find(¬ificationServices).Error; err != nil {
|
|
log.Printf("Error fetching notification services: %v", err)
|
|
}
|
|
|
|
// Convert to components.NotificationService
|
|
var componentServices []components.NotificationService
|
|
for _, service := range notificationServices {
|
|
componentServices = append(componentServices, components.NotificationService{
|
|
ID: service.ID,
|
|
Name: service.Name,
|
|
Type: service.Type,
|
|
IsEnabled: service.IsEnabled,
|
|
Config: service.Config,
|
|
Description: service.Description,
|
|
EventTriggers: service.EventTriggers,
|
|
PayloadTemplate: service.PayloadTemplate,
|
|
SecretKey: service.SecretKey,
|
|
RetryPolicy: service.RetryPolicy,
|
|
SuccessCount: service.SuccessCount,
|
|
FailureCount: service.FailureCount,
|
|
})
|
|
}
|
|
|
|
data := components.SettingsNotificationsData{
|
|
NotificationServices: componentServices,
|
|
}
|
|
|
|
ctx := h.CreateTemplateContext(c)
|
|
components.Notifications(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// HandleNewNotificationPage handles GET /admin/settings/notifications/new
|
|
func (h *Handlers) HandleNewNotificationPage(c *gin.Context) {
|
|
// Check if the user has permission to view settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
data := components.NotificationFormData{
|
|
IsNew: true,
|
|
NotificationService: &struct {
|
|
ID uint
|
|
Name string
|
|
Description string
|
|
Type string
|
|
IsEnabled bool
|
|
EventTriggers []string
|
|
RetryPolicy string
|
|
WebhookURL string
|
|
Method string
|
|
Headers string
|
|
PayloadTemplate string
|
|
SecretKey string
|
|
PushbulletAPIKey string
|
|
PushbulletDeviceID string
|
|
PushbulletTitleTemplate string
|
|
PushbulletBodyTemplate string
|
|
NtfyServer string
|
|
NtfyTopic string
|
|
NtfyPriority string
|
|
NtfyUsername string
|
|
NtfyPassword string
|
|
NtfyTitleTemplate string
|
|
NtfyMessageTemplate string
|
|
GotifyURL string
|
|
GotifyToken string
|
|
GotifyPriority string
|
|
GotifyTitleTemplate string
|
|
GotifyMessageTemplate string
|
|
PushoverAPIToken string
|
|
PushoverUserKey string
|
|
PushoverDevice string
|
|
PushoverPriority string
|
|
PushoverSound string
|
|
PushoverTitleTemplate string
|
|
PushoverMessageTemplate string
|
|
}{},
|
|
}
|
|
|
|
ctx := h.CreateTemplateContext(c)
|
|
components.NotificationForm(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// HandleEditNotificationPage handles GET /admin/settings/notifications/:id/edit
|
|
func (h *Handlers) HandleEditNotificationPage(c *gin.Context) {
|
|
// Check if the user has permission to view settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
// Get service ID from path
|
|
serviceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
h.handleNotificationsWithError(c, "Invalid notification service ID.")
|
|
return
|
|
}
|
|
|
|
// Find the notification service
|
|
var service db.NotificationService
|
|
if err := h.DB.First(&service, serviceID).Error; err != nil {
|
|
h.handleNotificationsWithError(c, "Notification service not found.")
|
|
return
|
|
}
|
|
|
|
// Convert to NotificationFormData
|
|
data := components.NotificationFormData{
|
|
IsNew: false,
|
|
NotificationService: &struct {
|
|
ID uint
|
|
Name string
|
|
Description string
|
|
Type string
|
|
IsEnabled bool
|
|
EventTriggers []string
|
|
RetryPolicy string
|
|
WebhookURL string
|
|
Method string
|
|
Headers string
|
|
PayloadTemplate string
|
|
SecretKey string
|
|
PushbulletAPIKey string
|
|
PushbulletDeviceID string
|
|
PushbulletTitleTemplate string
|
|
PushbulletBodyTemplate string
|
|
NtfyServer string
|
|
NtfyTopic string
|
|
NtfyPriority string
|
|
NtfyUsername string
|
|
NtfyPassword string
|
|
NtfyTitleTemplate string
|
|
NtfyMessageTemplate string
|
|
GotifyURL string
|
|
GotifyToken string
|
|
GotifyPriority string
|
|
GotifyTitleTemplate string
|
|
GotifyMessageTemplate string
|
|
PushoverAPIToken string
|
|
PushoverUserKey string
|
|
PushoverDevice string
|
|
PushoverPriority string
|
|
PushoverSound string
|
|
PushoverTitleTemplate string
|
|
PushoverMessageTemplate string
|
|
}{
|
|
ID: service.ID,
|
|
Name: service.Name,
|
|
Description: service.Description,
|
|
Type: service.Type,
|
|
IsEnabled: service.IsEnabled,
|
|
EventTriggers: service.EventTriggers,
|
|
RetryPolicy: service.RetryPolicy,
|
|
PayloadTemplate: service.PayloadTemplate,
|
|
SecretKey: service.SecretKey,
|
|
WebhookURL: service.Config["webhook_url"],
|
|
Method: service.Config["method"],
|
|
Headers: service.Config["headers"],
|
|
PushbulletAPIKey: service.Config["api_key"],
|
|
PushbulletDeviceID: service.Config["device_iden"],
|
|
PushbulletTitleTemplate: service.Config["title_template"],
|
|
PushbulletBodyTemplate: service.Config["body_template"],
|
|
NtfyServer: service.Config["server"],
|
|
NtfyTopic: service.Config["topic"],
|
|
NtfyPriority: service.Config["priority"],
|
|
NtfyUsername: service.Config["username"],
|
|
NtfyPassword: service.Config["password"],
|
|
NtfyTitleTemplate: service.Config["title_template"],
|
|
NtfyMessageTemplate: service.Config["message_template"],
|
|
GotifyURL: service.Config["url"],
|
|
GotifyToken: service.Config["token"],
|
|
GotifyPriority: service.Config["priority"],
|
|
GotifyTitleTemplate: service.Config["title_template"],
|
|
GotifyMessageTemplate: service.Config["message_template"],
|
|
PushoverAPIToken: service.Config["app_token"],
|
|
PushoverUserKey: service.Config["user_key"],
|
|
PushoverDevice: service.Config["device"],
|
|
PushoverPriority: service.Config["priority"],
|
|
PushoverSound: service.Config["sound"],
|
|
PushoverTitleTemplate: service.Config["title_template"],
|
|
PushoverMessageTemplate: service.Config["message_template"],
|
|
},
|
|
}
|
|
|
|
// Add type-specific fields
|
|
if service.Type == "webhook" {
|
|
data.NotificationService.WebhookURL = service.Config["webhook_url"]
|
|
data.NotificationService.Method = service.Config["method"]
|
|
data.NotificationService.Headers = service.Config["headers"]
|
|
}
|
|
|
|
ctx := h.CreateTemplateContext(c)
|
|
components.NotificationForm(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// HandleUpdateNotificationService handles PUT /admin/settings/notifications/:id
|
|
func (h *Handlers) HandleUpdateNotificationService(c *gin.Context) {
|
|
// Check if the user has permission to manage settings
|
|
if !h.checkPermission(c, "system.settings") {
|
|
c.Redirect(http.StatusFound, "/dashboard")
|
|
return
|
|
}
|
|
|
|
// Get service ID from path
|
|
serviceID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
h.handleNotificationsWithError(c, "Invalid notification service ID.")
|
|
return
|
|
}
|
|
|
|
// Find the notification service
|
|
var service db.NotificationService
|
|
if err := h.DB.First(&service, serviceID).Error; err != nil {
|
|
h.handleNotificationsWithError(c, "Notification service not found.")
|
|
return
|
|
}
|
|
|
|
// Parse form data
|
|
name := c.PostForm("name")
|
|
serviceType := c.PostForm("type")
|
|
description := c.PostForm("description")
|
|
isEnabled := c.PostForm("is_enabled") == "on"
|
|
|
|
// Validate required fields
|
|
if name == "" || serviceType == "" {
|
|
h.handleNotificationsWithError(c, "Name and type are required fields.")
|
|
return
|
|
}
|
|
|
|
// Update basic fields
|
|
service.Name = name
|
|
service.Type = serviceType
|
|
service.Description = description
|
|
service.IsEnabled = isEnabled
|
|
|
|
// Update type-specific fields based on service type
|
|
switch serviceType {
|
|
case "webhook":
|
|
// Update webhook-specific fields
|
|
service.Config["webhook_url"] = c.PostForm("webhook_url")
|
|
service.Config["method"] = c.PostForm("method")
|
|
service.Config["headers"] = c.PostForm("headers")
|
|
service.PayloadTemplate = c.PostForm("payload_template")
|
|
service.SecretKey = c.PostForm("secret_key")
|
|
service.RetryPolicy = c.PostForm("retry_policy")
|
|
|
|
// Update event triggers
|
|
eventTriggers := make([]string, 0)
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
service.EventTriggers = eventTriggers
|
|
|
|
case "pushbullet":
|
|
// Update Pushbullet-specific fields
|
|
service.Config["api_key"] = c.PostForm("pushbullet_api_key")
|
|
service.Config["device_iden"] = c.PostForm("pushbullet_device_iden")
|
|
service.Config["title_template"] = c.PostForm("pushbullet_title_template")
|
|
service.Config["body_template"] = c.PostForm("pushbullet_body_template")
|
|
|
|
// Update event triggers
|
|
eventTriggers := make([]string, 0)
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
service.EventTriggers = eventTriggers
|
|
|
|
case "ntfy":
|
|
// Update Ntfy-specific fields
|
|
service.Config["server"] = c.PostForm("ntfy_server")
|
|
service.Config["topic"] = c.PostForm("ntfy_topic")
|
|
service.Config["priority"] = c.PostForm("ntfy_priority")
|
|
service.Config["username"] = c.PostForm("ntfy_username")
|
|
service.Config["password"] = c.PostForm("ntfy_password")
|
|
service.Config["title_template"] = c.PostForm("ntfy_title_template")
|
|
service.Config["message_template"] = c.PostForm("ntfy_message_template")
|
|
|
|
// Update event triggers
|
|
eventTriggers := make([]string, 0)
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
service.EventTriggers = eventTriggers
|
|
|
|
case "gotify":
|
|
// Update Gotify-specific fields
|
|
service.Config["url"] = c.PostForm("gotify_url")
|
|
service.Config["token"] = c.PostForm("gotify_token")
|
|
service.Config["priority"] = c.PostForm("gotify_priority")
|
|
service.Config["title_template"] = c.PostForm("gotify_title_template")
|
|
service.Config["message_template"] = c.PostForm("gotify_message_template")
|
|
|
|
// Update event triggers
|
|
eventTriggers := make([]string, 0)
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
service.EventTriggers = eventTriggers
|
|
|
|
case "pushover":
|
|
// Update Pushover-specific fields
|
|
service.Config["app_token"] = c.PostForm("pushover_app_token")
|
|
service.Config["user_key"] = c.PostForm("pushover_user_key")
|
|
service.Config["device"] = c.PostForm("pushover_device")
|
|
service.Config["priority"] = c.PostForm("pushover_priority")
|
|
service.Config["sound"] = c.PostForm("pushover_sound")
|
|
service.Config["title_template"] = c.PostForm("pushover_title_template")
|
|
service.Config["message_template"] = c.PostForm("pushover_message_template")
|
|
|
|
// Update event triggers
|
|
eventTriggers := make([]string, 0)
|
|
if c.PostForm("trigger_job_start") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_start")
|
|
}
|
|
if c.PostForm("trigger_job_complete") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_complete")
|
|
}
|
|
if c.PostForm("trigger_job_error") == "on" {
|
|
eventTriggers = append(eventTriggers, "job_error")
|
|
}
|
|
service.EventTriggers = eventTriggers
|
|
}
|
|
|
|
// Save to database
|
|
if err := h.DB.Save(&service).Error; err != nil {
|
|
log.Printf("Error updating notification service: %v", err)
|
|
h.handleNotificationsWithError(c, "Failed to update notification service: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Create audit log
|
|
auditDetails := map[string]interface{}{
|
|
"name": service.Name,
|
|
"type": service.Type,
|
|
"is_enabled": service.IsEnabled,
|
|
"description": service.Description,
|
|
"event_triggers": service.EventTriggers,
|
|
}
|
|
|
|
auditLog := db.AuditLog{
|
|
Action: "update",
|
|
EntityType: "notification_service",
|
|
EntityID: service.ID,
|
|
UserID: c.GetUint("userID"),
|
|
Details: auditDetails,
|
|
}
|
|
|
|
if err := h.DB.Create(&auditLog).Error; err != nil {
|
|
log.Printf("Error creating audit log: %v", err)
|
|
}
|
|
|
|
// Redirect back to notifications page with success message
|
|
h.handleNotificationsWithSuccess(c, "Notification service updated successfully.")
|
|
}
|
|
|
|
// handleNotificationsWithError renders the notifications page with an error message
|
|
func (h *Handlers) handleNotificationsWithError(c *gin.Context, errorMessage string) {
|
|
var notificationServices []db.NotificationService
|
|
if err := h.DB.Find(¬ificationServices).Error; err != nil {
|
|
log.Printf("Error fetching notification services: %v", err)
|
|
}
|
|
|
|
// Convert to components.NotificationService
|
|
var componentServices []components.NotificationService
|
|
for _, service := range notificationServices {
|
|
componentServices = append(componentServices, components.NotificationService{
|
|
ID: service.ID,
|
|
Name: service.Name,
|
|
Type: service.Type,
|
|
IsEnabled: service.IsEnabled,
|
|
Config: service.Config,
|
|
Description: service.Description,
|
|
EventTriggers: service.EventTriggers,
|
|
PayloadTemplate: service.PayloadTemplate,
|
|
SecretKey: service.SecretKey,
|
|
RetryPolicy: service.RetryPolicy,
|
|
SuccessCount: service.SuccessCount,
|
|
FailureCount: service.FailureCount,
|
|
})
|
|
}
|
|
|
|
data := components.SettingsNotificationsData{
|
|
NotificationServices: componentServices,
|
|
ErrorMessage: errorMessage,
|
|
}
|
|
|
|
ctx := h.CreateTemplateContext(c)
|
|
components.Notifications(ctx, data).Render(ctx, c.Writer)
|
|
}
|
|
|
|
// handleNotificationsWithSuccess renders the notifications page with a success message
|
|
func (h *Handlers) handleNotificationsWithSuccess(c *gin.Context, successMessage string) {
|
|
var notificationServices []db.NotificationService
|
|
if err := h.DB.Find(¬ificationServices).Error; err != nil {
|
|
log.Printf("Error fetching notification services: %v", err)
|
|
}
|
|
|
|
// Convert to components.NotificationService
|
|
var componentServices []components.NotificationService
|
|
for _, service := range notificationServices {
|
|
componentServices = append(componentServices, components.NotificationService{
|
|
ID: service.ID,
|
|
Name: service.Name,
|
|
Type: service.Type,
|
|
IsEnabled: service.IsEnabled,
|
|
Config: service.Config,
|
|
Description: service.Description,
|
|
EventTriggers: service.EventTriggers,
|
|
PayloadTemplate: service.PayloadTemplate,
|
|
SecretKey: service.SecretKey,
|
|
RetryPolicy: service.RetryPolicy,
|
|
SuccessCount: service.SuccessCount,
|
|
FailureCount: service.FailureCount,
|
|
})
|
|
}
|
|
|
|
data := components.SettingsNotificationsData{
|
|
NotificationServices: componentServices,
|
|
SuccessMessage: successMessage,
|
|
}
|
|
|
|
ctx := h.CreateTemplateContext(c)
|
|
components.Notifications(ctx, data).Render(ctx, c.Writer)
|
|
}
|