mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
dev day #79
This commit is contained in:
@@ -28,6 +28,78 @@ type ContactController struct {
|
||||
emailService email.EmailService
|
||||
}
|
||||
|
||||
func NewContactController(db *gorm.DB, emailService email.EmailService) *ContactController {
|
||||
return &ContactController{DB: db, emailService: emailService}
|
||||
}
|
||||
|
||||
type NewsletterSubscriptionRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Preferences map[string]bool `json:"preferences"`
|
||||
}
|
||||
|
||||
// SubmitContactForm handles public contact form submissions
|
||||
// POST /api/v1/contact
|
||||
func (cc *ContactController) SubmitContactForm(c *gin.Context) {
|
||||
var input struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
Subject string `json:"subject" binding:"required"`
|
||||
Message string `json:"message" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid payload"})
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(input.Name)
|
||||
emailStr := strings.TrimSpace(input.Email)
|
||||
subject := strings.TrimSpace(input.Subject)
|
||||
message := strings.TrimSpace(input.Message)
|
||||
if name == "" || emailStr == "" || subject == "" || message == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "All fields are required"})
|
||||
return
|
||||
}
|
||||
if !strings.Contains(emailStr, "@") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Valid email is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if s, _ := services.FilterBadWords(subject); s != "" {
|
||||
subject = s
|
||||
}
|
||||
if m, _ := services.FilterBadWords(message); m != "" {
|
||||
message = m
|
||||
}
|
||||
|
||||
ip := c.ClientIP()
|
||||
ua := c.GetHeader("User-Agent")
|
||||
|
||||
msg := models.ContactMessage{
|
||||
Name: name,
|
||||
Email: emailStr,
|
||||
Subject: subject,
|
||||
Message: message,
|
||||
Source: "contact",
|
||||
IPAddress: ip,
|
||||
UserAgent: ua,
|
||||
}
|
||||
if err := cc.DB.Create(&msg).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save message"})
|
||||
return
|
||||
}
|
||||
|
||||
_ = cc.emailService.SendContactForm(&email.ContactFormData{
|
||||
Name: name,
|
||||
Email: emailStr,
|
||||
Subject: subject,
|
||||
Message: message,
|
||||
IPAddress: ip,
|
||||
UserAgent: ua,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Message received", "id": msg.ID})
|
||||
}
|
||||
|
||||
func (cc *ContactController) AdminSmtpTest(c *gin.Context) {
|
||||
if c.GetString("userRole") != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
||||
@@ -387,11 +459,24 @@ func (cc *ContactController) SendNewsletterTest(c *gin.Context) {
|
||||
for _, r := range recipients { _ = cc.emailService.SendNewsletterWelcomeBack(&email.NewsletterWelcomeBackData{Email: r}) }
|
||||
case "setup":
|
||||
for _, r := range recipients {
|
||||
token, tErr := utils.GenerateSubscriberToken(r, 60*24)
|
||||
if tErr != nil { logger.Error("Failed to generate token for setup test: %v", tErr); continue }
|
||||
token, _ := utils.GenerateSubscriberToken(r, 60*24) // 1 day
|
||||
baseFE := strings.TrimSuffix(config.AppConfig.FrontendBaseURL, "/")
|
||||
setupURL := baseFE + "/newsletter/setup?token=" + url.QueryEscape(token)
|
||||
setupEmail := &email.EmailData{Subject: "Test: Nastavte svůj newsletter", To: []string{r}, Template: "newsletter_setup", Data: struct{ SetupURL string }{SetupURL: setupURL}}
|
||||
|
||||
// Engagement: if a user already exists for this email, award points
|
||||
var subUser models.User
|
||||
if err := cc.DB.Where("LOWER(email) = LOWER(?)", r).First(&subUser).Error; err == nil && subUser.ID != 0 {
|
||||
es := services.NewEngagementService(cc.DB)
|
||||
_, _ = es.AwardPoints(subUser.ID, 12, "newsletter_subscribe", map[string]interface{}{"email": r})
|
||||
_ = es.CheckAndAwardAchievements(subUser.ID)
|
||||
}
|
||||
|
||||
setupEmail := &email.EmailData{
|
||||
Subject: "Nastavte svůj newsletter",
|
||||
To: []string{r},
|
||||
Template: "newsletter_setup",
|
||||
Data: struct{ SetupURL string }{SetupURL: setupURL},
|
||||
}
|
||||
_ = cc.emailService.SendEmail(setupEmail)
|
||||
}
|
||||
case "blogs", "events", "matches", "scores", "weekly":
|
||||
@@ -409,85 +494,6 @@ func (cc *ContactController) SendNewsletterTest(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Test email(s) sent", "recipients": recipients, "type": t})
|
||||
}
|
||||
|
||||
func NewContactController(db *gorm.DB, emailService email.EmailService) *ContactController {
|
||||
return &ContactController{
|
||||
DB: db,
|
||||
emailService: emailService,
|
||||
}
|
||||
}
|
||||
|
||||
type ContactFormRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Subject string `json:"subject" binding:"required"`
|
||||
Message string `json:"message" binding:"required"`
|
||||
Source string `json:"source"` // e.g., "contact", "sponsor"
|
||||
}
|
||||
|
||||
// SubmitContactForm handles contact form submissions
|
||||
// @Summary Submit contact form
|
||||
// @Description Handles contact form submissions and sends an email notification
|
||||
// @Tags contact
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param input body ContactFormRequest true "Contact form data"
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /api/v1/contact [post]
|
||||
func (cc *ContactController) SubmitContactForm(c *gin.Context) {
|
||||
var input ContactFormRequest
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Normalize source field
|
||||
source := strings.TrimSpace(input.Source)
|
||||
if source == "" {
|
||||
source = "contact"
|
||||
}
|
||||
|
||||
// Save to database
|
||||
contactMessage := models.ContactMessage{
|
||||
Name: input.Name,
|
||||
Email: input.Email,
|
||||
Subject: input.Subject,
|
||||
Message: input.Message,
|
||||
Source: source,
|
||||
IPAddress: c.ClientIP(),
|
||||
UserAgent: c.Request.UserAgent(),
|
||||
IsRead: false,
|
||||
}
|
||||
|
||||
if err := cc.DB.Create(&contactMessage).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save contact message"})
|
||||
return
|
||||
}
|
||||
|
||||
// Send email notification asynchronously to prevent frontend timeout
|
||||
go func() {
|
||||
emailData := &email.ContactFormData{
|
||||
Name: input.Name,
|
||||
Email: input.Email,
|
||||
Subject: input.Subject,
|
||||
Message: input.Message,
|
||||
IPAddress: c.ClientIP(),
|
||||
UserAgent: c.Request.UserAgent(),
|
||||
}
|
||||
|
||||
if err := cc.emailService.SendContactForm(emailData); err != nil {
|
||||
logger.Error("Failed to send contact form email: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Your message has been sent successfully"})
|
||||
}
|
||||
|
||||
type NewsletterSubscriptionRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Preferences map[string]bool `json:"preferences"`
|
||||
}
|
||||
|
||||
// SubscribeToNewsletter handles newsletter subscriptions
|
||||
// @Summary Subscribe to newsletter
|
||||
// @Description Handles newsletter subscription requests
|
||||
@@ -527,6 +533,14 @@ func (cc *ContactController) SubscribeToNewsletter(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Engagement: award points to matching user (if exists)
|
||||
var u models.User
|
||||
if err := cc.DB.Where("LOWER(email) = LOWER(?)", subscription.Email).First(&u).Error; err == nil && u.ID != 0 {
|
||||
es := services.NewEngagementService(cc.DB)
|
||||
_, _ = es.AwardPoints(u.ID, 12, "newsletter_subscribe", map[string]interface{}{"email": subscription.Email})
|
||||
_ = es.CheckAndAwardAchievements(u.ID)
|
||||
}
|
||||
|
||||
// Send welcome back email in a goroutine
|
||||
go func(sub models.NewsletterSubscription) {
|
||||
manageURL := ""
|
||||
@@ -579,18 +593,20 @@ func (cc *ContactController) SubscribeToNewsletter(c *gin.Context) {
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := cc.DB.Create(&subscription).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to subscribe to newsletter"})
|
||||
return
|
||||
}
|
||||
// Generate a subscriber token to include in follow-up emails (preferences links)
|
||||
token, _ := utils.GenerateSubscriberToken(subscription.Email, 60*24) // 1 day
|
||||
baseFE := strings.TrimSuffix(config.AppConfig.FrontendBaseURL, "/")
|
||||
setupURL := baseFE + "/newsletter/setup?token=" + url.QueryEscape(token)
|
||||
unsubscribeURL := baseFE + "/newsletter/preferences?token=" + url.QueryEscape(token)
|
||||
|
||||
// Generate a subscriber token to include in follow-up emails (preferences links)
|
||||
token, _ := utils.GenerateSubscriberToken(subscription.Email, 60*24) // 1 day
|
||||
baseFE := strings.TrimSuffix(config.AppConfig.FrontendBaseURL, "/")
|
||||
setupURL := baseFE + "/newsletter/setup?token=" + url.QueryEscape(token)
|
||||
unsubscribeURL := baseFE + "/newsletter/preferences?token=" + url.QueryEscape(token)
|
||||
|
||||
// Auto-create fan user account if not exists
|
||||
// Engagement: if a user already exists for this email, award points
|
||||
var u models.User
|
||||
if err := cc.DB.Where("LOWER(email) = LOWER(?)", subscription.Email).First(&u).Error; err == nil && u.ID != 0 {
|
||||
es := services.NewEngagementService(cc.DB)
|
||||
_, _ = es.AwardPointsCapped(u.ID, 12, "newsletter_subscribe", map[string]interface{}{"email": subscription.Email})
|
||||
_ = es.CheckAndAwardAchievements(u.ID)
|
||||
}
|
||||
// Auto-create fan user account if not exists
|
||||
var existingUser models.User
|
||||
if err := cc.DB.Where("LOWER(email) = LOWER(?)", subscription.Email).First(&existingUser).Error; err == gorm.ErrRecordNotFound {
|
||||
// Generate a strong random password (16 chars, mixed set)
|
||||
@@ -643,6 +659,10 @@ func (cc *ContactController) SubscribeToNewsletter(c *gin.Context) {
|
||||
if err := cc.emailService.SendEmail(credEmail); err != nil {
|
||||
logger.Error("Failed to send fan account created email: %v", err)
|
||||
}
|
||||
// Engagement: award points to new user
|
||||
es := services.NewEngagementService(cc.DB)
|
||||
_, _ = es.AwardPoints(u.ID, 12, "newsletter_subscribe", map[string]interface{}{"email": subscription.Email})
|
||||
_ = es.CheckAndAwardAchievements(u.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1233,7 +1253,26 @@ func (cc *ContactController) ForwardAllContactMessages(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(messages) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "No messages to forward"})
|
||||
// Even if there are no messages now, ensure auto-forward is configured
|
||||
if !input.SaveDefault {
|
||||
var set models.Settings
|
||||
if err := cc.DB.First(&set).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
set = models.Settings{}
|
||||
set.ContactForwardEnabled = true
|
||||
set.ContactForwardList = strings.Join(recipients, ", ")
|
||||
_ = cc.DB.Create(&set).Error
|
||||
}
|
||||
} else {
|
||||
set.ContactForwardEnabled = true
|
||||
set.ContactForwardList = strings.Join(recipients, ", ")
|
||||
_ = cc.DB.Save(&set).Error
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Automatické přeposílání je nastaveno. Zatím nejsou žádné zprávy k přeposlání.",
|
||||
"count": 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user