92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"embed"
|
|
"flag"
|
|
"fmt"
|
|
"io/fs"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
//go:embed dist/*
|
|
var distFS embed.FS
|
|
|
|
func main() {
|
|
port := flag.Int("port", 1111, "port to listen on")
|
|
apiTarget := flag.String("api", "http://localhost:8000", "backend API URL to proxy /api requests")
|
|
flag.Parse()
|
|
|
|
// Proxy for /api -> backend
|
|
backendURL, err := url.Parse(*apiTarget)
|
|
if err != nil {
|
|
log.Fatalf("invalid api url: %v", err)
|
|
}
|
|
|
|
proxy := httputil.NewSingleHostReverseProxy(backendURL)
|
|
originalDirector := proxy.Director
|
|
proxy.Director = func(r *http.Request) {
|
|
originalDirector(r)
|
|
// Rewrite /api/... -> /...
|
|
r.URL.Path = strings.TrimPrefix(r.URL.Path, "/api")
|
|
if r.URL.Path == "" {
|
|
r.URL.Path = "/"
|
|
}
|
|
r.Host = backendURL.Host
|
|
}
|
|
|
|
// Static files from embedded dist/
|
|
distSub, err := fs.Sub(distFS, "dist")
|
|
if err != nil {
|
|
log.Fatalf("failed to open embedded dist: %v", err)
|
|
}
|
|
fileServer := http.FileServer(http.FS(distSub))
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
// API proxy
|
|
mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
|
|
proxy.ServeHTTP(w, r)
|
|
})
|
|
|
|
// SPA: serve static files, fallback to index.html
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
|
|
// Try to serve the file directly
|
|
path := strings.TrimPrefix(r.URL.Path, "/")
|
|
if path == "" {
|
|
path = "index.html"
|
|
}
|
|
|
|
// Check if file exists in embedded FS
|
|
if f, err := distSub.Open(path); err == nil {
|
|
f.Close()
|
|
fileServer.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
// Fallback to index.html for SPA routing
|
|
indexFile, err := fs.ReadFile(distSub, "index.html")
|
|
if err != nil {
|
|
http.Error(w, "index.html not found", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
w.Write(indexFile)
|
|
})
|
|
|
|
addr := fmt.Sprintf(":%d", *port)
|
|
log.Printf("Serving on http://localhost%s", addr)
|
|
log.Printf("API proxy -> %s", *apiTarget)
|
|
|
|
if err := http.ListenAndServe(addr, mux); err != nil {
|
|
log.Fatal(err)
|
|
os.Exit(1)
|
|
}
|
|
}
|