main.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package main
  2. import (
  3. "context"
  4. "log"
  5. "net/http"
  6. "github.com/go-chi/chi/v5"
  7. chimiddleware "github.com/go-chi/chi/v5/middleware"
  8. "github.com/go-chi/cors"
  9. "github.com/redis/go-redis/v9"
  10. "github.com/photoplaces/backend/internal/config"
  11. "github.com/photoplaces/backend/internal/handlers"
  12. "github.com/photoplaces/backend/internal/middleware"
  13. "github.com/photoplaces/backend/internal/repository"
  14. "github.com/photoplaces/backend/internal/services"
  15. )
  16. func main() {
  17. cfg := config.Load()
  18. ctx := context.Background()
  19. pool, err := repository.NewPool(ctx, cfg.DatabaseURL)
  20. if err != nil {
  21. log.Fatalf("database connection: %v", err)
  22. }
  23. defer pool.Close()
  24. userRepo := repository.NewUserRepo(pool)
  25. refreshTokenRepo := repository.NewRefreshTokenRepo(pool)
  26. placeRepo := repository.NewPlaceRepo(pool)
  27. serviceRepo := repository.NewServiceRepo(pool)
  28. reviewRepo := repository.NewReviewRepo(pool)
  29. bookingRepo := repository.NewBookingRepo(pool)
  30. tagRepo := repository.NewTagRepo(pool)
  31. featureRepo := repository.NewFeatureRepo(pool)
  32. authSvc := services.NewAuthService(userRepo, refreshTokenRepo, cfg.JWTSecret, cfg.JWTRefreshSecret)
  33. placeSvc := services.NewPlaceService(placeRepo)
  34. authHandler := handlers.NewAuthHandler(authSvc, cfg.AppEnv)
  35. userHandler := handlers.NewUserHandler(userRepo)
  36. placeHandler := handlers.NewPlaceHandler(placeSvc)
  37. serviceHandler := handlers.NewServiceHandler(serviceRepo, tagRepo)
  38. reviewHandler := handlers.NewReviewHandler(reviewRepo)
  39. bookingHandler := handlers.NewBookingHandler(bookingRepo, placeRepo)
  40. tagHandler := handlers.NewTagHandler(tagRepo, featureRepo)
  41. wsHub := handlers.NewWSHub()
  42. uploadHandler, err := handlers.NewUploadHandler(cfg.S3Endpoint, cfg.S3PublicEndpoint, cfg.S3AccessKey, cfg.S3SecretKey, cfg.S3Bucket, false)
  43. if err != nil {
  44. log.Fatalf("upload handler: %v", err)
  45. }
  46. redisOpts, err := redis.ParseURL(cfg.RedisURL)
  47. if err != nil {
  48. log.Fatalf("parse redis url: %v", err)
  49. }
  50. redisClient := redis.NewClient(redisOpts)
  51. defer redisClient.Close()
  52. if err := redisClient.Ping(ctx).Err(); err != nil {
  53. log.Printf("Warning: Redis connection failed, rate limiting disabled: %v", err)
  54. }
  55. authLimiter, _ := middleware.RateLimitAuthEndpoints(redisClient)
  56. apiReadLimiter, _ := middleware.RateLimitAPIRead(redisClient)
  57. apiWriteLimiter, _ := middleware.RateLimitAPIWrite(redisClient)
  58. adminLimiter, _ := middleware.RateLimitAdmin(redisClient)
  59. r := chi.NewRouter()
  60. r.Use(chimiddleware.Logger)
  61. r.Use(chimiddleware.Recoverer)
  62. r.Use(chimiddleware.RequestID)
  63. r.Use(cors.Handler(cors.Options{
  64. AllowedOrigins: cfg.AllowedOrigins,
  65. AllowedMethods: []string{"GET", "POST", "PATCH", "DELETE", "OPTIONS"},
  66. AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
  67. AllowCredentials: true,
  68. MaxAge: 300,
  69. }))
  70. r.Route("/api/v1", func(r chi.Router) {
  71. r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
  72. w.Write([]byte(`{"status":"ok"}`))
  73. })
  74. // Public auth - strict rate limiting
  75. r.Group(func(r chi.Router) {
  76. if authLimiter != nil {
  77. r.Use(authLimiter.Middleware())
  78. }
  79. r.Post("/auth/register", authHandler.Register)
  80. r.Post("/auth/login", authHandler.Login)
  81. r.Post("/auth/refresh", authHandler.Refresh)
  82. })
  83. // Public read endpoints - moderate rate limiting
  84. r.Group(func(r chi.Router) {
  85. if apiReadLimiter != nil {
  86. r.Use(apiReadLimiter.Middleware())
  87. }
  88. r.Get("/reviews", reviewHandler.List)
  89. r.Get("/tags", tagHandler.ListTags)
  90. r.Get("/features", tagHandler.ListFeatures)
  91. r.Get("/services", serviceHandler.List)
  92. r.Get("/services/{id}", serviceHandler.GetByID)
  93. r.Get("/places", placeHandler.List)
  94. r.Get("/places/{id}", placeHandler.GetByID)
  95. r.Get("/users/{id}", userHandler.GetProfile)
  96. })
  97. // WebSocket visitor dots
  98. r.Get("/ws/visitors", wsHub.HandleWS)
  99. // Authenticated write endpoints - stricter rate limiting
  100. r.Group(func(r chi.Router) {
  101. r.Use(middleware.AuthMiddleware(authSvc))
  102. if apiWriteLimiter != nil {
  103. r.Use(apiWriteLimiter.Middleware())
  104. }
  105. r.Get("/auth/me", authHandler.Me)
  106. r.Post("/auth/logout", authHandler.Logout)
  107. // Users
  108. r.Patch("/users/me", userHandler.UpdateMe)
  109. // Upload
  110. r.Post("/upload/presigned-url", uploadHandler.PresignedURL)
  111. // Bookings
  112. r.Post("/bookings", bookingHandler.Create)
  113. r.Get("/bookings/me", bookingHandler.ListMy)
  114. r.Patch("/bookings/{id}/cancel", bookingHandler.Cancel)
  115. // Reviews
  116. r.Post("/reviews", reviewHandler.Create)
  117. // Services
  118. r.Post("/services", serviceHandler.Create)
  119. r.Patch("/services/{id}", serviceHandler.Update)
  120. r.Delete("/services/{id}", serviceHandler.Delete)
  121. // Place creation
  122. r.Post("/places", placeHandler.Create)
  123. r.Patch("/places/{id}", placeHandler.Update)
  124. r.Delete("/places/{id}", placeHandler.Delete)
  125. })
  126. // Moderator / Admin - higher limits
  127. r.Group(func(r chi.Router) {
  128. r.Use(middleware.AuthMiddleware(authSvc))
  129. r.Use(middleware.RoleMiddleware("moderator", "superadmin"))
  130. if adminLimiter != nil {
  131. r.Use(adminLimiter.Middleware())
  132. }
  133. r.Post("/places/{id}/moderate", placeHandler.Moderate)
  134. r.Get("/admin/users", userHandler.AdminListUsers)
  135. r.Patch("/admin/users/{id}", userHandler.AdminUpdateUser)
  136. r.Patch("/admin/bookings/{id}/confirm", bookingHandler.Confirm)
  137. r.Post("/admin/tags", tagHandler.CreateTag)
  138. r.Delete("/admin/tags", tagHandler.DeleteTag)
  139. r.Post("/admin/features", tagHandler.CreateFeature)
  140. r.Delete("/admin/features", tagHandler.DeleteFeature)
  141. })
  142. })
  143. port := cfg.ServerPort
  144. log.Printf("Starting server on :%s", port)
  145. if err := http.ListenAndServe(":"+port, r); err != nil {
  146. log.Fatal(err)
  147. }
  148. }