diff --git a/internals/config/loader.go b/internals/config/loader.go index dbdfaab9..802304dd 100644 --- a/internals/config/loader.go +++ b/internals/config/loader.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/codeshelldev/gotl/pkg/configutils" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/gotl/pkg/stringutils" "github.com/codeshelldev/secured-signal-api/internals/config/structure" @@ -63,13 +63,13 @@ func Load() { InitTokens() - log.Info("Finished Loading Configuration") + logger.Info("Finished Loading Configuration") } func Log() { - log.Dev("Loaded Config:", mainConf.Layer.Get("")) - log.Dev("Loaded Token Configs:", tokenConf.Layer.Get("")) - log.Dev("Parsed Configs: ", ENV) + logger.Dev("Loaded Config:", mainConf.Layer.Get("")) + logger.Dev("Loaded Token Configs:", tokenConf.Layer.Get("")) + logger.Dev("Parsed Configs: ", ENV) } func Clear() { @@ -102,7 +102,7 @@ func Normalize(id string, config *configutils.Config, path string, structure any old, ok := data.(map[string]any) if !ok { - log.Warn("Could not load `"+path+"`") + logger.Warn("Could not load `"+path+"`") return } @@ -124,7 +124,7 @@ func Normalize(id string, config *configutils.Config, path string, structure any func InitReload() { reload := func(path string) { - log.Debug(path, " changed, reloading...") + logger.Debug(path, " changed, reloading...") Load() Log() } @@ -145,16 +145,16 @@ func InitConfig() { } func LoadDefaults() { - log.Debug("Loading defaults ", ENV.DEFAULTS_PATH) + logger.Debug("Loading defaults ", ENV.DEFAULTS_PATH) _, err := defaultsConf.LoadFile(ENV.DEFAULTS_PATH, yaml.Parser()) if err != nil { - log.Warn("Could not Load Defaults", ENV.DEFAULTS_PATH) + logger.Warn("Could not Load Defaults", ENV.DEFAULTS_PATH) } } func LoadConfig() { - log.Debug("Loading Config ", ENV.CONFIG_PATH) + logger.Debug("Loading Config ", ENV.CONFIG_PATH) _, err := userConf.LoadFile(ENV.CONFIG_PATH, yaml.Parser()) if err != nil { @@ -166,7 +166,7 @@ func LoadConfig() { return } - log.Error("Could not Load Config ", ENV.CONFIG_PATH, ": ", err.Error()) + logger.Error("Could not Load Config ", ENV.CONFIG_PATH, ": ", err.Error()) } } diff --git a/internals/config/tokens.go b/internals/config/tokens.go index 470bfb97..bf37a16b 100644 --- a/internals/config/tokens.go +++ b/internals/config/tokens.go @@ -4,18 +4,18 @@ import ( "strconv" "github.com/codeshelldev/gotl/pkg/configutils" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/secured-signal-api/internals/config/structure" "github.com/knadh/koanf/parsers/yaml" ) func LoadTokens() { - log.Debug("Loading Configs in ", ENV.TOKENS_DIR) + logger.Debug("Loading Configs in ", ENV.TOKENS_DIR) err := tokenConf.LoadDir("tokenconfigs", ENV.TOKENS_DIR, ".yml", yaml.Parser()) if err != nil { - log.Error("Could not Load Configs in ", ENV.TOKENS_DIR, ": ", err.Error()) + logger.Error("Could not Load Configs in ", ENV.TOKENS_DIR, ": ", err.Error()) } tokenConf.TemplateConfig() @@ -57,9 +57,9 @@ func InitTokens() { } if len(apiTokens) <= 0 { - log.Warn("No API Tokens provided this is NOT recommended") + logger.Warn("No API Tokens provided this is NOT recommended") - log.Info("Disabling Security Features due to incomplete Congfiguration") + logger.Info("Disabling Security Features due to incomplete Congfiguration") ENV.INSECURE = true @@ -69,7 +69,7 @@ func InitTokens() { } if len(apiTokens) > 0 { - log.Debug("Registered " + strconv.Itoa(len(apiTokens)) + " Tokens") + logger.Debug("Registered " + strconv.Itoa(len(apiTokens)) + " Tokens") } } diff --git a/internals/proxy/middlewares/auth.go b/internals/proxy/middlewares/auth.go index 11b0eadd..23967ed9 100644 --- a/internals/proxy/middlewares/auth.go +++ b/internals/proxy/middlewares/auth.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/codeshelldev/gotl/pkg/logger" - log "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/gotl/pkg/request" "github.com/codeshelldev/secured-signal-api/internals/config" ) @@ -21,103 +20,105 @@ var Auth Middleware = Middleware{ Use: authHandler, } +const tokenKey contextKey = "token" + type AuthMethod struct { Name string - Authenticate func(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) + Authenticate func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) } var BearerAuth = AuthMethod { Name: "Bearer", - Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) { + Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { header := req.Header.Get("Authorization") headerParts := strings.SplitN(header, " ", 2) if len(headerParts) != 2 { - return false, nil + return "", nil } if strings.ToLower(headerParts[0]) == "bearer" { if isValidToken(tokens, headerParts[1]) { - return true, nil + return headerParts[1], nil } - return false, errors.New("invalid Bearer token") + return "", errors.New("invalid Bearer token") } - return false, nil + return "", nil }, } var BasicAuth = AuthMethod { Name: "Basic", - Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) { + Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { header := req.Header.Get("Authorization") if strings.TrimSpace(header) == "" { - return false, nil + return "", nil } headerParts := strings.SplitN(header, " ", 2) if len(headerParts) != 2 { - return false, nil + return "", nil } if strings.ToLower(headerParts[0]) == "basic" { base64Bytes, err := base64.StdEncoding.DecodeString(headerParts[1]) if err != nil { - log.Error("Could not decode Basic auth payload: ", err.Error()) - return false, errors.New("invalid base64 in Basic auth") + logger.Error("Could not decode Basic auth payload: ", err.Error()) + return "", errors.New("invalid base64 in Basic auth") } parts := strings.SplitN(string(base64Bytes), ":", 2) if len(parts) != 2 { - return false, errors.New("Basic auth must be user:password") + return "", errors.New("Basic auth must be user:password") } user, password := parts[0], parts[1] if strings.ToLower(user) == "api" && isValidToken(tokens, password) { - return true, nil + return password, nil } - return false, errors.New("invalid user:password") + return "", errors.New("invalid user:password") } - return false, nil + return "", nil }, } var BodyAuth = AuthMethod { Name: "Body", - Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) { + Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { const authField = "auth" body, err := request.GetReqBody(req) if err != nil { - return false, nil + return "", nil } body.Write(req) if body.Empty { - return false, nil + return "", nil } value, exists := body.Data[authField] if !exists { - return false, nil + return "", nil } auth, ok := value.(string) if !ok { - return false, nil + return "", nil } if isValidToken(tokens, auth) { @@ -125,22 +126,22 @@ var BodyAuth = AuthMethod { body.Write(req) - return true, nil + return auth, nil } - return false, errors.New("invalid Body token") + return "", errors.New("invalid Body token") }, } var QueryAuth = AuthMethod { Name: "Query", - Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) { + Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { const authQuery = "@authorization" auth := req.URL.Query().Get(authQuery) if strings.TrimSpace(auth) == "" { - return false, nil + return "", nil } if isValidToken(tokens, auth) { @@ -150,39 +151,39 @@ var QueryAuth = AuthMethod { req.URL.RawQuery = query.Encode() - return true, nil + return auth, nil } - return false, errors.New("invalid Query token") + return "", errors.New("invalid Query token") }, } var PathAuth = AuthMethod { Name: "Path", - Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) { + Authenticate: func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { parts := strings.Split(req.URL.Path, "/") if len(parts) == 0 { - return false, nil + return "", nil } unescaped, err := url.PathUnescape(parts[1]) if err != nil { - return false, nil + return "", nil } auth, exists := strings.CutPrefix(unescaped, "auth=") if !exists { - return false, nil + return "", nil } if isValidToken(tokens, auth) { - return true, nil + return auth, nil } - return false, errors.New("invalid Path token") + return "", errors.New("invalid Path token") }, } @@ -207,24 +208,26 @@ func authHandler(next http.Handler) http.Handler { return } - var authToken string - - success, _ := authChain.Eval(w, req, tokens) + token, _ := authChain.Eval(w, req, tokens) - if !success { - logger.Warn("User failed to provide auth") - w.Header().Set("WWW-Authenticate", "Basic realm=\"Login Required\", Bearer realm=\"Access Token Required\"") - http.Error(w, "Unauthorized", http.StatusUnauthorized) + if token == "" { + onUnauthorized(w) return } - ctx := context.WithValue(req.Context(), tokenKey, authToken) + ctx := context.WithValue(req.Context(), tokenKey, token) req = req.WithContext(ctx) next.ServeHTTP(w, req) }) } +func onUnauthorized(w http.ResponseWriter) { + w.Header().Set("WWW-Authenticate", "Basic realm=\"Login Required\", Bearer realm=\"Access Token Required\"") + + http.Error(w, "Unauthorized", http.StatusUnauthorized) +} + func isValidToken(tokens []string, match string) bool { return slices.Contains(tokens, match) } @@ -245,21 +248,21 @@ func (chain *AuthChain) Use(method AuthMethod) *AuthChain { return chain } -func (chain *AuthChain) Eval(w http.ResponseWriter, req *http.Request, tokens []string) (bool, error) { +func (chain *AuthChain) Eval(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { var err error - var success bool + var token string for _, method := range chain.methods { - success, err = method.Authenticate(w, req, tokens) + token, err = method.Authenticate(w, req, tokens) if err != nil { logger.Warn("User failed ", method.Name, " auth: ", err.Error()) } - if success { - return success, nil + if token != "" { + return token, nil } } - return false, err + return "", err } \ No newline at end of file diff --git a/internals/proxy/middlewares/common.go b/internals/proxy/middlewares/common.go index 1f2a9878..4cb4e6c1 100644 --- a/internals/proxy/middlewares/common.go +++ b/internals/proxy/middlewares/common.go @@ -13,8 +13,6 @@ type Context struct { type contextKey string -const tokenKey contextKey = "token" - func getConfigByReq(req *http.Request) *structure.CONFIG { token := req.Context().Value(tokenKey).(string) diff --git a/internals/proxy/middlewares/endpoints.go b/internals/proxy/middlewares/endpoints.go index bac6130a..363c1d5b 100644 --- a/internals/proxy/middlewares/endpoints.go +++ b/internals/proxy/middlewares/endpoints.go @@ -6,7 +6,7 @@ import ( "slices" "strings" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" ) var Endpoints Middleware = Middleware{ @@ -27,7 +27,7 @@ func endpointsHandler(next http.Handler) http.Handler { reqPath := req.URL.Path if isBlocked(reqPath, endpoints) { - log.Warn("User tried to access blocked endpoint: ", reqPath) + logger.Warn("User tried to access blocked endpoint: ", reqPath) http.Error(w, "Forbidden", http.StatusForbidden) return } diff --git a/internals/proxy/middlewares/log.go b/internals/proxy/middlewares/log.go index c786535f..edcfbd47 100644 --- a/internals/proxy/middlewares/log.go +++ b/internals/proxy/middlewares/log.go @@ -3,7 +3,7 @@ package middlewares import ( "net/http" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/gotl/pkg/request" ) @@ -14,15 +14,15 @@ var Logging Middleware = Middleware{ func loggingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if !log.IsDev() { - log.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) + if !logger.IsDev() { + logger.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) } else { body, _ := request.GetReqBody(req) if body.Data != nil && !body.Empty { - log.Dev(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery, body.Data) + logger.Dev(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery, body.Data) } else { - log.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) + logger.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery) } } diff --git a/internals/proxy/middlewares/mapping.go b/internals/proxy/middlewares/mapping.go index 0f92c4c7..74b1cba3 100644 --- a/internals/proxy/middlewares/mapping.go +++ b/internals/proxy/middlewares/mapping.go @@ -4,7 +4,7 @@ import ( "net/http" jsonutils "github.com/codeshelldev/gotl/pkg/jsonutils" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" request "github.com/codeshelldev/gotl/pkg/request" "github.com/codeshelldev/secured-signal-api/internals/config/structure" ) @@ -32,7 +32,7 @@ func mappingHandler(next http.Handler) http.Handler { body, err := request.GetReqBody(req) if err != nil { - log.Error("Could not get Request Body: ", err.Error()) + logger.Error("Could not get Request Body: ", err.Error()) http.Error(w, "Bad Request: invalid body", http.StatusBadRequest) } @@ -65,12 +65,12 @@ func mappingHandler(next http.Handler) http.Handler { err := body.Write(req) if err != nil { - log.Error("Could not write to Request Body: ", err.Error()) - http.Error(w, "Internal Error", http.StatusInternalServerError) + logger.Error("Could not write to Request Body: ", err.Error()) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } - log.Debug("Applied Data Aliasing: ", body.Data) + logger.Debug("Applied Data Aliasing: ", body.Data) } next.ServeHTTP(w, req) diff --git a/internals/proxy/middlewares/message.go b/internals/proxy/middlewares/message.go index 8af1a0b0..c5543074 100644 --- a/internals/proxy/middlewares/message.go +++ b/internals/proxy/middlewares/message.go @@ -3,7 +3,7 @@ package middlewares import ( "net/http" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" request "github.com/codeshelldev/gotl/pkg/request" ) @@ -30,7 +30,7 @@ func messageHandler(next http.Handler) http.Handler { body, err := request.GetReqBody(req) if err != nil { - log.Error("Could not get Request Body: ", err.Error()) + logger.Error("Could not get Request Body: ", err.Error()) http.Error(w, "Bad Request: invalid body", http.StatusBadRequest) } @@ -47,7 +47,7 @@ func messageHandler(next http.Handler) http.Handler { newData, err := TemplateMessage(messageTemplate, bodyData, headerData, variables) if err != nil { - log.Error("Error Templating Message: ", err.Error()) + logger.Error("Error Templating Message: ", err.Error()) } if newData["message"] != bodyData["message"] && newData["message"] != "" && newData["message"] != nil { @@ -63,12 +63,12 @@ func messageHandler(next http.Handler) http.Handler { err := body.Write(req) if err != nil { - log.Error("Could not write to Request Body: ", err.Error()) - http.Error(w, "Internal Error", http.StatusInternalServerError) + logger.Error("Could not write to Request Body: ", err.Error()) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } - log.Debug("Applied Message Templating: ", body.Data) + logger.Debug("Applied Message Templating: ", body.Data) } next.ServeHTTP(w, req) diff --git a/internals/proxy/middlewares/policy.go b/internals/proxy/middlewares/policy.go index 2415129b..9371696f 100644 --- a/internals/proxy/middlewares/policy.go +++ b/internals/proxy/middlewares/policy.go @@ -5,7 +5,7 @@ import ( "net/http" "reflect" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" request "github.com/codeshelldev/gotl/pkg/request" "github.com/codeshelldev/secured-signal-api/internals/config/structure" "github.com/codeshelldev/secured-signal-api/utils/requestkeys" @@ -29,7 +29,7 @@ func policyHandler(next http.Handler) http.Handler { body, err := request.GetReqBody(req) if err != nil { - log.Error("Could not get Request Body: ", err.Error()) + logger.Error("Could not get Request Body: ", err.Error()) http.Error(w, "Bad Request: invalid body", http.StatusBadRequest) } @@ -42,7 +42,7 @@ func policyHandler(next http.Handler) http.Handler { shouldBlock, field := doBlock(body.Data, headerData, policies) if shouldBlock { - log.Warn("User tried to use blocked field: ", field) + logger.Warn("User tried to use blocked field: ", field) http.Error(w, "Forbidden", http.StatusForbidden) return } diff --git a/internals/proxy/middlewares/port.go b/internals/proxy/middlewares/port.go new file mode 100644 index 00000000..f7f6151f --- /dev/null +++ b/internals/proxy/middlewares/port.go @@ -0,0 +1,54 @@ +package middlewares + +import ( + "errors" + "net" + "net/http" + "strings" + + "github.com/codeshelldev/gotl/pkg/logger" +) + +var Port Middleware = Middleware{ + Name: "Port", + Use: portHandler, +} + +func portHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + conf := getConfigByReq(req) + + allowedPort := conf.SERVICE.PORT + + if strings.TrimSpace(allowedPort) == "" { + next.ServeHTTP(w, req) + return + } + + port, err := getPort(req) + + if err != nil { + logger.Error("Could not get Port: ", err.Error()) + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + + if port != allowedPort { + logger.Warn("User tried using Token on wrong Port") + onUnauthorized(w) + return + } + }) +} + +func getPort(req *http.Request) (string, error) { + addr, ok := req.Context().Value(http.LocalAddrContextKey).(net.Addr) + + if !ok { + return "", errors.New("no local addr in context") + } + + _, port, err := net.SplitHostPort(addr.String()) + + return port, err +} \ No newline at end of file diff --git a/internals/proxy/middlewares/template.go b/internals/proxy/middlewares/template.go index eac13b5a..4577df46 100644 --- a/internals/proxy/middlewares/template.go +++ b/internals/proxy/middlewares/template.go @@ -8,7 +8,7 @@ import ( "strings" jsonutils "github.com/codeshelldev/gotl/pkg/jsonutils" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" query "github.com/codeshelldev/gotl/pkg/query" request "github.com/codeshelldev/gotl/pkg/request" templating "github.com/codeshelldev/gotl/pkg/templating" @@ -33,7 +33,7 @@ func templateHandler(next http.Handler) http.Handler { body, err := request.GetReqBody(req) if err != nil { - log.Error("Could not get Request Body: ", err.Error()) + logger.Error("Could not get Request Body: ", err.Error()) http.Error(w, "Bad Request: invalid body", http.StatusBadRequest) } @@ -49,7 +49,7 @@ func templateHandler(next http.Handler) http.Handler { bodyData, modified, err = TemplateBody(body.Data, headerData, variables) if err != nil { - log.Error("Error Templating JSON: ", err.Error()) + logger.Error("Error Templating JSON: ", err.Error()) } if modified { @@ -63,7 +63,7 @@ func templateHandler(next http.Handler) http.Handler { req.URL.RawQuery, bodyData, modified, err = TemplateQuery(req.URL, bodyData, variables) if err != nil { - log.Error("Error Templating Query: ", err.Error()) + logger.Error("Error Templating Query: ", err.Error()) } if modified { @@ -77,12 +77,12 @@ func templateHandler(next http.Handler) http.Handler { err := body.Write(req) if err != nil { - log.Error("Could not write to Request Body: ", err.Error()) - http.Error(w, "Internal Error", http.StatusInternalServerError) + logger.Error("Could not write to Request Body: ", err.Error()) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } - log.Debug("Applied Body Templating: ", body.Data) + logger.Debug("Applied Body Templating: ", body.Data) } if req.URL.Path != "" { @@ -91,11 +91,11 @@ func templateHandler(next http.Handler) http.Handler { req.URL.Path, modified, err = TemplatePath(req.URL, variables) if err != nil { - log.Error("Error Templating Path: ", err.Error()) + logger.Error("Error Templating Path: ", err.Error()) } if modified { - log.Debug("Applied Path Templating: ", req.URL.Path) + logger.Debug("Applied Path Templating: ", req.URL.Path) } } diff --git a/internals/proxy/proxy.go b/internals/proxy/proxy.go index 960930e2..c549e7d9 100644 --- a/internals/proxy/proxy.go +++ b/internals/proxy/proxy.go @@ -34,6 +34,7 @@ func (proxy Proxy) Init() http.Handler { Use(m.Logging). Use(m.Server). Use(m.Auth). + Use(m.Port). Use(m.Template). Use(m.Endpoints). Use(m.Mapping). diff --git a/internals/server/server.go b/internals/server/server.go new file mode 100644 index 00000000..680c0046 --- /dev/null +++ b/internals/server/server.go @@ -0,0 +1,127 @@ +package server + +import ( + "context" + "errors" + "net" + "net/http" + "sort" + "strconv" + "strings" + "sync" + + "github.com/codeshelldev/gotl/pkg/logger" +) + +type Server struct { + Host string + Ports []string + Handler http.Handler + Listeners map[string]*http.Server +} + +func Create(handler http.Handler, host string, ports ...string) *Server { + return &Server{ + Host: host, + Ports: ports, + Handler: handler, + Listeners: map[string]*http.Server{}, + } +} + +func (server *Server) ListenAndServer() { + var wg sync.WaitGroup + stopCh := make(chan struct{}) + + for _, port := range server.Ports { + addr := server.Host + ":" + port + listener, err := net.Listen("tcp", addr) + + if err != nil { + logger.Error("Error listening on ", port, ": ", err.Error()) + continue + } + + srv := &http.Server{ + Addr: server.Host + ":" + port, + Handler: server.Handler, + } + + wg.Add(1) + + go func(s *http.Server, l net.Listener, p string) { + defer wg.Done() + + logger.Debug("Listener on port ", port, " started") + + server.Listeners[port] = s + + err := s.Serve(l) + + if err != nil && err != http.ErrServerClosed { + logger.Error("Listener on port ", port, " exited with ", err.Error()) + } + }(srv, listener, port) + } + + go func() { + wg.Wait() + close(stopCh) + }() + + <- stopCh +} + +func (server *Server) Shutdown(ctx context.Context) error { + var errs []error + + for port, s := range server.Listeners { + logger.Debug("Shutting down listener on ", port) + + err := s.Shutdown(ctx) + + if err != nil { + errs = append(errs, err) + } + } + + return errors.Join(errs...) +} + +func PortsToRangeString(ports []string) string { + if len(ports) == 0 { + return "" + } + + sort.Strings(ports) + + result := []string{} + + end, _ := strconv.Atoi(ports[0]) + start, _ := strconv.Atoi(ports[0]) + + for i := 1; i < len(ports); i++ { + port, _ := strconv.Atoi(ports[i]) + + if port == end + 1 { + end = port + } else { + if start == end { + result = append(result, strconv.Itoa(start)) + } else { + result = append(result, strconv.Itoa(start) + "-" + strconv.Itoa(end)) + } + + start = port + end = port + } + } + + if start == end { + result = append(result, strconv.Itoa(start)) + } else { + result = append(result, strconv.Itoa(start) + "-" + strconv.Itoa(end)) + } + + return strings.Join(result, ",") +} \ No newline at end of file diff --git a/main.go b/main.go index 7ba52a94..5d3cbfb4 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,14 @@ package main import ( - "net/http" "os" + "slices" + "strings" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" config "github.com/codeshelldev/secured-signal-api/internals/config" reverseProxy "github.com/codeshelldev/secured-signal-api/internals/proxy" + httpServer "github.com/codeshelldev/secured-signal-api/internals/server" docker "github.com/codeshelldev/secured-signal-api/utils/docker" ) @@ -15,19 +17,19 @@ var proxy reverseProxy.Proxy func main() { logLevel := os.Getenv("LOG_LEVEL") - log.Init(logLevel) + logger.Init(logLevel) docker.Init() config.Load() - if config.DEFAULT.SERVICE.LOG_LEVEL != log.Level() { - log.Init(config.DEFAULT.SERVICE.LOG_LEVEL) + if config.DEFAULT.SERVICE.LOG_LEVEL != logger.Level() { + logger.Init(config.DEFAULT.SERVICE.LOG_LEVEL) } - log.Info("Initialized Logger with Level of ", log.Level()) + logger.Info("Initialized Logger with Level of ", logger.Level()) - log.Info(` + logger.Info(` ┌────────────────────────────────────────────────┐ │  🎄 Happy Holidays! 🎄  │ @@ -42,9 +44,9 @@ func main() { └────────────────────────────────────────────────┘ `) - if log.Level() == "dev" { - log.Dev("Welcome back Developer!") - log.Dev("CTRL+S config to Print to Console") + if logger.Level() == "dev" { + logger.Dev("Welcome back Developer!") + logger.Dev("CTRL+S config to Print to Console") } config.Log() @@ -53,23 +55,28 @@ func main() { handler := proxy.Init() - log.Info("Initialized Middlewares") + logger.Info("Initialized Middlewares") - addr := "0.0.0.0:" + config.DEFAULT.SERVICE.PORT + ports := []string{} - log.Info("Server Listening on ", addr) + for _, config := range config.ENV.CONFIGS { + port := strings.TrimSpace(config.SERVICE.PORT) - server := &http.Server{ - Addr: addr, - Handler: handler, + if port != "" && !slices.Contains(ports, port) { + ports = append(ports, port) + } } - stop := docker.Run(func() { - err := server.ListenAndServe() + server := httpServer.Create(handler, "0.0.0.0", ports...) - if err != nil && err != http.ErrServerClosed { - log.Fatal("Server error: ", err.Error()) + stop := docker.Run(func() { + if logger.IsDebug() && len(ports) > 1 { + logger.Debug("Server started with ", len(ports), " listeners on ", httpServer.PortsToRangeString(ports)) + } else { + logger.Info("Server listening on ", httpServer.PortsToRangeString(ports)) } + + server.ListenAndServer() }) <-stop diff --git a/utils/docker/docker.go b/utils/docker/docker.go index 21b4f2ff..2f3de3db 100644 --- a/utils/docker/docker.go +++ b/utils/docker/docker.go @@ -2,16 +2,16 @@ package docker import ( "context" - "net/http" "os" "time" "github.com/codeshelldev/gotl/pkg/docker" - log "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/gotl/pkg/logger" + "github.com/codeshelldev/secured-signal-api/internals/server" ) func Init() { - log.Info("Running ", os.Getenv("IMAGE_TAG"), " Image") + logger.Info("Running ", os.Getenv("IMAGE_TAG"), " Image") } func Run(main func()) chan os.Signal { @@ -19,15 +19,15 @@ func Run(main func()) chan os.Signal { } func Exit(code int) { - log.Info("Exiting...") + logger.Info("Exiting...") docker.Exit(code) } -func Shutdown(server *http.Server) { - log.Info("Shutdown signal received") +func Shutdown(server *server.Server) { + logger.Info("Shutdown signal received") - log.Sync() + logger.Sync() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -35,10 +35,10 @@ func Shutdown(server *http.Server) { err := server.Shutdown(ctx) if err != nil { - log.Fatal("Server shutdown failed: ", err.Error()) + logger.Fatal("Server shutdown failed: ", err.Error()) - log.Info("Server exited forcefully") + logger.Info("Server exited forcefully") } else { - log.Info("Server exited gracefully") + logger.Info("Server exited gracefully") } }