package router import ( "context" "net/http" "time" "github.com/celestia-trace/backend/config" "github.com/celestia-trace/backend/handlers" "github.com/celestia-trace/backend/middleware" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // Setup creates and configures the gin router func Setup(db *gorm.DB, cfg *config.Config) *gin.Engine { gin.SetMode(cfg.GinMode) r := gin.Default() // CORS middleware for dev r.Use(func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204) return } c.Next() }) v1 := r.Group("/v1") { v1.GET("/health", func(c *gin.Context) { sqlDB, err := db.DB() if err != nil { c.JSON(http.StatusServiceUnavailable, gin.H{"code": 1, "message": "database unavailable"}) return } ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) defer cancel() if err := sqlDB.PingContext(ctx); err != nil { c.JSON(http.StatusServiceUnavailable, gin.H{"code": 1, "message": "database unavailable"}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": "healthy"}) }) authHandler := handlers.AuthHandler{DB: db, Cfg: cfg} authRoutes := v1.Group("/auth") { authRoutes.POST("/register", authHandler.Register) authRoutes.POST("/login", authHandler.Login) authRoutes.POST("/refresh", authHandler.Refresh) // Auth required authRoutes.Use(middleware.Auth(cfg)) authRoutes.POST("/logout", authHandler.Logout) } userRoutes := v1.Group("/user") userRoutes.Use(middleware.Auth(cfg)) { userRoutes.GET("/profile", authHandler.GetProfile) userRoutes.PUT("/profile", authHandler.UpdateProfile) userRoutes.POST("/change-password", authHandler.ChangePassword) } // Device management routes deviceHandler := handlers.DeviceHandler{DB: db} deviceRoutes := v1.Group("/devices") deviceRoutes.Use(middleware.Auth(cfg)) { deviceRoutes.GET("", deviceHandler.GetDevices) deviceRoutes.POST("", deviceHandler.BindDevice) deviceRoutes.PUT("/:id", deviceHandler.UpdateDevice) deviceRoutes.DELETE("/:id", deviceHandler.UnbindDevice) } // Session / Field Record routes sessionHandler := handlers.SessionHandler{DB: db} mediaHandler := handlers.MediaHandler{DB: db, Cfg: cfg} sessionRoutes := v1.Group("/sessions") sessionRoutes.Use(middleware.Auth(cfg)) { sessionRoutes.GET("", sessionHandler.GetSessions) sessionRoutes.POST("", sessionHandler.CreateSession) sessionRoutes.GET("/:id", sessionHandler.GetSessionDetail) sessionRoutes.POST("/:id/events", sessionHandler.AddEventToSession) sessionRoutes.DELETE("/:id", sessionHandler.DeleteSession) sessionRoutes.POST("/:id/assets", mediaHandler.UploadAsset) sessionRoutes.GET("/:id/assets/:assetId", mediaHandler.DownloadAsset) // Chunked upload routes sessionRoutes.POST("/:id/assets/init", mediaHandler.InitChunkedUpload) sessionRoutes.POST("/:id/assets/chunk", mediaHandler.UploadChunk) sessionRoutes.POST("/:id/assets/complete", mediaHandler.CompleteChunkedUpload) } // Sync settings & summary routes syncHandler := handlers.SyncHandler{DB: db} syncRoutes := v1.Group("/sync") syncRoutes.Use(middleware.Auth(cfg)) { syncRoutes.GET("/settings", syncHandler.GetSyncSettings) syncRoutes.PUT("/settings", syncHandler.UpdateSyncSettings) syncRoutes.GET("/summary", syncHandler.GetSyncSummary) syncRoutes.POST("/trigger", syncHandler.TriggerManualSync) } // Storage quota route storageRoutes := v1.Group("/storage") storageRoutes.Use(middleware.Auth(cfg)) { storageRoutes.GET("/quota", syncHandler.GetStorageQuota) } } return r }