mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user