main.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package main
  2. import (
  3. "context"
  4. "embed"
  5. "fmt"
  6. "io/fs"
  7. "log"
  8. "net/http"
  9. "net/http/httputil"
  10. "net/url"
  11. "os"
  12. "path/filepath"
  13. "runtime/debug"
  14. "time"
  15. "wails-app/internal"
  16. "github.com/wailsapp/wails/v2"
  17. "github.com/wailsapp/wails/v2/pkg/options"
  18. "github.com/wailsapp/wails/v2/pkg/options/assetserver"
  19. "github.com/wailsapp/wails/v2/pkg/options/windows"
  20. )
  21. //go:embed all:frontend/dist
  22. var assets embed.FS
  23. func main() {
  24. defer func() {
  25. if recovered := recover(); recovered != nil {
  26. writeStartupError(fmt.Sprintf("panic: %v\n%s", recovered, debug.Stack()))
  27. }
  28. }()
  29. // Initialize the Gin backend (non-blocking goroutine, listens on :8888)
  30. internal.InitBackend()
  31. // Reverse proxy for /api/* requests to Gin backend (strips /api prefix)
  32. backendURL, _ := url.Parse("http://127.0.0.1:8888")
  33. proxy := httputil.NewSingleHostReverseProxy(backendURL)
  34. // Flush each MJPEG chunk immediately through the Wails asset proxy instead
  35. // of buffering it until the response is considered complete.
  36. proxy.FlushInterval = -1
  37. // Embedded frontend static files
  38. distFS, err := fs.Sub(assets, "frontend/dist")
  39. if err != nil {
  40. writeStartupError(fmt.Sprintf("failed to get embedded frontend: %v", err))
  41. return
  42. }
  43. fileServer := http.FileServer(http.FS(distFS))
  44. // Create a handler that proxies /api to Gin, serves everything else from embed
  45. mux := http.NewServeMux()
  46. mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
  47. // Strip /api prefix before forwarding to Gin
  48. r.URL.Path = r.URL.Path[4:] // remove "/api"
  49. if r.URL.Path == "" {
  50. r.URL.Path = "/"
  51. }
  52. r.RequestURI = r.URL.RequestURI()
  53. proxy.ServeHTTP(w, r)
  54. })
  55. mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
  56. r.URL.Path = "/"
  57. r.RequestURI = "/"
  58. proxy.ServeHTTP(w, r)
  59. })
  60. // Proxy uploaded edge images to the Gin backend. The backend owns the
  61. // configured local storage directory and serves /uploads/file/*.
  62. mux.HandleFunc("/uploads/", func(w http.ResponseWriter, r *http.Request) {
  63. proxy.ServeHTTP(w, r)
  64. })
  65. mux.Handle("/", fileServer)
  66. err = wails.Run(&options.App{
  67. Title: "智慧停车管理系统",
  68. Width: 1400,
  69. Height: 900,
  70. MinWidth: 1024,
  71. MinHeight: 768,
  72. AssetServer: &assetserver.Options{
  73. Assets: assets,
  74. Handler: mux,
  75. },
  76. OnStartup: func(ctx context.Context) {
  77. log.Println("Wails app started, backend running on :8888")
  78. },
  79. Windows: &windows.Options{
  80. WebviewIsTransparent: false,
  81. WindowIsTranslucent: false,
  82. },
  83. })
  84. if err != nil {
  85. writeStartupError(fmt.Sprintf("Wails failed to start: %v", err))
  86. log.Print(err)
  87. }
  88. }
  89. // writeStartupError runs before the configured logger may exist. Keep the
  90. // diagnostic beside the executable so production deployment failures are visible.
  91. func writeStartupError(message string) {
  92. directory, err := os.Executable()
  93. if err == nil {
  94. directory = filepath.Dir(directory)
  95. } else if directory, err = os.Getwd(); err != nil {
  96. return
  97. }
  98. file, err := os.OpenFile(filepath.Join(directory, "startup-error.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
  99. if err != nil {
  100. return
  101. }
  102. defer file.Close()
  103. _, _ = fmt.Fprintf(file, "[%s] %s\n\n", time.Now().Format(time.RFC3339), message)
  104. }