mirror of
https://github.com/Dvorinka/Dash.git
synced 2026-10-28 15:51:27 +00:00
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>
42 lines
1017 B
Go
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
|
|
}
|