| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package main
- import (
- "context"
- "log"
- "net/http"
- "github.com/go-chi/chi/v5"
- chimiddleware "github.com/go-chi/chi/v5/middleware"
- "github.com/go-chi/cors"
- "golang.org/x/time/rate"
- "github.com/photoplaces/backend/internal/config"
- "github.com/photoplaces/backend/internal/handlers"
- "github.com/photoplaces/backend/internal/middleware"
- "github.com/photoplaces/backend/internal/repository"
- "github.com/photoplaces/backend/internal/services"
- )
- func main() {
- cfg := config.Load()
- ctx := context.Background()
- pool, err := repository.NewPool(ctx, cfg.DatabaseURL)
- if err != nil {
- log.Fatalf("database connection: %v", err)
- }
- defer pool.Close()
- userRepo := repository.NewUserRepo(pool)
- placeRepo := repository.NewPlaceRepo(pool)
- serviceRepo := repository.NewServiceRepo(pool)
- reviewRepo := repository.NewReviewRepo(pool)
- bookingRepo := repository.NewBookingRepo(pool)
- tagRepo := repository.NewTagRepo(pool)
- featureRepo := repository.NewFeatureRepo(pool)
- authSvc := services.NewAuthService(userRepo, cfg.JWTSecret, cfg.JWTRefreshSecret)
- placeSvc := services.NewPlaceService(placeRepo)
- authHandler := handlers.NewAuthHandler(authSvc)
- userHandler := handlers.NewUserHandler(userRepo)
- placeHandler := handlers.NewPlaceHandler(placeSvc)
- serviceHandler := handlers.NewServiceHandler(serviceRepo, tagRepo)
- reviewHandler := handlers.NewReviewHandler(reviewRepo)
- bookingHandler := handlers.NewBookingHandler(bookingRepo, placeRepo)
- tagHandler := handlers.NewTagHandler(tagRepo, featureRepo)
- wsHub := handlers.NewWSHub()
- uploadHandler, err := handlers.NewUploadHandler(cfg.S3Endpoint, cfg.S3AccessKey, cfg.S3SecretKey, cfg.S3Bucket, false)
- if err != nil {
- log.Fatalf("upload handler: %v", err)
- }
- rl := middleware.NewRateLimiter(rate.Limit(60), 60)
- r := chi.NewRouter()
- r.Use(chimiddleware.Logger)
- r.Use(chimiddleware.Recoverer)
- r.Use(chimiddleware.RequestID)
- r.Use(cors.Handler(cors.Options{
- AllowedOrigins: cfg.AllowedOrigins,
- AllowedMethods: []string{"GET", "POST", "PATCH", "DELETE", "OPTIONS"},
- AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-Refresh-Token"},
- AllowCredentials: true,
- MaxAge: 300,
- }))
- r.Use(rl.Middleware())
- r.Route("/api/v1", func(r chi.Router) {
- r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
- w.Write([]byte(`{"status":"ok"}`))
- })
- // Public auth
- r.Post("/auth/register", authHandler.Register)
- r.Post("/auth/login", authHandler.Login)
- r.Post("/auth/refresh", authHandler.Refresh)
- // Public reviews (read)
- r.Get("/reviews", reviewHandler.List)
- // Public tags & features
- r.Get("/tags", tagHandler.ListTags)
- r.Get("/features", tagHandler.ListFeatures)
- // Public services (read)
- r.Get("/services", serviceHandler.List)
- r.Get("/services/{id}", serviceHandler.GetByID)
- // Public places (read)
- r.Get("/places", placeHandler.List)
- r.Get("/places/{id}", placeHandler.GetByID)
- // WebSocket visitor dots
- r.Get("/ws/visitors", wsHub.HandleWS)
- // Authenticated
- r.Group(func(r chi.Router) {
- r.Use(middleware.AuthMiddleware(authSvc))
- r.Get("/auth/me", authHandler.Me)
- // Users
- r.Patch("/users/me", userHandler.UpdateMe)
- // Upload
- r.Post("/upload/presigned-url", uploadHandler.PresignedURL)
- // Bookings
- r.Post("/bookings", bookingHandler.Create)
- r.Get("/bookings/me", bookingHandler.ListMy)
- r.Patch("/bookings/{id}/cancel", bookingHandler.Cancel)
- // Reviews
- r.Post("/reviews", reviewHandler.Create)
- // Services
- r.Post("/services", serviceHandler.Create)
- r.Patch("/services/{id}", serviceHandler.Update)
- r.Delete("/services/{id}", serviceHandler.Delete)
- // Place creation
- r.Post("/places", placeHandler.Create)
- r.Patch("/places/{id}", placeHandler.Update)
- r.Delete("/places/{id}", placeHandler.Delete)
- })
- // Public user profile
- r.Get("/users/{id}", userHandler.GetProfile)
- // Moderator / Admin
- r.Group(func(r chi.Router) {
- r.Use(middleware.AuthMiddleware(authSvc))
- r.Use(middleware.RoleMiddleware("moderator", "superadmin"))
- r.Post("/places/{id}/moderate", placeHandler.Moderate)
- r.Get("/admin/users", userHandler.AdminListUsers)
- r.Patch("/admin/users/{id}", userHandler.AdminUpdateUser)
- r.Patch("/admin/bookings/{id}/confirm", bookingHandler.Confirm)
- r.Post("/admin/tags", tagHandler.CreateTag)
- r.Delete("/admin/tags", tagHandler.DeleteTag)
- r.Post("/admin/features", tagHandler.CreateFeature)
- r.Delete("/admin/features", tagHandler.DeleteFeature)
- })
- })
- port := cfg.ServerPort
- log.Printf("Starting server on :%s", port)
- if err := http.ListenAndServe(":"+port, r); err != nil {
- log.Fatal(err)
- }
- }
|