diff options
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/internal/api/router.go | 83 | ||||
| -rw-r--r-- | backend/internal/static/static.go | 43 |
2 files changed, 126 insertions, 0 deletions
diff --git a/backend/internal/api/router.go b/backend/internal/api/router.go index d28da71..a88ffbd 100644 --- a/backend/internal/api/router.go +++ b/backend/internal/api/router.go @@ -1,8 +1,12 @@ package api import ( + "io/fs" + "log" "net/http" "strings" + + "yaum/internal/static" ) func NewRouter(h *Handler, stream *StreamHandler, auth *AuthHandler) http.Handler { @@ -33,9 +37,88 @@ func NewRouter(h *Handler, stream *StreamHandler, auth *AuthHandler) http.Handle mux.Handle("GET /api/stream", stream) + // Static file serving — register only if build FS is available + if buildFS := static.BuildFS(); buildFS != nil { + staticHandler := spaFileServer(buildFS) + mux.HandleFunc("/", staticHandler.ServeHTTP) + } else { + log.Println("[router] nenhum build frontend encontrado — servindo apenas API") + // Still register root to give a friendly message + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.URL.Path, "/api") { + http.NotFound(w, r) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte(`YAUM API — frontend build not available. +Start the frontend dev server (npm run dev) or set YAUM_STATIC_DIR.`)) + }) + } + return corsMiddleware(auth.Middleware(mux)) } +// spaFileServer returns an http.Handler that serves static files and falls +// back to index.html (or 200.html) for SPA routes like /admin, /status/123. +// Only GET/HEAD requests are handled; other methods return 405. +func spaFileServer(fsys fs.FS) http.Handler { + fileServer := http.FileServer(http.FS(fsys)) + + exists := func(name string) bool { + f, err := fsys.Open(name) + if err != nil { + return false + } + f.Close() + return true + } + + // Determine which SPA fallback file exists + fallbackFile := "200.html" + fallbackBytes, fallbackErr := fs.ReadFile(fsys, fallbackFile) + if fallbackErr != nil { + fallbackFile = "index.html" + fallbackBytes, fallbackErr = fs.ReadFile(fsys, fallbackFile) + } + if fallbackErr != nil { + log.Printf("[router] nenhum fallback SPA (200.html/index.html) encontrado") + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Only serve GET/HEAD for static files + if r.Method != http.MethodGet && r.Method != http.MethodHead { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // API paths should already be handled by the mux, but just in case + if strings.HasPrefix(r.URL.Path, "/api") { + http.NotFound(w, r) + return + } + + p := strings.TrimPrefix(r.URL.Path, "/") + + // Root path or non-existent file → serve SPA fallback + if p == "" || !exists(p) { + if fallbackErr == nil { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write(fallbackBytes) + return + } + http.NotFound(w, r) + return + } + + fileServer.ServeHTTP(w, r) + }) +} + + + + + func corsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") diff --git a/backend/internal/static/static.go b/backend/internal/static/static.go new file mode 100644 index 0000000..53523a3 --- /dev/null +++ b/backend/internal/static/static.go @@ -0,0 +1,43 @@ +package static + +import ( + "embed" + "io/fs" + "log" + "os" + "path/filepath" +) + +//go:embed build +var embeddedBuild embed.FS + +func BuildFS() fs.FS { + sub, err := fs.Sub(embeddedBuild, "build") + if err != nil { + log.Printf("[static] embed nao disponivel, tentando filesystem: %v", err) + // Fallback: look for build directory relative to the backend binary + dir := os.Getenv("YAUM_STATIC_DIR") + if dir == "" { + // Try common locations + candidates := []string{ + "../frontend/build", + "frontend/build", + "./build", + } + for _, c := range candidates { + if info, e := os.Stat(c); e == nil && info.IsDir() { + dir = c + break + } + } + } + if dir != "" { + abs, _ := filepath.Abs(dir) + log.Printf("[static] servindo de %s", abs) + return os.DirFS(dir) + } + return nil + } + log.Println("[static] servindo de embed") + return sub +} |