Clean up API documentation files by removing Phase X references: Remove 'API Explorer will be inserted here in Phase X' placeholders from: - 70+ API endpoint documentation files - Authentication endpoints (login, logout, register, refresh) - User endpoints (profile, settings, password) - Device endpoints (registration, sync, shelves) - Library endpoints (CRUD, folders, visibility) - Media endpoints (items, progress, highlights, notes) - Admin endpoints (users, analytics) - Sync endpoints (Kobo, KOReader) - OPDS endpoints - Scanner endpoints - Queue endpoints These placeholders were from planning documents and have no meaning to API consumers. The documentation is now clean and ready for use.
284 lines
5.1 KiB
Markdown
284 lines
5.1 KiB
Markdown
# WebSocket Sync API
|
|
|
|
Real-time bidirectional sync API for live updates and notifications.
|
|
|
|
**Endpoint**: `WS /ws/sync`
|
|
**Auth**: Required (JWT token or Device authentication)
|
|
|
|
## Connection
|
|
|
|
Connect to the WebSocket endpoint with authentication:
|
|
|
|
```javascript
|
|
const ws = new WebSocket('wss://bookhoard.example/ws/sync?token=eyJhbGci...');
|
|
|
|
// Or with device authentication
|
|
const ws = new WebSocket('wss://bookhoard.example/ws/sync?device_id=uuid&device_key=key');
|
|
```
|
|
|
|
## Message Format
|
|
|
|
All messages are JSON:
|
|
|
|
```json
|
|
{
|
|
"type": "message_type",
|
|
"data": { ... }
|
|
}
|
|
```
|
|
|
|
## Client→Server Messages
|
|
|
|
### Subscribe to Progress Updates
|
|
|
|
```json
|
|
{
|
|
"type": "subscribe",
|
|
"data": {
|
|
"topic": "progress",
|
|
"device_id": "device-uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Unsubscribe
|
|
|
|
```json
|
|
{
|
|
"type": "unsubscribe",
|
|
"data": {
|
|
"topic": "progress"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Heartbeat/Ping
|
|
|
|
```json
|
|
{
|
|
"type": "ping",
|
|
"data": {
|
|
"timestamp": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Server→Client Messages
|
|
|
|
### Progress Updated
|
|
|
|
```json
|
|
{
|
|
"type": "progress_updated",
|
|
"data": {
|
|
"media_item_id": "uuid",
|
|
"device_id": "device-uuid",
|
|
"percentage": 75.5,
|
|
"position": 1234,
|
|
"updated_at": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Conflict Detected
|
|
|
|
```json
|
|
{
|
|
"type": "conflict_detected",
|
|
"data": {
|
|
"conflict_id": "uuid",
|
|
"media_item_id": "uuid",
|
|
"severity": "high",
|
|
"created_at": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Scan Progress
|
|
|
|
```json
|
|
{
|
|
"type": "scan_progress",
|
|
"data": {
|
|
"job_id": "uuid",
|
|
"library_id": "uuid",
|
|
"percentage": 45.5,
|
|
"files_processed": 850,
|
|
"total_files": 1523,
|
|
"status": "in_progress"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Scan Complete
|
|
|
|
```json
|
|
{
|
|
"type": "scan_complete",
|
|
"data": {
|
|
"job_id": "uuid",
|
|
"library_id": "uuid",
|
|
"files_added": 125,
|
|
"files_updated": 45,
|
|
"files_failed": 3,
|
|
"completed_at": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Device Connected
|
|
|
|
```json
|
|
{
|
|
"type": "device_connected",
|
|
"data": {
|
|
"device_id": "uuid",
|
|
"device_name": "My Kobo",
|
|
"connected_at": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Device Disconnected
|
|
|
|
```json
|
|
{
|
|
"type": "device_disconnected",
|
|
"data": {
|
|
"device_id": "uuid",
|
|
"disconnected_at": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pong Response
|
|
|
|
```json
|
|
{
|
|
"type": "pong",
|
|
"data": {
|
|
"timestamp": "2026-02-08T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Connection Lifecycle
|
|
|
|
1. **Connect** - WebSocket connection established
|
|
2. **Authenticate** - Send JWT token or device credentials via query string
|
|
3. **Subscribed** - Server confirms authentication and subscribes to relevant topics
|
|
4. **Messages** - Server pushes real-time updates
|
|
5. **Heartbeat** - Client sends ping every 30 seconds
|
|
6. **Disconnect** - Connection closed
|
|
|
|
## Authentication
|
|
|
|
### User Authentication
|
|
|
|
```
|
|
wss://bookhoard.example/ws/sync?token=<jwt_token>
|
|
```
|
|
|
|
### Device Authentication
|
|
|
|
```
|
|
wss://bookhoard.example/ws/sync?device_id=<uuid>&device_key=<key>
|
|
```
|
|
|
|
## Error Messages
|
|
|
|
### Authentication Failed
|
|
|
|
```json
|
|
{
|
|
"type": "error",
|
|
"data": {
|
|
"code": "auth_failed",
|
|
"message": "Invalid or expired token"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Subscription Failed
|
|
|
|
```json
|
|
{
|
|
"type": "error",
|
|
"data": {
|
|
"code": "subscription_failed",
|
|
"message": "Cannot subscribe to topic"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Usage Example
|
|
|
|
```javascript
|
|
const ws = new WebSocket('wss://bookhoard.example/ws/sync?token=eyJhbGci...');
|
|
|
|
ws.onopen = () => {
|
|
console.log('Connected to sync WebSocket');
|
|
|
|
// Subscribe to progress updates
|
|
ws.send(JSON.stringify({
|
|
type: 'subscribe',
|
|
data: { topic: 'progress', device_id: 'device-uuid' }
|
|
}));
|
|
|
|
// Start heartbeat
|
|
setInterval(() => {
|
|
ws.send(JSON.stringify({
|
|
type: 'ping',
|
|
data: { timestamp: new Date().toISOString() }
|
|
}));
|
|
}, 30000);
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const message = JSON.parse(event.data);
|
|
|
|
switch (message.type) {
|
|
case 'progress_updated':
|
|
console.log('Progress updated:', message.data);
|
|
break;
|
|
case 'conflict_detected':
|
|
console.log('New conflict detected:', message.data);
|
|
break;
|
|
case 'scan_complete':
|
|
console.log('Scan complete:', message.data);
|
|
break;
|
|
case 'pong':
|
|
console.log('Pong received');
|
|
break;
|
|
default:
|
|
console.log('Unknown message type:', message.type);
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('WebSocket error:', error);
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
console.log('WebSocket connection closed');
|
|
};
|
|
```
|
|
|
|
## Topics
|
|
|
|
| Topic | Description | Events |
|
|
|-------|-------------|--------|
|
|
| progress | Reading progress updates | progress_updated |
|
|
| conflicts | Sync conflict events | conflict_detected, conflict_resolved |
|
|
| scanner | Library scan events | scan_progress, scan_complete |
|
|
| devices | Device connection events | device_connected, device_disconnected |
|
|
| queue | Sync queue events | queue_item_added, queue_item_processed |
|
|
|
|
## Best Practices
|
|
|
|
1. **Heartbeat**: Send ping every 30 seconds to keep connection alive
|
|
2. **Reconnect**: Implement exponential backoff for reconnection
|
|
3. **Message Queue**: Queue messages when disconnected and replay on reconnect
|
|
4. **Error Handling**: Handle all error types gracefully
|
|
5. **Cleanup**: Unsubscribe from topics when no longer needed
|