fiber.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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.PlaceRepository(),
  184. sp.ThingRepository(),
  185. sp.PlaceImageRepository(),
  186. sp.ThingImageRepository(),
  187. sp.PlaceThingRepository(),
  188. sp.ThingTagRepository(),
  189. sp.ThingImageRepository(),
  190. sp.FileRepository(),
  191. ),
  192. )
  193. r.Get(
  194. "/v1/things/:thingId<int>",
  195. thingAPI.GetThingHandler(sp.ThingRepository()),
  196. )
  197. r.Get(
  198. "/v1/things/search/:search",
  199. thingAPI.SearchThingHandler(sp.ThingRepository()),
  200. )
  201. r.Get(
  202. "/v1/things/place/:placeId<int>",
  203. thingAPI.GetPlaceThingsHandler(
  204. sp.ThingRepository(),
  205. sp.ThingTagRepository(),
  206. ),
  207. )
  208. r.Post(
  209. "/v1/things",
  210. thingAPI.AddThingHandler(
  211. sp.ThingRepository(),
  212. sp.PlaceThingRepository(),
  213. ),
  214. )
  215. r.Put(
  216. "/v1/things/:thingId<int>",
  217. thingAPI.UpdateThingHandler(
  218. sp.ThingRepository(),
  219. sp.PlaceThingRepository(),
  220. ),
  221. )
  222. r.Delete(
  223. "/v1/things/:thingId<int>",
  224. thingAPI.DeleteThingHandler(
  225. sp.ThingRepository(),
  226. sp.ThingTagRepository(),
  227. sp.PlaceThingRepository(),
  228. sp.ThingImageRepository(),
  229. sp.ThingNotificationRepository(),
  230. sp.FileRepository(),
  231. ),
  232. )
  233. r.Get(
  234. "/v1/images/place/:placeId<int>",
  235. imageAPI.GetPlaceImagesHandler(
  236. sp.ThingImageRepository(),
  237. sp.PlaceImageRepository(),
  238. ),
  239. )
  240. r.Get(
  241. "/v1/images/thing/:thingId<int>",
  242. imageAPI.GetThingImagesHandler(sp.ThingImageRepository()))
  243. r.Post(
  244. "/v1/images",
  245. imageAPI.AddImageHandler(
  246. sp.TransactionManager(),
  247. sp.FileRepository(),
  248. sp.ThingImageRepository(),
  249. sp.PlaceImageRepository(),
  250. ),
  251. )
  252. r.Delete(
  253. "/v1/images/place/:imageId<int>",
  254. imageAPI.DeletePlaceImageHandler(
  255. sp.TransactionManager(),
  256. sp.FileRepository(),
  257. sp.PlaceImageRepository(),
  258. ),
  259. )
  260. r.Delete(
  261. "/v1/images/thing/:imageId<int>",
  262. imageAPI.DeleteThingImageHandler(
  263. sp.TransactionManager(),
  264. sp.FileRepository(),
  265. sp.ThingImageRepository(),
  266. ),
  267. )
  268. r.Get(
  269. "/v1/tags",
  270. tagAPI.GetTagsHandler(
  271. sp.TagRepository(),
  272. ),
  273. )
  274. r.Get(
  275. "/v1/tags/:tagId<int>",
  276. tagAPI.GetTagHandler(
  277. sp.TagRepository(),
  278. ),
  279. )
  280. r.Get(
  281. "/v1/tags/thing/:thingId<int>",
  282. tagAPI.GetThingTagsHandler(
  283. sp.TagRepository(),
  284. ),
  285. )
  286. r.Post(
  287. "/v1/tags",
  288. tagAPI.AddTagHandler(
  289. sp.TagRepository(),
  290. ),
  291. )
  292. r.Post(
  293. "/v1/tags/:tagId<int>/thing/:thingId<int>",
  294. tagAPI.AddThingTagHandler(
  295. sp.TagRepository(),
  296. sp.ThingRepository(),
  297. sp.ThingTagRepository(),
  298. ),
  299. )
  300. r.Put(
  301. "/v1/tags/:tagId<int>",
  302. tagAPI.UpdateTagHandler(
  303. sp.TagRepository(),
  304. ),
  305. )
  306. r.Delete(
  307. "/v1/tags/:tagId<int>",
  308. tagAPI.DeleteTagHandler(
  309. sp.TagRepository(),
  310. sp.ThingTagRepository(),
  311. ),
  312. )
  313. r.Delete(
  314. "/v1/tags/:tagId<int>/thing/:thingId<int>",
  315. tagAPI.DeleteThingTagHandler(
  316. sp.TagRepository(),
  317. sp.ThingRepository(),
  318. sp.ThingTagRepository(),
  319. ),
  320. )
  321. r.Get(
  322. "/v1/tags/:id<int>",
  323. tagAPI.UpdateTagHandler(
  324. sp.TagRepository(),
  325. ),
  326. )
  327. r.Post(
  328. "/v1/tags",
  329. tagAPI.UpdateTagHandler(
  330. sp.TagRepository(),
  331. ),
  332. )
  333. r.Put(
  334. "/v1/tags/:id<int>",
  335. tagAPI.UpdateTagHandler(
  336. sp.TagRepository(),
  337. ),
  338. )
  339. r.Delete(
  340. "/v1/tags/:id<int>",
  341. tagAPI.DeleteTagHandler(
  342. sp.TagRepository(),
  343. sp.ThingTagRepository(),
  344. ),
  345. )
  346. r.Get(
  347. "/v1/things/notifications/:thingId<int>",
  348. notificationAPI.GetThingNotificationHandler(
  349. sp.ThingNotificationRepository(),
  350. ),
  351. )
  352. r.Get(
  353. "/v1/things/notifications/expired",
  354. notificationAPI.GetExpiredThingNotificationsHandler(
  355. sp.ThingNotificationRepository(),
  356. ),
  357. )
  358. r.Post(
  359. "/v1/things/notifications",
  360. notificationAPI.AddThingNotificationHandler(
  361. sp.ThingNotificationRepository(),
  362. ),
  363. )
  364. r.Put(
  365. "/v1/things/notifications/:thingId<int>",
  366. notificationAPI.UpdateThingNotificationHandler(
  367. sp.ThingNotificationRepository(),
  368. ),
  369. )
  370. r.Delete(
  371. "/v1/things/notifications/:thingId<int>",
  372. notificationAPI.DeleteThingNotificationHandler(
  373. sp.ThingNotificationRepository(),
  374. ),
  375. )
  376. r.Post(
  377. "/v1/users",
  378. userAPI.AddUserHandler(
  379. sp.AuthService(),
  380. sp.UserRepository(),
  381. ),
  382. )
  383. r.Put(
  384. "/v1/users",
  385. userAPI.UpdateUserHandler(
  386. sp.AuthService(),
  387. sp.UserRepository(),
  388. ),
  389. )
  390. }