feat: Add year-aware PDF shortcut URLs + Default CV modal option
## Shortcut URLs
- New routes: /cv-jamr-{year}-{lang}.pdf (e.g., /cv-jamr-2025-en.pdf)
- Year validation: Only current year accepted, returns 404 for past/future
- Auto-redirects (301) to: /export/pdf?lang={lang}&length=short&icons=show&version=with_skills
- Both languages supported: en and es
## PDF Modal Updates
- Replaced "Current View" option with "Default CV (Recommended)"
- Visual highlighting: purple gradient badge, star emoji ⭐, bold text
- Uses shortcut URL with dynamic year detection
- Clear recommendation for users (5 pages, short with skills)
## Technical Details
- Handler: DefaultCVShortcut() in internal/handlers/cv.go
- Pattern check in Home() handler for proper routing
- Helper function: window.openPdfModal() for references section
- Documentation: PDF-SHORTCUT-IMPLEMENTATION.md
Benefits:
- Memorable, shareable URLs (juan.andres.morenorub.io/cv-jamr-2025-en.pdf)
- Auto-updates yearly without code changes
- Clear user guidance for recommended CV format
This commit is contained in:
@@ -34,6 +34,12 @@ func NewCVHandler(tmpl *templates.Manager, serverAddr string) *CVHandler {
|
||||
|
||||
// Home renders the full CV page
|
||||
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
|
||||
// Check if this is a shortcut URL request (cv-jamr-{year}-{lang}.pdf)
|
||||
if strings.HasPrefix(r.URL.Path, "/cv-jamr-") && strings.HasSuffix(r.URL.Path, ".pdf") {
|
||||
h.DefaultCVShortcut(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Get language from query parameter, default to English
|
||||
lang := r.URL.Query().Get("lang")
|
||||
if lang == "" {
|
||||
@@ -219,6 +225,50 @@ func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultCVShortcut handles shortcut URLs for default CV downloads
|
||||
// Pattern: /cv-jamr-{year}-{lang}.pdf (e.g., /cv-jamr-2025-en.pdf)
|
||||
// Validates year matches current year and redirects to default PDF export
|
||||
func (h *CVHandler) DefaultCVShortcut(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract path (e.g., "/cv-jamr-2025-en.pdf")
|
||||
path := r.URL.Path
|
||||
log.Printf("DefaultCVShortcut called with path: %s", path)
|
||||
|
||||
// Parse filename pattern: cv-jamr-{year}-{lang}.pdf
|
||||
parts := strings.Split(strings.TrimPrefix(path, "/"), "-")
|
||||
if len(parts) != 4 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract year and language
|
||||
yearStr := parts[2]
|
||||
langWithExt := parts[3] // "en.pdf" or "es.pdf"
|
||||
lang := strings.TrimSuffix(langWithExt, ".pdf")
|
||||
|
||||
// Validate language
|
||||
if lang != "en" && lang != "es" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate year matches current year
|
||||
currentYear := fmt.Sprintf("%d", time.Now().Year())
|
||||
if yearStr != currentYear {
|
||||
// Return 404 if year doesn't match (old or future URLs)
|
||||
log.Printf("Invalid year in shortcut URL: %s (current: %s)", yearStr, currentYear)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Build redirect URL with default parameters (short + with_skills)
|
||||
redirectURL := fmt.Sprintf("/export/pdf?lang=%s&length=short&icons=show&version=with_skills", lang)
|
||||
|
||||
log.Printf("Shortcut URL: %s → %s", path, redirectURL)
|
||||
|
||||
// Redirect to PDF export endpoint
|
||||
http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
|
||||
}
|
||||
|
||||
// ExportPDF handles PDF export requests using chromedp
|
||||
func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract and validate query parameters
|
||||
|
||||
@@ -12,6 +12,10 @@ import (
|
||||
func Setup(cvHandler *handlers.CVHandler, healthHandler *handlers.HealthHandler) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Shortcut routes for default CV (year-aware) - MUST be before "/" route
|
||||
// Pattern: /cv-jamr-{year}-{lang}.pdf (e.g., /cv-jamr-2025-en.pdf)
|
||||
mux.HandleFunc("/cv-jamr-", cvHandler.DefaultCVShortcut)
|
||||
|
||||
// Public routes
|
||||
mux.HandleFunc("/", cvHandler.Home)
|
||||
mux.HandleFunc("/cv", cvHandler.CVContent)
|
||||
|
||||
Reference in New Issue
Block a user