сборка сервера фронта в бинарник
This commit is contained in:
3
server/go.mod
Normal file
3
server/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module protoc-frontend-server
|
||||
|
||||
go 1.25.5
|
||||
89
server/main.go
Normal file
89
server/main.go
Normal file
@@ -0,0 +1,89 @@
|
||||
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", 1112, "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) {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user