feat(proxy): Add reverse proxy feature
This commit is contained in:
@@ -1,11 +1,73 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
type Config interface {
|
type BaseConfiguration interface {
|
||||||
GetConfiguration() *Configuration
|
GetConfiguration() *Configuration
|
||||||
GetGatewayConfiguration() *GatewayConfiguration
|
}
|
||||||
GetFeatures() []string
|
|
||||||
GetRoutes() []RouteConfiguration
|
type Configuration struct {
|
||||||
GetApis() []ApiConfiguration
|
Gateway Gateway `yaml:"gateway"`
|
||||||
GetHost() string
|
Hosts []Host `yaml:"hosts"`
|
||||||
GetPort() int
|
Targets []Target `yaml:"targets"`
|
||||||
|
Apis []Api `yaml:"apis"`
|
||||||
|
Routes []Route `yaml:"routes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Gateway struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Features []string `yaml:"features"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Host struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Domain string `yaml:"domain"`
|
||||||
|
Secure bool `yaml:"secure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Target struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Api struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Target string `yaml:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Api string `yaml:"api"`
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
StripPrefix StripPrefix `yaml:"strip_prefix"`
|
||||||
|
Security Security `yaml:"security"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StripPrefix struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Prefix string `yaml:"prefix"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Security struct {
|
||||||
|
Auth Auth `yaml:"auth"`
|
||||||
|
WAF WAF `yaml:"waf"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Header string `yaml:"header"`
|
||||||
|
Path AuthPath `yaml:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthPath struct {
|
||||||
|
Include []string `yaml:"include"`
|
||||||
|
Exclude []string `yaml:"exclude"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WAF struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Methods []string `yaml:"methods"`
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/config/database.go
Normal file
34
app/config/database.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "git.secnex.io/secnex/api-gateway/utils"
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Database string
|
||||||
|
SSLMode string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatabaseConfiguration(host string, port int, user string, password string, database string, sslmode string) Database {
|
||||||
|
return Database{
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
User: user,
|
||||||
|
Password: password,
|
||||||
|
Database: database,
|
||||||
|
SSLMode: sslmode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatabaseConfigurationFromEnv() Database {
|
||||||
|
return NewDatabaseConfiguration(
|
||||||
|
utils.GetEnv("DATABASE_HOST", "localhost"),
|
||||||
|
utils.GetEnvInt("DATABASE_PORT", 5432),
|
||||||
|
utils.GetEnv("DATABASE_USER", "postgres"),
|
||||||
|
utils.GetEnv("DATABASE_PASSWORD", "postgres"),
|
||||||
|
utils.GetEnv("DATABASE_NAME", "secnex"),
|
||||||
|
utils.GetEnv("DATABASE_SSLMODE", "disable"),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -6,20 +6,20 @@ import (
|
|||||||
"go.yaml.in/yaml/v3"
|
"go.yaml.in/yaml/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileConfig struct {
|
type File struct {
|
||||||
filePath string
|
filePath string
|
||||||
config *Configuration
|
config *Configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileConfig(filePath string) (*FileConfig, error) {
|
func NewFile(filePath string) (*File, error) {
|
||||||
c := &FileConfig{filePath: filePath, config: &Configuration{}}
|
c := &File{filePath: filePath, config: &Configuration{}}
|
||||||
if err := c.loadConfig(); err != nil {
|
if err := c.loadConfig(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FileConfig) loadConfig() error {
|
func (c *File) loadConfig() error {
|
||||||
data, err := os.ReadFile(c.filePath)
|
data, err := os.ReadFile(c.filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -27,34 +27,6 @@ func (c *FileConfig) loadConfig() error {
|
|||||||
return yaml.Unmarshal(data, c.config)
|
return yaml.Unmarshal(data, c.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FileConfig) GetConfiguration() *Configuration {
|
func (c *File) GetConfiguration() *Configuration {
|
||||||
return c.config
|
return c.config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FileConfig) GetGatewayConfiguration() *GatewayConfiguration {
|
|
||||||
return &c.config.Gateway
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FileConfig) GetRoutes() []RouteConfiguration {
|
|
||||||
return c.config.Routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FileConfig) GetProxies() []ProxyConfiguration {
|
|
||||||
return c.config.Proxies
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FileConfig) GetHost() string {
|
|
||||||
return c.config.Gateway.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FileConfig) GetPort() int {
|
|
||||||
return c.config.Gateway.Port
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FileConfig) GetApis() []ApiConfiguration {
|
|
||||||
return c.config.Apis
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FileConfig) GetFeatures() []string {
|
|
||||||
return c.config.Gateway.Features
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
type Configuration struct {
|
|
||||||
Gateway GatewayConfiguration `yaml:"gateway"`
|
|
||||||
Apis []ApiConfiguration `yaml:"apis"`
|
|
||||||
Routes []RouteConfiguration `yaml:"routes"`
|
|
||||||
Proxies []ProxyConfiguration `yaml:"proxies"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GatewayConfiguration struct {
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
Port int `yaml:"port"`
|
|
||||||
Features []string `yaml:"features"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouteConfiguration struct {
|
|
||||||
ID string `yaml:"id"`
|
|
||||||
Path string `yaml:"path"`
|
|
||||||
StripPrefix struct {
|
|
||||||
Enabled bool `yaml:"enabled"`
|
|
||||||
Prefix string `yaml:"prefix"`
|
|
||||||
} `yaml:"strip_prefix"`
|
|
||||||
Security SecurityConfiguration `yaml:"security"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecurityConfiguration struct {
|
|
||||||
Auth AuthConfiguration `yaml:"auth"`
|
|
||||||
WAF WAFConfiguration `yaml:"waf"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WAFConfiguration struct {
|
|
||||||
Enabled bool `yaml:"enabled"`
|
|
||||||
Methods []string `yaml:"methods"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthConfiguration struct {
|
|
||||||
Enabled bool `yaml:"enabled"`
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
Header string `yaml:"header"`
|
|
||||||
Path AuthPathConfiguration `yaml:"path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthPathConfiguration struct {
|
|
||||||
Include []string `yaml:"include"`
|
|
||||||
Exclude []string `yaml:"exclude"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyConfiguration struct {
|
|
||||||
ID string `yaml:"id"`
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
Target string `yaml:"target"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApiConfiguration struct {
|
|
||||||
ID string `yaml:"id"`
|
|
||||||
Target string `yaml:"target"`
|
|
||||||
}
|
|
||||||
39
app/database/conn.go
Normal file
39
app/database/conn.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Connection *gorm.DB
|
||||||
|
|
||||||
|
func Connect(config config.Database) error {
|
||||||
|
masterlog.Info("Connecting to database", map[string]interface{}{
|
||||||
|
"host": config.Host,
|
||||||
|
"port": config.Port,
|
||||||
|
"user": config.User,
|
||||||
|
"database": config.Database,
|
||||||
|
"sslmode": config.SSLMode,
|
||||||
|
})
|
||||||
|
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", config.Host, config.Port, config.User, config.Password, config.Database, config.SSLMode)
|
||||||
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Silent),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
masterlog.Info("Connected to database", map[string]interface{}{
|
||||||
|
"host": config.Host,
|
||||||
|
"port": config.Port,
|
||||||
|
"user": config.User,
|
||||||
|
"database": config.Database,
|
||||||
|
"sslmode": config.SSLMode,
|
||||||
|
})
|
||||||
|
Connection = db
|
||||||
|
return nil
|
||||||
|
}
|
||||||
16
app/go.mod
16
app/go.mod
@@ -6,4 +6,20 @@ require (
|
|||||||
git.secnex.io/secnex/masterlog v0.1.0
|
git.secnex.io/secnex/masterlog v0.1.0
|
||||||
github.com/go-chi/chi/v5 v5.2.4
|
github.com/go-chi/chi/v5 v5.2.4
|
||||||
go.yaml.in/yaml/v3 v3.0.4
|
go.yaml.in/yaml/v3 v3.0.4
|
||||||
|
gorm.io/driver/postgres v1.6.0
|
||||||
|
gorm.io/gorm v1.31.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
45
app/go.sum
45
app/go.sum
@@ -1,8 +1,51 @@
|
|||||||
git.secnex.io/secnex/masterlog v0.1.0 h1:74j9CATpfeK0lxpWIQC9ag9083akwG8khi5BwLedD8E=
|
git.secnex.io/secnex/masterlog v0.1.0 h1:74j9CATpfeK0lxpWIQC9ag9083akwG8khi5BwLedD8E=
|
||||||
git.secnex.io/secnex/masterlog v0.1.0/go.mod h1:OnDlwEzdkKMnqY+G5O9kHdhoJ6fH1llbVdXpgSc5SdM=
|
git.secnex.io/secnex/masterlog v0.1.0/go.mod h1:OnDlwEzdkKMnqY+G5O9kHdhoJ6fH1llbVdXpgSc5SdM=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
|
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
|
||||||
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||||
|
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||||
|
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||||
|
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
package handlers
|
|
||||||
@@ -15,15 +15,14 @@ func main() {
|
|||||||
masterlog.SetPseudonymizer(pseudonymizer)
|
masterlog.SetPseudonymizer(pseudonymizer)
|
||||||
masterlog.AddSensitiveFields("user_id", "email", "ip")
|
masterlog.AddSensitiveFields("user_id", "email", "ip")
|
||||||
|
|
||||||
cfg, err := config.NewFileConfig("../gateway.yaml")
|
cfg, err := config.NewFile("../gateway.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
masterlog.Error("Failed to load config", map[string]interface{}{
|
masterlog.Error("Failed to load config", map[string]interface{}{
|
||||||
"error": err,
|
"error": err,
|
||||||
})
|
})
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
gateway := server.NewGateway(cfg)
|
|
||||||
routes := server.NewRoutes(cfg.GetRoutes(), cfg.GetApis())
|
gateway := server.NewGateway(cfg.GetConfiguration())
|
||||||
gateway.SetRoutes(routes)
|
|
||||||
gateway.Start()
|
gateway.Start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.secnex.io/secnex/api-gateway/config"
|
|
||||||
"git.secnex.io/secnex/api-gateway/res"
|
|
||||||
"git.secnex.io/secnex/masterlog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func authPathMatches(pattern, requestPath string) bool {
|
|
||||||
if pattern == "*" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if pattern == requestPath {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.Contains(pattern, "*") {
|
|
||||||
matched, _ := path.Match(pattern, requestPath)
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func Auth(next http.Handler, authType string, authHeader string, authPath config.AuthPathConfiguration) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
masterlog.Debug("Auth middleware", map[string]interface{}{
|
|
||||||
"path": r.URL.Path,
|
|
||||||
"include": authPath.Include,
|
|
||||||
"exclude": authPath.Exclude,
|
|
||||||
})
|
|
||||||
if len(authPath.Include) > 0 {
|
|
||||||
matched := false
|
|
||||||
for _, include := range authPath.Include {
|
|
||||||
if authPathMatches(include, r.URL.Path) {
|
|
||||||
matched = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !matched {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, exclude := range authPath.Exclude {
|
|
||||||
if authPathMatches(exclude, r.URL.Path) {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.Header.Get(authHeader) == "" {
|
|
||||||
res.Unauthorized(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.Header.Del(authHeader)
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
16
app/middlewares/host.go
Normal file
16
app/middlewares/host.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HostMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
masterlog.Info("HostMiddleware", map[string]interface{}{
|
||||||
|
"host": r.Host,
|
||||||
|
})
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
27
app/middlewares/logger.go
Normal file
27
app/middlewares/logger.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoggerMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
||||||
|
next.ServeHTTP(ww, r)
|
||||||
|
|
||||||
|
masterlog.Info("HTTP Request", map[string]interface{}{
|
||||||
|
"method": r.Method,
|
||||||
|
"path": r.URL.Path,
|
||||||
|
"status": ww.Status(),
|
||||||
|
"duration": time.Since(start).String(),
|
||||||
|
"host": r.Host,
|
||||||
|
"ip": r.RemoteAddr,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"git.secnex.io/secnex/api-gateway/config"
|
|
||||||
"git.secnex.io/secnex/api-gateway/res"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WAF(next http.Handler, wafConfig config.WAFConfiguration) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !wafConfig.Enabled {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if slices.Contains(wafConfig.Methods, "*") {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !slices.Contains(wafConfig.Methods, r.Method) {
|
|
||||||
res.Forbidden(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
46
app/server/api.go
Normal file
46
app/server/api.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Apis map[string]*Api
|
||||||
|
|
||||||
|
type Api struct {
|
||||||
|
ID string
|
||||||
|
Host *Host
|
||||||
|
Target *Target
|
||||||
|
domain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApis(cfg *config.Configuration, hosts Hosts, targets Targets) Apis {
|
||||||
|
apis := make(Apis)
|
||||||
|
for _, api := range cfg.Apis {
|
||||||
|
apis[api.ID] = NewApi(&api, hosts[api.Host], targets[api.Target])
|
||||||
|
}
|
||||||
|
return apis
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApi(api *config.Api, host *Host, target *Target) *Api {
|
||||||
|
return &Api{
|
||||||
|
ID: api.ID,
|
||||||
|
Host: host,
|
||||||
|
Target: target,
|
||||||
|
domain: host.Domain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as Apis) GetApi(domain string) *Api {
|
||||||
|
for _, api := range as {
|
||||||
|
if api.domain == domain {
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
a.Target.proxy.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.secnex.io/secnex/api-gateway/config"
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/api-gateway/middlewares"
|
||||||
"git.secnex.io/secnex/masterlog"
|
"git.secnex.io/secnex/masterlog"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
@@ -12,22 +13,39 @@ import (
|
|||||||
|
|
||||||
type Gateway struct {
|
type Gateway struct {
|
||||||
router *chi.Mux
|
router *chi.Mux
|
||||||
config config.Config
|
config config.Configuration
|
||||||
|
apis Apis
|
||||||
routes *Routes
|
routes *Routes
|
||||||
proxy *Proxy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGateway(config config.Config) *Gateway {
|
func (g *Gateway) configureProxies() {
|
||||||
|
for _, api := range g.apis {
|
||||||
|
masterlog.Info("Configuring proxy", map[string]interface{}{
|
||||||
|
"id": api.ID,
|
||||||
|
"host": api.Host.Domain,
|
||||||
|
"target": api.Target.URL.String(),
|
||||||
|
})
|
||||||
|
originalDirector := api.Target.proxy.Director
|
||||||
|
api.Target.proxy.Director = func(r *http.Request) {
|
||||||
|
originalDirector(r)
|
||||||
|
r.Host = api.Host.Domain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGateway(cfg *config.Configuration) *Gateway {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
for _, feature := range config.GetFeatures() {
|
for _, feature := range cfg.Gateway.Features {
|
||||||
switch feature {
|
switch feature {
|
||||||
case "request_id":
|
case "request_id":
|
||||||
r.Use(middleware.RequestID)
|
r.Use(middleware.RequestID)
|
||||||
case "real_ip":
|
case "real_ip":
|
||||||
r.Use(middleware.RealIP)
|
r.Use(middleware.RealIP)
|
||||||
case "logger":
|
case "logger":
|
||||||
r.Use(middleware.Logger)
|
r.Use(middlewares.LoggerMiddleware)
|
||||||
|
case "host":
|
||||||
|
r.Use(middlewares.HostMiddleware)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,30 +56,22 @@ func NewGateway(config config.Config) *Gateway {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return &Gateway{config: config, router: r, routes: nil, proxy: nil}
|
hosts := NewHosts(cfg)
|
||||||
}
|
targets := NewTargets(cfg)
|
||||||
|
apis := NewApis(cfg, hosts, targets)
|
||||||
|
routes := NewRoutes(cfg, apis)
|
||||||
|
|
||||||
func (g *Gateway) SetRoutes(routes *Routes) {
|
return &Gateway{config: *cfg, router: r, apis: apis, routes: routes}
|
||||||
g.routes = routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Gateway) SetProxy(proxy *Proxy) {
|
|
||||||
g.proxy = proxy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) Start() {
|
func (g *Gateway) Start() {
|
||||||
masterlog.Info("Starting gateway", map[string]interface{}{
|
masterlog.Info("Starting gateway", map[string]interface{}{
|
||||||
"host": g.config.GetGatewayConfiguration().Host,
|
"host": g.config.Gateway.Host,
|
||||||
"port": g.config.GetGatewayConfiguration().Port,
|
"port": g.config.Gateway.Port,
|
||||||
})
|
})
|
||||||
for path, handler := range g.routes.handlers {
|
|
||||||
masterlog.Info("Registering route", map[string]interface{}{
|
|
||||||
"path": path,
|
|
||||||
})
|
|
||||||
g.router.Handle(path, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
gatewayConfig := g.config.GetGatewayConfiguration()
|
g.configureProxies()
|
||||||
|
g.routes.Register(g.router)
|
||||||
|
|
||||||
http.ListenAndServe(fmt.Sprintf("%s:%d", gatewayConfig.Host, gatewayConfig.Port), g.router)
|
http.ListenAndServe(fmt.Sprintf("%s:%d", g.config.Gateway.Host, g.config.Gateway.Port), g.router)
|
||||||
}
|
}
|
||||||
|
|||||||
41
app/server/host.go
Normal file
41
app/server/host.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Hosts map[string]*Host
|
||||||
|
|
||||||
|
type Host struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Domain string
|
||||||
|
proxy *httputil.ReverseProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHosts(cfg *config.Configuration) Hosts {
|
||||||
|
hosts := make(Hosts)
|
||||||
|
for _, host := range cfg.Hosts {
|
||||||
|
hosts[host.ID] = NewHost(&host)
|
||||||
|
}
|
||||||
|
return hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHost(host *config.Host) *Host {
|
||||||
|
return &Host{
|
||||||
|
ID: host.ID,
|
||||||
|
Name: host.Name,
|
||||||
|
Domain: host.Domain,
|
||||||
|
proxy: httputil.NewSingleHostReverseProxy(&url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: host.Domain,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs Hosts) GetHost(domain string) *Host {
|
||||||
|
return hs[domain]
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"git.secnex.io/secnex/api-gateway/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Proxy struct {
|
|
||||||
proxies []config.ProxyConfiguration
|
|
||||||
handlers map[string]http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProxy(proxies []config.ProxyConfiguration) *Proxy {
|
|
||||||
handlers := make(map[string]http.Handler)
|
|
||||||
for _, proxy := range proxies {
|
|
||||||
backend, err := url.Parse(proxy.Target)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse proxy target: %v", err)
|
|
||||||
}
|
|
||||||
p := httputil.NewSingleHostReverseProxy(backend)
|
|
||||||
originalDirector := p.Director
|
|
||||||
p.Director = func(r *http.Request) {
|
|
||||||
originalDirector(r)
|
|
||||||
r.Host = backend.Host
|
|
||||||
}
|
|
||||||
handlers[proxy.Host] = p
|
|
||||||
}
|
|
||||||
return &Proxy{proxies: proxies, handlers: handlers}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
handler, ok := p.handlers[r.Host]
|
|
||||||
if !ok {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handler.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
@@ -1,85 +1,81 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"strings"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"git.secnex.io/secnex/api-gateway/config"
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
"git.secnex.io/secnex/api-gateway/middlewares"
|
|
||||||
"git.secnex.io/secnex/masterlog"
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Routes struct {
|
type Routes struct {
|
||||||
routes []config.RouteConfiguration
|
routes []Route
|
||||||
handlers map[string]http.Handler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRoutes(routes []config.RouteConfiguration, apis []config.ApiConfiguration) *Routes {
|
type Route struct {
|
||||||
handlers := createHandlers(routes, apis)
|
ID string
|
||||||
return &Routes{routes: routes, handlers: handlers}
|
Path string
|
||||||
|
StripPrefix config.StripPrefix
|
||||||
|
Api *Api
|
||||||
}
|
}
|
||||||
|
|
||||||
func findApi(apis []config.ApiConfiguration, id string) *config.ApiConfiguration {
|
func NewRoutes(cfg *config.Configuration, apis Apis) *Routes {
|
||||||
for _, api := range apis {
|
routes := make([]Route, 0)
|
||||||
if api.ID == id {
|
for _, route := range cfg.Routes {
|
||||||
return &api
|
routes = append(routes, Route{
|
||||||
}
|
ID: route.ID,
|
||||||
}
|
Path: route.Path,
|
||||||
return nil
|
StripPrefix: route.StripPrefix,
|
||||||
}
|
Api: apis[route.Api],
|
||||||
|
|
||||||
func createHandlers(routes []config.RouteConfiguration, apis []config.ApiConfiguration) map[string]http.Handler {
|
|
||||||
handlers := make(map[string]http.Handler)
|
|
||||||
for _, route := range routes {
|
|
||||||
masterlog.Debug("Creating handler for route", map[string]interface{}{
|
|
||||||
"path": route.Path,
|
|
||||||
"id": route.ID,
|
|
||||||
})
|
})
|
||||||
api := findApi(apis, route.ID)
|
}
|
||||||
if api == nil {
|
return &Routes{routes: routes}
|
||||||
log.Fatalf("API not found: %s", route.ID)
|
}
|
||||||
continue
|
|
||||||
}
|
func (rs *Routes) Register(r *chi.Mux) {
|
||||||
backendUrl, err := url.Parse(
|
for _, route := range rs.routes {
|
||||||
api.Target,
|
masterlog.Info("Registering route", map[string]interface{}{
|
||||||
)
|
"id": route.ID,
|
||||||
if err != nil {
|
"path": route.Path,
|
||||||
log.Fatalf("Failed to parse backend URL: %v", err)
|
"api": route.Api.ID,
|
||||||
}
|
})
|
||||||
proxy := httputil.NewSingleHostReverseProxy(backendUrl)
|
|
||||||
handlers[route.Path] = proxy
|
handler := route.createHandler()
|
||||||
if route.StripPrefix.Enabled {
|
r.Handle(route.Path, handler)
|
||||||
masterlog.Debug("Stripping prefix", map[string]interface{}{
|
}
|
||||||
"id": route.ID,
|
}
|
||||||
"path": route.Path,
|
|
||||||
"prefix": route.StripPrefix.Prefix,
|
func (r *Route) createHandler() http.Handler {
|
||||||
})
|
handler := http.Handler(r.Api)
|
||||||
handlers[route.Path] = http.StripPrefix(route.StripPrefix.Prefix, handlers[route.Path])
|
|
||||||
}
|
if r.StripPrefix.Enabled {
|
||||||
if route.Security.WAF.Enabled {
|
handler = newStripPrefixMiddleware(r.StripPrefix.Prefix, handler)
|
||||||
masterlog.Debug("Applying WAF middleware", map[string]interface{}{
|
}
|
||||||
"id": route.ID,
|
|
||||||
"path": route.Path,
|
return handler
|
||||||
"methods": route.Security.WAF.Methods,
|
}
|
||||||
})
|
|
||||||
handlers[route.Path] = middlewares.WAF(handlers[route.Path], route.Security.WAF)
|
type stripPrefixMiddleware struct {
|
||||||
}
|
prefix string
|
||||||
if route.Security.Auth.Enabled {
|
handler http.Handler
|
||||||
masterlog.Debug("Applying auth middleware", map[string]interface{}{
|
}
|
||||||
"id": route.ID,
|
|
||||||
"path": route.Path,
|
func newStripPrefixMiddleware(prefix string, handler http.Handler) http.Handler {
|
||||||
"type": route.Security.Auth.Type,
|
return &stripPrefixMiddleware{
|
||||||
"header": route.Security.Auth.Header,
|
prefix: prefix,
|
||||||
})
|
handler: handler,
|
||||||
handlers[route.Path] = middlewares.Auth(
|
}
|
||||||
handlers[route.Path],
|
}
|
||||||
route.Security.Auth.Type,
|
|
||||||
route.Security.Auth.Header,
|
func (m *stripPrefixMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
route.Security.Auth.Path,
|
// Remove prefix from path
|
||||||
)
|
if strings.HasPrefix(r.URL.Path, m.prefix) {
|
||||||
|
r.URL.Path = strings.TrimPrefix(r.URL.Path, m.prefix)
|
||||||
|
// Ensure path starts with /
|
||||||
|
if !strings.HasPrefix(r.URL.Path, "/") {
|
||||||
|
r.URL.Path = "/" + r.URL.Path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handlers
|
m.handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|||||||
43
app/server/target.go
Normal file
43
app/server/target.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Targets map[string]*Target
|
||||||
|
|
||||||
|
type Target struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
URL *url.URL
|
||||||
|
proxy *httputil.ReverseProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTargets(cfg *config.Configuration) Targets {
|
||||||
|
targets := make(Targets)
|
||||||
|
for _, target := range cfg.Targets {
|
||||||
|
targets[target.ID] = NewTarget(&target)
|
||||||
|
}
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTarget(target *config.Target) *Target {
|
||||||
|
url, err := url.Parse(target.URL)
|
||||||
|
if err != nil {
|
||||||
|
masterlog.Error("Failed to parse target URL", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
"target": target.URL,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &Target{
|
||||||
|
ID: target.ID,
|
||||||
|
Name: target.Name,
|
||||||
|
URL: url,
|
||||||
|
proxy: httputil.NewSingleHostReverseProxy(url),
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/utils/env.go
Normal file
35
app/utils/env.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEnv(key string, defaultValue string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnvInt(key string, defaultValue int) int {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
intValue, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return intValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnvBool(key string, defaultValue bool) bool {
|
||||||
|
value := strings.ToLower(os.Getenv(key))
|
||||||
|
if value == "" {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return value == "true" || value == "1" || value == "yes" || value == "y" || value == "on" || value == "enabled"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user