From f3842a348671fa93ed3c271ae5ed50abbf7c1f77 Mon Sep 17 00:00:00 2001 From: juanatsap Date: Tue, 2 Dec 2025 14:27:03 +0000 Subject: [PATCH] fix: connect EmailService to contact form handler The contact form was logging submissions but never actually sending emails. This commit: - Adds EmailService field to CVHandler - Initializes EmailService in main.go with SMTP config - Calls SendContactForm in HandleContact handler - Updates all test files to pass nil for emailService parameter --- internal/handlers/benchmarks_test.go | 12 ++++++------ internal/handlers/cv.go | 6 +++++- internal/handlers/cv_cmdk_test.go | 4 ++-- internal/handlers/cv_contact.go | 26 +++++++++++++++++++++++--- internal/handlers/cv_htmx_test.go | 10 +++++----- internal/handlers/cv_pages_test.go | 6 +++--- internal/handlers/cv_text_test.go | 4 ++-- internal/handlers/pdf_test.go | 14 +++++++------- main.go | 14 +++++++++++++- 9 files changed, 66 insertions(+), 30 deletions(-) diff --git a/internal/handlers/benchmarks_test.go b/internal/handlers/benchmarks_test.go index 0172ad0..95d5008 100644 --- a/internal/handlers/benchmarks_test.go +++ b/internal/handlers/benchmarks_test.go @@ -21,7 +21,7 @@ func BenchmarkHome(b *testing.B) { b.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -43,7 +43,7 @@ func BenchmarkCVContent(b *testing.B) { b.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -65,7 +65,7 @@ func BenchmarkToggleLength(b *testing.B) { b.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -101,7 +101,7 @@ func BenchmarkPrepareTemplateData(b *testing.B) { b.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -144,7 +144,7 @@ func BenchmarkParallelHome(b *testing.B) { b.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { @@ -168,7 +168,7 @@ func BenchmarkParallelToggleLength(b *testing.B) { b.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { diff --git a/internal/handlers/cv.go b/internal/handlers/cv.go index 96a9225..21a04c5 100644 --- a/internal/handlers/cv.go +++ b/internal/handlers/cv.go @@ -4,6 +4,7 @@ import ( "time" "github.com/juanatsap/cv-site/internal/pdf" + "github.com/juanatsap/cv-site/internal/services" "github.com/juanatsap/cv-site/internal/templates" ) @@ -12,18 +13,21 @@ import ( // - cv_pages.go: Page rendering (Home, CVContent, DefaultCVShortcut) // - cv_pdf.go: PDF export (ExportPDF) // - cv_htmx.go: HTMX toggles (ToggleLength, ToggleIcons, SwitchLanguage, ToggleTheme) +// - cv_contact.go: Contact form submission (HandleContact) // - cv_helpers.go: Helper functions (skills, dates, git, templates, cookies) type CVHandler struct { templates *templates.Manager pdfGenerator *pdf.Generator + emailService *services.EmailService serverAddr string } // NewCVHandler creates a new CV handler -func NewCVHandler(tmpl *templates.Manager, serverAddr string) *CVHandler { +func NewCVHandler(tmpl *templates.Manager, serverAddr string, emailService *services.EmailService) *CVHandler { return &CVHandler{ templates: tmpl, pdfGenerator: pdf.NewGenerator(30 * time.Second), + emailService: emailService, serverAddr: serverAddr, } } diff --git a/internal/handlers/cv_cmdk_test.go b/internal/handlers/cv_cmdk_test.go index e7ac8b4..a8748d2 100644 --- a/internal/handlers/cv_cmdk_test.go +++ b/internal/handlers/cv_cmdk_test.go @@ -29,7 +29,7 @@ func TestCmdKData(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -207,7 +207,7 @@ func TestCmdKDataCaching(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) req := httptest.NewRequest(http.MethodGet, "/api/cmd-k", nil) rec := httptest.NewRecorder() diff --git a/internal/handlers/cv_contact.go b/internal/handlers/cv_contact.go index 225a120..425579c 100644 --- a/internal/handlers/cv_contact.go +++ b/internal/handlers/cv_contact.go @@ -9,6 +9,7 @@ import ( "time" uimodel "github.com/juanatsap/cv-site/internal/models/ui" + "github.com/juanatsap/cv-site/internal/services" ) // ============================================================================== @@ -81,14 +82,33 @@ func (h *CVHandler) HandleContact(w http.ResponseWriter, r *http.Request) { return } - // Log the contact form submission (in production, send email or save to database) + // Log the contact form submission log.Printf("Contact form submission from %s (IP: %s)", formData.Email, getClientIP(r)) log.Printf(" Name: %s, Company: %s", formData.Name, formData.Company) log.Printf(" Subject: %s", formData.Subject) log.Printf(" Message length: %d characters", len(formData.Message)) - // TODO: Implement actual email sending or database storage here - // For now, we just log and return success + // Send email via EmailService + if h.emailService != nil { + emailData := &services.ContactFormData{ + Email: formData.Email, + Name: formData.Name, + Company: formData.Company, + Subject: formData.Subject, + Message: formData.Message, + IP: getClientIP(r), + Time: time.Now(), + } + + if err := h.emailService.SendContactForm(emailData); err != nil { + log.Printf("ERROR sending contact email: %v", err) + h.renderContactError(w, r, "Failed to send message. Please try again later.") + return + } + log.Printf("Contact email sent successfully to configured recipient") + } else { + log.Printf("WARNING: Email service not configured, skipping email send") + } // Render success response h.renderContactSuccess(w, r, lang) diff --git a/internal/handlers/cv_htmx_test.go b/internal/handlers/cv_htmx_test.go index b9b902d..8128947 100644 --- a/internal/handlers/cv_htmx_test.go +++ b/internal/handlers/cv_htmx_test.go @@ -21,7 +21,7 @@ func TestToggleLength(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -94,7 +94,7 @@ func TestToggleIcons(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -152,7 +152,7 @@ func TestSwitchLanguage(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -221,7 +221,7 @@ func TestToggleTheme(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -287,7 +287,7 @@ func TestHTMXHandlersRequirePost(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string diff --git a/internal/handlers/cv_pages_test.go b/internal/handlers/cv_pages_test.go index ea7703a..8931026 100644 --- a/internal/handlers/cv_pages_test.go +++ b/internal/handlers/cv_pages_test.go @@ -23,7 +23,7 @@ func TestHome(t *testing.T) { } // Create handler - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -95,7 +95,7 @@ func TestCVContent(t *testing.T) { } // Create handler - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -162,7 +162,7 @@ func TestDefaultCVShortcut(t *testing.T) { } // Create handler - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string diff --git a/internal/handlers/cv_text_test.go b/internal/handlers/cv_text_test.go index b5e9adc..87568bf 100644 --- a/internal/handlers/cv_text_test.go +++ b/internal/handlers/cv_text_test.go @@ -30,7 +30,7 @@ func TestPlainText(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string @@ -152,7 +152,7 @@ func TestPlainTextDownloadFilename(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmplManager, "localhost:8080") + handler := NewCVHandler(tmplManager, "localhost:8080", nil) tests := []struct { name string diff --git a/internal/handlers/pdf_test.go b/internal/handlers/pdf_test.go index 7385c00..bd785fc 100644 --- a/internal/handlers/pdf_test.go +++ b/internal/handlers/pdf_test.go @@ -29,7 +29,7 @@ func TestExportPDF_ParameterValidation(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) tests := []struct { name string @@ -157,7 +157,7 @@ func TestExportPDF_FilenameGeneration(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) tests := []struct { name string @@ -266,7 +266,7 @@ func TestExportPDF_DefaultParameters(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) // Request with no parameters req := httptest.NewRequest(http.MethodGet, "/export/pdf", nil) @@ -298,7 +298,7 @@ func TestExportPDF_LongWithSkills(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) tests := []struct { name string @@ -449,7 +449,7 @@ func TestExportPDF_SkillsSidebarFeatures(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) validVersions := []string{"clean", "with_skills"} @@ -484,7 +484,7 @@ func TestExportPDF_SkillsSidebarFeatures(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) modalTests := []struct { name string @@ -604,7 +604,7 @@ func TestExportPDF_CompactFontsIntegration(t *testing.T) { t.Fatalf("Failed to create template manager: %v", err) } - handler := NewCVHandler(tmpl, "localhost:1999") + handler := NewCVHandler(tmpl, "localhost:1999", nil) tests := []struct { name string diff --git a/main.go b/main.go index 518ae1f..0ac23df 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "github.com/juanatsap/cv-site/internal/config" "github.com/juanatsap/cv-site/internal/handlers" "github.com/juanatsap/cv-site/internal/routes" + "github.com/juanatsap/cv-site/internal/services" "github.com/juanatsap/cv-site/internal/templates" ) @@ -41,8 +42,19 @@ func main() { log.Fatalf("❌ Failed to initialize templates: %v", err) } + // Initialize email service + emailService := services.NewEmailService(&services.EmailConfig{ + SMTPHost: cfg.Email.SMTPHost, + SMTPPort: cfg.Email.SMTPPort, + SMTPUser: cfg.Email.SMTPUser, + SMTPPassword: cfg.Email.SMTPPassword, + FromEmail: cfg.Email.FromEmail, + ToEmail: cfg.Email.ContactEmail, + }) + log.Printf("📧 Email service configured (SMTP: %s:%s)", cfg.Email.SMTPHost, cfg.Email.SMTPPort) + // Initialize handlers - cvHandler := handlers.NewCVHandler(templateMgr, cfg.Address()) + cvHandler := handlers.NewCVHandler(templateMgr, cfg.Address(), emailService) healthHandler := handlers.NewHealthHandler(version) // Setup routes and middleware