backend-service/internal/server/server.go
ats-tech25 a7cd28f3af chore(dependencies): Update project dependencies and middleware packages
- Upgrade Go module dependencies to latest versions
- Add new middleware packages for CORS, logging, rate limiting, and security
- Update go.mod and go.sum with latest package versions
- Integrate new middleware components into server configuration
- Improve project dependency management and middleware infrastructure
2025-11-06 09:31:51 +00:00

268 lines
7.5 KiB
Go

package server
import (
"fmt"
"log"
"attune-heart-therapy/internal/config"
"attune-heart-therapy/internal/database"
"attune-heart-therapy/internal/handlers"
"attune-heart-therapy/internal/middleware"
"attune-heart-therapy/internal/services"
"github.com/gin-gonic/gin"
)
// MiddlewareContainer holds all middleware functions
type MiddlewareContainer struct {
jwtService services.JWTService
}
// Auth returns the authentication middleware
func (m *MiddlewareContainer) Auth() gin.HandlerFunc {
return middleware.AuthMiddleware(m.jwtService)
}
// RequireAdmin returns the admin authentication middleware
func (m *MiddlewareContainer) RequireAdmin() gin.HandlerFunc {
return middleware.RequireAdmin(m.jwtService)
}
// StrictRateLimit returns the strict rate limiting middleware
func (m *MiddlewareContainer) StrictRateLimit() gin.HandlerFunc {
return middleware.StrictRateLimitMiddleware()
}
type Server struct {
config *config.Config
db *database.DB
router *gin.Engine
middleware *MiddlewareContainer
authHandler *handlers.AuthHandler
paymentHandler *handlers.PaymentHandler
bookingHandler *handlers.BookingHandler
adminHandler *handlers.AdminHandler
}
func New(cfg *config.Config) *Server {
// Set Gin mode based on environment
gin.SetMode(gin.ReleaseMode)
router := gin.New()
// Configure middleware stack
setupMiddlewareStack(router)
return &Server{
config: cfg,
router: router,
}
}
// setupMiddlewareStack configures the Gin middleware stack
func setupMiddlewareStack(router *gin.Engine) {
// Security middleware - should be first
router.Use(middleware.SecurityMiddleware())
// CORS middleware for frontend integration
router.Use(middleware.CORSMiddleware())
// Request logging middleware
router.Use(middleware.StructuredLoggingMiddleware())
// Recovery middleware to handle panics
router.Use(gin.Recovery())
// Rate limiting middleware for API protection
router.Use(middleware.RateLimitMiddleware())
}
// Initialize sets up the database connection and runs migrations
func (s *Server) Initialize() error {
// Initialize database connection
db, err := database.New(s.config)
if err != nil {
return fmt.Errorf("failed to initialize database: %w", err)
}
s.db = db
// Run database migrations
if err := s.db.Migrate(); err != nil {
return fmt.Errorf("failed to run database migrations: %w", err)
}
// Seed database with initial data
if err := s.db.Seed(); err != nil {
return fmt.Errorf("failed to seed database: %w", err)
}
// Initialize services and handlers
s.initializeServices()
log.Println("Server initialization completed successfully")
return nil
}
func (s *Server) Start() error {
// Initialize database and run migrations
if err := s.Initialize(); err != nil {
return err
}
// Setup routes
s.setupRoutes()
// Start server
addr := fmt.Sprintf("%s:%s", s.config.Server.Host, s.config.Server.Port)
log.Printf("Starting server on %s", addr)
return s.router.Run(addr)
}
// Shutdown gracefully shuts down the server
func (s *Server) Shutdown() error {
if s.db != nil {
log.Println("Closing database connection...")
return s.db.Close()
}
return nil
}
func (s *Server) setupRoutes() {
// Health check endpoint - no middleware needed
s.router.GET("/health", s.healthCheck)
// API v1 routes group with base middleware
v1 := s.router.Group("/api")
{
// Public routes group - no authentication required
public := v1.Group("/")
{
// Auth routes - public endpoints for registration and login
auth := public.Group("/auth")
{
// Apply strict rate limiting to auth endpoints
auth.Use(s.middleware.StrictRateLimit())
auth.POST("/register", s.authHandler.Register)
auth.POST("/login", s.authHandler.Login)
}
// Schedule routes - public endpoint for getting available slots
public.GET("/schedules", s.bookingHandler.GetAvailableSlots)
// Payment webhook - public endpoint for Stripe webhooks (no auth needed)
public.POST("/payments/webhook", s.paymentHandler.HandleWebhook)
}
// Authenticated routes group - require JWT authentication
authenticated := v1.Group("/")
authenticated.Use(s.middleware.Auth())
{
// Auth profile routes - require authentication
authProfile := authenticated.Group("/auth")
{
authProfile.GET("/profile", s.authHandler.GetProfile)
authProfile.PUT("/profile", s.authHandler.UpdateProfile)
authProfile.POST("/logout", s.authHandler.Logout)
}
// Booking routes - require authentication
bookings := authenticated.Group("/bookings")
{
bookings.GET("/", s.bookingHandler.GetUserBookings)
bookings.POST("/", s.bookingHandler.CreateBooking)
bookings.PUT("/:id/cancel", s.bookingHandler.CancelBooking)
bookings.PUT("/:id/reschedule", s.bookingHandler.RescheduleBooking)
}
// Payment routes - require authentication (except webhook)
payments := authenticated.Group("/payments")
{
payments.POST("/intent", s.paymentHandler.CreatePaymentIntent)
payments.POST("/confirm", s.paymentHandler.ConfirmPayment)
}
}
// Admin routes - require admin authentication
admin := v1.Group("/admin")
admin.Use(s.middleware.RequireAdmin())
{
admin.GET("/dashboard", s.adminHandler.GetDashboard)
admin.POST("/schedules", s.adminHandler.CreateSchedule)
admin.PUT("/schedules/:id", s.adminHandler.UpdateSchedule)
admin.GET("/users", s.adminHandler.GetUsers)
admin.GET("/bookings", s.adminHandler.GetBookings)
admin.GET("/reports/financial", s.adminHandler.GetFinancialReports)
}
}
}
// initializeServices sets up all services and handlers
func (s *Server) initializeServices() {
// Initialize repositories
repos := s.db.GetRepositories()
// Initialize Jitsi service
jitsiService := services.NewJitsiService(&s.config.Jitsi)
// Initialize notification service
notificationService := services.NewNotificationService(repos.Notification, s.config)
// Initialize JWT service (needed for user service and middleware)
jwtService := services.NewJWTService(s.config.JWT.Secret, s.config.JWT.Expiration)
// Initialize middleware container with JWT service
s.middleware = &MiddlewareContainer{
jwtService: jwtService,
}
// Initialize user service with notification integration
userService := services.NewUserService(repos.User, jwtService, notificationService)
// Initialize payment service with notification integration
paymentService := services.NewPaymentService(s.config, repos.Booking, repos.User, notificationService)
// Initialize booking service with notification integration
bookingService := services.NewBookingService(
repos.Booking,
repos.Schedule,
repos.User,
jitsiService,
paymentService,
notificationService,
)
// Initialize admin service
adminService := services.NewAdminService(repos.User, repos.Booking, repos.Schedule)
// Initialize handlers
s.authHandler = handlers.NewAuthHandler(userService)
s.paymentHandler = handlers.NewPaymentHandler(paymentService)
s.bookingHandler = handlers.NewBookingHandler(bookingService)
s.adminHandler = handlers.NewAdminHandler(adminService)
}
// healthCheck handles the health check endpoint
func (s *Server) healthCheck(c *gin.Context) {
response := gin.H{
"status": "ok",
"message": "Video Conference Booking System API",
}
// Check database connectivity
if s.db != nil {
if err := s.db.Health(); err != nil {
response["status"] = "error"
response["database"] = "disconnected"
response["error"] = err.Error()
c.JSON(500, response)
return
}
response["database"] = "connected"
} else {
response["database"] = "not initialized"
}
c.JSON(200, response)
}