docs: update architecture and add contact handler unit tests
Architecture updates: - Add EmailService documentation with config and flow diagram - Update CVHandler struct to show all dependencies - Add new middleware components (BrowserOnly, RateLimiter, etc.) - Update package structure to include services, pdf, validation New unit tests for HandleContact (9 tests): - Valid submission - Missing email/message validation - Honeypot bot protection - Timing-based bot protection (too fast) - Invalid HTTP method (405) - Invalid email format - Message too short - Spanish language support Includes MockEmailService for isolated testing.
This commit is contained in:
+68
-6
@@ -19,9 +19,12 @@ cv/
|
||||
└── internal/ # Private packages (cannot be imported by other projects)
|
||||
├── config/ # Configuration management
|
||||
├── handlers/ # HTTP request handlers
|
||||
├── middleware/ # HTTP middleware
|
||||
├── middleware/ # HTTP middleware (security, logging, rate limiting)
|
||||
├── models/ # Data models and business logic
|
||||
└── templates/ # Template management
|
||||
├── pdf/ # PDF generation service
|
||||
├── services/ # Business services (email, etc.)
|
||||
├── templates/ # Template management
|
||||
└── validation/ # Input validation utilities
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
@@ -37,11 +40,15 @@ Handlers and services receive their dependencies through constructors:
|
||||
```go
|
||||
// ✅ Good: Dependencies injected
|
||||
type CVHandler struct {
|
||||
templates *templates.Manager
|
||||
templates *templates.Manager
|
||||
emailService *services.EmailService
|
||||
}
|
||||
|
||||
func NewCVHandler(tmpl *templates.Manager) *CVHandler {
|
||||
return &CVHandler{templates: tmpl}
|
||||
func NewCVHandler(tmpl *templates.Manager, addr string, email *services.EmailService) *CVHandler {
|
||||
return &CVHandler{
|
||||
templates: tmpl,
|
||||
emailService: email, // Can be nil for graceful degradation
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Bad: Global state
|
||||
@@ -146,11 +153,15 @@ manager.Render("index.html") // Hot-reloads in dev mode
|
||||
|
||||
```go
|
||||
type CVHandler struct {
|
||||
templates *templates.Manager
|
||||
templates *templates.Manager
|
||||
pdfGenerator *pdf.Generator
|
||||
emailService *services.EmailService
|
||||
serverAddr string
|
||||
}
|
||||
|
||||
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request)
|
||||
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request)
|
||||
func (h *CVHandler) HandleContact(w http.ResponseWriter, r *http.Request)
|
||||
```
|
||||
|
||||
**Features**:
|
||||
@@ -159,12 +170,63 @@ func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request)
|
||||
- Consistent error handling
|
||||
- HTMX-aware responses
|
||||
|
||||
### Email Service (`internal/services`)
|
||||
|
||||
**Pattern**: Service layer with dependency injection and interface-based design
|
||||
|
||||
```go
|
||||
type EmailService struct {
|
||||
config *EmailConfig
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
SMTPHost string
|
||||
SMTPPort string
|
||||
SMTPUser string
|
||||
SMTPPassword string
|
||||
FromEmail string
|
||||
ToEmail string
|
||||
}
|
||||
|
||||
func NewEmailService(config *EmailConfig) *EmailService
|
||||
func (e *EmailService) SendContactForm(data *ContactFormData) error
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- TLS support (port 465 implicit SSL, port 587 STARTTLS)
|
||||
- Multipart email formatting (HTML + plain text)
|
||||
- Input validation with header injection prevention
|
||||
- Reply-To header support for easy responses
|
||||
- Graceful degradation (nil service skips email sending)
|
||||
|
||||
**Email Flow**:
|
||||
```
|
||||
Contact Form → HandleContact → EmailService.SendContactForm
|
||||
↓
|
||||
Validation → Build HTML/Text Body → Connect SMTP → Send
|
||||
```
|
||||
|
||||
**Configuration** (via environment variables):
|
||||
```bash
|
||||
SMTP_HOST=smtp.dreamhost.com
|
||||
SMTP_PORT=465
|
||||
SMTP_USER=info@example.com
|
||||
SMTP_PASSWORD=secret
|
||||
SMTP_FROM_EMAIL=info@example.com
|
||||
CONTACT_EMAIL=recipient@example.com
|
||||
```
|
||||
|
||||
### Middleware (`internal/middleware`)
|
||||
|
||||
**Components**:
|
||||
1. **Recovery**: Catches panics, logs stack traces
|
||||
2. **Logger**: Structured request/response logging
|
||||
3. **SecurityHeaders**: CSP, XSS protection, clickjacking prevention
|
||||
4. **BrowserOnly**: Blocks non-browser requests (curl, wget, bots) for sensitive endpoints
|
||||
5. **RateLimiter**: Per-IP rate limiting with configurable limits and time windows
|
||||
6. **OriginChecker**: Validates request origin for CSRF protection
|
||||
7. **CacheControl**: Dynamic cache headers based on content type
|
||||
8. **PreferencesMiddleware**: Cookie-based user preference handling
|
||||
|
||||
## Security Features
|
||||
|
||||
|
||||
Reference in New Issue
Block a user