2025-05-29 11:49:16 +02:00

122 lines
2.8 KiB
Go

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
}