mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
159 lines
5.3 KiB
Go
159 lines
5.3 KiB
Go
package controllers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"fotbal-club/internal/models"
|
|
"fotbal-club/pkg/email"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// NotificationsController handles admin-triggered notifications (emails)
|
|
// It reuses the newsletter sending pipeline/templates.
|
|
type NotificationsController struct {
|
|
DB *gorm.DB
|
|
EmailService email.EmailService
|
|
}
|
|
|
|
func NewNotificationsController(db *gorm.DB, emailService email.EmailService) *NotificationsController {
|
|
return &NotificationsController{DB: db, EmailService: emailService}
|
|
}
|
|
|
|
// Common request payload for notifications
|
|
// Subject is required; body/content carries HTML. Recipients optional.
|
|
// If SendToSubscribers is true, all active newsletter subscribers will be included.
|
|
// Optional context identifiers (competition code or match ext ID) are accepted for logging/auditing
|
|
// or future template specialization, but are not required for sending right now.
|
|
type NotificationRequest struct {
|
|
Subject string `json:"subject" binding:"required"`
|
|
Body string `json:"body"`
|
|
Content string `json:"content"`
|
|
Recipients []string `json:"recipients"`
|
|
SendToSubscribers bool `json:"send_to_subscribers"`
|
|
CompetitionCode string `json:"competition_code"`
|
|
MatchExternalID string `json:"match_external_id"`
|
|
}
|
|
|
|
// SendCompetitionNotification sends a notification related to a competition
|
|
// @Summary Send competition notification (admin)
|
|
// @Description Sends a notification email for a competition to selected recipients and/or newsletter subscribers
|
|
// @Tags admin
|
|
// @Security Bearer
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param input body NotificationRequest true "Notification payload"
|
|
// @Success 200 {object} map[string]any
|
|
// @Failure 400 {object} map[string]string
|
|
// @Failure 401 {object} map[string]string
|
|
// @Failure 403 {object} map[string]string
|
|
// @Router /api/v1/admin/notifications/competition [post]
|
|
func (nc *NotificationsController) SendCompetitionNotification(c *gin.Context) {
|
|
nc.sendNotification(c)
|
|
}
|
|
|
|
// SendMatchNotification sends a notification related to a match
|
|
// @Summary Send match notification (admin)
|
|
// @Description Sends a notification email for a match to selected recipients and/or newsletter subscribers
|
|
// @Tags admin
|
|
// @Security Bearer
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param input body NotificationRequest true "Notification payload"
|
|
// @Success 200 {object} map[string]any
|
|
// @Failure 400 {object} map[string]string
|
|
// @Failure 401 {object} map[string]string
|
|
// @Failure 403 {object} map[string]string
|
|
// @Router /api/v1/admin/notifications/match [post]
|
|
func (nc *NotificationsController) SendMatchNotification(c *gin.Context) {
|
|
nc.sendNotification(c)
|
|
}
|
|
|
|
// Internal shared implementation
|
|
func (nc *NotificationsController) sendNotification(c *gin.Context) {
|
|
// Only admins
|
|
if c.GetString("userRole") != "admin" {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
|
return
|
|
}
|
|
|
|
var input NotificationRequest
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
|
|
return
|
|
}
|
|
|
|
// Prefer Body then Content
|
|
body := strings.TrimSpace(input.Body)
|
|
if body == "" {
|
|
body = strings.TrimSpace(input.Content)
|
|
}
|
|
if input.Subject == "" || body == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "subject and body/content are required"})
|
|
return
|
|
}
|
|
|
|
// Build recipients
|
|
recipients := make([]string, 0, len(input.Recipients)+16)
|
|
// Explicit recipients
|
|
for _, r := range input.Recipients {
|
|
r = strings.TrimSpace(strings.ToLower(r))
|
|
if r != "" {
|
|
recipients = append(recipients, r)
|
|
}
|
|
}
|
|
|
|
// Include subscribers if requested
|
|
if input.SendToSubscribers {
|
|
var subs []models.NewsletterSubscription
|
|
if err := nc.DB.Where("is_active = ?", true).Find(&subs).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load subscribers"})
|
|
return
|
|
}
|
|
for _, s := range subs {
|
|
if s.Email != "" {
|
|
recipients = append(recipients, strings.ToLower(strings.TrimSpace(s.Email)))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dedupe recipients
|
|
uniq := make(map[string]struct{}, len(recipients))
|
|
out := make([]string, 0, len(recipients))
|
|
for _, r := range recipients {
|
|
if r == "" {
|
|
continue
|
|
}
|
|
if _, exists := uniq[r]; !exists {
|
|
uniq[r] = struct{}{}
|
|
out = append(out, r)
|
|
}
|
|
}
|
|
|
|
if len(out) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No recipients resolved"})
|
|
return
|
|
}
|
|
|
|
// Send using newsletter template/pipeline
|
|
data := &email.NewsletterData{
|
|
Subject: input.Subject,
|
|
Content: body,
|
|
Recipients: out,
|
|
}
|
|
|
|
if err := nc.EmailService.SendNewsletter(data); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send notifications"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "Notifications sent",
|
|
"recipients": len(out),
|
|
"competition": input.CompetitionCode,
|
|
"match": input.MatchExternalID,
|
|
})
|
|
}
|