From d6bd471eb02a3956ad01a3ccac9ba2cd0e6d30c2 Mon Sep 17 00:00:00 2001 From: StarFleetCPTN Date: Thu, 20 Mar 2025 18:11:57 -0700 Subject: [PATCH] Add Email Testing Functionality to Admin Tools - Introduced a new section in the admin tools for testing email configurations. - Added a form to send test emails, including recipient, subject, and message fields. - Implemented backend logic to handle test email requests and send emails using the configured SMTP server. - Created a toast notification component to display success or failure messages for email tests. - Updated routes to include a new endpoint for handling test email submissions. --- components/admin_tools.templ | 169 +++++++++++++++++ internal/email/email.go | 179 ++++++++++++++++++ internal/web/handlers/admin_tools_handlers.go | 61 +++++- internal/web/handlers/routes.go | 3 + 4 files changed, 407 insertions(+), 5 deletions(-) diff --git a/components/admin_tools.templ b/components/admin_tools.templ index 583c721..e5a0a4e 100644 --- a/components/admin_tools.templ +++ b/components/admin_tools.templ @@ -36,6 +36,9 @@ type AdminToolsData struct { LogFiles []LogFile LogContent string CurrentLogFile string + EmailTestSuccess *bool + EmailTestMessage string + SmtpServer string } // Dialog component for confirmation dialogs @@ -463,6 +466,101 @@ templ AdminTools(ctx context.Context, data AdminToolsData) { + +
+
+
+

+ + Email Testing +

+
+
+

+ Test your email configuration by sending a test email to verify the server can send emails properly. +

+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ + + + + +
+

+ + Email Configuration +

+ if data.SmtpServer != "" { +

+ Current SMTP server: { data.SmtpServer } +

+ } else { +

+ Email settings are configured in your application configuration file. Make sure SMTP settings are properly configured before testing. +

+ } +
+
+
+
+
@BackupsList(data) @@ -1000,3 +1098,74 @@ var Commit = "unknown" func getCommit() string { return Commit } + +// EmailTestToast is a component for showing email test results +templ EmailTestToast(success bool, message string) { +
+
+
+ if success { + + } else { + + } +
+
+

+ if success { + Email Sent Successfully + } else { + Email Sending Failed + } +

+
+ { message } +
+
+
+ +
+
+
+} + +// Add a style for the animate-fade-in animation +script fadeInAnimation() { + // Add CSS animation if it doesn't exist + if (!document.getElementById('fade-in-animation')) { + const style = document.createElement('style'); + style.id = 'fade-in-animation'; + style.textContent = ` + @keyframes fadeIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } + } + .animate-fade-in { + animation: fadeIn 0.3s ease-out forwards; + } + `; + document.head.appendChild(style); + } +} diff --git a/internal/email/email.go b/internal/email/email.go index 7d7b9da..146c47b 100644 --- a/internal/email/email.go +++ b/internal/email/email.go @@ -270,3 +270,182 @@ func (s *Service) sendEmail(toEmail, subject, htmlContent string) error { return client.Quit() } } + +// SendTestEmail sends a test email to verify email configuration +func (s *Service) SendTestEmail(toEmail, subject, message string) error { + if !s.Config.Email.Enabled { + return fmt.Errorf("email service is disabled") + } + + // Use default subject if not provided + if subject == "" { + subject = "Test Email from GoMFT" + } + + // Use default message if not provided + if message == "" { + message = "This is a test email from GoMFT to verify the email configuration is working correctly." + } + + // Create email data for template + data := map[string]interface{}{ + "Subject": subject, + "Message": message, + "AppName": "GoMFT", + "Year": time.Now().Year(), + "SMTPServer": s.Config.Email.Host, + "SMTPPort": s.Config.Email.Port, + "FromEmail": s.Config.Email.FromEmail, + "CurrentTime": time.Now().Format(time.RFC1123Z), + } + + // Generate email content + htmlContent, err := s.generateTestEmailHTML(data) + if err != nil { + return err + } + + // Send the email + return s.sendEmail(toEmail, subject, htmlContent) +} + +// generateTestEmailHTML generates the HTML content for test emails +func (s *Service) generateTestEmailHTML(data map[string]interface{}) (string, error) { + // HTML template for test email + tmpl, err := template.New("testEmail").Parse(` + + + + + + {{.Subject}} + + + +
+
+ +

{{.Subject}}

+
+
+

{{.Message}}

+ +
+
+
SMTP Server:
+
{{.SMTPServer}}:{{.SMTPPort}}
+
+
+
From:
+
{{.FromEmail}}
+
+
+
Sent:
+
{{.CurrentTime}}
+
+
+ +
+

This is a test email sent from the GoMFT admin interface. If you've received this email, your email configuration is working correctly.

+
+
+ +
+ + +`) + if err != nil { + return "", err + } + + var result bytes.Buffer + if err := tmpl.Execute(&result, data); err != nil { + return "", err + } + + return result.String(), nil +} diff --git a/internal/web/handlers/admin_tools_handlers.go b/internal/web/handlers/admin_tools_handlers.go index 3ef70a9..2654afe 100644 --- a/internal/web/handlers/admin_tools_handlers.go +++ b/internal/web/handlers/admin_tools_handlers.go @@ -63,17 +63,28 @@ func (h *Handlers) HandleAdminTools(c *gin.Context) { data.TotalUsers = int(totalUsers) } - // Get last backup time and backup count - data.LastBackupTime, data.BackupCount = h.getBackupInfo() + // Get backup info (last backup time and count) + lastBackup, backupCount := h.getBackupInfo() + data.LastBackupTime = lastBackup + data.BackupCount = backupCount // Get list of backup files data.BackupFiles = h.getBackupFiles() - // Check for maintenance issues + // Get maintenance message if any data.MaintenanceMessage = h.checkMaintenanceIssues() - // Render the admin tools page - components.AdminTools(components.CreateTemplateContext(c), data).Render(c, c.Writer) + // Add SMTP server info if available + if h.Email != nil && h.Email.Config != nil && h.Email.Config.Email.Host != "" { + smtpServer := h.Email.Config.Email.Host + if h.Email.Config.Email.Port != 0 { + data.SmtpServer = fmt.Sprintf("%s:%d", smtpServer, h.Email.Config.Email.Port) + } else { + data.SmtpServer = smtpServer + } + } + + components.AdminTools(c.Request.Context(), data).Render(c.Request.Context(), c.Writer) } // HandleBackupDatabase handles the backup database request @@ -1368,3 +1379,43 @@ func (h *Handlers) HandleImportConfigsFromFile(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%d configs imported successfully", imported)}) } + +// HandleTestEmail handles the POST /admin/test-email route +func (h *Handlers) HandleTestEmail(c *gin.Context) { + // Parse the form + recipient := c.PostForm("recipient") + subject := c.PostForm("subject") + message := c.PostForm("message") + + // Validate required fields + if recipient == "" { + components.EmailTestToast(false, "Recipient email is required").Render(c.Request.Context(), c.Writer) + return + } + + // Get SMTP server info for display + smtpServer := "" + if h.Email != nil && h.Email.Config != nil && h.Email.Config.Email.Host != "" { + smtpServer = h.Email.Config.Email.Host + if h.Email.Config.Email.Port != 0 { + smtpServer = fmt.Sprintf("%s:%d", smtpServer, h.Email.Config.Email.Port) + } + } + + // Send the test email + if h.Email == nil { + components.EmailTestToast(false, "Email service is not configured").Render(c.Request.Context(), c.Writer) + return + } + + err := h.Email.SendTestEmail(recipient, subject, message) + if err != nil { + // Failed to send email + components.EmailTestToast(false, fmt.Sprintf("Failed to send email: %v", err)).Render(c.Request.Context(), c.Writer) + return + } + + // Email sent successfully + successMsg := fmt.Sprintf("Test email sent successfully to %s", recipient) + components.EmailTestToast(true, successMsg).Render(c.Request.Context(), c.Writer) +} diff --git a/internal/web/handlers/routes.go b/internal/web/handlers/routes.go index a84cac9..448627e 100644 --- a/internal/web/handlers/routes.go +++ b/internal/web/handlers/routes.go @@ -110,6 +110,9 @@ func (h *Handlers) RegisterRoutes(router *gin.Engine) { admin.GET("/logs/refresh", h.HandleRefreshLogs) admin.GET("/logs/view/:fileName", h.HandleViewLog) admin.GET("/logs/download/:fileName", h.HandleDownloadLog) + + // Email test route + admin.POST("/test-email", h.HandleTestEmail) } // API routes