fiber.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package fiber
  2. import (
  3. "html/template"
  4. "log"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/dmitriygnatenko/internal/interfaces"
  9. "github.com/dmitriygnatenko/internal/services/handler"
  10. adminHandler "github.com/dmitriygnatenko/internal/services/handler/admin"
  11. "github.com/gofiber/fiber/v2"
  12. "github.com/gofiber/fiber/v2/middleware/basicauth"
  13. "github.com/gofiber/fiber/v2/middleware/cors"
  14. "github.com/gofiber/fiber/v2/middleware/monitor"
  15. "github.com/gofiber/fiber/v2/middleware/recover"
  16. jwt "github.com/gofiber/jwt/v3"
  17. "github.com/gofiber/template/html"
  18. )
  19. const (
  20. appName = "dmitriygnatenko"
  21. templatesPath = "./../../internal/templates"
  22. staticPath = "../../web"
  23. metricsURI = "/metrics"
  24. )
  25. func Init(sp interfaces.ServiceProvider) (*fiber.App, error) {
  26. fiberApp := fiber.New(getConfig(sp))
  27. // Configure web root
  28. fiberApp.Static("/", staticPath)
  29. // Configure CORS middleware
  30. fiberApp.Use(cors.New(getCORSConfig(sp)))
  31. // Configure recover middleware
  32. fiberApp.Use(recover.New())
  33. // Configure JWT middleware
  34. fiberApp.Use(jwt.New(getJWTConfig(sp)))
  35. // Configure Basic auth
  36. basicAuth := basicauth.New(basicauth.Config{
  37. Users: map[string]string{
  38. sp.GetEnvService().GetBasicAuthUser(): sp.GetEnvService().GetBasicAuthPassword(),
  39. },
  40. })
  41. // Metrics
  42. fiberApp.Get(metricsURI, basicAuth, monitor.New(getMetricsConfig()))
  43. // Public handlers
  44. fiberApp.Get("/", handler.MainPageHandler(sp))
  45. fiberApp.Get("/tag/:tag", handler.TagHandler(sp))
  46. fiberApp.Get("/article/:article", handler.ArticleHandler(sp))
  47. // Protected handlers
  48. admin := fiberApp.Group("/admin", basicAuth)
  49. admin.Get("/", adminHandler.ArticleHandler(sp))
  50. admin.All("/article/add", adminHandler.AddArticleHandler(sp))
  51. admin.All("/article/edit/:id<int>", adminHandler.EditArticleHandler(sp))
  52. admin.All("/article/delete/:id<int>", adminHandler.DeleteArticleHandler(sp))
  53. admin.Get("/tag", adminHandler.TagHandler(sp))
  54. admin.All("/tag/add", adminHandler.AddTagHandler(sp))
  55. admin.All("/tag/edit/:id<int>", adminHandler.EditTagHandler(sp))
  56. admin.All("/tag/delete/:id<int>", adminHandler.DeleteTagHandler(sp))
  57. return fiberApp, nil
  58. }
  59. func getConfig(sp interfaces.ServiceProvider) fiber.Config {
  60. return fiber.Config{
  61. AppName: appName,
  62. DisableStartupMessage: true,
  63. Views: getViewsEngine(sp.GetEnvService()),
  64. ErrorHandler: getErrorHandler(sp),
  65. }
  66. }
  67. // nolint
  68. func getJWTConfig(sp interfaces.ServiceProvider) jwt.Config {
  69. return jwt.Config{
  70. SigningKey: []byte(sp.GetEnvService().GetJWTSecretKey()),
  71. ErrorHandler: func(fctx *fiber.Ctx, err error) error {
  72. return fiber.NewError(fiber.StatusForbidden, err.Error())
  73. },
  74. Filter: func(fctx *fiber.Ctx) bool {
  75. method := fctx.Method()
  76. path := fctx.Path()
  77. if method != fiber.MethodGet && method != fiber.MethodPost &&
  78. method != fiber.MethodPut && method != fiber.MethodDelete {
  79. return true
  80. }
  81. if strings.HasPrefix(path, "/api/") && path != "/api/v1/auth/login" {
  82. return false
  83. }
  84. return true
  85. },
  86. }
  87. }
  88. func getMetricsConfig() monitor.Config {
  89. return monitor.Config{
  90. Title: "dmitriygnatenko.ru metrics",
  91. }
  92. }
  93. func getCORSConfig(sp interfaces.ServiceProvider) cors.Config {
  94. return cors.Config{
  95. AllowOrigins: sp.GetEnvService().GetCORSAllowOrigins(),
  96. AllowMethods: sp.GetEnvService().GetCORSAllowMethods(),
  97. }
  98. }
  99. func getViewsEngine(env interfaces.Env) *html.Engine {
  100. engine := html.New(templatesPath, ".html")
  101. // nolint:gocritic
  102. engine.AddFunc("now", func() time.Time {
  103. return time.Now()
  104. })
  105. // nolint:gosec
  106. engine.AddFunc("noescape", func(str string) template.HTML {
  107. return template.HTML(str)
  108. })
  109. engine.AddFunc("gridsep", func(i, l int) bool {
  110. i++
  111. return i%3 == 0 && i != l
  112. })
  113. engine.AddFunc("version", func() string {
  114. return strconv.Itoa(env.GetStaticVersion())
  115. })
  116. engine.AddFunc("ga", func() string {
  117. return env.GetGAKey()
  118. })
  119. return engine
  120. }
  121. func getErrorHandler(sp interfaces.ServiceProvider) fiber.ErrorHandler {
  122. return func(fctx *fiber.Ctx, err error) error {
  123. errCode := fiber.StatusInternalServerError
  124. if e, ok := err.(*fiber.Error); ok {
  125. errCode = e.Code
  126. }
  127. if err.Error() != "" {
  128. errorsEmail := sp.GetEnvService().GetErrorsEmail()
  129. if errCode == fiber.StatusInternalServerError && errorsEmail != "" {
  130. log.Println(err)
  131. // nolint
  132. sp.GetMailerService().Send(
  133. errorsEmail,
  134. "AUTO - dmitriygnatenko.ru error",
  135. err.Error(),
  136. )
  137. }
  138. }
  139. var renderData fiber.Map
  140. if errCode == fiber.StatusNotFound {
  141. renderData = fiber.Map{
  142. "pageTitle": "Страница не найдена",
  143. "code": fiber.StatusNotFound,
  144. "text": "Запрашиваемая вами страница не найдена",
  145. }
  146. } else {
  147. renderData = fiber.Map{
  148. "pageTitle": "Внутренняя ошибка",
  149. "code": fiber.StatusInternalServerError,
  150. "text": "Внутренняя ошибка сервера, идем исправлять...",
  151. }
  152. }
  153. renderData["headTitle"] = "От слона к суслику - статьи про PHP, Go, алгоритмы"
  154. return fctx.Render("error", renderData, "_layout")
  155. }
  156. }