diff --git a/cmd/server/main.go b/cmd/server/main.go index 7d2a3e5..d9b8924 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -34,6 +34,36 @@ func (cv *CustomValidator) Validate(i interface{}) error { return cv.validator.Struct(i) } +func getTemplateUserWithTheme(c echo.Context, queries *database.Queries) (templates.User, error) { + userID := c.Get("user_id").(string) + userEmail := c.Get("user_email").(string) + userUsername := c.Get("user_username").(string) + userRole := c.Get("user_role").(string) + + userUUID, err := uuid.Parse(userID) + if err != nil { + return templates.User{}, err + } + + userDB, err := queries.GetUser(c.Request().Context(), pgtype.UUID{Bytes: userUUID, Valid: true}) + if err != nil { + return templates.User{}, err + } + + userTheme := "tokyo-night" + if userDB.Theme.Valid { + userTheme = userDB.Theme.String + } + + return templates.User{ + ID: userID, + Email: userEmail, + Username: userUsername, + Role: userRole, + Theme: userTheme, + }, nil +} + func main() { cfg := config.LoadConfig() @@ -144,7 +174,6 @@ 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, connManager) protected.GET("/auth/profile", authHandler.GetProfile) protected.PUT("/auth/profile", authHandler.UpdateProfile) @@ -522,6 +551,63 @@ func main() { return c.HTML(http.StatusOK, buf.String()) }) + // Queue Management route (protected) - SSR version + // Progress visualization route (protected) - SSR version + protected.GET("/progress", func(c echo.Context) error { + user, err := getTemplateUserWithTheme(c, queries) + if err != nil { + return c.HTML(http.StatusInternalServerError, "Error loading user") + } + + progressData, err := h.GetAllProgressData(c) + if err != nil { + return c.HTML(http.StatusInternalServerError, "Error loading progress") + } + + progressItems := make([]templates.ProgressItemData, len(progressData)) + for i, p := range progressData { + deviceIcon := "" + deviceType := "" + switch p.LastSyncDevice { + case "koreader": + deviceIcon = "📖" + deviceType = "KOReader" + case "kobo": + deviceIcon = "📚" + deviceType = "Kobo" + case "web": + deviceIcon = "🌐" + deviceType = "Web" + case "mobile": + deviceIcon = "📱" + deviceType = "Mobile" + } + + progressItems[i] = templates.ProgressItemData{ + MediaItemID: uuid.UUID(p.MediaItemID).String(), + Title: p.Title, + Author: p.Author, + CoverImagePath: p.CoverImagePath, + CurrentPage: p.CurrentPage, + TotalPages: p.TotalPages, + ProgressPercentage: p.Percentage, + LastUpdated: p.LastReadAt.Format("2006-01-02T15:04:05Z07:00"), + DeviceName: p.LastSyncDevice, + DeviceType: deviceType, + DeviceIcon: deviceIcon, + EpubCFI: p.Epubcfi, + } + } + + var buf bytes.Buffer + err = templates.Progress(user, progressItems).Render(c.Request().Context(), &buf) + if err != nil { + return err + } + return c.HTML(http.StatusOK, buf.String()) + }) + + // Queue Management route (protected) - SSR version protected.GET("/queue", func(c echo.Context) error { userID := c.Get("user_id").(string) @@ -572,6 +658,127 @@ func main() { return c.HTML(http.StatusOK, buf.String()) }) + // Collections management route (protected) - SSR version + collectionHandler := handlers.NewCollectionHandler(queries) + collections := protected.Group("/collections") + collections.GET("", func(c echo.Context) error { + user, err := getTemplateUserWithTheme(c, queries) + if err != nil { + return c.HTML(http.StatusInternalServerError, "Error loading user") + } + + // Fetch collections for SSR + collectionData, err := collectionHandler.GetCollectionsData(c) + if err != nil { + return c.HTML(http.StatusInternalServerError, "Error loading collections") + } + + // Convert to template format + collectionsList := make([]templates.CollectionData, len(collectionData)) + for i, col := range collectionData { + description := "" + if col.Description.Valid { + description = col.Description.String + } + color := "" + if col.Color.Valid { + color = col.Color.String + } + icon := "" + if col.Icon.Valid { + icon = col.Icon.String + } + + collectionsList[i] = templates.CollectionData{ + ID: uuid.UUID(col.ID.Bytes).String(), + Name: col.Name, + Description: description, + Color: color, + Icon: icon, + } + } + + // Render template WITH data (SSR) + var buf bytes.Buffer + err = templates.Collection(user, collectionsList).Render(c.Request().Context(), &buf) + if err != nil { + return err + } + return c.HTML(http.StatusOK, buf.String()) + }) + + collections.GET("/:id", func(c echo.Context) error { + user, err := getTemplateUserWithTheme(c, queries) + if err != nil { + return c.HTML(http.StatusInternalServerError, "Error loading user") + } + + collectionID, err := uuid.Parse(c.Param("id")) + if err != nil { + return c.HTML(http.StatusBadRequest, "Invalid collection ID") + } + + // Fetch collection for SSR + collectionDB, err := collectionHandler.GetCollectionData(c, collectionID) + if err != nil { + return c.HTML(http.StatusNotFound, "Collection not found") + } + + // Fetch books for SSR + booksData, err := collectionHandler.GetCollectionBooksData(c, collectionID) + if err != nil { + return c.HTML(http.StatusInternalServerError, "Error loading books") + } + + // Convert to template format + description := "" + if collectionDB.Description.Valid { + description = collectionDB.Description.String + } + color := "" + if collectionDB.Color.Valid { + color = collectionDB.Color.String + } + icon := "" + if collectionDB.Icon.Valid { + icon = collectionDB.Icon.String + } + + collectionDetail := templates.CollectionDetailData{ + ID: uuid.UUID(collectionDB.ID.Bytes).String(), + Name: collectionDB.Name, + Description: description, + Color: color, + Icon: icon, + } + + books := make([]templates.BookData, len(booksData)) + for i, book := range booksData { + author := "" + if book.Author.Valid { + author = book.Author.String + } + coverPath := "" + if book.CoverImagePath.Valid { + coverPath = book.CoverImagePath.String + } + books[i] = templates.BookData{ + MediaItemID: uuid.UUID(book.MediaItemID.Bytes).String(), + Title: book.Title, + Author: author, + CoverImagePath: coverPath, + } + } + + // Render template WITH data (SSR) + var buf bytes.Buffer + err = templates.CollectionDetail(user, collectionDetail, books).Render(c.Request().Context(), &buf) + if err != nil { + return err + } + return c.HTML(http.StatusOK, buf.String()) + }) + // Start server log.Printf("Starting server on port %s", cfg.ServerPort) e.Logger.Fatal(e.Start(":" + cfg.ServerPort))