package main import ( "embed" "flag" "fmt" "io/fs" "log" "net/http" "net/http/httputil" "net/url" "os" "strings" "github.com/go-chi/chi/v5" ) //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) 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)) r := chi.NewRouter() // API routes r.Route("/api", func(api chi.Router) { // Protocol generation handled locally (xlsx fill via excelize) api.Post("/protocols/", protocolHandler(*apiTarget)) // All other /api/* requests proxied to Python backend api.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) }) }) // SPA: serve static files, fallback to index.html r.Get("/*", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "no-store") path := strings.TrimPrefix(r.URL.Path, "/") if path == "" { path = "index.html" } if f, err := distSub.Open(path); err == nil { f.Close() fileServer.ServeHTTP(w, r) return } 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, r); err != nil { log.Fatal(err) os.Exit(1) } }