# Server Package This package implements the HTTP server for the member-console application, including HTMX partial handlers. ## Overview The server uses Go's standard `net/http` package with: - Bootstrap 5 for styling - HTMX 2.0 for dynamic content updates - gorilla/csrf for CSRF protection - gorilla/sessions for session management ## Architecture The HTTP server handles several types of requests: - **Static Files** (`/static/*`) - CSS, JS, images served from embedded assets - **Page Handlers** (`/`) - Full HTML page rendering - **Partial Handlers** (`/partials/*`) - HTMX partial HTML responses - **Auth Handlers** (`/login`, `/logout`, `/callback`) - OAuth2/OIDC authentication Partial handlers interact with Temporal workflows for async operations like site provisioning. ## Files - `server.go` - Main server setup, middleware chain, route registration - `fedwiki_partials.go` - HTMX partial handlers for FedWiki site management ## HTMX Integration ### Partial Handler Pattern Partial handlers return HTML fragments (not full pages) that HTMX swaps into the DOM: ```go // Handler returns HTML fragment func (h *Handler) GetSites(w http.ResponseWriter, r *http.Request) { // ... fetch data ... w.Header().Set("Content-Type", "text/html") h.Templates.ExecuteTemplate(w, "fedwiki_sites.html", data) } ``` ```html
{{ range .Sites }} ... {{ end }}
``` ### HTMX Triggers Use `HX-Trigger` header to trigger events that refresh other parts of the page: ```go // After successful operation, trigger refresh w.Header().Set("HX-Trigger", `{"refreshSites": true}`) ``` ```html
``` ### CSRF with HTMX CSRF tokens are included via `hx-headers` on forms: ```html
``` Or globally in the page template using `hx-vals` or a meta tag approach. ## Synchronous Workflow Execution Unlike fire-and-forget patterns, this server waits for Temporal workflows to complete: ```go func (h *Handler) CreateSite(w http.ResponseWriter, r *http.Request) { // 1. Start the workflow we, err := h.TemporalClient.ExecuteWorkflow(ctx, options, workflow, input) // 2. Wait for completion (BLOCKING) var result WorkflowOutput err = we.Get(ctx, &result) // 3. Render result based on workflow outcome if !result.Success { h.renderError(w, result.ErrorMessage) return } h.renderSuccess(w, result) } ``` **Benefits:** - User sees accurate success/failure immediately - Spinner shows during actual operation - Error messages come from workflow **Trade-offs:** - Request blocks until workflow completes - Long operations may timeout (mitigated by workflow retry handling) ## Route Organization Routes are organized by feature using handler structs: ```go // FedWikiPartialsHandler groups related routes type FedWikiPartialsHandler struct { ... } func (h *FedWikiPartialsHandler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /partials/fedwiki/sites", h.GetSites) mux.HandleFunc("POST /partials/fedwiki/sites", h.CreateSite) mux.HandleFunc("DELETE /partials/fedwiki/sites/{domain}", h.DeleteSite) // ... } ``` ## Error Rendering Errors are rendered back into the same partial/modal that initiated the request: ```go func (h *Handler) renderCreateFormError(w http.ResponseWriter, userID int64, errMsg string) { // Re-fetch form data data := h.getCreateFormData(userID) data.Error = errMsg // Re-render the form partial with error displayed w.Header().Set("Content-Type", "text/html") h.Templates.ExecuteTemplate(w, "fedwiki_create_form.html", data) } ``` This keeps the user in context - they see the error in the same modal/form they were using. ## Middleware Stack ```go handler = middleware.Logging(logger)(handler) // Request logging handler = middleware.Recover(logger)(handler) // Panic recovery handler = middleware.Compress()(handler) // Gzip compression handler = middleware.Decompress()(handler) // Gzip decompression handler = middleware.SecurityHeaders()(handler) // Security headers handler = middleware.RequestID()(handler) // Request ID generation handler = csrf.Protect(key, opts...)(handler) // CSRF protection ```