package main import ( "context" "embed" "fmt" "io/fs" "log" "net/http" "net/http/httputil" "net/url" "os" "path/filepath" "runtime/debug" "time" "wails-app/internal" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/windows" ) //go:embed all:frontend/dist var assets embed.FS func main() { defer func() { if recovered := recover(); recovered != nil { writeStartupError(fmt.Sprintf("panic: %v\n%s", recovered, debug.Stack())) } }() // Initialize the Gin backend (non-blocking goroutine, listens on :8888) internal.InitBackend() // Reverse proxy for /api/* requests to Gin backend (strips /api prefix) backendURL, _ := url.Parse("http://127.0.0.1:8888") proxy := httputil.NewSingleHostReverseProxy(backendURL) // Flush each MJPEG chunk immediately through the Wails asset proxy instead // of buffering it until the response is considered complete. proxy.FlushInterval = -1 // Embedded frontend static files distFS, err := fs.Sub(assets, "frontend/dist") if err != nil { writeStartupError(fmt.Sprintf("failed to get embedded frontend: %v", err)) return } fileServer := http.FileServer(http.FS(distFS)) // Create a handler that proxies /api to Gin, serves everything else from embed mux := http.NewServeMux() mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) { // Strip /api prefix before forwarding to Gin r.URL.Path = r.URL.Path[4:] // remove "/api" if r.URL.Path == "" { r.URL.Path = "/" } r.RequestURI = r.URL.RequestURI() proxy.ServeHTTP(w, r) }) mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { r.URL.Path = "/" r.RequestURI = "/" proxy.ServeHTTP(w, r) }) // Proxy uploaded edge images to the Gin backend. The backend owns the // configured local storage directory and serves /uploads/file/*. mux.HandleFunc("/uploads/", func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) }) mux.Handle("/", fileServer) err = wails.Run(&options.App{ Title: "智慧停车管理系统", Width: 1400, Height: 900, MinWidth: 1024, MinHeight: 768, AssetServer: &assetserver.Options{ Assets: assets, Handler: mux, }, OnStartup: func(ctx context.Context) { log.Println("Wails app started, backend running on :8888") }, Windows: &windows.Options{ WebviewIsTransparent: false, WindowIsTranslucent: false, }, }) if err != nil { writeStartupError(fmt.Sprintf("Wails failed to start: %v", err)) log.Print(err) } } // writeStartupError runs before the configured logger may exist. Keep the // diagnostic beside the executable so production deployment failures are visible. func writeStartupError(message string) { directory, err := os.Executable() if err == nil { directory = filepath.Dir(directory) } else if directory, err = os.Getwd(); err != nil { return } file, err := os.OpenFile(filepath.Join(directory, "startup-error.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return } defer file.Close() _, _ = fmt.Fprintf(file, "[%s] %s\n\n", time.Now().Format(time.RFC3339), message) }