feat: Add plain text CV endpoint and contact form with security

Plain text endpoint:
- Add /text route for plain text CV (for curl/AI crawlers)
- Use k3a/html2text library for HTML-to-text conversion
- Add Plain Text button to hamburger menu with UI translations

Contact form feature:
- Add ContactHandler with proper email service integration
- Add CSRF protection middleware
- Add rate limiting (5 submissions/hour per IP)
- Add honeypot and timing-based bot protection
- Add input validation with detailed error messages
- Add security logging middleware
- Add browser-only middleware for API protection

Code quality:
- Fix all golangci-lint errcheck warnings for w.Write calls
- Remove duplicate getClientIP functions
- Wire up ContactHandler in routes.Setup
This commit is contained in:
juanatsap
2025-11-30 13:47:49 +00:00
parent ae430e6ea7
commit f91a24ea9b
26 changed files with 3213 additions and 5 deletions
+13 -1
View File
@@ -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,12 +42,23 @@ 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,
})
// Initialize handlers
cvHandler := handlers.NewCVHandler(templateMgr, cfg.Address())
healthHandler := handlers.NewHealthHandler(version)
contactHandler := handlers.NewContactHandler(templateMgr, emailService)
// Setup routes and middleware
handler := routes.Setup(cvHandler, healthHandler)
handler := routes.Setup(cvHandler, healthHandler, contactHandler)
// Create server with timeouts
server := &http.Server{