Files
flatrender/services/gateway/internal/proxy/proxy.go
T
soroush.asadi 90ac0b81d1 feat: V2 microservices stack — backend services, gateway, JWT auth
Add full V2 architecture: identity, content, studio (.NET 10) and file,
render, notification, gateway (Go) services with vendored deps, plus DB
migrations, event/API contracts, and an init-db script.

Wire the Next.js frontend to the gateway: server-side JWT auth routes
(login/register/refresh/logout/me), gateway fetch helper, and session/
cookie/jwt helpers under src/lib.

Containerize the stack via docker-compose.v2.yml and per-service
Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and
MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via
next/font/local to avoid Google Fonts (geo-blocked).

Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 23:29:31 +03:30

65 lines
1.8 KiB
Go

package proxy
import (
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
"github.com/gin-gonic/gin"
)
// Upstream holds a reverse proxy for one backend service.
type Upstream struct {
Name string
rp *httputil.ReverseProxy
}
// New creates an Upstream reverse proxy pointing at target (e.g. "http://localhost:5010").
func New(name, target string) *Upstream {
u, err := url.Parse(target)
if err != nil {
panic("invalid upstream URL for " + name + ": " + err.Error())
}
rp := httputil.NewSingleHostReverseProxy(u)
rp.Transport = &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
DisableCompression: false,
}
// Custom error handler so we return JSON rather than Go's default HTML
rp.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadGateway)
_, _ = w.Write([]byte(`{"code":"bad_gateway","message":"upstream unavailable"}`))
}
// Rewrite the request before forwarding
orig := rp.Director
rp.Director = func(req *http.Request) {
orig(req)
req.Header.Set("X-Forwarded-Host", req.Host)
req.Header.Del("X-Forwarded-For") // let httputil re-add it properly
req.Host = u.Host
}
return &Upstream{Name: name, rp: rp}
}
// Handler returns a gin.HandlerFunc that proxies the request.
func (up *Upstream) Handler() gin.HandlerFunc {
return func(c *gin.Context) {
up.rp.ServeHTTP(c.Writer, c.Request)
}
}
// StripPrefixHandler proxies after stripping a URL prefix (e.g. "/api" → "").
func (up *Upstream) StripPrefixHandler(prefix string) gin.HandlerFunc {
return func(c *gin.Context) {
c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, prefix)
if c.Request.URL.Path == "" {
c.Request.URL.Path = "/"
}
up.rp.ServeHTTP(c.Writer, c.Request)
}
}