docs: Add comprehensive backend handler documentation
Create public-facing documentation explaining backend architecture: New Documentation: - doc/14-BACKEND-HANDLERS.md (900+ lines) * Handler architecture and file organization * Request/response type system with examples * Middleware pattern and preferences handling * Comprehensive testing strategy * Data flow diagrams and best practices * Code examples for all major patterns Updated: - doc/README.md * Add Backend Handlers to technical implementation section * Update total active docs count (13 → 14) * Add quick navigation links Content Coverage: - Handler responsibilities (pages, PDF, HTMX) - Type-safe request handling with validation - Middleware architecture and context usage - Test coverage across all handler types - Request processing flow diagrams - Best practices with do/don't examples Audience: - Backend developers - API consumers - New contributors - Technical documentation readers Complements: - Educational docs in _go-learning/refactorings/ - Internal architecture documentation - API reference guide
This commit is contained in:
@@ -0,0 +1,566 @@
|
|||||||
|
# Backend Handler Architecture
|
||||||
|
|
||||||
|
**Last Updated**: November 20, 2024
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document explains how the backend handles HTTP requests, focusing on the handler architecture, type safety, middleware pattern, and testing strategy implemented in the CV website.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Handler Architecture](#handler-architecture)
|
||||||
|
2. [Request/Response Types](#requestresponse-types)
|
||||||
|
3. [Middleware Pattern](#middleware-pattern)
|
||||||
|
4. [Testing Strategy](#testing-strategy)
|
||||||
|
5. [Data Flow](#data-flow)
|
||||||
|
6. [Best Practices](#best-practices)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Handler Architecture
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
|
||||||
|
The handler layer is organized by responsibility into focused files:
|
||||||
|
|
||||||
|
```
|
||||||
|
internal/handlers/
|
||||||
|
├── cv.go # Core handler struct and constructor
|
||||||
|
├── cv_pages.go # Page rendering handlers
|
||||||
|
├── cv_pdf.go # PDF export handler
|
||||||
|
├── cv_htmx.go # HTMX toggle handlers
|
||||||
|
├── cv_helpers.go # Shared helper functions
|
||||||
|
├── types.go # Request/response types
|
||||||
|
├── errors.go # Error handling utilities
|
||||||
|
└── *_test.go # Comprehensive test suites
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handler Responsibilities
|
||||||
|
|
||||||
|
#### 1. Page Handlers (cv_pages.go)
|
||||||
|
|
||||||
|
**Purpose**: Render full HTML pages and content sections
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Home - Renders the complete CV page with all content
|
||||||
|
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
|
// CVContent - Renders CV content for HTMX swaps
|
||||||
|
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
|
// DefaultCVShortcut - Handles shortcut URLs like /cv-jamr-2025-en.pdf
|
||||||
|
func (h *CVHandler) DefaultCVShortcut(w http.ResponseWriter, r *http.Request)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Flow**:
|
||||||
|
```
|
||||||
|
Browser Request → Home() → prepareTemplateData() → Render HTML → Response
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. PDF Handler (cv_pdf.go)
|
||||||
|
|
||||||
|
**Purpose**: Generate PDF exports with customizable options
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ExportPDF - Generates PDF with parameters: lang, length, icons, version
|
||||||
|
func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Multi-language support (English, Spanish)
|
||||||
|
- Length variants (short, long)
|
||||||
|
- Icon visibility toggle (show, hide)
|
||||||
|
- Theme variants (default with skills, clean without skills)
|
||||||
|
- Smart filename generation
|
||||||
|
- Print-optimized CSS rendering
|
||||||
|
|
||||||
|
**Example Request**:
|
||||||
|
```bash
|
||||||
|
GET /export-pdf?lang=es&length=long&icons=show&version=with_skills
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. HTMX Toggle Handlers (cv_htmx.go)
|
||||||
|
|
||||||
|
**Purpose**: Handle interactive toggles via HTMX
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ToggleLength - Toggle between short and long CV
|
||||||
|
func (h *CVHandler) ToggleLength(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
|
// ToggleIcons - Show/hide skill and tool icons
|
||||||
|
func (h *CVHandler) ToggleIcons(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
|
// SwitchLanguage - Switch between English and Spanish
|
||||||
|
func (h *CVHandler) SwitchLanguage(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
|
// ToggleTheme - Toggle between default and clean theme
|
||||||
|
func (h *CVHandler) ToggleTheme(w http.ResponseWriter, r *http.Request)
|
||||||
|
```
|
||||||
|
|
||||||
|
**HTMX Pattern**:
|
||||||
|
1. User clicks toggle button
|
||||||
|
2. HTMX sends POST request
|
||||||
|
3. Handler updates cookie
|
||||||
|
4. Handler returns HTML fragment with out-of-band swaps
|
||||||
|
5. HTMX swaps multiple DOM elements atomically
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Request/Response Types
|
||||||
|
|
||||||
|
### Type-Safe Request Handling
|
||||||
|
|
||||||
|
Instead of manually parsing query parameters, we use structured types with validation:
|
||||||
|
|
||||||
|
#### PDF Export Request
|
||||||
|
|
||||||
|
```go
|
||||||
|
// PDFExportRequest represents all PDF export parameters
|
||||||
|
type PDFExportRequest struct {
|
||||||
|
Lang string // "en" or "es"
|
||||||
|
Length string // "short" or "long"
|
||||||
|
Icons string // "show" or "hide"
|
||||||
|
Version string // "with_skills" or "clean"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and validate in one call
|
||||||
|
req, err := ParsePDFExportRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
// Return 400 Bad Request with clear error message
|
||||||
|
HandleError(w, r, BadRequestError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-safe access
|
||||||
|
filename := fmt.Sprintf("cv-%s-%s.pdf", req.Length, req.Lang)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Benefits
|
||||||
|
|
||||||
|
✅ **Type Safety**: Compile-time guarantees prevent typos
|
||||||
|
✅ **Self-Documenting**: Struct fields show all valid parameters
|
||||||
|
✅ **Centralized Validation**: One place to update validation rules
|
||||||
|
✅ **Clear Errors**: Descriptive error messages for invalid requests
|
||||||
|
|
||||||
|
**Example Validation**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Automatic validation with helpful error messages
|
||||||
|
GET /export-pdf?lang=fr
|
||||||
|
→ 400 Bad Request: "unsupported language: fr (use 'en' or 'es')"
|
||||||
|
|
||||||
|
GET /export-pdf?length=medium
|
||||||
|
→ 400 Bad Request: "unsupported length: medium (use 'short' or 'long')"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Language Request
|
||||||
|
|
||||||
|
```go
|
||||||
|
// LanguageRequest for endpoints that only need language
|
||||||
|
type LanguageRequest struct {
|
||||||
|
Lang string // "en" or "es"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
req, err := ParseLanguageRequest(r)
|
||||||
|
// Defaults to "en" if not specified
|
||||||
|
// Validates against supported languages
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Middleware Pattern
|
||||||
|
|
||||||
|
### Preferences Middleware
|
||||||
|
|
||||||
|
**Purpose**: Read user preferences from cookies once and make them available via context
|
||||||
|
|
||||||
|
#### Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Request
|
||||||
|
↓
|
||||||
|
PreferencesMiddleware
|
||||||
|
├─ Read all preference cookies
|
||||||
|
├─ Migrate old values (extended → long, true → show)
|
||||||
|
├─ Store in request context
|
||||||
|
└─ Pass to next handler
|
||||||
|
↓
|
||||||
|
Handler
|
||||||
|
├─ Get preferences from context
|
||||||
|
├─ No cookie reading needed
|
||||||
|
└─ Use preferences in business logic
|
||||||
|
↓
|
||||||
|
Response
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Implementation
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Middleware reads cookies and stores in context
|
||||||
|
func PreferencesMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
prefs := &Preferences{
|
||||||
|
CVLength: getPreferenceCookie(r, "cv-length", "short"),
|
||||||
|
CVIcons: getPreferenceCookie(r, "cv-icons", "show"),
|
||||||
|
CVLanguage: getPreferenceCookie(r, "cv-language", "en"),
|
||||||
|
CVTheme: getPreferenceCookie(r, "cv-theme", "default"),
|
||||||
|
ColorTheme: getPreferenceCookie(r, "color-theme", "light"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatic migration of old preference values
|
||||||
|
if prefs.CVLength == "extended" {
|
||||||
|
prefs.CVLength = "long"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store in context for handlers
|
||||||
|
ctx := context.WithValue(r.Context(), PreferencesKey, prefs)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers access preferences via context
|
||||||
|
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Get preferences from context (already read by middleware)
|
||||||
|
prefs := middleware.GetPreferences(r)
|
||||||
|
|
||||||
|
// Use preferences
|
||||||
|
cvLengthClass := "cv-short"
|
||||||
|
if prefs.CVLength == "long" {
|
||||||
|
cvLengthClass = "cv-long"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Benefits
|
||||||
|
|
||||||
|
✅ **Performance**: Cookies read once per request
|
||||||
|
✅ **Consistency**: All handlers get same preference values
|
||||||
|
✅ **Maintainability**: Migration logic in one place
|
||||||
|
✅ **Testability**: Easy to mock preferences via context
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Test Coverage
|
||||||
|
|
||||||
|
The handler layer has comprehensive test coverage across multiple files:
|
||||||
|
|
||||||
|
```
|
||||||
|
internal/handlers/
|
||||||
|
├── cv_pages_test.go # Page handler tests
|
||||||
|
├── cv_htmx_test.go # HTMX toggle tests
|
||||||
|
├── pdf_test.go # PDF generation tests (integration)
|
||||||
|
└── cv_security_test.go # Security validation tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Page Handler Tests
|
||||||
|
|
||||||
|
**File**: `cv_pages_test.go`
|
||||||
|
**Test Cases**: 15+
|
||||||
|
**Coverage**: Language validation, rendering, shortcuts
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Example test structure
|
||||||
|
func TestHome(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
lang string
|
||||||
|
expectStatus int
|
||||||
|
expectContains string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Default language (English)",
|
||||||
|
lang: "",
|
||||||
|
expectStatus: http.StatusOK,
|
||||||
|
expectContains: "Juan Andrés Moreno Rubio",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid language",
|
||||||
|
lang: "fr",
|
||||||
|
expectStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Test implementation
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTMX Handler Tests
|
||||||
|
|
||||||
|
**File**: `cv_htmx_test.go`
|
||||||
|
**Test Cases**: 20+
|
||||||
|
**Coverage**: Toggles, cookies, method validation, migrations
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Example: Testing toggle behavior
|
||||||
|
func TestToggleLength(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
currentLength string
|
||||||
|
expectedToggle string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Toggle from short to long",
|
||||||
|
currentLength: "short",
|
||||||
|
expectedToggle: "long",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Migration: extended → long",
|
||||||
|
currentLength: "extended",
|
||||||
|
expectedToggle: "short", // extended becomes long, then toggles
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method Validation Tests
|
||||||
|
|
||||||
|
All HTMX endpoints enforce POST-only requests:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestHTMXHandlersRequirePost(t *testing.T) {
|
||||||
|
// Tests verify GET requests return 405 Method Not Allowed
|
||||||
|
handlers := []struct {
|
||||||
|
name string
|
||||||
|
handler func(http.ResponseWriter, *http.Request)
|
||||||
|
}{
|
||||||
|
{"ToggleLength", handler.ToggleLength},
|
||||||
|
{"ToggleIcons", handler.ToggleIcons},
|
||||||
|
{"ToggleTheme", handler.ToggleTheme},
|
||||||
|
}
|
||||||
|
|
||||||
|
// All should reject GET with 405
|
||||||
|
for _, h := range handlers {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/endpoint", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
h.handler(w, req)
|
||||||
|
assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all unit tests (excludes PDF generation)
|
||||||
|
go test -short ./...
|
||||||
|
|
||||||
|
# Run specific handler tests
|
||||||
|
go test -short ./internal/handlers/... -v
|
||||||
|
|
||||||
|
# Run all tests including integration tests
|
||||||
|
make test-all
|
||||||
|
|
||||||
|
# Pre-commit hook runs tests automatically
|
||||||
|
git commit -m "changes" # Tests run before commit
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Request Processing Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Client Request
|
||||||
|
├─ Browser/HTMX makes HTTP request
|
||||||
|
└─ URL: /export-pdf?lang=es&length=long
|
||||||
|
|
||||||
|
2. Middleware Chain
|
||||||
|
├─ Recovery (catch panics)
|
||||||
|
├─ Logger (request logging)
|
||||||
|
├─ Security Headers (CSP, HSTS)
|
||||||
|
└─ PreferencesMiddleware (read cookies)
|
||||||
|
|
||||||
|
3. Router
|
||||||
|
├─ Match URL pattern
|
||||||
|
└─ Dispatch to handler
|
||||||
|
|
||||||
|
4. Handler
|
||||||
|
├─ Parse request (type-safe)
|
||||||
|
│ └─ ParsePDFExportRequest(r)
|
||||||
|
├─ Validate parameters
|
||||||
|
│ └─ Return 400 if invalid
|
||||||
|
├─ Prepare data
|
||||||
|
│ └─ prepareTemplateData(lang)
|
||||||
|
├─ Generate response
|
||||||
|
│ └─ Render template or generate PDF
|
||||||
|
└─ Return response
|
||||||
|
|
||||||
|
5. Client Response
|
||||||
|
├─ HTML page
|
||||||
|
├─ HTMX fragment
|
||||||
|
├─ PDF download
|
||||||
|
└─ Error page
|
||||||
|
```
|
||||||
|
|
||||||
|
### Template Data Preparation
|
||||||
|
|
||||||
|
Central helper function used by multiple handlers:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *CVHandler) prepareTemplateData(lang string) (map[string]interface{}, error) {
|
||||||
|
// Load CV data (cached)
|
||||||
|
cv, err := cvmodel.LoadCV(lang)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load UI translations (cached)
|
||||||
|
ui, err := uimodel.LoadUI(lang)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate dynamic data
|
||||||
|
for i := range cv.Experience {
|
||||||
|
cv.Experience[i].Duration = calculateDuration(
|
||||||
|
cv.Experience[i].StartDate,
|
||||||
|
cv.Experience[i].EndDate,
|
||||||
|
cv.Experience[i].Current,
|
||||||
|
lang,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process projects
|
||||||
|
for i := range cv.Projects {
|
||||||
|
processProjectDates(&cv.Projects[i], lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare skills
|
||||||
|
skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)
|
||||||
|
|
||||||
|
// Return complete data map
|
||||||
|
return map[string]interface{}{
|
||||||
|
"CV": cv,
|
||||||
|
"UI": ui,
|
||||||
|
"Lang": lang,
|
||||||
|
"SkillsLeft": skillsLeft,
|
||||||
|
"SkillsRight": skillsRight,
|
||||||
|
"YearsOfExperience": calculateYearsOfExperience(),
|
||||||
|
"CurrentYear": time.Now().Year(),
|
||||||
|
"CanonicalURL": fmt.Sprintf("https://juan.andres.morenorub.io/?lang=%s", lang),
|
||||||
|
"AlternateEN": "https://juan.andres.morenorub.io/?lang=en",
|
||||||
|
"AlternateES": "https://juan.andres.morenorub.io/?lang=es",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Type-Safe Requests
|
||||||
|
|
||||||
|
✅ **DO**: Use structured request types
|
||||||
|
```go
|
||||||
|
req, err := ParsePDFExportRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
HandleError(w, r, BadRequestError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
❌ **DON'T**: Manually parse parameters
|
||||||
|
```go
|
||||||
|
lang := r.URL.Query().Get("lang")
|
||||||
|
if lang == "" { lang = "en" }
|
||||||
|
if lang != "en" && lang != "es" {
|
||||||
|
// Repetitive validation code
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Centralized Validation
|
||||||
|
|
||||||
|
✅ **DO**: Validate in request parser
|
||||||
|
```go
|
||||||
|
func ParsePDFExportRequest(r *http.Request) (*PDFExportRequest, error) {
|
||||||
|
req := &PDFExportRequest{ /* parse */ }
|
||||||
|
|
||||||
|
// All validation in one place
|
||||||
|
if req.Lang != "en" && req.Lang != "es" {
|
||||||
|
return nil, fmt.Errorf("unsupported language: %s", req.Lang)
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
❌ **DON'T**: Scatter validation across handlers
|
||||||
|
```go
|
||||||
|
// Validation duplicated in multiple places
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Reuse Helper Functions
|
||||||
|
|
||||||
|
✅ **DO**: Use shared data preparation
|
||||||
|
```go
|
||||||
|
func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data, err := h.prepareTemplateData(lang)
|
||||||
|
// Add page-specific fields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data, err := h.prepareTemplateData(lang)
|
||||||
|
// Reuse same data preparation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
❌ **DON'T**: Duplicate data preparation logic
|
||||||
|
```go
|
||||||
|
// 100+ lines duplicated across handlers
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Test All Handlers
|
||||||
|
|
||||||
|
✅ **DO**: Write comprehensive tests
|
||||||
|
```go
|
||||||
|
func TestToggleLength(t *testing.T) {
|
||||||
|
// Test toggle behavior
|
||||||
|
// Test cookie persistence
|
||||||
|
// Test migration from old values
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **DO**: Test error cases
|
||||||
|
```go
|
||||||
|
func TestInvalidLanguage(t *testing.T) {
|
||||||
|
// Verify 400 Bad Request
|
||||||
|
// Check error message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Use Middleware for Cross-Cutting Concerns
|
||||||
|
|
||||||
|
✅ **DO**: Extract common logic to middleware
|
||||||
|
```go
|
||||||
|
// PreferencesMiddleware reads cookies once
|
||||||
|
// Handlers get preferences from context
|
||||||
|
```
|
||||||
|
|
||||||
|
❌ **DON'T**: Read cookies in every handler
|
||||||
|
```go
|
||||||
|
// Cookie reading duplicated across handlers
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- [Architecture Overview](./1-ARCHITECTURE.md) - System architecture patterns
|
||||||
|
- [API Reference](./3-API.md) - Complete API documentation
|
||||||
|
- [Security](./9-SECURITY.md) - Security implementation details
|
||||||
|
- [PDF Export](./11-PDF-EXPORT.md) - PDF generation details
|
||||||
|
- [Testing Guide](../_go-learning/refactorings/) - Detailed refactoring documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
- **Nov 20, 2024**: Initial documentation covering handler refactoring, type safety, middleware pattern, and testing strategy
|
||||||
+3
-1
@@ -18,6 +18,7 @@
|
|||||||
- [5. Zoom Implementation](5-ZOOM-IMPLEMENTATION.md) - Custom zoom feature technical details
|
- [5. Zoom Implementation](5-ZOOM-IMPLEMENTATION.md) - Custom zoom feature technical details
|
||||||
- [12. CSS Architecture](12-CSS-ARCHITECTURE.md) - Modular CSS structure and ITCSS organization ⭐
|
- [12. CSS Architecture](12-CSS-ARCHITECTURE.md) - Modular CSS structure and ITCSS organization ⭐
|
||||||
- [13. Toast Notifications](13-TOAST-NOTIFICATIONS.md) - Toast notification system for PDF downloads and user feedback
|
- [13. Toast Notifications](13-TOAST-NOTIFICATIONS.md) - Toast notification system for PDF downloads and user feedback
|
||||||
|
- [14. Backend Handlers](14-BACKEND-HANDLERS.md) - Handler architecture, type safety, middleware, and testing ⭐
|
||||||
|
|
||||||
**Deployment & Operations**
|
**Deployment & Operations**
|
||||||
- [8. Deployment Guide](8-DEPLOYMENT.md) - Production deployment instructions
|
- [8. Deployment Guide](8-DEPLOYMENT.md) - Production deployment instructions
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
| 5 | [ZOOM_IMPLEMENTATION.md](5-ZOOM-IMPLEMENTATION.md) | Zoom feature implementation details | Feature developers |
|
| 5 | [ZOOM_IMPLEMENTATION.md](5-ZOOM-IMPLEMENTATION.md) | Zoom feature implementation details | Feature developers |
|
||||||
| 12 | [CSS-ARCHITECTURE.md](12-CSS-ARCHITECTURE.md) | Modular CSS structure, ITCSS layers, HTMX integration | Frontend developers, designers |
|
| 12 | [CSS-ARCHITECTURE.md](12-CSS-ARCHITECTURE.md) | Modular CSS structure, ITCSS layers, HTMX integration | Frontend developers, designers |
|
||||||
| 13 | [TOAST-NOTIFICATIONS.md](13-TOAST-NOTIFICATIONS.md) | Toast notification system, PDF download feedback, user notifications | Frontend developers, UX designers |
|
| 13 | [TOAST-NOTIFICATIONS.md](13-TOAST-NOTIFICATIONS.md) | Toast notification system, PDF download feedback, user notifications | Frontend developers, UX designers |
|
||||||
|
| 14 | [BACKEND-HANDLERS.md](14-BACKEND-HANDLERS.md) | Handler architecture, type safety, middleware pattern, testing strategy | Backend developers |
|
||||||
|
|
||||||
### User & Operations Documentation
|
### User & Operations Documentation
|
||||||
|
|
||||||
@@ -141,4 +143,4 @@ All documentation in this project follows these standards:
|
|||||||
|
|
||||||
**Last Updated**: 2025-11-20
|
**Last Updated**: 2025-11-20
|
||||||
**Documentation Status**: ✅ Clean, organized, zero redundancy
|
**Documentation Status**: ✅ Clean, organized, zero redundancy
|
||||||
**Total Active Docs**: 13 core documents + archive
|
**Total Active Docs**: 14 core documents + archive
|
||||||
|
|||||||
Reference in New Issue
Block a user