fiber.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. package fiber
  2. import (
  3. "errors"
  4. "log"
  5. "github.com/gofiber/fiber/v2"
  6. "github.com/gofiber/fiber/v2/middleware/basicauth"
  7. "github.com/gofiber/fiber/v2/middleware/cors"
  8. "github.com/gofiber/fiber/v2/middleware/monitor"
  9. "github.com/gofiber/fiber/v2/middleware/recover"
  10. fiberJwt "github.com/gofiber/jwt/v3"
  11. "github.com/gofiber/swagger"
  12. _ "git.dmitriygnatenko.ru/dima/homethings/docs" //nolint
  13. authAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/auth"
  14. imageAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/image"
  15. notificationAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/notification"
  16. placeAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/place"
  17. tagAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag"
  18. thingAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing"
  19. userAPI "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/user"
  20. "git.dmitriygnatenko.ru/dima/homethings/internal/factory"
  21. "git.dmitriygnatenko.ru/dima/homethings/internal/middleware/timezone"
  22. "git.dmitriygnatenko.ru/dima/homethings/internal/repositories"
  23. authService "git.dmitriygnatenko.ru/dima/homethings/internal/services/auth"
  24. envService "git.dmitriygnatenko.ru/dima/homethings/internal/services/env"
  25. mailerService "git.dmitriygnatenko.ru/dima/homethings/internal/services/mailer"
  26. )
  27. const (
  28. staticPath = "../../web/public"
  29. swaggerURI = "/docs/*"
  30. metricsURI = "/metrics"
  31. )
  32. type (
  33. ServiceProvider interface {
  34. GetEnvService() *envService.Service
  35. GetMailerService() *mailerService.Service
  36. GetAuthService() *authService.Service
  37. GetUserRepository() *repositories.UserRepository
  38. GetPlaceRepository() *repositories.PlaceRepository
  39. GetThingRepository() *repositories.ThingRepository
  40. GetTagRepository() *repositories.TagRepository
  41. GetPlaceThingRepository() *repositories.PlaceThingRepository
  42. GetPlaceImageRepository() *repositories.PlaceImageRepository
  43. GetThingImageRepository() *repositories.ThingImageRepository
  44. GetThingTagRepository() *repositories.ThingTagRepository
  45. GetThingNotificationRepository() *repositories.ThingNotificationRepository
  46. GetFileRepository() *repositories.FileRepository
  47. }
  48. )
  49. func Init(sp ServiceProvider) (*fiber.App, error) {
  50. fiberApp := fiber.New(getFiberConfig(sp))
  51. // Configure web root
  52. fiberApp.Static("/", staticPath)
  53. // Configure CORS middleware
  54. fiberApp.Use(cors.New(getCORSConfig(sp)))
  55. // Configure recover middleware
  56. fiberApp.Use(recover.New())
  57. // Configure client timezone middleware
  58. fiberApp.Use(timezone.New())
  59. // Configure JWT middleware
  60. jwtAuth := fiberJwt.New(getJWTConfig(sp))
  61. // Configure Basic auth
  62. basicAuth := basicauth.New(basicauth.Config{
  63. Users: map[string]string{
  64. sp.GetEnvService().BasicAuthUser(): sp.GetEnvService().BasicAuthPassword(),
  65. },
  66. })
  67. // Swagger
  68. fiberApp.Get(swaggerURI, swagger.HandlerDefault)
  69. // Metrics
  70. fiberApp.Get(metricsURI, basicAuth, monitor.New(getMetricsConfig()))
  71. // API
  72. api := fiberApp.Group("/api", jwtAuth)
  73. registerHandlers(api, sp)
  74. return fiberApp, nil
  75. }
  76. func getFiberConfig(sp ServiceProvider) fiber.Config {
  77. return fiber.Config{
  78. AppName: "Homethings",
  79. DisableStartupMessage: true,
  80. ErrorHandler: getErrorHandler(sp),
  81. }
  82. }
  83. func getErrorHandler(sp ServiceProvider) fiber.ErrorHandler {
  84. return func(fctx *fiber.Ctx, err error) error {
  85. errCode := fiber.StatusInternalServerError
  86. var e *fiber.Error
  87. if errors.As(err, &e) {
  88. errCode = e.Code
  89. }
  90. if err.Error() != "" {
  91. errorsEmail := sp.GetEnvService().ErrorsEmail()
  92. if errCode == fiber.StatusInternalServerError && errorsEmail != "" {
  93. log.Println(err)
  94. // nolint
  95. sp.GetMailerService().Send(
  96. errorsEmail,
  97. "AUTO - Homethings error",
  98. err.Error(),
  99. )
  100. }
  101. return fctx.Status(errCode).JSON(factory.CreateErrorResponse(err))
  102. }
  103. return fctx.Status(errCode).JSON(factory.CreateEmptyResponse())
  104. }
  105. }
  106. // nolint
  107. func getJWTConfig(sp ServiceProvider) fiberJwt.Config {
  108. return fiberJwt.Config{
  109. SigningKey: []byte(sp.GetEnvService().JWTSecretKey()),
  110. ErrorHandler: func(fctx *fiber.Ctx, err error) error {
  111. return fiber.NewError(fiber.StatusForbidden, err.Error())
  112. },
  113. Filter: func(fctx *fiber.Ctx) bool {
  114. method := fctx.Method()
  115. path := fctx.Path()
  116. if method != fiber.MethodGet && method != fiber.MethodPost &&
  117. method != fiber.MethodPut && method != fiber.MethodDelete {
  118. return true
  119. }
  120. if path == "/api/v1/auth/login" {
  121. return true
  122. }
  123. return false
  124. },
  125. }
  126. }
  127. func getMetricsConfig() monitor.Config {
  128. return monitor.Config{
  129. Title: "Homethings metrics",
  130. }
  131. }
  132. func getCORSConfig(sp ServiceProvider) cors.Config {
  133. return cors.Config{
  134. AllowOrigins: sp.GetEnvService().CORSAllowOrigins(),
  135. AllowMethods: sp.GetEnvService().CORSAllowMethods(),
  136. }
  137. }
  138. func registerHandlers(r fiber.Router, sp ServiceProvider) {
  139. // Public routes
  140. r.Post(
  141. "/v1/auth/login",
  142. authAPI.LoginHandler(
  143. sp.GetAuthService(),
  144. sp.GetUserRepository(),
  145. ),
  146. )
  147. // Protected routes
  148. r.Get(
  149. "/v1/auth/check",
  150. authAPI.CheckAuthHandler(
  151. sp.GetAuthService(),
  152. sp.GetUserRepository(),
  153. ),
  154. )
  155. r.Get(
  156. "/v1/places",
  157. placeAPI.GetPlacesHandler(sp.GetPlaceRepository()),
  158. )
  159. r.Get(
  160. "/v1/places/tree",
  161. placeAPI.GetPlaceTreeHandler(sp.GetPlaceRepository()),
  162. )
  163. r.Get(
  164. "/v1/places/:placeId<int>",
  165. placeAPI.GetPlaceHandler(sp.GetPlaceRepository()),
  166. )
  167. r.Get(
  168. "/v1/places/:parentPlaceId<int>/nested",
  169. placeAPI.GetNestedPlacesHandler(sp.GetPlaceRepository()),
  170. )
  171. r.Post(
  172. "/v1/places",
  173. placeAPI.AddPlaceHandler(sp.GetPlaceRepository()),
  174. )
  175. r.Put(
  176. "/v1/places/:placeId<int>",
  177. placeAPI.UpdatePlaceHandler(sp.GetPlaceRepository()),
  178. )
  179. r.Delete(
  180. "/v1/places/:placeId<int>",
  181. placeAPI.DeletePlaceHandler(
  182. sp.GetPlaceRepository(),
  183. sp.GetThingRepository(),
  184. sp.GetPlaceImageRepository(),
  185. sp.GetThingImageRepository(),
  186. sp.GetPlaceThingRepository(),
  187. sp.GetThingTagRepository(),
  188. sp.GetThingImageRepository(),
  189. sp.GetFileRepository(),
  190. ),
  191. )
  192. r.Get(
  193. "/v1/things/:thingId<int>",
  194. thingAPI.GetThingHandler(sp.GetThingRepository()),
  195. )
  196. r.Get(
  197. "/v1/things/search/:search",
  198. thingAPI.SearchThingHandler(sp.GetThingRepository()),
  199. )
  200. r.Get(
  201. "/v1/things/place/:placeId<int>",
  202. thingAPI.GetPlaceThingsHandler(
  203. sp.GetThingRepository(),
  204. sp.GetThingTagRepository(),
  205. ),
  206. )
  207. r.Post(
  208. "/v1/things",
  209. thingAPI.AddThingHandler(
  210. sp.GetThingRepository(),
  211. sp.GetPlaceThingRepository(),
  212. ),
  213. )
  214. r.Put(
  215. "/v1/things/:thingId<int>",
  216. thingAPI.UpdateThingHandler(
  217. sp.GetThingRepository(),
  218. sp.GetPlaceThingRepository(),
  219. ),
  220. )
  221. r.Delete(
  222. "/v1/things/:thingId<int>",
  223. thingAPI.DeleteThingHandler(
  224. sp.GetThingRepository(),
  225. sp.GetThingTagRepository(),
  226. sp.GetPlaceThingRepository(),
  227. sp.GetThingImageRepository(),
  228. sp.GetThingNotificationRepository(),
  229. sp.GetFileRepository(),
  230. ),
  231. )
  232. r.Get(
  233. "/v1/images/place/:placeId<int>",
  234. imageAPI.GetPlaceImagesHandler(
  235. sp.GetThingImageRepository(),
  236. sp.GetPlaceImageRepository(),
  237. ),
  238. )
  239. r.Get(
  240. "/v1/images/thing/:thingId<int>",
  241. imageAPI.GetThingImagesHandler(sp.GetThingImageRepository()))
  242. r.Post(
  243. "/v1/images",
  244. imageAPI.AddImageHandler(
  245. sp.GetFileRepository(),
  246. sp.GetThingImageRepository(),
  247. sp.GetPlaceImageRepository(),
  248. ),
  249. )
  250. r.Delete(
  251. "/v1/images/place/:imageId<int>",
  252. imageAPI.DeletePlaceImageHandler(
  253. sp.GetFileRepository(),
  254. sp.GetPlaceImageRepository(),
  255. ),
  256. )
  257. r.Delete(
  258. "/v1/images/thing/:imageId<int>",
  259. imageAPI.DeleteThingImageHandler(
  260. sp.GetFileRepository(),
  261. sp.GetThingImageRepository(),
  262. ),
  263. )
  264. r.Get(
  265. "/v1/tags",
  266. tagAPI.GetTagsHandler(sp.GetTagRepository()),
  267. )
  268. r.Get(
  269. "/v1/tags/:tagId<int>",
  270. tagAPI.GetTagHandler(sp.GetTagRepository()),
  271. )
  272. r.Get(
  273. "/v1/tags/thing/:thingId<int>",
  274. tagAPI.GetThingTagsHandler(sp.GetTagRepository()),
  275. )
  276. r.Post(
  277. "/v1/tags",
  278. tagAPI.AddTagHandler(sp.GetTagRepository()),
  279. )
  280. r.Post(
  281. "/v1/tags/:tagId<int>/thing/:thingId<int>",
  282. tagAPI.AddThingTagHandler(
  283. sp.GetTagRepository(),
  284. sp.GetThingRepository(),
  285. sp.GetThingTagRepository(),
  286. ),
  287. )
  288. r.Put(
  289. "/v1/tags/:tagId<int>",
  290. tagAPI.UpdateTagHandler(sp.GetTagRepository()),
  291. )
  292. r.Delete(
  293. "/v1/tags/:tagId<int>",
  294. tagAPI.DeleteTagHandler(
  295. sp.GetTagRepository(),
  296. sp.GetThingTagRepository(),
  297. ),
  298. )
  299. r.Delete(
  300. "/v1/tags/:tagId<int>/thing/:thingId<int>",
  301. tagAPI.DeleteThingTagHandler(
  302. sp.GetTagRepository(),
  303. sp.GetThingRepository(),
  304. sp.GetThingTagRepository(),
  305. ),
  306. )
  307. r.Get(
  308. "/v1/tags/:id<int>",
  309. tagAPI.GetTagHandler(sp.GetTagRepository()),
  310. )
  311. r.Post(
  312. "/v1/tags",
  313. tagAPI.AddTagHandler(sp.GetTagRepository()),
  314. )
  315. r.Put(
  316. "/v1/tags/:id<int>",
  317. tagAPI.UpdateTagHandler(sp.GetTagRepository()),
  318. )
  319. r.Delete(
  320. "/v1/tags/:id<int>",
  321. tagAPI.DeleteTagHandler(
  322. sp.GetTagRepository(),
  323. sp.GetThingTagRepository(),
  324. ),
  325. )
  326. r.Get(
  327. "/v1/things/notifications/:thingId<int>",
  328. notificationAPI.GetThingNotificationHandler(sp.GetThingNotificationRepository()),
  329. )
  330. r.Get(
  331. "/v1/things/notifications/expired",
  332. notificationAPI.GetExpiredThingNotificationsHandler(sp.GetThingNotificationRepository()),
  333. )
  334. r.Post(
  335. "/v1/things/notifications",
  336. notificationAPI.AddThingNotificationHandler(sp.GetThingNotificationRepository()),
  337. )
  338. r.Put(
  339. "/v1/things/notifications/:thingId<int>",
  340. notificationAPI.UpdateThingNotificationHandler(sp.GetThingNotificationRepository()),
  341. )
  342. r.Delete(
  343. "/v1/things/notifications/:thingId<int>",
  344. notificationAPI.DeleteThingNotificationHandler(sp.GetThingNotificationRepository()),
  345. )
  346. r.Post(
  347. "/v1/users",
  348. userAPI.AddUserHandler(
  349. sp.GetAuthService(),
  350. sp.GetUserRepository(),
  351. ),
  352. )
  353. r.Put(
  354. "/v1/users",
  355. userAPI.UpdateUserHandler(
  356. sp.GetAuthService(),
  357. sp.GetUserRepository(),
  358. ),
  359. )
  360. }