# Progress Routes Analysis & Thoughts ## Overview This document explores the current state of progress tracking in Bookhoard, the migration from legacy media-item-specific routes to universal cross-device progress, and considerations for the future. --- ## Current State ### Legacy Routes (Marked as Deprecated) Located in `internal/handlers/ebook.go:114-117`: ```go // Legacy progress routes (deprecated - use universal progress instead) g.GET("/api/media-items/:id/progress", h.GetMediaReadingProgress) g.PUT("/api/media-items/:id/progress", h.UpdateMediaReadingProgress) g.DELETE("/api/media-items/:id/progress", h.DeleteMediaReadingProgress) ``` **Purpose**: These routes handle progress tracking for a specific media item from the `media_items` table. **Data Source**: Likely queries the `reading_progress` table filtered by `media_item_id`. **Current Status**: Explicitly marked as "legacy" and "deprecated" in code comments. --- ### Universal Progress Routes (Phase 1 Implementation) Located in `internal/handlers/ebook.go:119-122`: ```go // Universal Progress routes (Phase 1) g.GET("/api/progress/:id", h.GetUniversalProgress) g.POST("/api/progress/:id", h.UpdateUniversalProgress) g.GET("/api/progress/:id/history", h.GetProgressHistory) ``` **Purpose**: These routes provide "universal" progress tracking that works across devices and media types. **Data Source**: Uses enhanced `reading_progress` table with additional fields: - `percentage` - Universal percentage (0-1) - `character_offset` - Character-based positioning - `epubcfi` - EPUB Canonical Fragment Identifier - `chapter` + `chapter_progress` - Chapter-based tracking - Viewport coordinates (viewport_x, viewport_y, zoom_level) - Scroll positions (scroll_position_x, scroll_position_y) - Panel number for comics/manga - Reading mode indicator **Device Sync Metadata**: - `last_sync_device` - Which device last updated - `last_sync_source` - Source type (koreader, kobo, web, etc.) - `last_sync_timestamp` - When sync occurred - `conflict_detected` - Boolean flag for conflicts - `conflict_resolved` - Boolean flag for resolution status --- ## Why the Migration Happened ### 1. **Cross-Platform Kindle Ecosystem Vision** Bookhoard aims to replace the Kindle ecosystem, which requires: - Syncing progress across multiple devices (Kindle, Kobo, phone, web) - Handling different progress formats (page numbers, percentages, CFI, character offsets) - Maintaining reading state across different device types - Supporting offline reading with sync queues ### 2. **Format Diversity** Different e-readers and formats use different progress indicators: | Format/Device | Progress Type | Example | |---------------|---------------|---------| | EPUB (KOReader) | EPUBCFI | `epubcfi(/6/4[chap1ref]!/4/2/1:0)` | | EPUB (Kobo) | Page # + Total | `page 234 of 456` | | PDF | Page # | `page 45` | | Web Reader | Percentage | `0.45 (45%)` | | TXT/Mobi | Character Offset | `offset 12345` | | Comics/Manga | Panel # | `panel 7` | | Kindle | Location # | `location 1234` | The legacy `media-items/:id/progress` routes couldn't handle this diversity. ### 3. **Device Sync Architecture** Universal progress enables: - Real-time sync via WebSocket (`/ws/sync`) - Offline queue support (`/api/queue/*`) - Conflict detection and resolution - Checkpoint mode for battery optimization - Progress history tracking --- ## Current Database Schema From `database/schema/schema.sql:130-158`: ```sql CREATE TABLE reading_progress ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- Legacy fields current_page INTEGER DEFAULT 0, total_pages INTEGER, last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), -- Universal Progress Tracking (Phase 1) percentage FLOAT CHECK (percentage >= 0 AND percentage <= 1), character_offset BIGINT, epubcfi TEXT, chapter INTEGER, chapter_progress FLOAT CHECK (chapter_progress >= 0 AND chapter_progress <= 1), viewport_x FLOAT DEFAULT 0, viewport_y FLOAT DEFAULT 0, zoom_level FLOAT DEFAULT 1.0, scroll_position_x FLOAT DEFAULT 0, scroll_position_y FLOAT DEFAULT 0, panel_number INTEGER, reading_mode VARCHAR(20), -- Device Sync Metadata (Phase 1) last_sync_device VARCHAR(50), last_sync_source VARCHAR(20), last_sync_timestamp TIMESTAMP WITH TIME ZONE, conflict_detected BOOLEAN DEFAULT FALSE, conflict_resolved BOOLEAN DEFAULT TRUE, UNIQUE(media_item_id, user_id) ); ``` **Backward Compatibility View** (Line 160-168): ```sql CREATE VIEW ebook_reading_progress AS SELECT rp.*, mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility FROM reading_progress rp JOIN media_items mi ON rp.media_item_id = mi.id JOIN libraries l ON mi.library_id = l.id JOIN library_types lt ON l.library_type_id = lt.id WHERE lt.name = 'ebooks'; ``` --- ## The Migration Challenge ### Issue: Two Parallel Systems Currently, **both** systems exist side-by-side: 1. **Legacy routes** (`/api/media-items/:id/progress`) - Likely use simple `current_page` / `total_pages` fields - Media-item scoped - No device sync metadata 2. **Universal routes** (`/api/progress/:id`) - Use rich progress tracking with multiple formats - Device-aware - Include sync metadata ### Question: What Does `:id` Mean? **Legacy**: `:id` = `media_item_id` (UUID of the book) **Universal**: `:id` = ??? (Could be same media_item_id, or could be a different identifier) **Ambiguity**: The routes use the same parameter name but might mean different things. ### Potential Problems 1. **Data Duplication**: If both systems write to `reading_progress` table, they might overwrite each other 2. **Client Confusion**: Which endpoint should clients use? 3. **Migration Path**: How do existing clients using legacy endpoints transition? 4. **API Consistency**: Having two different endpoints for similar functionality is confusing --- ## Observations & Concerns ### 1. **Incomplete Migration** The legacy routes are marked as deprecated but **still active**. This suggests: - Migration is ongoing, not complete - Some clients might still depend on legacy routes - Fear of breaking existing integrations ### 2. **Backward Compatibility View** The `ebook_reading_progress` view exists to maintain compatibility with the old `ebooks` table. This adds: - Query overhead (JOINs to filter by library type) - Developer confusion (which table/view to query?) - Technical debt (maintaining two ways to access data) ### 3. **Route Naming Inconsistency** - Legacy: `/api/media-items/:id/progress` (RESTful, nested under media-item) - Universal: `/api/progress/:id` (flat structure, not nested) **Question**: Should universal progress be under `/api/media-items/:id/universal-progress` for consistency? ### 4. **HTTP Method Mismatch** Legacy routes use: - `PUT /api/media-items/:id/progress` (update progress) Universal routes use: - `POST /api/progress/:id` (update progress) **REST convention**: `PUT` is idempotent, `POST` is not. For progress updates, `PUT` might be more appropriate since setting the same progress twice should have the same effect. ### 5. **Missing Delete Operation** Universal routes don't have a `DELETE /api/progress/:id` endpoint. Legacy does: - `DELETE /api/media-items/:id/progress` (clear progress) **Question**: Should there be a way to reset progress via universal routes? --- ## Potential Future Directions ### Option 1: Full Migration (Clean Break) **Action**: Remove all legacy routes and views. **Steps**: 1. Deprecate legacy routes in API documentation (return `Warning` header) 2. Add a 6-month migration timeline 3. Remove `/api/media-items/:id/progress` routes 4. Drop `ebook_reading_progress` view 5. Update all clients to use universal routes **Pros**: - Cleaner API surface - Single source of truth - Less maintenance burden - Clearer documentation **Cons**: - Breaking change for existing clients - Mobile apps might need updates - External integrations could break ### Option 2: Compatibility Layer (Adapter Pattern) **Action**: Keep legacy routes but make them thin wrappers around universal routes. **Implementation**: ```go // Legacy route calls universal route internally func (h *Handler) GetMediaReadingProgress(c echo.Context) error { mediaItemID := c.Param("id") // Extract user_id from JWT // Call h.GetUniversalProgress with same IDs // Transform response if needed } ``` **Pros**: - No breaking changes - Gradual migration path - Single implementation (universal routes) **Cons**: - Maintains API surface area - Slight performance overhead (function call) - Still confusing to have two endpoints ### Option 3: Unified Endpoint (Best of Both) **Action**: Create a single endpoint that handles both use cases. **Proposed**: ``` GET /api/media-items/:id/progress?format=universal PUT /api/media-items/:id/progress?format=universal DELETE /api/media-items/:id/progress ``` The `format` query parameter determines: - `format=simple` (default): Returns basic page/percentage (legacy behavior) - `format=universal`: Returns full device-aware progress with metadata **Pros**: - Single endpoint - Backward compatible - Clear migration path via query parameter - RESTful structure (nested under media-items) **Cons**: - More complex handler logic - Need to maintain both formats in response ### Option 4: Versioned API (Cleanest Long-Term) **Action**: Use API versioning to separate old and new. **Proposed**: ``` # v1 (Legacy) GET /api/v1/media-items/:id/progress PUT /api/v1/media-items/:id/progress DELETE /api/v1/media-items/:id/progress # v2 (Universal) GET /api/v2/media-items/:id/progress PUT /api/v2/media-items/:id/progress GET /api/v2/media-items/:id/progress/history ``` **Pros**: - Clean separation - Can deprecate v1 independently - Standard industry practice - Clear migration documentation **Cons**: - Need to implement version routing - More upfront work - Maintenance of two versions temporarily --- ## Unanswered Questions for Discussion 1. **Are any clients currently using the legacy progress routes?** - If yes, which ones? (mobile app, web app, third-party integrations?) - Can they be updated easily? 2. **What does the `:id` parameter represent in universal progress routes?** - Is it still `media_item_id`? - Or is it a `reading_progress` record ID? - Need to check implementation to confirm 3. **Why was `/api/progress/:id` chosen instead of `/api/media-items/:id/universal-progress`?** - Flat structure vs nested structure design decision - Might indicate plans for progress to exist independently of media items? 4. **Is the legacy route implementation actually different, or just deprecated?** - Need to read the handler implementations to compare - They might be calling the same underlying code 5. **Should we maintain progress deletion functionality?** - Universal routes don't have DELETE - Is deleting progress a necessary feature? 6. **What's the timeline for removing legacy routes?** - Already marked deprecated, but when can we delete them? - Need to coordinate with mobile app releases 7. **How does the backward compatibility view affect performance?** - The `ebook_reading_progress` view requires JOINs - Is it used anywhere, or can it be dropped? --- ## Recommendations ### Immediate Actions (Discussion Phase) 1. **Audit Current Usage** - Search codebase for references to legacy routes - Check if any external documentation mentions these endpoints - Identify all clients (web, mobile, third-party) 2. **Compare Implementations** - Read handler code for both legacy and universal routes - Document differences in behavior - Determine if they're truly different or just deprecated wrappers 3. **Clarify API Contract** - Define what `:id` means in universal routes - Document expected request/response formats - Add examples for different device types 4. **Performance Analysis** - Query database to see how many records use legacy fields vs universal - Check if backward compatibility view is actually used - Benchmark query performance with/without views ### Future Considerations 1. **Choose a Migration Strategy** - Review Options 1-4 above - Consider breaking changes vs compatibility - Plan timeline based on client usage 2. **API Versioning Decision** - Decide if we want `/api/v1/` and `/api/v2/` structure - Or use different approach (headers, content negotiation) 3. **Documentation Updates** - Update API_REFERENCE.md with clear deprecation notices - Add migration guide for clients - Document best practices for progress tracking 4. **Test Coverage** - Ensure both legacy and universal routes have comprehensive tests - Add integration tests for cross-device sync scenarios - Test conflict resolution workflows --- ## Next Steps for Discussion 1. **Review handler implementations** to understand actual differences 2. **Check client usage** (web app, mobile apps, Bruno tests) 3. **Decide on migration timeline** and breaking change tolerance 4. **Choose unified strategy** (Options 1-4 or hybrid) 5. **Plan implementation** with backward compatibility in mind --- *Document created for future discussion. No changes to be made without review.*