package proxy import ( "context" "errors" "fmt" "log/slog" "net" "net/http" "net/http/httputil" "net/url" "time" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/router" ) var Logger *slog.Logger func ServeProxyPassingToNuxt(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] { Logger = app.Logger() proxyUrl, _ := app.RootCmd.Flags().GetString("proxy") if len(proxyUrl) > 0 { target, _ := url.Parse(proxyUrl) // Node.js app proxy := httputil.NewSingleHostReverseProxy(target) originalDirector := proxy.Director proxy.Director = func(req *http.Request) { originalDirector(req) req.Host = target.Host } proxy.Transport = &loggingTransport{http.DefaultTransport} proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) { if errors.Is(err, context.Canceled) { return } app.Logger().Error(fmt.Sprintf("Proxy error: %v for %s %s", err, req.Method, req.URL.Path), "type", "proxy") http.Error(rw, "Proxy error", http.StatusBadGateway) } return se.Router.Any("/", func(e *core.RequestEvent) error { // Ping the backend with a HEAD request (or TCP dial) backendUp := isBackendAlive(target) if !backendUp { app.Logger().Error(fmt.Sprintf("Backend %s is unreachable, sending 502", target), "type", "proxy", "path", e.Request.URL.Path, "remoteIP", e.Request.RemoteAddr) e.Response.WriteHeader(http.StatusBadGateway) e.Response.Write([]byte("502 Backend is unavailable")) return nil } // Forward to Node.js proxy.ServeHTTP(e.Response, e.Request) return nil }) } else { return nil } } func isBackendAlive(target *url.URL) bool { conn, err := net.DialTimeout("tcp", target.Host, 500*time.Millisecond) if err != nil { return false } conn.Close() return true } type loggingTransport struct { rt http.RoundTripper } func (t *loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) { start := time.Now() // Do the actual request resp, err := t.rt.RoundTrip(req) duration := time.Since(start) // Prepare fields remoteAddr := req.RemoteAddr if ip := req.Header.Get("X-Real-IP"); len(ip) > 0 { remoteAddr = ip } method := req.Method uri := req.URL.RequestURI() // proto := req.Proto status := 0 size := 0 if resp != nil { status = resp.StatusCode if resp.ContentLength > 0 { size = int(resp.ContentLength) } } referer := req.Referer() ua := req.UserAgent() // timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700") Logger.Info( fmt.Sprintf("%s %s", method, uri), "type", "proxy", // "method", method, // "url", uri, "referer", referer, "remoteIP", remoteAddr, "userAgent", ua, "execTime", fmt.Sprintf("%.4fms", float64(duration.Milliseconds())), "status", status, "size", size, ) return resp, err }