- Introduced main.go for the server with CORS middleware and static asset handling. - Added desktop/app.go for local API server with logging and file handling capabilities. - Implemented desktop/main.go to initialize the application with Wails framework and asset management.
142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"embed"
|
|
"flag"
|
|
"io/fs"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
//go:embed build/*
|
|
var buildAssets embed.FS
|
|
|
|
func corsMiddleware(allowedOrigins []string) func(http.HandlerFunc) http.HandlerFunc {
|
|
return func(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
origin := r.Header.Get("Origin")
|
|
if origin == "" {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
allowed := false
|
|
if len(allowedOrigins) == 0 {
|
|
allowed = true
|
|
} else {
|
|
for _, o := range allowedOrigins {
|
|
if o == "*" || o == origin {
|
|
allowed = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if allowed {
|
|
w.Header().Set("Access-Control-Allow-Origin", origin)
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
|
}
|
|
|
|
if r.Method == "OPTIONS" {
|
|
if allowed {
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
}
|
|
return
|
|
}
|
|
|
|
if !allowed && len(allowedOrigins) > 0 {
|
|
log.Printf("Blocked CORS request from origin: %s", origin)
|
|
http.Error(w, "CORS Origin Not Allowed", http.StatusForbidden)
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
frontendPath := flag.String("frontend", "", "Path to custom frontend build directory (overrides embedded assets)")
|
|
host := flag.String("host", "0.0.0.0", "Host to bind the server to")
|
|
port := flag.String("port", "", "Port to listen on (overrides PORT env var)")
|
|
allowedOriginsStr := flag.String("allowed-origins", os.Getenv("ALLOWED_ORIGINS"), "Comma-separated list of allowed CORS origins")
|
|
|
|
flag.Parse()
|
|
|
|
var allowedOrigins []string
|
|
if *allowedOriginsStr != "" {
|
|
origins := strings.Split(*allowedOriginsStr, ",")
|
|
for _, o := range origins {
|
|
allowedOrigins = append(allowedOrigins, strings.TrimSpace(o))
|
|
}
|
|
}
|
|
|
|
if *port == "" {
|
|
*port = os.Getenv("PORT")
|
|
if *port == "" {
|
|
*port = "8080"
|
|
}
|
|
}
|
|
|
|
// Middleware chains
|
|
cors := corsMiddleware(allowedOrigins)
|
|
|
|
http.HandleFunc("/api/ping", cors(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte(`{"status":"ok"}`))
|
|
}))
|
|
|
|
// Static Assets
|
|
var staticFS fs.FS
|
|
if *frontendPath != "" {
|
|
log.Printf("Using custom frontend from: %s\n", *frontendPath)
|
|
staticFS = os.DirFS(*frontendPath)
|
|
} else {
|
|
sub, err := fs.Sub(buildAssets, "build")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
staticFS = sub
|
|
}
|
|
|
|
fileServer := http.FileServer(http.FS(staticFS))
|
|
|
|
// SPA Handler
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
path := strings.TrimPrefix(r.URL.Path, "/")
|
|
if path == "" {
|
|
path = "index.html"
|
|
}
|
|
|
|
_, err := staticFS.Open(path)
|
|
if err != nil {
|
|
// If file doesn't exist, serve index.html for SPA routing
|
|
r.URL.Path = "/"
|
|
}
|
|
fileServer.ServeHTTP(w, r)
|
|
})
|
|
|
|
addr := net.JoinHostPort(*host, *port)
|
|
log.Printf("Linking Tool server starting on %s...\n", addr)
|
|
|
|
server := &http.Server{
|
|
Addr: addr,
|
|
Handler: nil,
|
|
ReadTimeout: 15 * time.Second,
|
|
WriteTimeout: 15 * time.Second,
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
|
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|