76dcb05bd3
Dos primitivas reutilizables para apps web del registry: - random_hex_id_go_core: IDs hex aleatorios (apps con SQLite + IDs string) - spa_handler_go_infra: http.Handler que sirve embed.FS con fallback a index.html (patron SPA para React Router/dnd-kit) Ambas creadas via fn-constructor durante apps/kanban (issue 0053). Tests pasan, fn index OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
2.1 KiB
Markdown
61 lines
2.1 KiB
Markdown
---
|
|
name: spa_handler
|
|
kind: function
|
|
lang: go
|
|
domain: infra
|
|
version: "1.0.0"
|
|
purity: pure
|
|
signature: "func SPAHandler(fsys fs.FS, indexFile string) http.Handler"
|
|
description: "Retorna un http.Handler que sirve los archivos estaticos de un fs.FS y hace fallback a indexFile cuando el path no existe — patron SPA para React Router y similares."
|
|
tags: [http, spa, frontend, embed, fileserver]
|
|
uses_functions: []
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: ""
|
|
imports: [io/fs, net/http, strings]
|
|
params:
|
|
- name: fsys
|
|
desc: "fs.FS (tipico iofs.Sub de embed.FS) que contiene los archivos estaticos de la SPA"
|
|
- name: indexFile
|
|
desc: "ruta dentro de fsys del HTML de fallback (ej. \"index.html\")"
|
|
output: "http.Handler que sirve archivos de fsys y hace fallback a indexFile cuando el path no existe — patron SPA para React Router"
|
|
tested: true
|
|
tests:
|
|
- "sirve archivo estatico existente con status 200"
|
|
- "ruta inexistente hace fallback a index.html con Content-Type text/html"
|
|
- "raiz retorna index.html"
|
|
- "path con .. retorna 400"
|
|
test_file_path: "functions/infra/spa_handler_test.go"
|
|
file_path: "functions/infra/spa_handler.go"
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```go
|
|
//go:embed all:frontend/dist
|
|
var staticFiles embed.FS
|
|
|
|
func main() {
|
|
sub, _ := iofs.Sub(staticFiles, "frontend/dist")
|
|
spa := SPAHandler(sub, "index.html")
|
|
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/api/", apiRouter())
|
|
mux.Handle("/", spa)
|
|
|
|
http.ListenAndServe(":8080", mux)
|
|
}
|
|
```
|
|
|
|
## Notas
|
|
|
|
Funcion pura — construye el handler a partir de los parametros sin I/O. El handler resultante tiene los siguientes comportamientos:
|
|
|
|
1. Si el path contiene `..`, responde 400 (defensa contra path traversal).
|
|
2. Si el path normalizado corresponde a un archivo real en `fsys` (no directorio), lo sirve con `http.FileServer`.
|
|
3. En cualquier otro caso (ruta no existe o es directorio), sirve `indexFile` con `Content-Type: text/html; charset=utf-8` y status 200.
|
|
4. Si `indexFile` no puede leerse, responde 500.
|
|
|
|
Pensado para apps que usan `//go:embed all:frontend/dist` y React Router (o cualquier router del lado del cliente). Centraliza el patron que de otro modo cada app escribiria inline.
|