package router import ( "net/http" "time" "github.com/gin-gonic/gin" "github.com/minio/minio-go/v7" "gorm.io/gorm" "smoop-api/internal/config" "smoop-api/internal/crypto" "smoop-api/internal/handlers" "smoop-api/internal/middleware" ) func Build(db *gorm.DB, minio *minio.Client, cfg *config.Config) *gin.Engine { // r := gin.Default() r := gin.New() r.Use(handlers.BodyLogger(), gin.Logger(), gin.Recovery()) jwtMgr := crypto.NewJWT(cfg.JWTSecret) // --- Handlers authH := handlers.NewAuthHandler(db, jwtMgr) usersH := handlers.NewUsersHandler(db) devH := handlers.NewDevicesHandler(db) recH := handlers.NewRecordsHandler(db, minio, cfg.MinIO.RecordsBucket, cfg.MinIO.PresignTTL) liveH := handlers.NewLivestreamHandler(minio, cfg.MinIO.LivestreamBucket) // --- MediaMTX handler mediamtxH := handlers.NewMediaMTXHandler(db, jwtMgr, cfg.MediaMTX) /// --- GPS tracker handler trackersH := handlers.NewTrackersHandler(db) tasksH := handlers.NewTasksHandler(db, mediamtxH) certsH := handlers.NewCertsHandler(db, &cfg.PkiIot, "720h") certsAdminH := handlers.NewCertsAdminHandler(db, &cfg.PkiIot) // --- Public auth r.POST("/auth/signup", authH.SignUp) r.POST("/auth/signin", authH.SignIn) r.POST("/auth/check_token", authH.CheckToken) // Protected authMW := handlers.Auth(jwtMgr) adminOnly := handlers.RequireRole("admin") r.POST("/auth/change_password", authMW, authH.ChangePassword) r.GET("/users/profile", authMW, usersH.Profile) r.POST("/users/:id/set_role", authMW, adminOnly, usersH.SetRole) r.GET("/users", authMW, adminOnly, usersH.List) r.POST("/users/create", authMW, adminOnly, usersH.Create) r.DELETE("/users/:id", authMW, adminOnly, usersH.Delete) r.GET("/devices", authMW, middleware.DeviceAccessFilter(), devH.List) r.POST("/devices/create", authMW, adminOnly, devH.Create) r.POST("/devices/:guid/rename", authMW, devH.Rename) r.POST("/devices/:guid/add_to_user", authMW, devH.AddToUser) r.POST("/devices/:guid/set_users", authMW, adminOnly, devH.SetUsers) r.POST("/devices/:guid/remove_from_user", authMW, devH.RemoveFromUser) r.POST("/device/:guid/task", authMW, middleware.DeviceAccessFilter(), tasksH.CreateTask) r.GET("/device/:guid/tasks", authMW, middleware.DeviceAccessFilter(), tasksH.ListDeviceTasks) r.GET("/device/:guid/certs", authMW, adminOnly, devH.ListCertsByDevice) r.POST("/certs/revoke", authMW, adminOnly, certsAdminH.Revoke) r.GET("/device/:guid/config", authMW, middleware.DeviceAccessFilter(), devH.GetDeviceConfig) r.POST("/device/:guid/config", authMW, adminOnly, devH.CreateDeviceConfig) r.PUT("/device/:guid/config", authMW, middleware.DeviceAccessFilter(), devH.UpdateDeviceConfig) r.POST("/records/upload", middleware.MTLSGuardUpload(db), recH.Upload) r.GET("/records", authMW, recH.List) r.GET("/records/:id/file", authMW, recH.File) // WebSocket livestream r.GET("/livestream", authMW, liveH.Upgrade) // health r.GET("/healthz", func(c *gin.Context) { c.String(http.StatusOK, "ok") }) // --- NEW: MediaMTX integration routes // External auth (called by MediaMTX) r.POST("/mediamtx/auth", mediamtxH.Auth) // Token minting for device/user flows r.POST("/mediamtx/token/publish", mediamtxH.MintPublish) r.POST("/mediamtx/token/read", authMW, middleware.DeviceAccessFilter(), mediamtxH.MintRead) // Admin controls r.GET("/mediamtx/paths", authMW, adminOnly, mediamtxH.ListPaths) r.POST("/mediamtx/webrtc/kick/:id", authMW, adminOnly, mediamtxH.KickWebRTC) // SSE endpoint for audio stream UI r.GET("/mediamtx/:guid/wait", authMW, middleware.DeviceAccessFilter(), mediamtxH.WaitLiveSSE) r.GET("/trackers", authMW, middleware.TrackerAccessFilter(), trackersH.List) r.POST("/trackers/create", authMW, trackersH.Create) r.POST("/trackers/:guid/rename", authMW, trackersH.Rename) r.POST("/trackers/:guid/set_users", authMW, adminOnly, trackersH.SetUsers) // --- Device Job/Task API r.GET("/tasks/:guid", middleware.MTLSGuard(db), tasksH.DeviceNextTask) // heartbeat + fetch next task r.POST("/tasks/:guid", middleware.MTLSGuard(db), tasksH.DevicePostResult) // device posts result r.POST("/enroll/:guid", certsH.Enroll) // simple device-exists check is inside handler r.POST("/renew/:guid", middleware.MTLSGuard(db), certsH.Renew) // sensible defaults r.MaxMultipartMemory = 64 << 20 // 64 MiB _ = time.Now() // appease linters return r } // --- JWT middleware & helpers (kept here to avoid new dirs) ---