fiber.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package fiber
  2. import (
  3. "log"
  4. _ "git.dmitriygnatenko.ru/dima/homethings/docs" //nolint
  5. authAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/auth"
  6. imageAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/image"
  7. notificationAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/notification"
  8. placeAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/place"
  9. tagAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag"
  10. thingAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing"
  11. userAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/user"
  12. "git.dmitriygnatenko.ru/dima/homethings/internal/factory"
  13. "git.dmitriygnatenko.ru/dima/homethings/internal/interfaces"
  14. "git.dmitriygnatenko.ru/dima/homethings/internal/middleware/timezone"
  15. "github.com/gofiber/fiber/v2"
  16. "github.com/gofiber/fiber/v2/middleware/basicauth"
  17. "github.com/gofiber/fiber/v2/middleware/cors"
  18. "github.com/gofiber/fiber/v2/middleware/monitor"
  19. "github.com/gofiber/fiber/v2/middleware/recover"
  20. jwt "github.com/gofiber/jwt/v3"
  21. "github.com/gofiber/swagger"
  22. )
  23. const (
  24. staticPath = "../../web/public"
  25. swaggerURI = "/docs/*"
  26. metricsURI = "/metrics"
  27. )
  28. func Init(sp interfaces.ServiceProvider) (*fiber.App, error) {
  29. fiberApp := fiber.New(getFiberConfig(sp))
  30. // Configure web root
  31. fiberApp.Static("/", staticPath)
  32. // Configure CORS middleware
  33. fiberApp.Use(cors.New(getCORSConfig(sp)))
  34. // Configure recover middleware
  35. fiberApp.Use(recover.New())
  36. // Configure client timezone middleware
  37. fiberApp.Use(timezone.New())
  38. // Configure JWT middleware
  39. jwtAuth := jwt.New(getJWTConfig(sp))
  40. // Configure Basic auth
  41. basicAuth := basicauth.New(basicauth.Config{
  42. Users: map[string]string{
  43. sp.GetEnvService().GetBasicAuthUser(): sp.GetEnvService().GetBasicAuthPassword(),
  44. },
  45. })
  46. // Swagger
  47. fiberApp.Get(swaggerURI, swagger.HandlerDefault)
  48. // Metrics
  49. fiberApp.Get(metricsURI, basicAuth, monitor.New(getMetricsConfig()))
  50. // API
  51. api := fiberApp.Group("/api", jwtAuth)
  52. registerHandlers(api, sp)
  53. return fiberApp, nil
  54. }
  55. func getFiberConfig(sp interfaces.ServiceProvider) fiber.Config {
  56. return fiber.Config{
  57. AppName: "Homethings",
  58. DisableStartupMessage: true,
  59. ErrorHandler: getErrorHandler(sp),
  60. }
  61. }
  62. func getErrorHandler(sp interfaces.ServiceProvider) fiber.ErrorHandler {
  63. return func(fctx *fiber.Ctx, err error) error {
  64. errCode := fiber.StatusInternalServerError
  65. if e, ok := err.(*fiber.Error); ok {
  66. errCode = e.Code
  67. }
  68. if err.Error() != "" {
  69. errorsEmail := sp.GetEnvService().GetErrorsEmail()
  70. if errCode == fiber.StatusInternalServerError && errorsEmail != "" {
  71. log.Println(err)
  72. // nolint
  73. sp.GetMailerService().Send(
  74. errorsEmail,
  75. "AUTO - Homethings error",
  76. err.Error(),
  77. )
  78. }
  79. return fctx.Status(errCode).JSON(factory.CreateErrorResponse(err))
  80. }
  81. return fctx.Status(errCode).JSON(factory.CreateEmptyResponse())
  82. }
  83. }
  84. // nolint
  85. func getJWTConfig(sp interfaces.ServiceProvider) jwt.Config {
  86. return jwt.Config{
  87. SigningKey: []byte(sp.GetEnvService().GetJWTSecretKey()),
  88. ErrorHandler: func(fctx *fiber.Ctx, err error) error {
  89. return fiber.NewError(fiber.StatusForbidden, err.Error())
  90. },
  91. Filter: func(fctx *fiber.Ctx) bool {
  92. method := fctx.Method()
  93. path := fctx.Path()
  94. if method != fiber.MethodGet && method != fiber.MethodPost &&
  95. method != fiber.MethodPut && method != fiber.MethodDelete {
  96. return true
  97. }
  98. if path == "/api/v1/auth/login" {
  99. return true
  100. }
  101. return false
  102. },
  103. }
  104. }
  105. func getMetricsConfig() monitor.Config {
  106. return monitor.Config{
  107. Title: "Homethings metrics",
  108. }
  109. }
  110. func getCORSConfig(sp interfaces.ServiceProvider) cors.Config {
  111. return cors.Config{
  112. AllowOrigins: sp.GetEnvService().GetCORSAllowOrigins(),
  113. AllowMethods: sp.GetEnvService().GetCORSAllowMethods(),
  114. }
  115. }
  116. func registerHandlers(r fiber.Router, sp interfaces.ServiceProvider) {
  117. // Public routes
  118. r.Post("/v1/auth/login", authAPI.LoginHandler(sp))
  119. // Protected routes
  120. r.Get("/v1/auth/check", authAPI.CheckAuthHandler(sp))
  121. r.Get("/v1/places", placeAPI.GetPlacesHandler(sp))
  122. r.Get("/v1/places/tree", placeAPI.GetPlaceTreeHandler(sp))
  123. r.Get("/v1/places/:placeId<int>", placeAPI.GetPlaceHandler(sp))
  124. r.Get("/v1/places/:parentPlaceId<int>/nested", placeAPI.GetNestedPlacesHandler(sp))
  125. r.Post("/v1/places", placeAPI.AddPlaceHandler(sp))
  126. r.Put("/v1/places/:placeId<int>", placeAPI.UpdatePlaceHandler(sp))
  127. r.Delete("/v1/places/:placeId<int>", placeAPI.DeletePlaceHandler(sp))
  128. r.Get("/v1/things/:thingId<int>", thingAPI.GetThingHandler(sp))
  129. r.Get("/v1/things/search/:search", thingAPI.SearchThingHandler(sp))
  130. r.Get("/v1/things/place/:placeId<int>", thingAPI.GetPlaceThingsHandler(sp))
  131. r.Post("/v1/things", thingAPI.AddThingHandler(sp))
  132. r.Put("/v1/things/:thingId<int>", thingAPI.UpdateThingHandler(sp))
  133. r.Delete("/v1/things/:thingId<int>", thingAPI.DeleteThingHandler(sp))
  134. r.Get("/v1/images/place/:placeId<int>", imageAPI.GetPlaceImagesHandler(sp))
  135. r.Get("/v1/images/thing/:thingId<int>", imageAPI.GetThingImagesHandler(sp))
  136. r.Post("/v1/images", imageAPI.AddImageHandler(sp))
  137. r.Delete("/v1/images/place/:imageId<int>", imageAPI.DeletePlaceImageHandler(sp))
  138. r.Delete("/v1/images/thing/:imageId<int>", imageAPI.DeleteThingImageHandler(sp))
  139. r.Get("/v1/tags", tagAPI.GetTagsHandler(sp))
  140. r.Get("/v1/tags/:tagId<int>", tagAPI.GetTagHandler(sp))
  141. r.Get("/v1/tags/thing/:thingId<int>", tagAPI.GetThingTagsHandler(sp))
  142. r.Post("/v1/tags", tagAPI.AddTagHandler(sp))
  143. r.Post("/v1/tags/:tagId<int>/thing/:thingId<int>", tagAPI.AddThingTagHandler(sp))
  144. r.Put("/v1/tags/:tagId<int>", tagAPI.UpdateTagHandler(sp))
  145. r.Delete("/v1/tags/:tagId<int>", tagAPI.DeleteTagHandler(sp))
  146. r.Delete("/v1/tags/:tagId<int>/thing/:thingId<int>", tagAPI.DeleteThingTagHandler(sp))
  147. r.Get("/v1/tags/:id<int>", tagAPI.GetTagHandler(sp))
  148. r.Post("/v1/tags", tagAPI.AddTagHandler(sp))
  149. r.Put("/v1/tags/:id<int>", tagAPI.UpdateTagHandler(sp))
  150. r.Delete("/v1/tags/:id<int>", tagAPI.DeleteTagHandler(sp))
  151. r.Get("/v1/things/notifications/:thingId<int>", notificationAPI.GetThingNotificationHandler(sp))
  152. r.Get("/v1/things/notifications/expired", notificationAPI.GetExpiredThingNotificationsHandler(sp))
  153. r.Post("/v1/things/notifications", notificationAPI.AddThingNotificationHandler(sp))
  154. r.Put("/v1/things/notifications/:thingId<int>", notificationAPI.UpdateThingNotificationHandler(sp))
  155. r.Delete("/v1/things/notifications/:thingId<int>", notificationAPI.DeleteThingNotificationHandler(sp))
  156. r.Post("/v1/users", userAPI.AddUserHandler(sp))
  157. r.Put("/v1/users", userAPI.UpdateUserHandler(sp))
  158. }