generated from CubeCraft-Creations/Tracehound
RemoteRig: Core infrastructure — MQTT subscriber, Pi deployment, ESP32 firmware, hardware design #5
@@ -13,6 +13,10 @@ dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Frontend build output (embedded at Go build time)
|
||||
# Allow the fallback placeholder so embed always has at least index.html
|
||||
!src/dist/index.html
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
|
||||
@@ -3,7 +3,9 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -22,6 +24,9 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:embed all:src/dist
|
||||
var frontendFS embed.FS
|
||||
|
||||
// Config holds the application configuration.
|
||||
type Config struct {
|
||||
DBPath string `yaml:"db_path"`
|
||||
@@ -84,6 +89,9 @@ func main() {
|
||||
// API routes (auth required if API key is configured)
|
||||
r.Mount("/api/v1", auth.Middleware(cfg.APIKey)(apiRouter(sseHub, sqlDB)))
|
||||
|
||||
// Serve embedded React frontend with SPA fallback
|
||||
r.Mount("/", frontendHandler())
|
||||
|
||||
// Create server
|
||||
httpServer := &http.Server{
|
||||
Addr: ":" + cfg.Port,
|
||||
@@ -159,3 +167,36 @@ func loadConfig(path string) (*Config, error) {
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// frontendHandler returns an http.Handler that serves the embedded React
|
||||
// frontend from src/dist/ with SPA-style fallback: any path that doesn't
|
||||
// match a static file serves index.html for client-side routing.
|
||||
//
|
||||
// The frontend is embedded via //go:embed all:src/dist at build time.
|
||||
// If src/dist/ is empty or missing at build time, the embedded fallback
|
||||
// index.html (committed to the repo) is served instead, showing a
|
||||
// "run npm run build" message.
|
||||
func frontendHandler() http.Handler {
|
||||
distFS, err := fs.Sub(frontendFS, "src/dist")
|
||||
if err != nil {
|
||||
// Shouldn't happen if embed worked, but be defensive.
|
||||
panic("embedded frontend filesystem not found: " + err.Error())
|
||||
}
|
||||
|
||||
fileServer := http.FileServer(http.FS(distFS))
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Try to serve the requested file.
|
||||
f, err := distFS.Open(r.URL.Path[1:]) // strip leading "/"
|
||||
if err != nil {
|
||||
// File not found — serve index.html for SPA routing.
|
||||
r.URL.Path = "/"
|
||||
fileServer.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// File exists, serve it.
|
||||
fileServer.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>RemoteRig - Frontend Not Built</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
.message {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 { color: #e74c3c; margin-bottom: 0.5rem; }
|
||||
code {
|
||||
background: #eee;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="message">
|
||||
<h1>Frontend Not Built</h1>
|
||||
<p>The React frontend has not been built yet.</p>
|
||||
<p>Run <code>npm run build</code> from the project root, then rebuild the Go binary.</p>
|
||||
<p><small>API is still available at <code>/api/v1/</code> and health at <code>/health</code></small></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user