From d77585a6f54307aed4fdb6ce808fa746fb4cb6bc Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Fri, 30 Jan 2026 21:48:21 -0500 Subject: [PATCH] feat: add WebSocket endpoint and ConnectionManager setup Add WebSocket infrastructure to main server: - Import sync package for ConnectionManager - Create and start ConnectionManager with cleanup task - Initialize WSHandler with auth dependencies - Add /ws/sync WebSocket endpoint - Update handler initialization to pass ConnectionManager The WebSocket endpoint at /ws/sync enables real-time progress updates across all connected clients (web, mobile, devices). --- cmd/server/main.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 26067b1..6f12037 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,6 +6,7 @@ import ( "bookmann/internal/handlers" "bookmann/internal/middleware" ratelimit "bookmann/internal/middleware" + "bookmann/internal/sync" "bookmann/templates" "bytes" "context" @@ -50,10 +51,15 @@ func main() { authHandler := handlers.NewAuthHandler(queries, cfg.JWTSecret, loginAttemptTracker) libraryHandler := handlers.NewLibraryHandler(queries) deviceHandler := handlers.NewDeviceHandler(queries, cfg.JWTSecret, cfg) - koreaderHandler := handlers.NewKOReaderHandler(queries) - deviceAuthMiddleware := middleware.NewDeviceAuthMiddleware(queries) + // Create WebSocket connection manager + connManager := sync.NewConnectionManager() + connManager.StartCleanupTask() + + koreaderHandler := handlers.NewKOReaderHandler(queries, connManager) + wsHandler := handlers.NewWSHandler(queries, connManager, cfg.JWTSecret, deviceAuthMiddleware) + e := echo.New() // Set up validator @@ -117,7 +123,7 @@ func main() { protected := e.Group("/api", jwtMiddleware) // Setup ebook handler routes first (so we can use it for library scan) - h := handlers.SetupRoutes(protected, queries) + h := handlers.SetupRoutes(protected, queries, connManager) // Public library types endpoint (no authentication required) e.GET("/api/libraries/types", libraryHandler.GetLibraryTypes) @@ -196,6 +202,9 @@ func main() { devices.DELETE("/:id", deviceHandler.DeleteDevice) devices.GET("/pending", deviceHandler.ListPendingRegistrations) + // WebSocket endpoint for real-time sync + e.GET("/ws/sync", wsHandler.HandleWebSocket) + // Static files e.Static("/static", "web/static")