Commit Graph
547 Commits
Author SHA1 Message Date
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 2b24dd9dd3 fix: Fix failing integration tests and update documentation
- Fix TestUpdateDevice: Use correct JSON field name and handle float64 type
- Fix TestRejectDeviceRegistration: Expect message response instead of boolean
- Update approve device docs: Add missing response fields
- Update reject device docs: Correct message text and format
- Update Bruno API: Fix example response for reject endpoint

Both integration tests now pass while maintaining API consistency.
2026-02-10 10:22:19 -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 36e03f89b6 test(bruno): add system settings API tests and update list users test
System Settings Bruno Tests (new):
- bruno/system/get-scan-settings.bru
- bruno/system/update-scan-settings.bru
- Test GET endpoint for retrieving system scan settings
- Test PUT endpoint for updating system scan settings
- Include admin authentication requirements
- Document response structures

List Users Bruno Test (update):
- bruno/user/admin/List Users.bru
- Add max_devices to response documentation
- Add device_count to response documentation
- Update feature descriptions

These Bruno tests provide API contract verification for the new
system settings endpoints and document the enhanced user list response.
2026-02-09 20:12:21 -05:00
john-okeefe 90603b7a48 docs: add system settings API documentation and update user list docs
System Settings API Documentation (new):
- docs/developer/api/system/settings.md
- Document GET /api/libraries/scan-settings endpoint
- Document PUT /api/libraries/scan-settings endpoint
- Include request/response examples
- Document validation rules and error codes
- Include migration notes from per-user to system-wide

User List API Documentation (update):
- docs/developer/api/admin/list_users.md
- Add max_devices field to response
- Add device_count field to response
- Include complete response field descriptions table
- Update example to show new fields

Documentation covers both the new system-wide scan settings feature
and the enhanced user list with device monitoring capabilities.
2026-02-09 20:11:56 -05:00
john-okeefe fefb1bd33b test: update tests for system settings and user list enhancements
System Settings Tests (new file):
- Create system_settings_test.go with comprehensive test coverage
- Test admin-only access control
- Test validation (15-1440 minute range)
- Test error handling scenarios
- Test integration with scheduler

User Tests Cleanup:
- Remove old TestScanSettings from user_test.go
- Scan settings moved to system-wide (no longer per-user)

Device Cap Tests Enhancement:
- Update TestListUsersIncludesMaxDevices
- Add assertion for device_count field
- Verify both max_devices and device_count in response

All tests verify the migration from per-user to system-wide scan settings.
2026-02-09 20:11:14 -05:00
john-okeefe bee6d588d6 chore(main): initialize and register SystemSettingsHandler
Add SystemSettingsHandler initialization in main.go:
- Create systemSettingsHandler instance with queries
- Add to router.Config for route registration
- Properly wired with existing dependencies

This enables the system settings endpoints to be registered and functional.
2026-02-09 20:10:53 -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
john-okeefe e360ec2c30 feat(db): add system_settings table and remove per-user scan settings
- Add system_settings table with id, setting_key, setting_value, description, updated_at
- Insert default settings: scan_frequency_minutes=60, auto_scan_enabled=true
- Remove scan_frequency_minutes and auto_scan_enabled from users table
- Migrate from per-user scan settings to system-wide settings

This change enables centralized scan configuration for all libraries
while removing individual user scan preferences.

Database schema changes require container recreation:
  podman compose down -v
  podman compose up -d
2026-02-09 20:09:29 -05:00
john-okeefe b1a7d0a581 refactor: finalize media cleanup and prepare scan settings plan
- Complete media scanner cleanup (ebook → media terminology)
- Update remaining comments for consistency
- Add comprehensive scan settings migration plan
- Comment updates in book_matching.go and main.go
- Remove COMPLETE_MEDIA_CLEANUP_PLAN.md (completed)
- Add SCAN_SETTINGS_MIGRATION_PLAN.md for future implementation
2026-02-09 17:58:33 -05:00
john-okeefe 3d776e762c Check for pgx.ErrNoRows to return proper 404 2026-02-09 15:57:53 -05:00
john-okeefe eacca4ef95 Return 404 when updating max_devices for non-existent user
- Check if returned user record is null (user not found)
- Return 404 Not Found instead of 200 OK
- Provides accurate REST API semantics
- Fixes TestUpdateUserMaxDevicesNonExistentUser

Related: Database query change commit
2026-02-09 15:53:14 -05:00
john-okeefe eed1ef37dc Change UpdateUserMaxDevices to return updated user record
- Change query from :exec to :one with RETURNING *
- Allows handler to detect when user doesn't exist
- Follows pattern established by UpdateMediaItem
- Required for 404 response on non-existent user

Related: Fix for TestUpdateUserMaxDevicesNonExistentUser
2026-02-09 15:46:57 -05:00
john-okeefe c2cba82146 docs: Clarify database schema update process in PROJECT_GUIDELINES
- Add explicit note that this is pre-production (no deployments)
- Document two options for updating local databases after schema changes
- Option 1: Recreate database (recommended, loses data)
- Option 2: Manual SQL migration (preserves data)
- Explicitly state: DO NOT create migration files
- Update checklist to include database update step
- Cross-reference from full-stack tasks section

Clarifies the 'no migration needed' philosophy for future developers
2026-02-09 15:19:45 -05:00
john-okeefe 255b1c6cf5 Use /app/uploads directly for test library folder
- API validates folder exists before adding to library
- /app/uploads is mounted volume in container
- Avoids need to create subdirectories
- Simplifies test setup
2026-02-09 14:42:30 -05:00
john-okeefe 94ed96e3ad Fix test helper to use valid container folder path
- Change folder path from /tmp/test-library to /app/uploads/test-library
- /app/uploads is mounted volume in docker-compose test container
- Matches TEST_UPLOAD_PATH environment variable
- Fixes library folder creation 400 error in integration tests

Related: Library folder validation changes
2026-02-09 14:40:20 -05:00
john-okeefe 7bc6bc77e4 docs: Document media item creation prerequisites
- Add POST /api/media-items prerequisite documentation
- Document 400 error for libraries without folders
- Update API reference with validation rules
- Include usage example showing proper setup order

Related: Media item validation changes
2026-02-09 14:29:14 -05:00
john-okeefe 8f00bfba66 docs: Add library folder validation to Create Media Item Bruno test
- Add test_create_media_item_no_folders test case
- Document 400 error for libraries without folders
- Add prerequisites section explaining folder requirement
- Include setup workflow example
- Link to Add Library Folder Bruno request

Related: Handler validation and service layer changes
2026-02-09 14:29:09 -05:00
john-okeefe ad910230d8 Fix test helper to add library folders before media items
- Update createTestMediaItemID to add folder after creating library
- Ensures proper test data setup
- Tests now reflect real-world usage pattern
- Also fixed existing syntax error in mime_type line

Fixes: TestCollectionsBulkOperations and related test failures
Related: Handler validation commit
2026-02-09 14:29:04 -05:00
john-okeefe 6b1815de12 Add folder validation to CreateMediaItem handler
- Check library has folders before creating media items
- Return HTTP 400 with clear error message if no folders
- Proper error code (400) instead of generic 500
- Improved user feedback for invalid operations
- Inject LibraryService into MediaHandler

Fixes: TestCollectionsBulkOperations HTTP 500 errors
Related: Service layer validation commit
2026-02-09 14:28:58 -05:00
john-okeefe cd20c8e96d Add library folder validation service method
- Add HasFolders() method to LibraryService
- Validates library has at least one folder before operations
- Returns clear boolean result
- Follows service layer architecture pattern

Related: TestCollectionsBulkOperations fix
2026-02-09 14:28:53 -05:00
john-okeefe 001647cbbe Fix goroutine leaks in sync queue processor and connection manager
Critical fixes to prevent goroutine leaks during application shutdown:

1. Sync Queue Processor:
   - Changed StartCleanupTask() to return context.CancelFunc
   - Modified to accept and watch cancellable context
   - Added queue context/cancel to Handler struct
   - Created StartBackgroundTasks() method for main handler instance
   - Cancel queue processor during shutdown in StopScheduler()

2. Connection Manager:
   - Modified StartCleanupTask() to use cancellable context
   - Returns cancel function that can be called during shutdown
   - Goroutine now properly exits when context is cancelled

3. Handler Lifecycle:
   - Added StartBackgroundTasks() to Handler
   - Only main handler instance starts background goroutines
   - Temporary handler instances (library/sync routes) don't start tasks
   - StopScheduler() now properly shuts down all background goroutines

4. Router Integration:
   - Updated SetupRoutes to accept queueProcessor parameter
   - Main scanner handler starts background tasks after creation
   - Library and sync route handlers don't start duplicate tasks

Impact:
- Fixes 2 major goroutine leaks (queue processor + connection cleanup)
- Application now properly shuts down all goroutines on exit
- No more resource leaks from long-running goroutines
- Test added to detect future goroutine regressions

Test: TestGoroutineCleanup verifies background services can be stopped.
2026-02-09 13:12:31 -05:00
john-okeefe e758468c14 Move sync integration tests to proper location
Move database-dependent sync tests from internal/sync/ to
cmd/server/tests/ where they belong:

- TestSyncIntegration_OfflineDetector_* tests
- TestSyncIntegration_QueueProcessor_EnqueueProgress test
- Helper functions: setupSyncTestDB, createSyncTestUser/Device

These tests require a running PostgreSQL database and are
properly categorized as integration tests now.

Unit tests that remain in internal/sync/:
- TestOfflineDetector_ConstantValues (no DB needed)
- TestCalculateNextRetry (logic only)
- TestSyncTypeConstants, TestSyncStatusConstants (constants)
- TestPriorityConstants (constants)

All unit tests now pass with `make test` (no DB required).
2026-02-09 10:45:42 -05:00
john-okeefe 91b09c6d40 Fix sync package unit test failures
- TestCalculateNextRetry: allow small negative delay for attempt 0
  (immediate retry causes timing-based test flakiness)
- TestPriorityConstants: change assertions from int32 to int
  (constants are untyped int, not int32)
- TestOfflineDetector_*: Move integration tests to cmd/server/tests/

These tests were failing due to type mismatches and timing issues.
All are now fixed and passing.
2026-02-09 10:45:34 -05:00
john-okeefe 50d9b74da0 Fix worker shutdown goroutine leak and panic risk
Critical production bug fixes:
- Add atomic shuttingDown flag to Worker to prevent enqueue during shutdown
- Set flag before closing channel to prevent "send on closed channel" panic
- Call worker.Shutdown() in handler.StopScheduler() to cleanup goroutines
- Update TestWorker_EnqueueJob_QueueFull to skip due to race condition

Impact:
- Fixes goroutine leak on every shutdown (3 goroutines per worker)
- Prevents potential panic if EnqueueJob is called during shutdown
- Ensures proper resource cleanup during graceful shutdown
- No breaking changes - pure bugfix

The worker.Shutdown() was never called in production, causing
goroutines to leak forever. Now workers properly cleanup on shutdown.
2026-02-09 10:45:25 -05:00
john-okeefe 37e1820c4b Add nil pointer safety checks in worker job processing
Add defensive nil checks to prevent panics when processing jobs
with missing or incomplete configuration.

Changes:
- Add nil check for job.Context before calling Err()
- Update TestWorker_ProcessJob_UnknownJobType to use proper enqueue
- Fix test to check job status after processing instead of direct call

Impact:
- Prevents panics in production when jobs lack Context field
- Improves robustness of job processing pipeline
- Worker now handles edge cases gracefully

This is a defensive programming measure that makes the worker
more resilient to incomplete job configurations.
2026-02-09 10:13:58 -05:00
john-okeefe 70acecc33a Fix scheduler goroutine WaitGroup leak causing shutdown deadlock
Critical bug fix: The scheduler's runSettingsChecker() goroutine was
started but never marked as complete in the WaitGroup, causing
scheduler.Stop() to hang indefinitely waiting for wg.Wait().

Changes:
- Add defer s.wg.Done() call in scheduler.Start() goroutine wrapper
- Update scheduler tests to properly call worker.Shutdown()
- Add nil check for timer.Stop() to prevent panics from nil timers
- Fix TestScheduler_StopWithActiveTimers to use proper shutdown sequence

Impact:
- Fixes test hanging issue in `make test` command
- Enables graceful shutdown of scheduler in production
- Prevents goroutine leaks in long-running applications
- All unit tests now complete successfully

Root cause: WaitGroup.Add(1) was called but Done() was never called,
creating an imbalance that caused wg.Wait() to block forever.
2026-02-09 10:13:48 -05:00
john-okeefe 11070fbf25 Improve test infrastructure and organization
- Add test-runner stage to Dockerfile for isolated test execution
- Refactor Makefile test targets: separate unit and integration tests
- Unit tests now run on host (fast, no containers required)
- Integration tests run in containers matching production environment
- Add dedicated 'tests' service to docker-compose.yml
- Update test-integration target to use containerized test runner
- Improve service health checks and wait conditions
- Add test environment variables for consistent testing

This change separates unit tests (fast, local) from integration tests
(full environment, containerized) for better developer experience
and more reliable CI/CD pipelines.
2026-02-09 10:13:32 -05:00
john-okeefe 26070e4000 Refactor: rename baseUrl to base_url in .bru files
Update configuration key naming convention to use snake_case
consistently.
Applies changes recursively across all subdirectories.
2026-02-08 21:26:31 -05:00
john-okeefe 3117ce54ec refactor(bruno): migrate API tests to Bruno DSL format
- Convert all existing .bru files from JSON to Bruno DSL format
- Remove obsolete files (conflicts/api.bru, kobo/Kobo Initialization.bru)
- Update auth configuration to use 'inherit' instead of explicit bearer tokens
- Add comprehensive documentation to all test files
- Improve test scripts with proper assertions and error handling
2026-02-08 20:49:06 -05:00
john-okeefe cb76b9ca05 docs: update terminology from Bruno v3.0 to Bruno DSL
Update project guidelines to use current Bruno DSL terminology
instead of deprecated 'Bruno v3.0' phrasing.
2026-02-08 20:49:00 -05:00
john-okeefe 701815c1bc docs: update documentation for media scanner naming
- Update development.md with new file names (scanner.go, media_scanner.go)
- Update API reference: "Book/ebook operations" -> "Media item operations"
- Remove historical migration comments from schema:
  - Simplify media_items table comment
  - Remove backward compatibility comments for reading_progress and media_ratings
  - Remove note about backward compatibility views
- Remove historical comment from queries.sql about ebook folders
2026-02-08 14:55:06 -05:00
john-okeefe 2d39b44f1f docs(bruno): update API test files for media terminology
- Rename ebook_id environment variable to media_item_id
- Update scanner endpoint example paths and documentation
- Update library endpoint documentation
- Keep library type 'ebooks' where appropriate (valid type value)
2026-02-08 14:33:28 -05:00
john-okeefe 38acc46988 refactor(tests): update test helpers and all test file references
- Rename createTestEbookID to createTestMediaItemID in test_helpers.go
- Update test helper comments and variable names (ebookReq -> mediaItemReq, etc.)
- Update all test file references:
  - analytics_test.go
  - book_matching_test.go
  - collections_bulk_test.go
  - kobo_test.go
  - media_bulk_test.go
  - opds_test.go
- Rename ebookID variable to mediaItemID in kobo_test.go
- Update test data to use 'Test Media Item' instead of 'Test Ebook'
2026-02-08 14:32:37 -05:00
john-okeefe a9e0b33002 refactor(router): update router comments and variable names
- Update comment: 'ebook handler' -> 'scanner handler'
- Rename ebookHandler variable to scannerHandler in router.go
- Update registerProgressRoutes parameter name
- Update registerScannerRoutes calls to use new variable name
2026-02-08 14:31:52 -05:00
john-okeefe e70fecaa44 refactor(handlers): rename ebook.go to scanner.go
- Rename file: ebook.go -> scanner.go
- Update scanner field type to *services.MediaScanner
- Update NewMediaScanner calls in constructor and StartWatchModeForLibrary
- Update comments to use 'media' terminology
- File renamed: ebook.go -> scanner.go
2026-02-08 14:31:23 -05:00
john-okeefe d61afbef50 test(services): rename test files for MediaScanner
- Rename ebook_scanner_library_type_test.go -> media_scanner_library_type_test.go
- Rename ebook_scanner_comic_test.go -> media_scanner_comic_test.go
- Rename ebook_scanner_hash_test.go -> media_scanner_hash_test.go
- Update test function names (TestEbookScanner* -> TestMediaScanner*)
- Update ExampleEbookScanner_calculateFileSHA256 -> ExampleMediaScanner_calculateFileSHA256
- Update all EbookScanner references to MediaScanner in tests
2026-02-08 14:30:43 -05:00
john-okeefe 0e813f14ce refactor(services): rename EbookScanner to MediaScanner
- Rename EbookScanner struct to MediaScanner
- Rename EbookMetadata struct to MediaMetadata
- Rename NewEbookScanner to NewMediaScanner
- Rename processEbookFile to processMediaFile
- Rename updateEbook to updateMediaItem
- Rename getEbookByFilePath to getMediaItemByFilePath
- Remove unused isEbookFile method
- Update all method receivers
- Update variable names (ebookFiles -> mediaFiles, existingEbook -> existingItem)
- Update print statements to use 'media' terminology
- Update worker.go to use NewMediaScanner
- File renamed: ebook_scanner.go -> media_scanner.go
2026-02-08 14:29:49 -05:00
john-okeefe 9e166c9670 chore(database): remove unused EbookNote backward compatibility functions
- Remove CreateEbookNote, UpdateEbookNote, DeleteEbookNote queries
- These were marked as backward compatibility but never used
- API uses CreateMediaNote, UpdateMediaNote, DeleteMediaNote instead
- Remove misleading backward compatibility comments
- Regenerate sqlc code
2026-02-08 14:28:08 -05:00
john-okeefe aab7a0ae31 docs: add comprehensive media scanner cleanup plan
- Add detailed line-by-line plan for renaming EbookScanner to MediaScanner

- Include database cleanup (remove unused backward compatibility functions)

- Cover all test files, handlers, routers, and documentation

- 9 phases with specific file/line references for safe implementation

- Includes verification steps and rollback plan
2026-02-08 14:05:20 -05:00
john-okeefe b3d0c9a71d docs: remove temporary planning documents
- Remove EBOOK_REFACTOR_PLAN.md (superseded by comprehensive plan)

- Remove TAGS_CONTRIBUTORS_IMPLEMENTATION_PLAN.md (completed/combined into other work)
2026-02-08 14:05:15 -05:00
john-okeefe a4314db2d7 docs(api): update existing documentation
- Update api-reference.md with current endpoint list

- Update bulk_update_books.md with improved documentation

- Update scanner/overview.md to reference scan_library.md
2026-02-08 13:33:12 -05:00
john-okeefe c437528522 docs(api): add new endpoint documentation
- Add create_media_item.md for media item creation API

- Add delete_rating.md for rating deletion endpoint

- Add update_rating.md for rating update endpoint
2026-02-08 13:33:09 -05:00
john-okeefe adc7acbe0f refactor(api): rename ScanEbooks to ScanLibrary for clarity
- Rename ScanEbooks method to ScanLibrary to reflect generic media scanning

- Rename ScanEbooksRequest to ScanLibraryRequest

- Update route handlers in scanner.go and library.go

- Rename scan_ebooks.md to scan_library.md

- No breaking changes: API endpoint remains POST /api/scanner/scan
2026-02-08 13:33:04 -05:00