Files
cv-site/internal/config/config.go
T
juanatsap 58c1237326 feat: Add secure contact form with comprehensive security features
- Add contact form dialog with HTMX integration (hx-post)
- Implement browser-only access middleware (blocks curl/Postman/wget)
- Add rate limiting (5 requests/hour per IP) for contact endpoint
- Implement honeypot and timing-based bot detection
- Add input validation (email format, message length 10-5000 chars)
- Create contact button in desktop and mobile navigation (last position)

Security features:
- Browser-only middleware validates User-Agent, Referer/Origin, HX-Request headers
- Honeypot field returns fake success to fool bots while logging spam
- Timing validation rejects forms submitted < 2 seconds
- All security events logged for monitoring

Documentation:
- docs/SECURITY.md - Comprehensive security documentation
- docs/HACK-CHALLENGE.md - "Try to Hack Me!" challenge for security researchers
- docs/SECURITY-AUDIT-REPORT.md - Full security audit report
- docs/CONTACT-FORM-QUICKSTART.md - Integration guide

Form fields: email (required), name, company, subject, message (required)
2025-11-30 14:31:58 +00:00

109 lines
2.5 KiB
Go

package config
import (
"fmt"
"os"
"strconv"
)
// Config holds all application configuration
type Config struct {
Server ServerConfig
Template TemplateConfig
Data DataConfig
Email EmailConfig
}
// ServerConfig contains server-specific settings
type ServerConfig struct {
Port string
Host string
ReadTimeout int
WriteTimeout int
}
// TemplateConfig contains template-specific settings
type TemplateConfig struct {
Dir string
PartialsDir string
HotReload bool
}
// DataConfig contains data directory settings
type DataConfig struct {
Dir string
}
// EmailConfig contains email/SMTP settings
type EmailConfig struct {
SMTPHost string
SMTPPort string
SMTPUser string
SMTPPassword string
FromEmail string
ContactEmail string
}
// Load creates a new Config with values from environment or defaults
func Load() *Config {
return &Config{
Server: ServerConfig{
Port: getEnv("PORT", "1999"),
Host: getEnv("HOST", "localhost"),
ReadTimeout: getEnvAsInt("READ_TIMEOUT", 15),
WriteTimeout: getEnvAsInt("WRITE_TIMEOUT", 15),
},
Template: TemplateConfig{
Dir: getEnv("TEMPLATE_DIR", "templates"),
PartialsDir: getEnv("PARTIALS_DIR", "templates/partials"),
HotReload: getEnvAsBool("TEMPLATE_HOT_RELOAD", isDevelopment()),
},
Data: DataConfig{
Dir: getEnv("DATA_DIR", "data"),
},
Email: EmailConfig{
SMTPHost: getEnv("SMTP_HOST", "smtp.gmail.com"),
SMTPPort: getEnv("SMTP_PORT", "587"),
SMTPUser: getEnv("SMTP_USER", ""),
SMTPPassword: getEnv("SMTP_PASSWORD", ""),
FromEmail: getEnv("SMTP_FROM_EMAIL", ""),
ContactEmail: getEnv("CONTACT_EMAIL", "txeo.msx@gmail.com"),
},
}
}
// Address returns the server address in host:port format
func (c *Config) Address() string {
return fmt.Sprintf("%s:%s", c.Server.Host, c.Server.Port)
}
// Helper functions
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func getEnvAsInt(key string, defaultValue int) int {
valueStr := os.Getenv(key)
if value, err := strconv.Atoi(valueStr); err == nil {
return value
}
return defaultValue
}
func getEnvAsBool(key string, defaultValue bool) bool {
valueStr := os.Getenv(key)
if value, err := strconv.ParseBool(valueStr); err == nil {
return value
}
return defaultValue
}
func isDevelopment() bool {
env := getEnv("GO_ENV", "development")
return env == "development" || env == "dev"
}