github.com/abolfazlbeh/zhycan@v0.0.0-20230819144214-24cf38237387/internal/http/fiber_wrapper.go (about)

     1  package http
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"github.com/abolfazlbeh/zhycan/internal/config"
     7  	"github.com/abolfazlbeh/zhycan/internal/utils"
     8  	"github.com/gofiber/fiber/v2"
     9  	"github.com/gofiber/fiber/v2/middleware/favicon"
    10  	"github.com/gofiber/fiber/v2/middleware/logger"
    11  	"os"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  // Mark: Definitions
    17  
    18  // Server struct
    19  type Server struct {
    20  	name                  string
    21  	config                ServerConfig
    22  	app                   *fiber.App
    23  	versionGroups         map[string]fiber.Router
    24  	groups                map[string]fiber.Router
    25  	supportedMiddlewares  []string
    26  	defaultRequestMethods []string
    27  }
    28  
    29  // init - Server Constructor - It initializes the server
    30  func (s *Server) init(name string, serverConfig ServerConfig) error {
    31  	s.name = name
    32  	s.config = serverConfig
    33  
    34  	// Get application name from the config manager
    35  	appName := config.GetManager().GetName()
    36  	requestMethods := fiber.DefaultMethods
    37  	if s.config.Config.RequestMethods != nil {
    38  		if s.config.Config.RequestMethods[0] != "ALL" {
    39  			requestMethods = s.config.Config.RequestMethods
    40  		}
    41  	}
    42  	s.defaultRequestMethods = requestMethods
    43  
    44  	s.app = fiber.New(fiber.Config{
    45  		Prefork:        false,
    46  		ServerHeader:   "Zhycan",
    47  		AppName:        appName,
    48  		RequestMethods: requestMethods,
    49  	})
    50  
    51  	if s.config.SupportStatic == true {
    52  		s.setupStatic()
    53  	}
    54  
    55  	s.groups = make(map[string]fiber.Router)
    56  	s.supportedMiddlewares = []string{
    57  		"logger",
    58  		"favicon",
    59  	}
    60  	return nil
    61  }
    62  
    63  func (s *Server) createVersionGroups(versions []string) {
    64  	s.versionGroups = make(map[string]fiber.Router)
    65  	for _, item := range versions {
    66  		s.versionGroups[item] = s.app.Group(item)
    67  	}
    68  }
    69  
    70  func (s *Server) attachMiddlewares(orders []string) {
    71  	for _, item := range orders {
    72  		if utils.ArrayContains(&s.supportedMiddlewares, item) {
    73  			switch item {
    74  			case "logger":
    75  				key := fmt.Sprintf("middlewares.%s", item)
    76  				// read config
    77  				loggerCfg, err := config.GetManager().Get(s.name, key)
    78  				if err == nil {
    79  					jsonBody, err2 := json.Marshal(loggerCfg.(interface{}))
    80  					if err2 == nil {
    81  						var obj LoggerMiddlewareConfig
    82  						err := json.Unmarshal(jsonBody, &obj)
    83  						if err == nil {
    84  							// Everything is ok and let's go define logger config
    85  							loggerMiddlewareCfg := logger.Config{
    86  								Next:         nil,
    87  								Done:         nil,
    88  								Format:       obj.Format,
    89  								TimeFormat:   obj.TimeFormat,
    90  								TimeZone:     obj.TimeZone,
    91  								TimeInterval: time.Duration(obj.TimeInterval) * time.Millisecond,
    92  							}
    93  							if obj.Output == "stdout" {
    94  								loggerMiddlewareCfg.Output = os.Stdout
    95  							}
    96  							s.app.Use(logger.New(loggerMiddlewareCfg))
    97  							break
    98  						}
    99  					}
   100  				}
   101  				s.app.Use(logger.New())
   102  
   103  			case "favicon":
   104  				key := fmt.Sprintf("middlewares.%s", item)
   105  				// read config
   106  				loggerCfg, err := config.GetManager().Get(s.name, key)
   107  				if err == nil {
   108  					jsonBody, err2 := json.Marshal(loggerCfg.(interface{}))
   109  					if err2 == nil {
   110  						var obj FaviconMiddlewareConfig
   111  						err := json.Unmarshal(jsonBody, &obj)
   112  						if err != nil {
   113  							faviconMiddlewareCfg := favicon.Config{
   114  								File:         obj.File,
   115  								URL:          obj.URL,
   116  								CacheControl: obj.CacheControl,
   117  							}
   118  							s.app.Use(favicon.New(faviconMiddlewareCfg))
   119  							break
   120  						}
   121  					}
   122  				}
   123  				s.app.Use(favicon.New())
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  func (s *Server) addGroup(keyName string, groupName string, router fiber.Router, f func(c *fiber.Ctx) error) {
   130  	if f == nil {
   131  		s.groups[keyName] = router.Group(groupName)
   132  	} else {
   133  		s.groups[keyName] = router.Group(groupName, f)
   134  	}
   135  }
   136  
   137  func (s *Server) setupStatic() {
   138  	s.app.Static(s.config.Static.Prefix, s.config.Static.Root, s.config.Static.Config)
   139  }
   140  
   141  // MARK: Public functions
   142  
   143  // NewServer - create a new instance of Server and return it
   144  func NewServer(name string, config ServerConfig) (*Server, error) {
   145  	server := &Server{}
   146  	err := server.init(name, config)
   147  	if err != nil {
   148  		return nil, NewCreateServerErr(err)
   149  	}
   150  
   151  	server.attachMiddlewares(config.Middlewares.Order)
   152  	server.createVersionGroups(config.Versions)
   153  	return server, nil
   154  }
   155  
   156  // Start - start the server and listen to provided address
   157  func (s *Server) Start() error {
   158  	err := s.app.Listen(s.config.ListenAddress)
   159  	if err != nil {
   160  		return NewStartServerErr(s.config.ListenAddress, err)
   161  	}
   162  	return nil
   163  }
   164  
   165  // Stop - stop the server
   166  func (s *Server) Stop() error {
   167  	err := s.app.Shutdown()
   168  	if err != nil {
   169  		return NewShutdownServerErr(err)
   170  	}
   171  	return nil
   172  }
   173  
   174  // AttachErrorHandler - attach a custom error handler to the server
   175  func (s *Server) AttachErrorHandler(f func(ctx *fiber.Ctx, err error) error) {
   176  	oldConfig := s.app.Config()
   177  	oldConfig.ErrorHandler = f
   178  	s.app = fiber.New(oldConfig)
   179  }
   180  
   181  // AddRoute - add a route to the server
   182  func (s *Server) AddRoute(method string, path string, f func(c *fiber.Ctx) error, routeName string, versions []string, groups []string) error {
   183  	// check that whether is acceptable to add this route method
   184  	if utils.ArrayContains(&s.defaultRequestMethods, method) {
   185  		groupsExist := false
   186  		if groups != nil {
   187  			if len(groups) > 0 {
   188  				groupsExist = true
   189  			}
   190  		}
   191  
   192  		versionsExist := false
   193  		if versions != nil {
   194  			if len(versions) > 0 {
   195  				versionsExist = true
   196  			}
   197  		}
   198  
   199  		if groupsExist {
   200  			for _, g := range groups {
   201  				if versionsExist {
   202  					for _, v := range versions {
   203  						if v == "all" {
   204  							for k := range s.versionGroups {
   205  								newKey := fmt.Sprintf("%s.%s", k, g)
   206  								if router, ok := s.groups[newKey]; ok {
   207  									router.Add(method, path, f)
   208  									if strings.TrimSpace(routeName) != "" {
   209  										//router.Name(routeName)
   210  										s.app.Name(routeName)
   211  									}
   212  								}
   213  							}
   214  							break
   215  						} else if v == "" {
   216  							if router, ok := s.groups[g]; ok {
   217  								router.Add(method, path, f)
   218  								if strings.TrimSpace(routeName) != "" {
   219  									//router.Name(routeName)
   220  									s.app.Name(routeName)
   221  								}
   222  							}
   223  							break
   224  						} else {
   225  							newKey := fmt.Sprintf("%s.%s", v, g)
   226  							if router, ok := s.groups[newKey]; ok {
   227  								router.Add(method, path, f)
   228  								if strings.TrimSpace(routeName) != "" {
   229  									s.app.Name(routeName)
   230  									//router.Name(routeName)
   231  								}
   232  							}
   233  						}
   234  					}
   235  				} else {
   236  					if savedGroup, ok := s.groups[g]; ok {
   237  						savedGroup.Add(method, path, f)
   238  						if strings.TrimSpace(routeName) != "" {
   239  							//savedGroup.Name(routeName)
   240  							s.app.Name(routeName)
   241  
   242  						}
   243  					}
   244  				}
   245  			}
   246  		} else {
   247  			if versionsExist {
   248  				for _, v := range versions {
   249  					if router, ok := s.versionGroups[v]; ok {
   250  						router.Add(method, path, f)
   251  						if strings.TrimSpace(routeName) != "" {
   252  							//router.Name(routeName)
   253  							s.app.Name(routeName)
   254  						}
   255  					} else {
   256  						if v == "all" {
   257  							for _, router1 := range s.versionGroups {
   258  								router1.Add(method, path, f)
   259  								if strings.TrimSpace(routeName) != "" {
   260  									//router1.Name(routeName)
   261  									s.app.Name(routeName)
   262  								}
   263  							}
   264  							break
   265  						} else if v == "" {
   266  							s.app.Add(method, path, f)
   267  							if strings.TrimSpace(routeName) != "" {
   268  								s.app.Name(routeName)
   269  							}
   270  							break
   271  						}
   272  					}
   273  				}
   274  			} else {
   275  				s.app.Add(method, path, f)
   276  				if strings.TrimSpace(routeName) != "" {
   277  					s.app.Name(routeName)
   278  				}
   279  			}
   280  		}
   281  		return nil
   282  	}
   283  
   284  	return NewNotSupportedHttpMethodErr(method)
   285  }
   286  
   287  // AddGroup - add a group to the server
   288  func (s *Server) AddGroup(groupName string, f func(c *fiber.Ctx) error, groups ...string) error {
   289  	if len(groups) > 0 {
   290  		for _, g := range groups {
   291  			for key := range s.versionGroups {
   292  				gKey := fmt.Sprintf("%s.%s", key, g)
   293  				if r, ok := s.groups[gKey]; ok {
   294  					newKey := fmt.Sprintf("%s.%s.%s", key, g, groupName)
   295  					s.addGroup(newKey, groupName, r, f)
   296  				} else {
   297  					return NewGroupRouteNotExistErr(gKey)
   298  				}
   299  			}
   300  
   301  			newKey := fmt.Sprintf("%s.%s", g, groupName)
   302  			s.addGroup(newKey, groupName, s.app, f)
   303  		}
   304  	} else {
   305  		for key, item := range s.versionGroups {
   306  			newKey := fmt.Sprintf("%s.%s", key, groupName)
   307  			s.addGroup(newKey, groupName, item, f)
   308  		}
   309  
   310  		s.addGroup(groupName, groupName, s.app, f)
   311  	}
   312  
   313  	return nil
   314  }
   315  
   316  // GetRouteByName _ get route by its name
   317  func (s *Server) GetRouteByName(name string) (*fiber.Route, error) {
   318  	route := s.app.GetRoute(name)
   319  	if route.Name != name {
   320  		return nil, NewGetRouteByNameErr(name)
   321  	}
   322  	return &route, nil
   323  }
   324  
   325  // AddRouteWithMultiHandlers - add a route to the server
   326  func (s *Server) AddRouteWithMultiHandlers(method string, path string, f []func(c *fiber.Ctx) error, routeName string, versions []string, groups []string) error {
   327  	// check that whether is acceptable to add this route method
   328  	if utils.ArrayContains(&s.defaultRequestMethods, method) {
   329  		if len(groups) > 0 {
   330  			for _, g := range groups {
   331  				if len(versions) > 0 {
   332  					for _, v := range versions {
   333  						if v == "all" {
   334  							for k := range s.versionGroups {
   335  								newKey := fmt.Sprintf("%s.%s", k, g)
   336  								if router, ok := s.groups[newKey]; ok {
   337  									router.Add(method, path, f...)
   338  									if strings.TrimSpace(routeName) != "" {
   339  										//router.Name(routeName)
   340  										s.app.Name(routeName)
   341  									}
   342  								}
   343  							}
   344  							break
   345  						} else if v == "" {
   346  							if router, ok := s.groups[g]; ok {
   347  								router.Add(method, path, f...)
   348  								if strings.TrimSpace(routeName) != "" {
   349  									//router.Name(routeName)
   350  									s.app.Name(routeName)
   351  								}
   352  							}
   353  							break
   354  						} else {
   355  							newKey := fmt.Sprintf("%s.%s", v, g)
   356  							if router, ok := s.groups[newKey]; ok {
   357  								router.Add(method, path, f...)
   358  								if strings.TrimSpace(routeName) != "" {
   359  									s.app.Name(routeName)
   360  									//router.Name(routeName)
   361  								}
   362  							}
   363  						}
   364  					}
   365  				} else {
   366  					if savedGroup, ok := s.groups[g]; ok {
   367  						savedGroup.Add(method, path, f...)
   368  						if strings.TrimSpace(routeName) != "" {
   369  							//savedGroup.Name(routeName)
   370  							s.app.Name(routeName)
   371  
   372  						}
   373  					}
   374  				}
   375  			}
   376  		} else {
   377  			if len(versions) > 0 {
   378  				for _, v := range versions {
   379  					if router, ok := s.versionGroups[v]; ok {
   380  						router.Add(method, path, f...)
   381  						if strings.TrimSpace(routeName) != "" {
   382  							//router.Name(routeName)
   383  							s.app.Name(routeName)
   384  						}
   385  					} else {
   386  						if v == "all" {
   387  							for _, router1 := range s.versionGroups {
   388  								router1.Add(method, path, f...)
   389  								if strings.TrimSpace(routeName) != "" {
   390  									//router1.Name(routeName)
   391  									s.app.Name(routeName)
   392  								}
   393  							}
   394  							break
   395  						} else if v == "" {
   396  							s.app.Add(method, path, f...)
   397  							if strings.TrimSpace(routeName) != "" {
   398  								s.app.Name(routeName)
   399  							}
   400  							break
   401  						}
   402  					}
   403  				}
   404  			} else {
   405  				s.app.Add(method, path, f...)
   406  				if strings.TrimSpace(routeName) != "" {
   407  					s.app.Name(routeName)
   408  				}
   409  			}
   410  		}
   411  		return nil
   412  	}
   413  
   414  	return NewNotSupportedHttpMethodErr(method)
   415  }