fiber.go 9.4 KB

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