Files
Dash/apps/backend/internal/web/web.go
T
Tomas Dvorak b60821350a feat: phase 0 foundation — monorepo scaffold, backend, frontend, CI, docker
Establishes the skeleton every later phase builds on: Go+Gin API with
zap logging, /api/healthz, SQLite+goose runner; Vite+React18+TS strict+
Tailwind v4 frontend with bento-mono theme tokens; openapi.yaml driving
a generated TS client; oxlint with vendored anti-slop rules; GitHub CI;
multi-stage Dockerfile -> 33.6MB distroless image; docker compose up
serves the blank board.


Generated with [Devin](https://devin.ai)

Authored By: TDvorak <[email protected]>
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:29:21 +02:00

42 lines
1017 B
Go

// Package web serves the embedded frontend build (SPA with index.html
// fallback). dist/ is populated by the frontend build in Docker/CI; the
// committed .keep keeps //go:embed satisfied on a bare clone.
package web
import (
"embed"
"io/fs"
"net/http"
"github.com/gin-gonic/gin"
)
//go:embed all:dist
var dist embed.FS
// Register mounts the SPA on the router. Non-/api paths serve static files;
// unknown paths fall back to index.html for client-side routing.
func Register(r *gin.Engine) error {
sub, err := fs.Sub(dist, "dist")
if err != nil {
return err
}
fileServer := http.FileServer(http.FS(sub))
r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
if len(path) >= 4 && path[:4] == "/api" {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
if f, err := sub.Open(path[1:]); err == nil {
_ = f.Close()
fileServer.ServeHTTP(c.Writer, c.Request)
return
}
c.Request.URL.Path = "/"
fileServer.ServeHTTP(c.Writer, c.Request)
})
return nil
}