docs: add Carousel dashboard implementation plan

This commit is contained in:
2026-02-17 17:00:46 -05:00
parent fce16b53f7
commit 96730d9475
407 changed files with 10834 additions and 10983 deletions
@@ -1,84 +0,0 @@
meta {
name: Bulk Dismiss Conflicts
type: http
seq: 1
}
post {
url: {{base_url}}/api/conflicts/bulk-dismiss
body: json
auth: inherit
}
headers {
Content-Type: application/json
Authorization: Bearer {{authToken}}
}
body:json {
{
"conflict_ids": [
"{{conflictId1}}",
"{{conflictId2}}"
]
}
}
script:post-response {
function onResponse(res) {
if (res.getStatus() === 200) {
tests['Bulk dismiss successful'] = true;
const body = res.getBody();
tests['Has results array'] = Array.isArray(body.results);
tests['Has total count'] = body.total !== undefined;
tests['Has success count'] = body.success !== undefined;
tests['Has failed count'] = body.failed !== undefined;
} else {
tests['Bulk dismiss failed'] = false;
}
}
onResponse(res);
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Bulk Dismiss Conflicts
Dismisses multiple sync conflicts without resolving them. This removes them from the conflict list while leaving the data unchanged.
**Method:** POST
**Endpoint:** /api/conflicts/bulk-dismiss
**Authentication:** Bearer token
**Request Body:**
- `conflict_ids` (array): Array of conflict UUIDs to dismiss
**Response:**
- `results` (array): Results for each dismissal
- `total` (number): Total number of conflicts processed
- `success` (number): Number of successfully dismissed conflicts
- `failed` (number): Number of failed dismissals
**Status Codes:**
- 200: Success
- 400: Invalid request data
- 401: Unauthorized
- 403: Forbidden
- 404: One or more conflicts not found
- 500: Internal server error
**Example:**
```json
{
"conflict_ids": ["uuid-1", "uuid-2"]
}
```
**Note:** Dismissing a conflict removes it from the conflict list but does not merge or resolve the conflicting data. Use this when you want to ignore a conflict and handle it manually.
}
@@ -1,93 +0,0 @@
meta {
name: Bulk Resolve Conflicts
type: http
seq: 1
}
post {
url: {{base_url}}/api/conflicts/bulk-resolve
body: json
auth: inherit
}
headers {
Content-Type: application/json
Authorization: Bearer {{authToken}}
}
body:json {
{
"conflict_ids": [
"{{conflictId1}}",
"{{conflictId2}}",
"{{conflictId3}}"
],
"strategy": "most_recent"
}
}
script:post-response {
function onResponse(res) {
if (res.getStatus() === 200) {
tests['Bulk resolve successful'] = true;
const body = res.getBody();
tests['Has results array'] = Array.isArray(body.results);
tests['Has total count'] = body.total !== undefined;
tests['Has success count'] = body.success !== undefined;
tests['Has failed count'] = body.failed !== undefined;
tests['Total equals sum of success and failed'] = body.total === body.success + body.failed;
} else {
tests['Bulk resolve failed'] = false;
}
}
onResponse(res);
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Bulk Resolve Conflicts
Resolves multiple sync conflicts in a single request using a specified resolution strategy.
**Method:** POST
**Endpoint:** /api/conflicts/bulk-resolve
**Authentication:** Bearer token
**Request Body:**
- `conflict_ids` (array): Array of conflict UUIDs to resolve
- `strategy` (string): Resolution strategy
- `most_recent`: Use the most recently updated progress
- `highest_progress`: Use the reading progress with the highest percent read
- `server`: Always prefer server-side data
- `device`: Always prefer device-side data
**Response:**
- `results` (array): Results for each conflict resolution
- `total` (number): Total number of conflicts processed
- `success` (number): Number of successfully resolved conflicts
- `failed` (number): Number of failed resolutions
**Status Codes:**
- 200: Success (with partial results if some failed)
- 400: Invalid request data
- 401: Unauthorized
- 403: Forbidden
- 404: One or more conflicts not found
- 500: Internal server error
**Example:**
```json
{
"conflict_ids": ["uuid-1", "uuid-2", "uuid-3"],
"strategy": "most_recent"
}
```
**Note:** Conflicts are resolved atomically per conflict. If one resolution fails, others may still succeed.
}
@@ -1,74 +0,0 @@
meta {
name: Bulk Resolve with Highest Progress Strategy
type: http
seq: 1
}
post {
url: {{base_url}}/api/conflicts/bulk-resolve
body: json
auth: inherit
}
headers {
Content-Type: application/json
Authorization: Bearer {{authToken}}
}
body:json {
{
"conflict_ids": [
"{{conflictId1}}"
],
"strategy": "highest_progress"
}
}
script:post-response {
function onResponse(res) {
if (res.getStatus() === 200) {
tests['Highest progress strategy successful'] = true;
const body = res.getBody();
tests['At least one conflict resolved'] = body.success > 0;
} else {
tests['Strategy failed'] = false;
}
}
onResponse(res);
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Bulk Resolve with Highest Progress Strategy
Resolves multiple sync conflicts using the "highest_progress" strategy, which keeps the reading progress with the highest percentage read.
**Method:** POST
**Endpoint:** /api/conflicts/bulk-resolve
**Authentication:** Bearer token
**Request Body:**
- `conflict_ids` (array): Array of conflict UUIDs to resolve
- `strategy` (string): Must be "highest_progress"
**Response:**
- `results` (array): Results for each conflict resolution
- `total` (number): Total number of conflicts processed
- `success` (number): Number of successfully resolved conflicts
- `failed` (number): Number of failed resolutions
**Status Codes:**
- 200: Success
- 400: Invalid request data
- 401: Unauthorized
- 403: Forbidden
- 500: Internal server error
**Note:** The highest progress strategy is ideal when you want to preserve the most reading progress across devices. Use this when you've been reading on multiple devices and want to keep the furthest position.
}
-50
View File
@@ -1,50 +0,0 @@
meta {
name: Delete Conflict
type: http
seq: 4
}
delete {
url: {{base_url}}/api/conflicts/{{conflict_id}}
body: none
auth: inherit
}
headers {
Authorization: Bearer {{token}}
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Delete Conflict
Permanently deletes a specific conflict record from the system.
**Method:** DELETE
**Endpoint:** /api/conflicts/{conflict_id}
**Authentication:** Bearer token
**Path Parameters:**
- `conflict_id` (string): Conflict UUID to delete
**Response:** 204 No Content on success
**Status Codes:**
- 204: Success - conflict deleted
- 401: Unauthorized
- 404: Conflict not found
- 500: Internal server error
**Use Cases:**
- Conflict was created in error
- Dismissing a conflict without resolving it
- Conflict is no longer relevant (e.g., book deleted)
**Note:** This permanently removes the conflict record with no undo option. Consider resolving the conflict instead if you want to maintain an audit trail of what happened.
}
-54
View File
@@ -1,54 +0,0 @@
meta {
name: Dismiss All Resolved Conflicts
type: http
seq: 5
}
post {
url: {{base_url}}/api/conflicts/dismiss-all
body: none
auth: inherit
}
headers {
Authorization: Bearer {{token}}
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Dismiss All Resolved Conflicts
Deletes all resolved conflicts for the authenticated user, cleaning up the conflict list.
**Method:** POST
**Endpoint:** /api/conflicts/dismiss-all
**Authentication:** Bearer token
**Response:**
- `deleted` (number): Number of conflict records that were deleted
**Status Codes:**
- 200: Success - conflicts deleted
- 401: Unauthorized
- 500: Internal server error
**Example Response:**
```json
{
"deleted": 5
}
```
**Use Cases:**
- Clean up conflicts list after reviewing resolutions
- Remove old resolved conflicts no longer needed
- Maintain a clean conflict history
**Note:** Only conflicts with status "user_resolved" or "auto_resolved" are deleted. Unresolved conflicts are preserved.
}
-93
View File
@@ -1,93 +0,0 @@
meta {
name: Get Conflict Details
type: http
seq: 2
}
get {
url: {{base_url}}/api/conflicts/{{conflict_id}}
body: none
auth: inherit
}
headers {
Authorization: Bearer {{token}}
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Get Conflict Details
Retrieves detailed information about a specific conflict, including side-by-side comparison of conflicting data from all sources.
**Method:** GET
**Endpoint:** /api/conflicts/{conflict_id}
**Authentication:** Bearer token
**Path Parameters:**
- `conflict_id` (string): Conflict UUID
**Response:**
- `id` (string): Conflict UUID
- `media_item_id` (string): Associated book UUID
- `media_item_title` (string): Book title
- `conflict_type` (string): Type of conflict (progress, note, highlight)
- `conflict_data` (object): Side-by-side comparison from each source
- Each source includes:
- `source` (string): Device/source identifier (koreader, kobo, web, etc.)
- `timestamp` (string): When this data was recorded
- `data` (object): The conflicting data
- `percentage` (number): Reading progress
- `epubcfi` (string): EPUB location
- `chapter` (number): Chapter number
- `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved)
- `resolution_data` (object, optional): If resolved, includes resolution details
- `resolved_by` (string, optional): User ID who resolved it
- `resolved_at` (string, optional): When it was resolved
- `created_at` (string): When conflict was detected
**Status Codes:**
- 200: Success
- 401: Unauthorized
- 404: Conflict not found
- 500: Internal server error
**Example Response:**
```json
{
"id": "conflict-uuid",
"media_item_id": "book-uuid",
"media_item_title": "Foundation",
"conflict_type": "progress",
"conflict_data": {
"koreader": {
"source": "koreader",
"timestamp": "2026-01-30T20:10:00Z",
"data": {
"percentage": 0.45,
"epubcfi": "epubcfi(/6/4/2:15)",
"chapter": 3
}
},
"kobo": {
"source": "kobo",
"timestamp": "2026-01-30T20:05:00Z",
"data": {
"percentage": 0.42,
"location": "unknown"
}
}
},
"resolution_status": "unresolved",
"created_at": "2026-01-30T20:10:00Z"
}
```
**Note:** Use this to get full details before resolving, showing exactly what data differs between sources.
}
-94
View File
@@ -1,94 +0,0 @@
meta {
name: List Conflicts
type: http
seq: 1
}
get {
url: {{base_url}}/api/conflicts?status=unresolved
body: none
auth: inherit
}
headers {
Authorization: Bearer {{token}}
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## List Conflicts
Lists all sync conflicts for the authenticated user with optional filtering.
**Method:** GET
**Endpoint:** /api/conflicts
**Authentication:** Bearer token
**Query Parameters:**
- `status` (string, optional): Filter by resolution status
- `unresolved`: Only unresolved conflicts (default)
- `user_resolved`: Conflicts resolved by user
- `auto_resolved`: Automatically resolved conflicts
- `all`: All conflicts regardless of status
- `type` (string, optional): Filter by conflict type
- `progress`: Reading progress conflicts
- `note`: Bookmark/note conflicts
- `highlight`: Highlight conflicts
**Response:**
- `conflicts` (array): Array of conflict objects
- `total` (number): Total number of conflicts matching filters
- `unresolved` (number): Number of unresolved conflicts
**Each Conflict Object:**
- `id` (string): Conflict UUID
- `media_item_id` (string): Associated book UUID
- `media_item_title` (string): Book title
- `conflict_type` (string): Type of conflict (progress, note, highlight)
- `conflict_data` (object): Side-by-side comparison of conflicting data
- `koreader`: Data from KOReader device
- `kobo`: Data from Kobo device
- `web`: Data from web interface
- `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved)
- `created_at` (string): ISO 8601 timestamp when conflict was detected
**Status Codes:**
- 200: Success
- 401: Unauthorized
- 500: Internal server error
**Example Request:**
```
GET /api/conflicts?status=unresolved&type=progress
```
**Example Response:**
```json
{
"conflicts": [
{
"id": "conflict-uuid",
"media_item_id": "book-uuid",
"media_item_title": "Foundation",
"conflict_type": "progress",
"conflict_data": {
"koreader": { "percentage": 0.65, "epubcfi": "..." },
"kobo": { "percentage": 0.43, "epubcfi": "..." }
},
"resolution_status": "unresolved",
"created_at": "2026-01-31T12:00:00Z"
}
],
"total": 1,
"unresolved": 1
}
```
**Note:** Conflicts occur when multiple devices update the same book data without syncing first.
}
-103
View File
@@ -1,103 +0,0 @@
meta {
name: Resolve Conflict
type: http
seq: 3
}
post {
url: {{base_url}}/api/conflicts/{{conflict_id}}/resolve
body: json
auth: inherit
}
headers {
Authorization: Bearer {{token}}
Content-Type: application/json
}
body:json {
{
"winner": "koreader",
"manual_data": null,
"apply_to_all_future_conflicts": false,
"reason": "User chose more recent progress"
}
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Resolve Conflict
Resolves a sync conflict by choosing which source to use for the conflicting data.
**Method:** POST
**Endpoint:** /api/conflicts/{conflict_id}/resolve
**Authentication:** Bearer token
**Path Parameters:**
- `conflict_id` (string): Conflict UUID
**Request Body:**
- `winner` (string): Source to choose
- `koreader`: Use KOReader device data
- `kobo`: Use Kobo device data
- `web`: Use web interface data
- `manual`: Use custom merged data (requires manual_data)
- `manual_data` (object, optional): Required if winner is "manual"
- `percentage` (number): Reading progress percentage (0-1)
- `epubcfi` (string): EPUB Canonical Fragment Identifier
- `chapter` (number, optional): Chapter number
- `page` (number, optional): Page number
- `apply_to_all_future_conflicts` (boolean): Auto-resolve future conflicts from this source
- `reason` (string, optional): Explanation for the resolution choice
**Response:**
- `conflict_resolved` (boolean): True if successful
- `applied_to` (string): What was updated (progress, annotations, etc.)
- `devices_synced` (array): List of device IDs that were notified
**Status Codes:**
- 200: Success - conflict resolved
- 400: Invalid request data
- 401: Unauthorized
- 404: Conflict not found
- 500: Internal server error
**Example - Choose KOReader:**
```json
{
"winner": "koreader",
"manual_data": null,
"apply_to_all_future_conflicts": false,
"reason": "More recent progress"
}
```
**Example - Manual Override:**
```json
{
"winner": "manual",
"manual_data": {
"percentage": 0.43,
"epubcfi": "epubcfi(/6/4/2:20)",
"chapter": 3
},
"apply_to_all_future_conflicts": false,
"reason": "Custom merged position"
}
```
**After Resolution:**
- Winning data is applied to reading progress
- All connected devices notified via WebSocket
- Conflict status changes to "user_resolved"
- Resolution stored for audit trail
**Note:** Manual override allows precise control when automatic resolution doesn't capture the correct state.
}