Phase 2 Week 5: Device Registration & Management

Implement device registration and management system for universal sync.

Database Changes:
- Add device queries to queries.sql (CRUD operations, registration, auth)
- Add sync queue management queries
- Add conflict resolution queries
- Regenerate sqlc models with new device-related types

Device Handler (devices.go):
- InitiateRegistration: Start device registration with auth URL and QR code
- CheckRegistrationStatus: Poll for registration approval
- ListDevices: Get all devices for current user
- GetDevice: Get specific device details
- UpdateDevice: Update device settings (name, sync settings, frequency)
- DeleteDevice: Remove device from account
- ApproveDevice: User approves device registration via web
- RejectDevice: Reject pending device registration
- ListPendingRegistrations: Show all pending registrations
- generateDeviceToken: Generate secure Bearer token for devices

Device Authentication Middleware (device_auth.go):
- Authenticate: Validate device Bearer tokens
- RequirePermission: Check device permissions by type
- hasPermission: Define permissions per device type
- UpdateLastSeen: Auto-update device last_seen timestamp

Configuration:
- Add BaseURL field to Config for device setup URLs

API Endpoints:
POST /api/devices/register - Initiate device registration
POST /api/devices/register/status - Check registration status
GET /api/devices/approve/:id - Approve device (web UI)
POST /api/devices/reject/:id - Reject device
GET /api/devices - List user's devices
GET /api/devices/:id - Get device details
PUT /api/devices/:id - Update device settings
DELETE /api/devices/:id - Delete device
GET /api/devices/pending - List pending registrations

Bruno API Collection:
- Initiate Device Registration
- Check Registration Status
- List Devices
- Get Device
- Update Device
- Delete Device

Dependencies:
- github.com/skip2/go-qrcode for QR code generation

Device Types Supported:
- koreader: Calibre-compatible sync
- kobo: Kobo sync protocol
- web: Web interface
- mobile: Mobile apps

Device Permissions:
- sync:progress
- sync:annotations
- sync:metadata
- device:manage (web only)
This commit is contained in:
2026-01-30 16:45:10 -05:00
parent a2424bcf74
commit 23ad70158c
15 changed files with 1696 additions and 0 deletions
+30
View File
@@ -16,6 +16,12 @@ type Querier interface {
// Bulk update format group for all media items
BulkUpdateFormatGroups(ctx context.Context) error
CleanupExpiredRefreshTokens(ctx context.Context) error
ClearDeviceSyncQueue(ctx context.Context, deviceID pgtype.UUID) error
// ============================================
// PHASE 2: DEVICE MANAGEMENT & AUTH (Weeks 5-6)
// ============================================
// Device Registration & Management
CreateDevice(ctx context.Context, arg CreateDeviceParams) (Devices, error)
// Backward compatibility - Ebook Notes queries (using views)
CreateEbookNote(ctx context.Context, arg CreateEbookNoteParams) (MediaNotes, error)
// Libraries queries
@@ -31,7 +37,13 @@ type Querier interface {
CreateReadingHistory(ctx context.Context, arg CreateReadingHistoryParams) (ReadingHistory, error)
// Refresh Tokens queries
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshTokens, error)
// Conflict Resolution
CreateSyncConflict(ctx context.Context, arg CreateSyncConflictParams) (SyncConflicts, error)
// Sync Queue Management
CreateSyncQueueItem(ctx context.Context, arg CreateSyncQueueItemParams) (SyncQueue, error)
CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error)
DeleteDevice(ctx context.Context, id pgtype.UUID) error
DeleteDeviceByToken(ctx context.Context, authToken string) error
DeleteEbookNote(ctx context.Context, id pgtype.UUID) error
DeleteLibrary(ctx context.Context, id pgtype.UUID) error
DeleteLibraryFolder(ctx context.Context, arg DeleteLibraryFolderParams) (LibraryFolders, error)
@@ -40,7 +52,12 @@ type Querier interface {
DeleteMediaNote(ctx context.Context, id pgtype.UUID) error
DeleteMediaRating(ctx context.Context, arg DeleteMediaRatingParams) error
DeleteReadingProgress(ctx context.Context, arg DeleteReadingProgressParams) error
DeleteSyncConflict(ctx context.Context, id pgtype.UUID) error
DeleteSyncQueueItem(ctx context.Context, id pgtype.UUID) error
DeleteUser(ctx context.Context, id pgtype.UUID) error
GetDevice(ctx context.Context, id pgtype.UUID) (Devices, error)
GetDeviceByAuthToken(ctx context.Context, authToken string) (Devices, error)
GetDeviceByIdentifier(ctx context.Context, deviceIdentifier string) (Devices, error)
GetLibrary(ctx context.Context, id pgtype.UUID) (GetLibraryRow, error)
GetLibraryByFolder(ctx context.Context, folderPath string) (GetLibraryByFolderRow, error)
GetLibraryFolders(ctx context.Context, libraryID pgtype.UUID) ([]LibraryFolders, error)
@@ -62,6 +79,8 @@ type Querier interface {
GetReadingProgress(ctx context.Context, arg GetReadingProgressParams) (ReadingProgress, error)
GetRefreshToken(ctx context.Context, token string) (GetRefreshTokenRow, error)
GetScanSettings(ctx context.Context, id pgtype.UUID) (GetScanSettingsRow, error)
GetSyncConflict(ctx context.Context, id pgtype.UUID) (SyncConflicts, error)
GetSyncQueueItem(ctx context.Context, id pgtype.UUID) (SyncQueue, error)
// Get universal progress for a book
GetUniversalProgress(ctx context.Context, arg GetUniversalProgressParams) (GetUniversalProgressRow, error)
GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, error)
@@ -71,13 +90,20 @@ type Querier interface {
GetUserForLogin(ctx context.Context, email string) (GetUserForLoginRow, error)
GetUserPasswordHash(ctx context.Context, id pgtype.UUID) (string, error)
GetUserVisibleLibraries(ctx context.Context, userID pgtype.UUID) ([]GetUserVisibleLibrariesRow, error)
ListDevicesByType(ctx context.Context, deviceType string) ([]Devices, error)
ListDevicesByUser(ctx context.Context, userID pgtype.UUID) ([]Devices, error)
ListLibraries(ctx context.Context) ([]ListLibrariesRow, error)
ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error)
ListMediaItemsByLibrary(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryRow, error)
ListMediaItemsFiltered(ctx context.Context, arg ListMediaItemsFilteredParams) ([]ListMediaItemsFilteredRow, error)
ListMediaItemsSorted(ctx context.Context, arg ListMediaItemsSortedParams) ([]ListMediaItemsSortedRow, error)
ListPendingSyncQueueItems(ctx context.Context, arg ListPendingSyncQueueItemsParams) ([]SyncQueue, error)
ListSyncConflictsByMediaItem(ctx context.Context, arg ListSyncConflictsByMediaItemParams) ([]SyncConflicts, error)
ListSyncConflictsByUser(ctx context.Context, userID pgtype.UUID) ([]ListSyncConflictsByUserRow, error)
ListUsers(ctx context.Context) ([]ListUsersRow, error)
ResolveSyncConflict(ctx context.Context, arg ResolveSyncConflictParams) (SyncConflicts, error)
RevokeAllUserRefreshTokens(ctx context.Context, userID pgtype.UUID) error
RevokeDevice(ctx context.Context, id pgtype.UUID) error
RevokeRefreshToken(ctx context.Context, token string) error
// Note: User ebook folders replaced by library folders system
// Legacy folder management is now handled through libraries
@@ -86,6 +112,9 @@ type Querier interface {
SearchMediaItemsFuzzy(ctx context.Context, arg SearchMediaItemsFuzzyParams) ([]SearchMediaItemsFuzzyRow, error)
// Library Visibility queries
SetLibraryVisibility(ctx context.Context, arg SetLibraryVisibilityParams) (LibraryVisibility, error)
UpdateDevice(ctx context.Context, arg UpdateDeviceParams) (Devices, error)
UpdateDeviceLastSeen(ctx context.Context, id pgtype.UUID) (Devices, error)
UpdateDeviceLastSync(ctx context.Context, id pgtype.UUID) (Devices, error)
UpdateEbookNote(ctx context.Context, arg UpdateEbookNoteParams) (MediaNotes, error)
UpdateEmail(ctx context.Context, arg UpdateEmailParams) error
UpdateLibrary(ctx context.Context, arg UpdateLibraryParams) (Libraries, error)
@@ -101,6 +130,7 @@ type Querier interface {
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
UpdateReadingProgress(ctx context.Context, arg UpdateReadingProgressParams) (ReadingProgress, error)
UpdateScanSettings(ctx context.Context, arg UpdateScanSettingsParams) error
UpdateSyncQueueItemStatus(ctx context.Context, arg UpdateSyncQueueItemStatusParams) (SyncQueue, error)
// Update universal progress
UpdateUniversalProgress(ctx context.Context, arg UpdateUniversalProgressParams) (ReadingProgress, error)
UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) error