# Request Flow Diagram ## Complete HTTP Request Lifecycle ``` ┌─────────────────────────────────────────────────────────────────┐ │ Full Request Lifecycle │ └─────────────────────────────────────────────────────────────────┘ Client Browser │ ├─→ User visits /?lang=es&cv-length=long │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HTTP Request │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ GET /?lang=es&cv-length=long HTTP/1.1 │ │ │ │ Host: localhost:8080 │ │ │ │ Cookie: cv-length=short; cv-icons=show │ │ │ │ Accept: text/html │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Go HTTP Server (net/http) │ │ ├─ Port :8080 │ │ ├─ ServeMux Router │ │ └─ Match route pattern │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ MIDDLEWARE CHAIN (4 layers) │ │ │ │ 1. Recovery Middleware │ │ └─→ Wraps entire request in defer/recover │ │ │ │ 2. Logger Middleware │ │ └─→ Logs: [GET] / 127.0.0.1 │ │ │ │ 3. SecurityHeaders Middleware │ │ └─→ Sets: CSP, X-Frame-Options, etc. │ │ │ │ 4. PreferencesMiddleware │ │ ├─→ Reads cookies │ │ ├─→ Migrates old values │ │ └─→ Stores in request context │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ROUTER (ServeMux) │ │ ├─ Pattern: / │ │ ├─ Match: Home handler │ │ └─ Call: handler.Home(w, r) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HANDLER: CVHandler.Home() │ │ (internal/handlers/cv_pages.go) │ │ │ │ Step 1: Get preferences from context │ │ ├─→ prefs := middleware.GetPreferences(r) │ │ └─→ Result: CVLength="long", CVLanguage="es" │ │ │ │ Step 2: Validate language from query params │ │ ├─→ lang := r.URL.Query().Get("lang") │ │ ├─→ Fallback to: prefs.CVLanguage if empty │ │ └─→ Validate: must be "en" or "es" │ │ │ │ Step 3: Prepare template data │ │ ├─→ Call: h.prepareTemplateData(lang) │ │ └─→ Returns: map with CV, UI, preferences │ │ │ │ Step 4: Render template │ │ ├─→ Call: h.tmpl.Render(w, "index.html", data) │ │ └─→ Returns: HTML response │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ TEMPLATE PREPARATION │ │ (prepareTemplateData helper) │ │ │ │ 1. Load CV data │ │ ├─→ cv, err := cvmodel.LoadCV(lang) │ │ └─→ Read: data/cv-es.json │ │ │ │ 2. Load UI strings │ │ ├─→ ui, err := uimodel.LoadUI(lang) │ │ └─→ Read: data/ui-es.json │ │ │ │ 3. Calculate experience durations │ │ └─→ For each experience: years/months │ │ │ │ 4. Split skills into columns │ │ └─→ Distribute skills evenly across columns │ │ │ │ 5. Build data map │ │ └─→ Return: CV, UI, preferences, SEO metadata │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ TEMPLATE RENDERING │ │ (internal/templates/manager.go) │ │ │ │ 1. Get cached template │ │ ├─→ tmpl := m.templates["index.html"] │ │ └─→ (or reload if hot reload enabled) │ │ │ │ 2. Execute template │ │ ├─→ tmpl.Execute(w, data) │ │ ├─→ Process: {{.CV.Name}}, {{range .CV.Experience}} │ │ └─→ Include partials: header, footer, sections │ │ │ │ 3. Generate HTML │ │ └─→ Full HTML page with data injected │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ RESPONSE GENERATION │ │ │ │ Headers: │ │ ├─ Content-Type: text/html; charset=utf-8 │ │ ├─ Content-Security-Policy: [CSP rules] │ │ ├─ X-Frame-Options: DENY │ │ └─ Set-Cookie: cv-language=es; Path=/; Max-Age=... │ │ │ │ Body: │ │ └─ │ │ │ │
... │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ LOGGER MIDDLEWARE (completion) │ │ └─→ Log: Completed in 45ms (status: 200) │ └─────────────────────────────────────────────────────────────┘ │ ▼ Client Browser receives HTML ``` ## HTMX Toggle Request Flow ``` ┌─────────────────────────────────────────────────────────────┐ │ HTMX Toggle Request (Partial Update) │ └─────────────────────────────────────────────────────────────┘ User clicks toggle button │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HTMX Request │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ GET /toggle/length?current=short HTTP/1.1 │ │ │ │ HX-Request: true │ │ │ │ HX-Trigger: toggle-length-btn │ │ │ │ HX-Target: #main-content │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ Middleware Chain (same as above) │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ROUTER │ │ ├─ Pattern: /toggle/length │ │ └─ Handler: CVHandler.ToggleCVLength(w, r) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HANDLER: CVHandler.ToggleCVLength() │ │ (internal/handlers/cv_htmx.go) │ │ │ │ 1. Get current preferences │ │ └─→ prefs := middleware.GetPreferences(r) │ │ │ │ 2. Toggle state │ │ ├─→ currentLength := prefs.CVLength │ │ └─→ newLength := "long" if current == "short" │ │ │ │ 3. Save new preference │ │ └─→ middleware.SetPreferenceCookie(w, "cv-length", newLength) │ │ │ │ 4. Get language and prepare data │ │ └─→ data := h.prepareTemplateData(lang) │ │ │ │ 5. Render partial template │ │ └─→ h.tmpl.Render(w, "partials/cv_content.html", data) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ PARTIAL TEMPLATE RENDERING │ │ └─ Only renders: partials/cv_content.html │ │ (Not full page, just the content section) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HTMX Response │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ HTTP/1.1 200 OK │ │ │ │ Content-Type: text/html │ │ │ │ Set-Cookie: cv-length=long; Path=/; Max-Age=... │ │ │ │ │ │ │ │