package zgate import ( "context" "net/http" "net/url" "strconv" "strings" "github.com/gorilla/mux" "github.com/zeevdiukman/go-reverseproxy" "github.com/zeevdiukman/go-router" "github.com/zeevdiukman/go-server" "github.com/zeevdiukman/go-zgate/pkg/config" ) const CERTS_PATH string = "./assets/certs/" type Zgate struct { Context context.Context Config *config.Config // DomainRouters DomainRouters EntryPoints EntryPoints ActiveEntryPoints ActiveEntryPoints } type ActiveEntryPoints map[string]bool // type DomainRouters map[string]*mux.Router type EntryPoints map[string]*EntryPoint type EntryPoint struct { IsTLS bool Server *server.Server Router *mux.Router // Router *router.Router HostRouters HostRouters Name string } type HostRouters map[string]*router.DomainRouter func New() *Zgate { zGate := &Zgate{ Context: context.Background(), Config: config.New(), // DomainRouters: DomainRouters{}, EntryPoints: EntryPoints{}, ActiveEntryPoints: ActiveEntryPoints{}, } zGate.initActiveEntryPoints() zGate.Config.Viper.WatchConfig() return zGate } func (zGate *Zgate) NewReverseProxy(serviceName string) *reverseproxy.ReverseProxy { serviceURL := zGate.Config.HTTP.Services[serviceName].URL newReverseProxy := reverseproxy.New(zGate.Context, serviceURL) return newReverseProxy } func (ep EntryPoints) ForEach(f func(string, *EntryPoint)) { forEach(ep, f) } func forEach[K comparable, V any](mp map[K]V, f func(K, V)) { for k, v := range mp { f(k, v) } } func (zGate *Zgate) initActiveEntryPoints() { zGate.Config.EntryPoints.ForEach(func(epName string, epConfig *config.EntryPoint) { zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) { if rConfig.EntryPoint == epName { zGate.ActiveEntryPoints[epName] = true } }) }) } // func (zGate *Zgate) BuildActiveEntryPoints() { // zGate.Config.EntryPoints.ForEach(func(entryPointName string, entryPointConfig *config.EntryPoint) { // v, ok := isOK(zGate.ActiveEntryPoints, entryPointName) // if ok && v { // port := strAddressPortToInt(entryPointConfig.Address) // newEntryPoint := zGate.EntryPoints.NewEntryPoint(entryPointName) // newEntryPoint.Server.Port(port).Name(entryPointName) // newEntryPoint.Server.Router(newEntryPoint.Router) // } // }) // } func (entryPoints EntryPoints) GetEntryPoint(name string) *EntryPoint { return entryPoints[name] } func (entryPoints EntryPoints) NewEntryPoint(name string) *EntryPoint { entryPoints[name] = &EntryPoint{ Server: server.New(), Router: mux.NewRouter(), HostRouters: HostRouters{}, Name: name, IsTLS: false, } return entryPoints[name] } func (entryPoint *EntryPoint) SetName(name string) *EntryPoint { entryPoint.Name = name return entryPoint } func (entryPoint *EntryPoint) NewServer() *EntryPoint { entryPoint.Server = server.New() entryPoint.Server.Name(entryPoint.Name) return entryPoint } func (entryPoint *EntryPoint) Port(port int) *EntryPoint { entryPoint.Server.Port(port) return entryPoint } func (entryPoint *EntryPoint) SetServer(s *server.Server) *EntryPoint { entryPoint.Server = s return entryPoint } func (entryPoints EntryPoints) GetServer(name string) *server.Server { return entryPoints[name].Server } func (entryPoints EntryPoints) GetRouter(name string) *mux.Router { return entryPoints[name].Router } func (entryPoint *EntryPoint) NewRouter() *EntryPoint { r := mux.NewRouter() entryPoint.SetRouter(r) entryPoint.Router.Name(entryPoint.Name) return entryPoint } func (entryPoint *EntryPoint) SetRouter(r *mux.Router) *EntryPoint { entryPoint.Server.Handler = r entryPoint.Router = r return entryPoint } // func (entryPoint *EntryPoint) IsEntryPointTLS() bool { // name := entryPoint.IsTLS // z // return entryPoint // } // func (zGate *Zgate) BuildRouters() { // zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) { // v, ok := isOK(zGate.ActiveEntryPoints, rConfig.EntryPoint) // if ok && v { // domain := rConfig.Rules.Map["Domain"].Value // serviceURL := zGate.Config.HTTP.Services[rConfig.Service].URL // if isOK(zGate.EntryPoints, rConfig.EntryPoint); ok { // entryPoint := zGate.EntryPoints[rConfig.EntryPoint] // if _, ok := entryPoint.HostRouters[domain]; !ok { // entryPoint.HostRouters = make(map[string]*router.DomainRouter) // } // rp := reverseproxy.New(zGate.Context, serviceURL) // rp.DirectorFunc(func(jup reverseproxy.JoinURLPathFunc) reverseproxy.DirectorFunc { // return DefaultDirector(rp, jup) // }) // r := zGate.EntryPoints[rConfig.EntryPoint].Router // hostRouter := r.NewDomainRouter(domain) // hostRouter.Handler(rp) // zGate.EntryPoints[rConfig.EntryPoint].HostRouters[domain] = hostRouter // } // } // }) // } func (zGate *Zgate) ListenAndServe() { zGate.EntryPoints.ForEach(func(s string, zGateEntryPoint *EntryPoint) { //TODO: TLS per domain zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) { if s == rConfig.EntryPoint { } }) zGateEntryPoint.Server.CertKey(CERTS_PATH, "z.com.cert.pem", "z.com.key.pem") go zGateEntryPoint.Server.ListenAndServeTLS() }) } func isOK[K comparable, V comparable](mp map[K]V, key K) (V, bool) { if v, ok := mp[key]; ok { return v, true } else { return v, false } } func DefaultDirector(rpHandler *reverseproxy.ReverseProxy, jup reverseproxy.JoinURLPathFunc) func(*http.Request) { return func(r *http.Request) { r = r.WithContext(rpHandler.Context) ctxKey := reverseproxy.CtxKey("host") hostFromCtx := rpHandler.Context.Value(ctxKey).(string) target, _ := url.Parse(hostFromCtx) targetQuery := target.RawQuery r.URL.Scheme = target.Scheme r.URL.Host = target.Host r.URL.Path, r.URL.RawPath = jup(target, r.URL) if targetQuery == "" || r.URL.RawQuery == "" { r.URL.RawQuery = targetQuery + r.URL.RawQuery } else { r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery } } } func StrAddressPortToInt(addr string) int { _, portStr, _ := strings.Cut(addr, ":") port, _ := strconv.Atoi(portStr) return port }