update
This commit is contained in:
parent
7d9181c26a
commit
f7ca358b21
2 changed files with 381 additions and 33 deletions
241
pkg/config/config.go
Normal file
241
pkg/config/config.go
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeevdiukman/go-config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const VIPER_NAME string = "config"
|
||||||
|
const VIPER_TYPE string = "yaml"
|
||||||
|
const VIPER_PATH string = "./assets/config/."
|
||||||
|
const SERVICES_KEY string = "http.services"
|
||||||
|
const ROUTERS_KEY string = "http.routers"
|
||||||
|
const ENTRYPOINTS_KEY string = "entrypoints"
|
||||||
|
const TLS_KEY string = "tls.certproviders"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
HTTP *HTTP
|
||||||
|
TLS *TLS
|
||||||
|
EntryPoints EntryPoints
|
||||||
|
}
|
||||||
|
type HTTP struct {
|
||||||
|
Routers Routers
|
||||||
|
Services Services
|
||||||
|
}
|
||||||
|
type TLS struct {
|
||||||
|
CertProviders CertProviders
|
||||||
|
}
|
||||||
|
type CertProviders map[string]*CertProvider
|
||||||
|
type CertProvider struct {
|
||||||
|
Key string
|
||||||
|
Cert string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Services map[string]*Service
|
||||||
|
type Service struct {
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
type EntryPoints map[string]*EntryPoint
|
||||||
|
type EntryPoint struct {
|
||||||
|
Address string
|
||||||
|
TLS *EntryPointTLS
|
||||||
|
}
|
||||||
|
type EntryPointTLS struct {
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
type Routers map[string]*Router
|
||||||
|
type Router struct {
|
||||||
|
EntryPoint string
|
||||||
|
Service string
|
||||||
|
Rules *Rules
|
||||||
|
TLS *RouterTLS
|
||||||
|
}
|
||||||
|
type RouterTLS struct {
|
||||||
|
CertProvider string
|
||||||
|
}
|
||||||
|
type Rules struct {
|
||||||
|
Raw string
|
||||||
|
Map map[string]*Rule
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *Config {
|
||||||
|
|
||||||
|
c := config.NewConfig(VIPER_NAME, VIPER_TYPE, VIPER_PATH)
|
||||||
|
c.Load()
|
||||||
|
|
||||||
|
appConfig := &Config{
|
||||||
|
HTTP: &HTTP{
|
||||||
|
Routers: Routers{},
|
||||||
|
Services: Services{},
|
||||||
|
},
|
||||||
|
TLS: &TLS{
|
||||||
|
CertProviders: CertProviders{},
|
||||||
|
},
|
||||||
|
EntryPoints: EntryPoints{},
|
||||||
|
}
|
||||||
|
|
||||||
|
appConfig.InitRouters(c)
|
||||||
|
appConfig.InitServices(c)
|
||||||
|
appConfig.InitEntryPoints(c)
|
||||||
|
appConfig.InitTLS(c)
|
||||||
|
|
||||||
|
return appConfig
|
||||||
|
}
|
||||||
|
func (r Routers) ForEach(fn func(routerName string, routerConfig *Router)) {
|
||||||
|
for k, v := range r {
|
||||||
|
fn(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (ep EntryPoints) ForEach(fn func(entryPointName string, entryPointConfig *EntryPoint)) {
|
||||||
|
forEach(ep, fn)
|
||||||
|
}
|
||||||
|
func (s Services) ForEach(fn func(serviceName string, serviceConfig *Service)) {
|
||||||
|
forEach(s, fn)
|
||||||
|
}
|
||||||
|
func (c *Config) InitRouters(vConfig *config.Config) {
|
||||||
|
c.HTTP.Routers = make(Routers)
|
||||||
|
for k, v := range vConfig.Sub(ROUTERS_KEY).AllSettings() {
|
||||||
|
val := v.(map[string]any)
|
||||||
|
|
||||||
|
var tls string = ""
|
||||||
|
var services string = ""
|
||||||
|
var rawRules string = ""
|
||||||
|
var entryPoint string = ""
|
||||||
|
|
||||||
|
if value, ok := val["service"]; ok {
|
||||||
|
services = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := val["rules"]; ok {
|
||||||
|
rawRules = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := val["entrypoint"]; ok {
|
||||||
|
entryPoint = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := val["tls"]; ok {
|
||||||
|
tls = value.(map[string]any)["certprovider"].(string)
|
||||||
|
}
|
||||||
|
rls := &Rules{
|
||||||
|
Raw: rawRules,
|
||||||
|
Map: make(map[string]*Rule),
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &Router{
|
||||||
|
Service: services,
|
||||||
|
Rules: rls,
|
||||||
|
EntryPoint: entryPoint,
|
||||||
|
TLS: &RouterTLS{CertProvider: tls},
|
||||||
|
}
|
||||||
|
r.ForRuleFunc("Domain", func(domain string) {
|
||||||
|
r.Rules.Map["Domain"] = &Rule{
|
||||||
|
Name: "Domain",
|
||||||
|
Value: domain,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
r.ForRuleFunc("Path", func(path string) {
|
||||||
|
r.Rules.Map["Path"] = &Rule{
|
||||||
|
Name: "Path",
|
||||||
|
Value: path,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
c.HTTP.Routers[k] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (c *Config) InitServices(vConfig *config.Config) {
|
||||||
|
c.HTTP.Services = make(map[string]*Service)
|
||||||
|
for k, v := range vConfig.Sub(SERVICES_KEY).AllSettings() {
|
||||||
|
c.HTTP.Services[k] = &Service{
|
||||||
|
URL: v.(string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (c *Config) InitEntryPoints(vConfig *config.Config) {
|
||||||
|
c.EntryPoints = make(EntryPoints)
|
||||||
|
for k, v := range vConfig.Sub(ENTRYPOINTS_KEY).AllSettings() {
|
||||||
|
v := v.(map[string]any)
|
||||||
|
address := ""
|
||||||
|
enabled := false
|
||||||
|
if v, ok := v["tls"]; ok {
|
||||||
|
if v, ok := v.(map[string]any)["enabled"]; ok {
|
||||||
|
enabled = v.(bool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, ok := v["address"]; ok {
|
||||||
|
address = value.(string)
|
||||||
|
}
|
||||||
|
c.EntryPoints[k] = &EntryPoint{
|
||||||
|
Address: address,
|
||||||
|
TLS: &EntryPointTLS{
|
||||||
|
Enabled: enabled,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) InitTLS(vConfig *config.Config) {
|
||||||
|
c.TLS.CertProviders = make(map[string]*CertProvider)
|
||||||
|
|
||||||
|
for k, v := range vConfig.Sub(TLS_KEY).AllSettings() {
|
||||||
|
cert := v.(map[string]any)["cert"].(string)
|
||||||
|
key := v.(map[string]any)["key"].(string)
|
||||||
|
c.TLS.CertProviders[k] = &CertProvider{
|
||||||
|
Cert: cert,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func forEach[K comparable, V comparable](mp map[K]V, fn func(key K, value V)) {
|
||||||
|
for k, v := range mp {
|
||||||
|
fn(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) ForRuleFunc(ruleName string, fn func(string)) {
|
||||||
|
rules := r.Rules.Raw
|
||||||
|
rulesSlice := splitStatementByRegex(rules, "\\(`(.*?)`\\)")
|
||||||
|
for idx, rule := range rulesSlice {
|
||||||
|
rule, _ = strings.CutPrefix(rule, ".")
|
||||||
|
if rule == ruleName {
|
||||||
|
fn(rulesSlice[idx+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitStatementByRegex(str string, regx string) []string {
|
||||||
|
|
||||||
|
input := str
|
||||||
|
re := regexp.MustCompile(regx)
|
||||||
|
matches := re.FindAllStringSubmatch(input, -1)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return []string{str} // Return original if no backticks found
|
||||||
|
}
|
||||||
|
|
||||||
|
result := []string{}
|
||||||
|
lastIndex := 0
|
||||||
|
|
||||||
|
for _, match := range matches {
|
||||||
|
|
||||||
|
startIndex := strings.Index(input, match[0])
|
||||||
|
|
||||||
|
if startIndex > lastIndex {
|
||||||
|
result = append(result, input[lastIndex:startIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, match[1]) // Append the content within backticks
|
||||||
|
lastIndex = startIndex + len(match[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastIndex < len(input) {
|
||||||
|
result = append(result, input[lastIndex:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
181
zgate.go
181
zgate.go
|
|
@ -1,56 +1,163 @@
|
||||||
package zgate
|
package zgate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeevdiukman/go-reverseproxy"
|
||||||
|
"github.com/zeevdiukman/go-router"
|
||||||
"github.com/zeevdiukman/go-server"
|
"github.com/zeevdiukman/go-server"
|
||||||
|
"github.com/zeevdiukman/go-zgate/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const CERTS_PATH string = "./assets/certs/"
|
||||||
|
|
||||||
type Zgate struct {
|
type Zgate struct {
|
||||||
EntryPoints map[string]*EntryPoint
|
Context context.Context
|
||||||
// TLS *TLS
|
// Routers struct {
|
||||||
|
// Maps map[string]*router.HostRouter
|
||||||
|
// }
|
||||||
|
// map[string]*router.HostRouter
|
||||||
|
// Rules map[string]map[string]string
|
||||||
|
// Services map[string]string
|
||||||
|
Config *config.Config
|
||||||
|
DomainRouters DomainRouters
|
||||||
|
EntryPoints EntryPoints
|
||||||
|
ActiveEntryPoints ActiveEntryPoints
|
||||||
}
|
}
|
||||||
|
type ActiveEntryPoints map[string]bool
|
||||||
|
type DomainRouters map[string]*router.DomainRouter
|
||||||
|
type EntryPoints map[string]*EntryPoint
|
||||||
type EntryPoint struct {
|
type EntryPoint struct {
|
||||||
Name string
|
|
||||||
// Port int
|
|
||||||
// _IP net.IP
|
|
||||||
*server.Server
|
*server.Server
|
||||||
isTLS bool
|
*router.Router
|
||||||
|
HostRouters HostRouters
|
||||||
|
}
|
||||||
|
type HostRouters map[string]*router.DomainRouter
|
||||||
|
|
||||||
|
func New() *Zgate {
|
||||||
|
zGate := &Zgate{}
|
||||||
|
zGate.Context = context.Background()
|
||||||
|
zGate.Config = config.New()
|
||||||
|
zGate.initActiveEntryPoints()
|
||||||
|
zGate.DomainRouters = make(DomainRouters)
|
||||||
|
zGate.EntryPoints = make(EntryPoints)
|
||||||
|
return zGate
|
||||||
}
|
}
|
||||||
|
|
||||||
// type TLS struct {
|
func (ep EntryPoints) ForEach(f func(string, *EntryPoint)) {
|
||||||
// CertPath string
|
forEach(ep, f)
|
||||||
// CertFile string
|
}
|
||||||
// KeyFile string
|
|
||||||
|
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() {
|
||||||
|
activeEntryPoints := make(ActiveEntryPoints)
|
||||||
|
zGate.Config.EntryPoints.ForEach(func(entryPointName string, entryPointConfig *config.EntryPoint) {
|
||||||
|
zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
|
||||||
|
if rConfig.EntryPoint == entryPointName {
|
||||||
|
activeEntryPoints[entryPointName] = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
zGate.ActiveEntryPoints = activeEntryPoints
|
||||||
|
}
|
||||||
|
func (zGate *Zgate) BuildActiveEntryPoints() {
|
||||||
|
zGate.Config.EntryPoints.ForEach(func(entryPointName string, entryPointConfig *config.EntryPoint) {
|
||||||
|
if isUsed, ok := zGate.ActiveEntryPoints[entryPointName]; ok && isUsed {
|
||||||
|
if !isOK(zGate.ActiveEntryPoints, entryPointName) {
|
||||||
|
zGate.EntryPoints = make(map[string]*EntryPoint)
|
||||||
|
}
|
||||||
|
r := router.NewRouter()
|
||||||
|
_, portStr, _ := strings.Cut(entryPointConfig.Address, ":")
|
||||||
|
port, _ := strconv.Atoi(portStr)
|
||||||
|
s := server.New().Name(entryPointName).Port(port)
|
||||||
|
s.Router(r)
|
||||||
|
zGate.EntryPoints[entryPointName] = &EntryPoint{
|
||||||
|
Server: s,
|
||||||
|
Router: r,
|
||||||
|
HostRouters: make(map[string]*router.DomainRouter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (zGate *Zgate) BuildRouters() {
|
||||||
|
zGate.Config.HTTP.Routers.ForEach(func(rName string, rConfig *config.Router) {
|
||||||
|
if isUsed, ok := zGate.ActiveEntryPoints[rConfig.EntryPoint]; ok && isUsed {
|
||||||
|
domain := rConfig.Rules.Map["Domain"].Value
|
||||||
|
serviceURL := zGate.Config.HTTP.Services[rConfig.Service].URL
|
||||||
|
if _, ok := 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.CertKey(CERTS_PATH, "z.com.cert.pem", "z.com.key.pem")
|
||||||
|
go zGateEntryPoint.ListenAndServeTLS()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (activeEntryPoints ActiveEntryPoints) BuildActiveEntryPoint(entryPointName string, port int) {
|
||||||
|
// r := router.NewRouter()
|
||||||
|
// _, portStr, _ := strings.Cut(port, ":")
|
||||||
|
// port, _ := strconv.Atoi(portStr)
|
||||||
|
// s := server.New().Name(entryPointName).Port(port)
|
||||||
|
// s.Router(r)
|
||||||
|
// entryPoint = &EntryPoint{
|
||||||
|
// Server: s,
|
||||||
|
// Router: r,
|
||||||
|
// HostRouters: make(map[string]*router.DomainRouter),
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func NewGate() *Zgate {
|
func isOK[K comparable, V comparable](mp map[K]V, key K) bool {
|
||||||
return &Zgate{
|
if _, ok := mp[key]; !ok {
|
||||||
EntryPoints: make(map[string]*EntryPoint),
|
return false
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *Zgate) NewEntryPoint(entryPointName string) *EntryPoint {
|
func defaultDirector(rpHandler *reverseproxy.ReverseProxy, jup reverseproxy.JoinURLPathFunc) func(*http.Request) {
|
||||||
e := newEntryPoint(z, entryPointName)
|
return func(r *http.Request) {
|
||||||
e.Server = server.New()
|
r = r.WithContext(rpHandler.Context)
|
||||||
return e
|
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 (e *EntryPoint) IP(ipAddress string) *EntryPoint {
|
|
||||||
// if ipAddress == "" {
|
|
||||||
// ipAddress = "127.0.0.1"
|
|
||||||
// }
|
|
||||||
// e._IP = net.ParseIP(ipAddress)
|
|
||||||
// return e
|
|
||||||
// }
|
|
||||||
|
|
||||||
func newEntryPoint(z *Zgate, entryPointName string) *EntryPoint {
|
|
||||||
e := &EntryPoint{}
|
|
||||||
z.EntryPoints[entryPointName] = e
|
|
||||||
return e
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (z *EntryPoint) initServer() *EntryPoint{
|
|
||||||
// server.New()
|
|
||||||
// return z.isTLS
|
|
||||||
// }
|
|
||||||
// func
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue