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:
@@ -12,6 +12,7 @@ type UI struct {
|
||||
PdfModal PdfModal `json:"pdfModal"`
|
||||
ShortcutsModal ShortcutsModal `json:"shortcutsModal"`
|
||||
InfoModal InfoModal `json:"infoModal"`
|
||||
ContactModal ContactModal `json:"contactModal"`
|
||||
Widgets Widgets `json:"widgets"`
|
||||
}
|
||||
|
||||
@@ -142,6 +143,38 @@ type TechStack struct {
|
||||
CSS3 string `json:"css3"`
|
||||
}
|
||||
|
||||
// ContactModal labels
|
||||
type ContactModal struct {
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
Description string `json:"description"`
|
||||
Close string `json:"close"`
|
||||
Form ContactFormLabel `json:"form"`
|
||||
Success ContactResult `json:"success"`
|
||||
Error ContactResult `json:"error"`
|
||||
}
|
||||
|
||||
type ContactFormLabel struct {
|
||||
Email string `json:"email"`
|
||||
EmailPlaceholder string `json:"emailPlaceholder"`
|
||||
Name string `json:"name"`
|
||||
NamePlaceholder string `json:"namePlaceholder"`
|
||||
Company string `json:"company"`
|
||||
CompanyPlaceholder string `json:"companyPlaceholder"`
|
||||
Subject string `json:"subject"`
|
||||
SubjectPlaceholder string `json:"subjectPlaceholder"`
|
||||
Message string `json:"message"`
|
||||
MessagePlaceholder string `json:"messagePlaceholder"`
|
||||
Submit string `json:"submit"`
|
||||
Sending string `json:"sending"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
type ContactResult struct {
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// Widget label types
|
||||
type Widgets struct {
|
||||
BackToTop WidgetLabel `json:"backToTop"`
|
||||
@@ -152,6 +185,7 @@ type Widgets struct {
|
||||
ZoomToggle WidgetLabel `json:"zoomToggle"`
|
||||
ZoomControl ZoomControlLabel `json:"zoomControl"`
|
||||
PdfToast PdfToastLabel `json:"pdfToast"`
|
||||
Contact WidgetLabel `json:"contact"`
|
||||
ActionButtons ActionButtonsLabel `json:"actionButtons"`
|
||||
}
|
||||
|
||||
@@ -177,4 +211,6 @@ type PdfToastLabel struct {
|
||||
type ActionButtonsLabel struct {
|
||||
DownloadPdf string `json:"downloadPdf"`
|
||||
PrintFriendly string `json:"printFriendly"`
|
||||
PlainText string `json:"plainText"`
|
||||
Contact string `json:"contact"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user