2025-11-05 15:06:07 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
|
|
|
|
|
"attune-heart-therapy/internal/config"
|
|
|
|
|
"attune-heart-therapy/internal/database"
|
2025-11-05 15:34:11 +00:00
|
|
|
"attune-heart-therapy/internal/handlers"
|
|
|
|
|
"attune-heart-therapy/internal/services"
|
2025-11-05 15:06:07 +00:00
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Server struct {
|
2025-11-05 15:34:11 +00:00
|
|
|
config *config.Config
|
|
|
|
|
db *database.DB
|
|
|
|
|
router *gin.Engine
|
|
|
|
|
paymentHandler *handlers.PaymentHandler
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func New(cfg *config.Config) *Server {
|
|
|
|
|
// Set Gin mode based on environment
|
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
|
|
|
|
|
|
router := gin.New()
|
|
|
|
|
|
|
|
|
|
// Add basic middleware
|
|
|
|
|
router.Use(gin.Logger())
|
|
|
|
|
router.Use(gin.Recovery())
|
|
|
|
|
|
|
|
|
|
return &Server{
|
|
|
|
|
config: cfg,
|
|
|
|
|
router: router,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:34:11 +00:00
|
|
|
// Initialize services and handlers
|
|
|
|
|
s.initializeServices()
|
|
|
|
|
|
2025-11-05 15:06:07 +00:00
|
|
|
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
|
|
|
|
|
s.router.GET("/health", s.healthCheck)
|
|
|
|
|
|
|
|
|
|
// API v1 routes group
|
|
|
|
|
v1 := s.router.Group("/api/v1")
|
|
|
|
|
{
|
|
|
|
|
// Auth routes (will be implemented in later tasks)
|
|
|
|
|
auth := v1.Group("/auth")
|
|
|
|
|
{
|
|
|
|
|
auth.POST("/register", func(c *gin.Context) {
|
|
|
|
|
c.JSON(501, gin.H{"message": "Not implemented yet"})
|
|
|
|
|
})
|
|
|
|
|
auth.POST("/login", func(c *gin.Context) {
|
|
|
|
|
c.JSON(501, gin.H{"message": "Not implemented yet"})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Booking routes (will be implemented in later tasks)
|
|
|
|
|
bookings := v1.Group("/bookings")
|
|
|
|
|
{
|
|
|
|
|
bookings.GET("/", func(c *gin.Context) {
|
|
|
|
|
c.JSON(501, gin.H{"message": "Not implemented yet"})
|
|
|
|
|
})
|
|
|
|
|
bookings.POST("/", func(c *gin.Context) {
|
|
|
|
|
c.JSON(501, gin.H{"message": "Not implemented yet"})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Schedule routes (will be implemented in later tasks)
|
|
|
|
|
schedules := v1.Group("/schedules")
|
|
|
|
|
{
|
|
|
|
|
schedules.GET("/", func(c *gin.Context) {
|
|
|
|
|
c.JSON(501, gin.H{"message": "Not implemented yet"})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:34:11 +00:00
|
|
|
// Payment routes
|
2025-11-05 15:06:07 +00:00
|
|
|
payments := v1.Group("/payments")
|
|
|
|
|
{
|
2025-11-05 15:34:11 +00:00
|
|
|
payments.POST("/intent", s.paymentHandler.CreatePaymentIntent)
|
|
|
|
|
payments.POST("/confirm", s.paymentHandler.ConfirmPayment)
|
|
|
|
|
payments.POST("/webhook", s.paymentHandler.HandleWebhook)
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Admin routes (will be implemented in later tasks)
|
|
|
|
|
admin := v1.Group("/admin")
|
|
|
|
|
{
|
|
|
|
|
admin.GET("/dashboard", func(c *gin.Context) {
|
|
|
|
|
c.JSON(501, gin.H{"message": "Not implemented yet"})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:34:11 +00:00
|
|
|
// initializeServices sets up all services and handlers
|
|
|
|
|
func (s *Server) initializeServices() {
|
2025-11-05 15:42:59 +00:00
|
|
|
// Initialize repositories
|
|
|
|
|
repos := s.db.GetRepositories()
|
|
|
|
|
|
|
|
|
|
// Initialize Jitsi service
|
|
|
|
|
jitsiService := services.NewJitsiService(&s.config.Jitsi)
|
|
|
|
|
|
2025-11-05 15:34:11 +00:00
|
|
|
// Initialize payment service
|
|
|
|
|
paymentService := services.NewPaymentService(s.config)
|
|
|
|
|
|
2025-11-05 15:42:59 +00:00
|
|
|
// Initialize booking service with Jitsi integration
|
|
|
|
|
bookingService := services.NewBookingService(
|
|
|
|
|
repos.Booking,
|
|
|
|
|
repos.Schedule,
|
|
|
|
|
jitsiService,
|
|
|
|
|
paymentService,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Store services for later use (if needed)
|
|
|
|
|
_ = bookingService // Will be used when booking handlers are implemented
|
|
|
|
|
|
2025-11-05 15:34:11 +00:00
|
|
|
// Initialize payment handler
|
|
|
|
|
s.paymentHandler = handlers.NewPaymentHandler(paymentService)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:06:07 +00:00
|
|
|
// 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)
|
|
|
|
|
}
|