Commit Graph
232 Commits
Author SHA1 Message Date
john-okeefe a92bf99aee feat(dashboard): implement Phase 10.5 Custom Section Builder
Phase 10.5.1: Add /custom-section frontend route
- Added route handler in internal/router/frontend.go
- Fetches user libraries and renders custom section builder template

Phase 10.5.2: Create custom section builder template
- Created templates/custom_section.templ with full UI
- Includes section details form, filter rules builder, manual book selection
- Live preview functionality with preview container
- Form actions for save/cancel

Phase 10.5.3: Create custom-section-builder TypeScript
- Created web/src/custom-section-builder.ts with 13+ filter fields
- Filter fields: title, author, genre, series, progress, rating, date_added, last_read, publisher, language, format, tags, narrators
- Procedural/imperative style (no OOP) as per guidelines
- Rule builder with AND/OR logic support
- Book search and multi-select functionality
- Live preview via /api/collections/preview endpoint
- Form validation and submission to /api/collections

Phase 10.5.4: Build TypeScript modules
- Compiled custom-section-builder.ts to web/static/custom-section-builder.js
- Verified successful compilation with no errors
- All existing TypeScript modules continue to compile

Phase 10.5.5: Add Bruno tests for custom section creation
- create-custom-section-rules.bru: Test creating section with filter rules
- create-custom-section-manual.bru: Test creating section with manual book selection
- create-custom-section-missing-fields.bru: Test error handling for missing required fields

Phase 10.6: Build Verification
-  TypeScript modules compile successfully
-  Templates generate successfully
-  Go build succeeds with no compilation errors
-  All build artifacts verified (dashboard.js, custom-section-builder.js, dashboard_templ.go, custom_section_templ.go)

This completes the Custom Section Builder feature, allowing users to create
personalized dashboard sections with flexible filter rules or manual book selection.
2026-02-19 21:22:15 -05:00
john-okeefe 29e9f66c71 feat(dashboard): implement Phase 4.5 Collections Preview Endpoint
Add preview endpoint for custom section builder and rule evaluation:

Handler Implementation (internal/handlers/collections.go):
- PreviewCollection method: Evaluates filter rules and returns matching items without saving
  * Accepts library_id, rules array, manual_book_ids array, and limit
  * Evaluates rules against all library items using collectionService.EvaluateRules
  * Adds manually selected books to results
  * Deduplicates manual books (avoids adding same book twice)
  * Applies limit (default: 20, max: 100)
  * Returns array of BookInfo with matching items
- Helper function: mediaItemsToListMediaItemsRow
  * Converts database.MediaItems to database.ListMediaItemsRow
  * Required for EvaluateRules which expects ListMediaItemsRow type

Route Registration (internal/router/collections.go):
- POST /api/collections/preview
- Protected by JWT middleware
- Part of collections API group

Why This Endpoint is Necessary:
- Allows users to see what books match their filter rules BEFORE saving
- Avoids creating incorrect collections
- Enables testing different rule combinations quickly
- Reuses existing service logic (collectionService.EvaluateRules)
- Client-side preview would require downloading entire library (10,000+ books)
- Would duplicate 500+ lines of rule evaluation logic in TypeScript
- Would create maintenance nightmare keeping Go and TypeScript in sync

Bruno Test (bruno/collections/preview-collection.bru):
- Tests POST /api/collections/preview endpoint
- Validates status 200 response
- Validates items array in response
- Example request with genre filter rule

This endpoint is required for both the web UI Custom Section Builder and future mobile apps.
2026-02-19 21:15:03 -05:00
john-okeefe 77c7ef965f feat(dashboard): implement Phase 8 SSR template routes for Carousel-style dashboard
Update /dashboard route in frontend.go to use unified collections architecture:

Route Changes:
- Use DashboardService to fetch user dashboard preferences
- Get all dashboard sections (system + user collections)
- Pass sections and library data to template
- Support library_id query parameter for library switching
- Default to first visible library if no library_id specified

Service Integration:
- cfg.DashboardService.GetDashboardPreferences: Fetch user preferences
  * hidden_collections: Collections to hide from dashboard
  * collection_order: Custom collection ordering
  * items_per_section: Number of items per collection
- cfg.DashboardService.GetDashboardSections: Fetch all sections
  * System collections (user_id = NULL): continue-reading, recently-added, recently-read, not-started
  * User collections: User-created collections marked for dashboard
  * Applies user preferences: filters hidden, reorders, sorts by priority
- handlers.BuildSections: Convert service types to handler types

Data Flow:
1. Get user template data with theme
2. Get library_id from query param or default to first library
3. Fetch user dashboard preferences
4. Fetch dashboard sections with preferences applied
5. Convert to handler types for template rendering
6. Render template with sections and library data

Template Signature Change:
- OLD: templates.Dashboard(user)
- NEW: templates.Dashboard(user, sections, libData, currentLibraryID)

This implements Phase 8: SSR Template Routes with unified collections architecture.
2026-02-19 21:11:40 -05:00
john-okeefe 380af685dc feat(dashboard): implement Phase 7 router registration and config setup
Add DashboardService and DashboardHandler to application configuration:

Router Config Updates (internal/router/router.go):
- Add services import for DashboardService type
- Add DashboardService field to Config struct
- DashboardService: Used by SSR routes in frontend.go for data fetching
- DashboardHandler: Used by API routes in dashboard.go for JSON endpoints

Server Initialization (cmd/server/main.go):
- Create dashboardService instance using services.NewDashboardService(queries)
- Keep dashboardHandler creation (already exists from Phase 4)
- Add DashboardService to routerConfig
- Both services now available for dependency injection

Test Helpers (cmd/server/tests/test_helpers.go):
- Create dashboardService instance for testing
- Create dashboardHandler instance for testing
- Add both DashboardService and DashboardHandler to routerConfig
- Ensures test environment matches production setup

Architecture Rationale:
- DashboardService: Service layer with business logic (reusable by SSR, mobile)
- DashboardHandler: HTTP handler layer (JSON API endpoints)
- Separation allows SSR templates to call service directly
- API routes use handler for proper HTTP response handling
- Mobile apps can use API endpoints via DashboardHandler

All three files updated consistently for complete integration.
2026-02-19 21:06:23 -05:00
john-okeefe 91288a0695 feat(dashboard): register dashboard API routes
Add dashboard route registration and wire up handler:

Router Changes:
- Add DashboardHandler to router.Config struct
- Create internal/router/dashboard.go with dashboard route registration
- Register dashboard routes in main RegisterRoutes function

Dashboard Routes (all protected by JWT):
- GET /api/dashboard/sections: Get dashboard sections for user
  * Query params: library_id (required), limit (optional, default 20, max 100)
  * Returns: JSON with sections array
- PUT /api/dashboard/preferences: Update dashboard preferences
  * Body: library_id, hidden_collections, collection_order, items_per_section
  * Returns: Updated preferences
- POST /api/dashboard/restore-system-collection: Restore system collection to defaults
  * Body: collection_name (must be valid system collection)
  * Returns: Success message

Server Integration:
- Create dashboardHandler in cmd/server/main.go
- Add dashboardHandler to routerConfig
- Routes are automatically registered on server startup
2026-02-19 20:59:24 -05:00
john-okeefe 810316694a feat(dashboard): implement Phase 4 API handlers for Carousel-style dashboard
Add dashboard API endpoints with handler layer:

Step 1: Add SectionData to collections.go
- SectionData struct represents dashboard section (carousel of books)
- Used by: Dashboard handler, Templates (SSR), API JSON responses
- Shared type from collections.go (no duplicate definitions)
- Fields: ID, IsSystem, Title, Description, Icon, Items, ViewAllURL, Priority

Step 2: Create dashboard.go handler
- DashboardHandler struct with injected database and dashboard service
- GetSections: Returns dashboard sections as JSON (mobile apps, web UI TypeScript, plugins)
  * Validates library_id parameter
  * Fetches user dashboard preferences
  * Configurable limit (default 20, max 100)
  * Calls service layer for business logic
  * Converts service types to handler types for JSON serialization
- UpdatePreferences: Saves dashboard preferences
  * Validates library_id
  * Upserts user dashboard preferences
- RestoreSystemCollection: Resets system collection to defaults
  * Validates collection_name against allowed system collections
  * Deletes user's copy (system collection reappears automatically)
- BuildSections: Converts service DashboardSection to handler SectionData
  * Converts database.MediaItems to handlers.BookInfo
  * Uses shared types from collections.go
- getViewAllURL: Maps system collections to their view-all URLs
- Reuses existing textToString helper from collections.go

Architecture Compliance:
- Generic API handler for reuse by SSR, mobile, plugins
- Uses shared types from collections.go (SectionData, BookInfo)
- IsSystem bool matches database field (no string conversion)
- Single service method returns structured data (simpler, less bugs)
- Handler just converts types (no matching logic needed)
- Reusable by mobile apps, web UI, plugins
2026-02-19 20:59:19 -05:00
john-okeefe 336f5fc6d4 feat(dashboard): implement Phase 2 dashboard service layer
Create DashboardService with business logic for Carousel-style dashboard:

Service Methods:
- NewDashboardService: Create service instance with injected dependencies
- GetDashboardSections: Fetch all collections (system + user) with their items
  * Gets system collections (user_id = NULL) by query type
  * Gets user collections with manual + auto-assigned items
  * Filters hidden collections based on user preferences
  * Reorders collections based on user custom order
  * Sorts by priority if no custom order exists
- GetDashboardPreferences: Fetch user dashboard preferences for library
- UpsertDashboardPreferences: Save or update user dashboard preferences
- RestoreSystemCollection: Reset user's copy of system collection to defaults

Helper Methods:
- filterHiddenCollections: Remove hidden collections from results
- reorderCollections: Reorder sections based on user preference
- sortByPriority: Sort sections by priority (lower numbers first)
- getCollectionItemsByQueryType: Return items for system collections by query type
- getUserCollectionItems: Return items for user collections (manual + auto-assign)

Type Conversion Helpers:
- mediaItemsToListMediaItemsRow: Convert MediaItems to ListMediaItemsRow for rule evaluation
- getCollectionItemsRowToMediaItems: Convert GetCollectionItemsForDashboardRow to MediaItems

Architecture Compliance:
- Service layer holds all business logic (reusable by SSR, API, mobile)
- Returns database types (type safety at DB layer)
- Handler converts to API types (clean JSON contracts)
- Uses existing database queries and collection service
- Procedural/imperative style (no OOP)
- Follows existing pattern from collections.go
2026-02-19 20:56:13 -05:00
john-okeefe dd1e56d2f7 chore(dashboard): regenerate database code from Phase 3 queries
Run sqlc generate to create Go code for dashboard queries:
- GetDashboardPreferences / UpsertDashboardPreferences / UpdateDashboardPreferences
- GetSystemCollectionsForDashboard / GetUserCollectionsForDashboard
- DeleteUserSystemCollection
- GetContinueReadingItems / GetRecentlyAddedItems / GetRecentlyReadItems / GetNotStartedItems
- GetCollectionItemsForDashboard / GetLibraryItems

Auto-generated from queries.sql changes.
2026-02-19 20:55:59 -05:00
john-okeefe 1f80f6acfd feat(dashboard): add Phase 3 database queries for Carousel-style dashboard
Add SQL queries for dashboard functionality and system collections:

Dashboard Preferences Queries:
- GetDashboardPreferences: Fetch user preferences for a library
- UpsertDashboardPreferences: Create or update user dashboard preferences
- UpdateDashboardPreferences: Update existing preferences

Dashboard Collections Queries:
- GetSystemCollectionsForDashboard: Fetch system collections (user_id IS NULL)
- GetUserCollectionsForDashboard: Fetch user collections marked for dashboard
- DeleteUserSystemCollection: Delete user's copy of a system collection

System Collection Smart Queries:
- GetContinueReadingItems: Books with 0 < progress < 1
- GetRecentlyAddedItems: Newly added items to library
- GetRecentlyReadItems: Books with progress >= 1
- GetNotStartedItems: Books with progress = 0 or no record

Collection Management Queries:
- GetCollectionItemsForDashboard: Fetch collection items with excluded flag
- GetLibraryItems: Fetch all items in a library

These queries support the unified collections architecture where system
defaults and user-created sections are both collections with user_id
NULL for system-owned and NOT NULL for user-created.
2026-02-19 20:55:54 -05:00
john-okeefe 3af2fb0ba4 schema(dashboard): implement Phase 1 unified collections architecture
Add support for Carousel-style dashboard with unified collections architecture:

Database Schema Changes:
- Add user_dashboard_preferences table:
  - hidden_collections: TEXT[] for managing section visibility
  - collection_order: TEXT[] for custom ordering
  - items_per_section: INT for limiting items per section
- Update collections table:
  - user_id: Make nullable to support system-owned collections (NULL = system)
  - show_on_dashboard: BOOLEAN for controlling visibility
  - query_type: TEXT for different query types (continue-reading, recently-added, etc.)
  - priority: INT for display order (lower = higher priority)
  - is_system_collection: BOOLEAN for flagging system defaults
- Update collection_items table:
  - Add excluded BOOLEAN for user overrides of auto-assigned items

Indexes:
- idx_collections_dashboard: (user_id, show_on_dashboard, priority) WHERE show_on_dashboard = true
- idx_dashboard_prefs_user_library: (user_id, library_id)
- idx_collection_items_excluded: (collection_id, excluded) WHERE excluded = true

System Collections (pre-seeded defaults):
- continue-reading: Books with 0 < progress < 1
- recently-added: Newly added items to library
- recently-read: Books with progress >= 1
- not-started: Books with progress = 0 or no record

This implements Phase 1 of the Carousel-style dashboard redesign plan.
2026-02-19 20:52:21 -05:00
john-okeefe fce16b53f7 fix(auth): return JSON for HTMX login failures instead of HTML 2026-02-16 21:08:15 -05:00
john-okeefe 7b07645ee2 feat(auth): show session expired message on login page
- Update Login template to accept sessionExpired boolean parameter
- Add conditional message box when session=expired query param present
- Update /login route handler to parse session query param
- Pass sessionExpired flag to Login template
- Regenerate login_templ.go with new signature

Displays friendly message: "Your session has expired. Please log in
again to continue." when users are redirected due to expired sessions.
2026-02-16 16:50:10 -05:00
john-okeefe 7952bc7f6a feat(router): add smart 401 error handler for HTML vs API requests
- Add strings import for Accept header parsing
- Add wantsHTML() helper function to detect HTML vs API requests
  - Checks Accept header for text/html
  - Checks HX-Request header for HTMX requests
  - Checks X-Requested-With for AJAX (should return JSON)
  - Defaults to JSON for API routes
- Update JWT middleware ErrorHandler to:
  - Redirect HTML requests to /login?session=expired
  - Return JSON error for API requests with session_expired message
- Enables browser navigation to redirect gracefully while API calls
  return proper error responses

This fixes the issue where protected routes returned JSON 401
for browser navigation instead of redirecting to login.
2026-02-16 16:49:54 -05:00
john-okeefe 2e1af8d20b feat(auth): extend session duration to 7 days using constants
- Add SessionDuration constant (7 days) and SessionDurationSec computed value
- Update JWT token expiration to use SessionDuration instead of 1 hour
- Update register/login cookie MaxAge to use SessionDurationSec (604800)
- Update register/login API response ExpiresIn to use SessionDurationSec
- Update refresh token endpoint ExpiresIn to use SessionDurationSec
- Remove redundant client-side document.cookie lines from login/register
- Add TODO comment for HTTPS cookie Secure flag

This provides Google-like persistent sessions with a single source of truth
for session duration, eliminating hardcoded values throughout the codebase.
2026-02-16 16:49:43 -05:00
john-okeefe b5156bbe16 feat: add HTTP-only cookie for browser authentication
- Set HTTP-only cookie in login handler for SSR authentication
- Set HTTP-only cookie in registration handler
- Change default redirect from /bookshelf to /dashboard
- Cookie enables browser page navigation without JavaScript
2026-02-15 21:36:42 -05:00
john-okeefe 6b3ccdfc55 feat: add protected frontend SSR routes
- Add frontendProtected group for authenticated pages
- Add /dashboard, /collections, /progress, /devices, /conflicts, /analytics routes
- Add /admin, /admin/, /admin/profile, /admin/library routes
- Keep legacy /api/devices-page and /api/conflicts-page for backward compatibility
- All routes use JWT middleware for authentication
2026-02-15 21:36:36 -05:00
john-okeefe 1803ac2ee7 feat: add ScannerHandler to router Config
- Add ScannerHandler field to Config struct for frontend route access
- Move scannerHandler creation before registerFrontendRoutes call
- Enables /progress page to access scanner data
2026-02-15 21:36:30 -05:00
john-okeefe b682f09fbc feat: add static file serving and theme safelist
- Serve static files from web/static directory
- Add theme class safelist to Tailwind config for dynamic theming support
- Regenerate CSS with updated configuration
2026-02-15 16:53:34 -05:00
john-okeefe 6346e9bc27 test: add new handler test files for analytics, auth, kobo, library, progress, and sidecar 2026-02-14 21:38:08 -05:00
john-okeefe acd194c217 test: add device token validation and text utility tests 2026-02-14 21:38:00 -05:00
john-okeefe 557f057621 fix(db): cast status to varchar in sync queue update for proper enum comparison 2026-02-14 21:37:38 -05:00
john-okeefe d7ab22c399 fix(auth): add jti claim to JWT tokens for unique token identification 2026-02-14 21:37:29 -05:00
john-okeefe 02ff078adf fix: validate UUIDs in OPDS middleware before authentication
- Add UUID validation in device_auth middleware for OPDS routes
- Return 400 Bad Request for invalid device/book IDs instead of 401
- Remove redundant UUID validation from OPDS handlers (middleware handles it)
2026-02-14 00:12:15 -05:00
john-okeefe 030e8c87e3 fix: normalize negative offset to zero in media filter 2026-02-14 00:12:08 -05:00
john-okeefe 2706ae52c1 refactor: remove Phase X terminology from source code comments
Remove planning document phase references from code comments:

app_test.go:
- Remove Phase 5 references from 8 test function comments

querier.go & queries.sql.go:
- Remove Phase 1, 2, 3, 4, 6 references from section headers
- Clean up week numbers (Weeks 5-6, Week 3-4, etc.)

queries.sql:
- Remove Phase 4 references from Kobo queries

kobo.go:
- Remove Phase 6 references from ContentId mapping comments

progress.go:
- Remove Phase 1 reference from route comment

media_scanner.go & media_scanner_library_type_test.go:
- Remove Phase 2 references from library type scanning comments

schema.sql:
- Remove Phase 1, 2, 3, 4, 5, 7 references from table/section comments
- Clean up: Format Detection, Progress Tracking, Device Registry,
  Sync Queue, Conflict Resolution, Reading History, Indexes, etc.

test_helpers.go:
- Remove Phase 6 reference from handler setup comment

These phase numbers were from internal planning documents and have no
meaning in the codebase. Removing them makes the code self-documenting.
2026-02-13 21:50:29 -05:00
john-okeefe 368c790c67 refactor(tests): enhance test infrastructure with library/collection helpers
- Add LibraryTestData struct to TestDeviceSetup
- Implement CreateLibrary() for proper library creation in tests
- Implement CreateCollection() for test collection support
- Improve test isolation with dedicated library creation

This provides a more robust foundation for integration tests that need
proper library management support.
2026-02-13 20:04:47 -05:00
john-okeefe ed5b4c4ca1 fix: add error handler to JWT middleware for better API responses
- Improve error response format for authentication failures
- Return consistent JSON error messages
- Enhance API client experience
2026-02-13 16:37:54 -05:00
john-okeefe 289284522b test: add test reliability plan and device test coverage
- Add TEST_RELIABILITY_PLAN.md documenting test strategy
- Add devices_test.go with device handler tests
- Add device_auth_test.go with device authentication middleware tests
2026-02-13 16:37:54 -05:00
john-okeefe e9cd445ff3 feat: add device token management UI
- Display sync URLs for Kobo devices with copy button
- Display auth tokens for KOReader devices with copy button
- Add regenerate token button with confirmation
- Show warning about token invalidation
2026-02-13 12:12:38 -05:00
john-okeefe b1fcf2ce95 feat: support multiple device authentication methods
- Bearer token in Authorization header (KOReader, API clients)
- URL path parameter (Kobo sync: /api/sync/kobo/:token/...)
- Query parameter (OPDS: ?token=...)
- Update Kobo sync routes to use token in path
- Add authentication method documentation to OPDS routes
2026-02-13 12:12:36 -05:00
john-okeefe 81fbcfac11 feat: add RegenerateDeviceToken API endpoint
- Add handler to regenerate device auth tokens
- Add PUT /api/devices/:id/regenerate-token route
- Returns new token and sync URLs for device configuration
2026-02-13 12:12:28 -05:00
john-okeefe 8321149957 test: add Bruno API test collections for device authentication
- Device token regeneration tests (success, forbidden, not found, unauthorized)
- OPDS authentication tests (Bearer token, query token)
- Kobo sync tests with token authentication
- Test various authentication methods and error cases
2026-02-13 12:12:17 -05:00
john-okeefe 2a64ca423f Refactor: Eliminate duplicate types - Use handler types directly
- Deleted templates.CollectionDetailData - using templates.CollectionData everywhere
- Deleted templates.BookData - using handlers.BookInfo everywhere
- Deleted templates.DeviceData - using handlers.DeviceInfo everywhere
- Deleted templates.ProgressItemData - using handlers.ProgressWithMedia everywhere
- Deleted templates.convertDevices() helper - Use handlers types directly in templates
- Enhanced handlers.ProgressWithMedia with device metadata fields
- Added handlers.getDeviceIcon() helper
- Updated all templates to import handlers package
- Cleaned up unused imports

This aligns codebase with templ's design philosophy (use Go types directly, no parallel type system)
2026-02-12 19:48:07 -05:00
john-okeefe c156176988 fix(opds): Require device authentication for OPDS catalog endpoints
- Apply DeviceAuthMiddleware.Authenticate to /opds/devices/* routes
- OPDS now uses same authentication model as sync API (devices.auth_token)
- Removes security vulnerability allowing unauthorized device enumeration
- Update test expectations to require 401 for unauthenticated requests
- Fix query parameter name from 'query' to 'q' in search endpoints
- Update router comments to clarify authentication requirements
2026-02-11 18:40:14 -05:00
john-okeefe 249884435c fix: allow media item creation with invalid ISBN and stabilize test
- Allow media items to be created/updated with invalid ISBN by storing empty string
- Fix test to use valid ISBN-13 format (9780306406157)
- Add small delay to prevent race condition in pagination test
2026-02-11 18:09:42 -05:00
john-okeefe 0f8db2ab07 Add ISBN-10 to ISBN-13 validation and conversion
Enhance NormalizeISBN to validate and convert ISBNs:
- Validate length (10 or 13 digits), return error if invalid
- Convert ISBN-10 to ISBN-13 by prefixing '978' and recalculating checksum
- Add NormalizeISBNSafe for backward compatibility in scanners

This ensures all ISBNs stored in database are valid ISBN-13 format.
2026-02-11 09:42:18 -05:00
john-okeefe bddb411a3d fix: add validation for empty media_item_id in bulk update
Add strict validation to return 400 Bad Request when any media_item_id
is empty in the bulk-update request, rather than treating it as a
partial failure with 200 OK.

This aligns the handler behavior with test expectations for the
BulkUpdateBooks_EmptyBookIDs test case.
2026-02-10 20:51:23 -05:00
john-okeefe 03225cf6e2 refactor: rename bulk operations from /api/books/ to /api/media-items/
- Rename routes: /api/books/bulk-{delete,update} → /api/media-items/bulk-{delete,update}
- Rename route: /api/books/:uuid/download → /api/media-items/:uuid/download
- Update request fields: book_ids → media_item_ids
- Update response fields: success → deleted/updated
- Update result fields: book_id → media_item_id
- Update collection handler: success → added

This change improves API semantic correctness as the system handles
multiple media types (ebooks, comics, manga), not just books.

BREAKING CHANGE: All bulk operation endpoints and field names renamed
2026-02-10 19:56:59 -05:00
john-okeefe 1f65933653 fix: handle schema.sql file path in containerized environment
- Try multiple locations for schema.sql file
- Support both local dev and containerized deployment paths
- Add informative logging when schema is loaded
- Prevent runtime.Caller issues in containers

Locations checked:
- database/schema/schema.sql (working directory)
- /app/database/schema/schema.sql (container)
- ../database/schema/schema.sql (relative)
- ../../database/schema/schema.sql (relative)

This fixes the 'no such file or directory' error in production containers.
2026-02-10 16:52:12 -05:00
john-okeefe 9d1a1e0228 feat: add schema initialization logic (Phase 2)
- Create internal/database/schema.go with full initialization logic
- Parse table names from schema.sql using regex (handles both formats)
- Execute schema in atomic transaction
- Verify all expected tables exist
- Verify all critical functions exist (6 functions)
- PostgreSQL advisory locking with 30-second timeout
- Self-healing from partial/corrupted state
- Load schema.sql from filesystem at runtime

Features:
- Defensive regex handles IF NOT EXISTS and legacy CREATE TABLE
- Lock timeout prevents indefinite hangs
- Function verification ensures sync operations work
- Clear error messages with debug hints
2026-02-10 16:48:28 -05:00
john-okeefe 80ad45e7f9 feat(handlers): Add Kobo sync enhancements and last-read-place support
- Add nil UUID checks after mapContentIdToBookhoardUUID in all handlers
- Add ContentType detection for Kobo EPUB/PDF sync (EPUB=6, PDF=5)
- Add "last-read-place" bookmark type support with EPUB CFI position tracking
- Restore broken mapContentIdToBookhoardUUID function with UUID parsing
- Restore mapBookhoardUUIDToKoboContentId helper function
- Restore getCollectionMetadataForBook helper function

This fixes the catastrophic file corruption from commit 2200720 which
deleted 414 lines and inserted code in the wrong location.

Phase 1-3 of KOBO_IMPLEMENTATION_PLAN.md completed:
- Step 4: Nil UUID checks in Markup, Bookmark, AnalyticsGettests, SyncFromServer
- Step 5: ContentType field added to KoboReadingSync struct
- Step 6: last-read-place case added to Markup handler switch statement

Testing: Code compiles successfully, all handlers properly structured
2026-02-10 12:41:04 -05:00
john-okeefe 82a5cf70f2 fix(middleware): Correct rate limit header type conversion
- Fix string(rune(remaining)) to strconv.Itoa(remaining) in device_auth.go
- Prevents garbage characters in X-RateLimit-Remaining header
- No functionality changes, only fixes broken headers

Testing: Verified with code inspection that headers return proper integers
2026-02-10 12:40:52 -05:00
john-okeefe 2200720537 fix(middleware): Correct rate limit header type conversion 2026-02-10 12:06:12 -05:00
john-okeefe 1413c75b26 fix: Fix device response fields and add missing approved confirmation
feat: Improve device test infrastructure with setupDeviceTest helper

refactor: Standardize pending registrations API response field names
2026-02-10 09:31:35 -05:00
john-okeefe 0551f17f0e refactor(scheduler): migrate from per-user to system-wide scan settings
Update scheduler to use system-wide settings instead of per-user:
- Change Database interface to use GetSystemSetting
- Remove GetScanSettings (per-user method)
- Update checkAndScheduleScans to read system settings
- Apply system-wide scan frequency to all libraries

Scheduler now respects global scan settings for all library scanning,
enabling consistent system-wide scan behavior.
2026-02-09 20:10:40 -05:00
john-okeefe 938e5eed53 feat(router): add system settings routes and handler wiring
Router configuration updates:
- Add SystemSettingsHandler to Config struct
- Register GET /api/libraries/scan-settings (admin-only)
- Register PUT /api/libraries/scan-settings (admin-only)
- Wire SystemSettingsHandler in main.go

These routes replace per-user scan settings endpoints with
system-wide admin-only endpoints.
2026-02-09 20:10:30 -05:00
john-okeefe 4488ed5d16 refactor(handlers): remove per-user scan settings handlers
Clean up auth.go after migrating to system-wide settings:
- Remove UpdateScanSettings handler (moved to system_settings.go)
- Remove GetScanSettings handler (moved to system_settings.go)
- Remove UpdateScanSettingsRequest type (now in system_settings.go)

These handlers are now in SystemSettingsHandler with system-wide scope
instead of per-user functionality.
2026-02-09 20:10:18 -05:00
john-okeefe 3266093248 feat(handlers): add system-wide scan settings handler
Create new SystemSettingsHandler for managing system-wide scan settings:
- GetScanSettings: retrieve scan frequency and auto-scan status
- UpdateScanSettings: update scan settings (15-1440 minutes range)
- Admin-only access (no user-specific data)
- Key-value based storage instead of per-user settings

Replaces per-user scan settings with centralized system configuration.
This handler is used by /api/libraries/scan-settings endpoints.
2026-02-09 20:10:06 -05:00
john-okeefe b506046be0 chore(db): regenerate database code from sqlc
Auto-generated changes from running 'sqlc generate' after query updates:
- models.go: updated with SystemSettings struct, removed scan fields from Users
- querier.go: updated interface with new system settings methods
- queries.sql.go: regenerated with new query methods

Generated via: cd internal/database && sqlc generate
2026-02-09 20:09:58 -05:00
john-okeefe 363e747cb3 feat(db): add system settings queries and enhance user queries
System Settings Migration:
- Add GetSystemSetting query for single setting retrieval
- Add UpdateSystemSetting query for updating settings
- Add GetAllSystemSettings query for all settings
- Remove UpdateScanSettings and GetScanSettings (per-user queries)

User Query Enhancement:
- Add max_devices field to GetUser query
- Add device_count computed field to GetUser query
- Add max_devices field to ListUsers query
- Add device_count computed field to ListUsers query

These changes support:
1. System-wide scan settings instead of per-user settings
2. Users can now see their device count and limits
3. Admins can monitor device usage across all users
2026-02-09 20:09:48 -05:00