diff --git a/bruno/admin/Add Ebook Folder.bru b/bruno/admin/Add Ebook Folder.bru deleted file mode 100644 index 8657838..0000000 --- a/bruno/admin/Add Ebook Folder.bru +++ /dev/null @@ -1,50 +0,0 @@ -meta { - name: Add Ebook Folder - type: http - seq: 1 -} - -post { - url: {{base_url}}/api/auth/ebook-folders - body: json - auth: inherit -} - -body:json { - { - "folder_path": "/app/uploads" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Add Ebook Folder - DEPRECATED - - ⚠️ **This endpoint is deprecated and will return HTTP 410 Gone.** - - **Use new library system instead:** - 1. Create a library: `POST /api/libraries` with type "ebooks" - 2. Add folder to library: `POST /api/libraries/{id}/folders` - 3. Manage library visibility: `POST /api/libraries/visibility` - - **Migration Example:** - Old: POST `/api/auth/ebook-folders` with `{ "folder_path": "/path/to/books" }` - New: POST `/api/libraries` with `{ "name": "My Books", "type": "ebooks" }` - Then POST `/api/libraries/{library-id}/folders` with `{ "folder_path": "/path/to/books" }` - - **Response:** HTTP 410 Gone with deprecation message - - **Status Codes:** - - 410: Gone (endpoint deprecated) - - 401: Unauthorized - - 403: Forbidden (admin access required) - - **Replacement Endpoints:** - - `POST /api/libraries` - Create new library - - `POST /api/libraries/{id}/folders` - Add folder to library - - `GET /api/libraries` - List all libraries (admin) -} diff --git a/bruno/admin/Delete Ebook Folder.bru b/bruno/admin/Delete Ebook Folder.bru deleted file mode 100644 index 96ca017..0000000 --- a/bruno/admin/Delete Ebook Folder.bru +++ /dev/null @@ -1,54 +0,0 @@ -meta { - name: Delete Ebook Folder - type: http - seq: 3 -} - -delete { - url: {{base_url}}/api/auth/ebook-folders - body: json - auth: inherit -} - -body:json { - { - "folder_path": "/path/to/ebooks" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Delete Ebook Folder - DEPRECATED - - ⚠️ **This endpoint is deprecated and will return HTTP 410 Gone.** - - **Method:** DELETE - - **Endpoint:** /api/auth/ebook-folders - - **Authentication:** Required (Admin only) - - **Request Body:** JSON object with: - - `folder_path` (string, required): Path to the ebook folder to delete - - **Response:** Success message object: - - `message` (string): "ebook folder deleted successfully" - - **Status Codes:** - - 200: Folder deleted successfully - - 400: Bad request (invalid folder path) - - 401: Unauthorized - - 403: Forbidden (admin access required) - - 404: Folder not found - - 500: Internal server error - - **Features:** - - Admin-only endpoint for deleting ebook folders - - Path normalization for matching - - Supports both absolute paths and home directory (~) paths - - Prevents deletion of non-existent folders -} diff --git a/bruno/admin/Get Ebook Folders.bru b/bruno/admin/Get Ebook Folders.bru deleted file mode 100644 index 05f824f..0000000 --- a/bruno/admin/Get Ebook Folders.bru +++ /dev/null @@ -1,44 +0,0 @@ -meta { - name: Get Ebook Folders - type: http - seq: 2 -} - -get { - url: {{base_url}}/api/auth/ebook-folders - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Get Ebook Folders - DEPRECATED - - ⚠️ **This endpoint is deprecated and will return HTTP 410 Gone.** - - **Use the new library system instead:** - - Use `/api/libraries` to manage libraries - - Use `/api/libraries/{id}/folders` to manage library folders - - **Migration Path:** - 1. Create a library of type "ebooks" - 2. Add your ebook folders to that library - 3. Use library visibility controls instead of user-specific folders - - **Response:** HTTP 410 Gone with deprecation message - - **Status Codes:** - - 410: Gone (endpoint deprecated) - - 401: Unauthorized - - 403: Forbidden (admin access required) - - **Replacement Endpoints:** - - `GET /api/libraries` - List all libraries (admin) - - `GET /api/libraries/visible` - List user's visible libraries - - `POST /api/libraries` - Create new library - - `POST /api/libraries/{id}/folders` - Add folder to library -} diff --git a/bruno/ebooks/Create Ebook - ISBN with hyphens.bru b/bruno/ebooks/Create Ebook - ISBN with hyphens.bru deleted file mode 100644 index ca730c4..0000000 --- a/bruno/ebooks/Create Ebook - ISBN with hyphens.bru +++ /dev/null @@ -1,36 +0,0 @@ -meta { - name: Create Ebook with Various ISBN Formats - type: http - seq: 9 -} - -post { - url: {{base_url}}/api/ebooks - body: json - auth: inherit -} - -body:json { - { - "title": "Ebook with ISBN-13 (with hyphens)", - "author": "Test Author", - "isbn": "978-0-12345-678-9", - "description": "Testing ISBN normalization with hyphens", - "file_path": "/path/to/ebook1.epub", - "file_size": 1048576, - "mime_type": "application/epub+zip" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Create Ebook with ISBN-13 (with hyphens) - - **Expected Behavior:** ISBN should be normalized to `9780123456789` - - This test demonstrates that ISBNs with hyphens are automatically normalized. -} diff --git a/bruno/ebooks/Create Ebook - ISBN with spaces.bru b/bruno/ebooks/Create Ebook - ISBN with spaces.bru deleted file mode 100644 index b5313bc..0000000 --- a/bruno/ebooks/Create Ebook - ISBN with spaces.bru +++ /dev/null @@ -1,36 +0,0 @@ -meta { - name: Create Ebook with ISBN (with spaces) - type: http - seq: 10 -} - -post { - url: {{base_url}}/api/ebooks - body: json - auth: inherit -} - -body:json { - { - "title": "Ebook with ISBN-13 (with spaces)", - "author": "Test Author", - "isbn": "978 0123456789", - "description": "Testing ISBN normalization with spaces", - "file_path": "/path/to/ebook2.epub", - "file_size": 1048576, - "mime_type": "application/epub+zip" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Create Ebook with ISBN-13 (with spaces) - - **Expected Behavior:** ISBN should be normalized to `9780123456789` - - This test demonstrates that ISBNs with spaces are automatically normalized. -} diff --git a/bruno/ebooks/Create Ebook - No Library Error.bru b/bruno/ebooks/Create Ebook - No Library Error.bru deleted file mode 100644 index c4209f6..0000000 --- a/bruno/ebooks/Create Ebook - No Library Error.bru +++ /dev/null @@ -1,46 +0,0 @@ -meta { - name: Create Ebook - No Library Error - type: http - seq: 11 -} - -post { - url: {{base_url}}/api/ebooks - body: json - auth: inherit -} - -body:json { - { - "title": "Test Ebook", - "isbn": "9780123456789", - "description": "Testing error when no library exists", - "file_path": "/path/to/ebook.epub", - "file_size": 1048576, - "mime_type": "application/epub+zip" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Create Ebook - No Library Error - - **Expected Behavior:** Should return 400 Bad Request with error message: "no ebook library found. Please create an ebook library first" - - This test demonstrates the graceful error handling when attempting to create an ebook without first creating an ebook library. - - **Setup Required:** - - Ensure NO ebook library exists in the database - - Run as admin user - - **Expected Response:** - ```json - { - "error": "no ebook library found. Please create an ebook library first" - } - ``` -} diff --git a/bruno/ebooks/Create Ebook.bru b/bruno/ebooks/Create Ebook.bru deleted file mode 100644 index 4a776e1..0000000 --- a/bruno/ebooks/Create Ebook.bru +++ /dev/null @@ -1,87 +0,0 @@ -meta { - name: Create Ebook - type: http - seq: 8 -} - -post { - url: {{base_url}}/api/ebooks - body: json - auth: inherit -} - -body:json { - { - "title": "Sample Ebook Title", - "author": "Author Name", - "isbn": "9780123456789", - "description": "A sample ebook description", - "file_path": "/path/to/ebook.epub", - "file_size": 1048576, - "mime_type": "application/epub+zip", - "cover_image_path": "/path/to/cover.jpg", - "series": "Sample Series", - "series_number": 1, - "tags": "fiction,adventure", - "asin": "B00EXAMPLE", - "date_published": "2023-01-15", - "publisher": "Sample Publisher", - "contributors": "Contributor Name" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Create Ebook - - Creates a new ebook entry in the database. - - **Method:** POST - - **Endpoint:** /api/ebooks - - **Authentication:** Required (Admin only) - - **Request Body:** JSON object with: - - `title` (string, required): Ebook title (1-500 characters) - - `author` (string, optional): Author name - - `isbn` (string, optional): ISBN number (will be normalized to remove hyphens and spaces) - - `description` (string, optional): Ebook description - - `file_path` (string, required): Path to ebook file - - `file_size` (integer, required): File size in bytes - - `mime_type` (string, required): MIME type of ebook - - `cover_image_path` (string, optional): Path to cover image - - `series` (string, optional): Series name - - `series_number` (integer, optional): Number in series - - `tags` (string, optional): Comma-separated tags - - `asin` (string, optional): Amazon ASIN - - `date_published` (string, optional): Publication date (YYYY-MM-DD) - - `publisher` (string, optional): Publisher name - - `contributors` (string, optional): Contributors list - - **Response:** Complete ebook object with all fields - - **Status Codes:** - - 201: Ebook created successfully - - 400: Bad request (invalid data, or no ebook library exists) - - 401: Unauthorized - - 403: Forbidden (admin access required) - - 500: Internal server error - - **Features:** - - Admin-only endpoint for creating ebooks - - Full validation of required fields - - Supports all ebook metadata fields - - ISBN normalization: automatically removes hyphens and spaces (e.g., "978-0-12345-678-9" becomes "9780123456789") - - Requires an existing ebook library to create ebooks - - Associates ebook with creating admin user - - **ISBN Formats Supported:** - - ISBN-13: `9780123456789` or `978-0-12345-678-9` or `978-0123456789` - - ISBN-10: `0123456789` or `0-12345-678-9` - - All formats are automatically normalized (hyphens and spaces removed) -} \ No newline at end of file diff --git a/bruno/ebooks/Create-Update Ebook Rating.bru b/bruno/ebooks/Create-Update Ebook Rating.bru deleted file mode 100644 index ec55b62..0000000 --- a/bruno/ebooks/Create-Update Ebook Rating.bru +++ /dev/null @@ -1,55 +0,0 @@ -meta { - name: Create/Update Ebook Rating - type: http - seq: 7 -} - -post { - url: {{base_url}}/api/ebooks/{{ebook_id}}/rating - body: json - auth: inherit -} - -body:json { - { - "rating": 7 - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Create/Update Ebook Rating - - Creates or updates the authenticated user's rating for a specific ebook. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Ebook UUID - - **Request Body:** - - `rating` (number, required): Rating value (1-10, where odd numbers = half-stars) - - 1,3,5,7,9 = 0.5,1.5,2.5,3.5,4.5 stars (half-star precision) - - 2,4,6,8,10 = 1,2,3,4,5 stars (full stars) - - **Example Request:** - - `"rating": 7` = 3.5 stars (frontend display) - - `"rating": 8` = 4.0 stars (frontend display) - - **Response:** - - `id` (string): Rating UUID - - `ebook_id` (string): Ebook UUID - - `user_id` (string): User UUID - - `rating` (number): Rating value (1-10) - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Error Responses:** - - 400: Invalid rating value (must be 1-10) - - 401: Invalid authentication - - 404: Ebook not found -} diff --git a/bruno/ebooks/Delete Ebook Rating.bru b/bruno/ebooks/Delete Ebook Rating.bru deleted file mode 100644 index 8ba58c2..0000000 --- a/bruno/ebooks/Delete Ebook Rating.bru +++ /dev/null @@ -1,33 +0,0 @@ -meta { - name: Delete Ebook Rating - type: http - seq: 8 -} - -delete { - url: {{base_url}}/api/ebooks/{{ebook_id}}/rating - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Delete Ebook Rating - - Deletes the authenticated user's rating for a specific ebook. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Ebook UUID - - **Response:** No content (204) - - **Error Responses:** - - 401: Invalid authentication - - 404: Rating not found -} \ No newline at end of file diff --git a/bruno/ebooks/Delete Ebook.bru b/bruno/ebooks/Delete Ebook.bru deleted file mode 100644 index bbfddbb..0000000 --- a/bruno/ebooks/Delete Ebook.bru +++ /dev/null @@ -1,53 +0,0 @@ -meta { - name: Delete Ebook - type: http - seq: 10 -} - -delete { - url: {{base_url}}/api/ebooks/{{ebook_id}} - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Delete Ebook - - Deletes an ebook from the database. - - **Method:** DELETE - - **Endpoint:** /api/ebooks/:id - - **Authentication:** Required (Admin only) - - **Path Parameters:** - - `id` (string): Ebook ID (UUID) - - **Response:** No content (204) or success message - - **Status Codes:** - - 204: Ebook deleted successfully (no content) - - 400: Bad request (invalid ID format) - - 401: Unauthorized - - 403: Forbidden (admin access required) - - 404: Ebook not found - - 500: Internal server error - - **Features:** - - Admin-only endpoint for deleting ebooks - - Permanent deletion (cannot be undone) - - Cascades to delete related data (reading progress, ratings) - - Requires valid UUID format for ebook ID - - **Important Notes:** - - This is a destructive operation - - All user reading progress for this ebook will be lost - - All user ratings for this ebook will be lost - - Use with caution -} \ No newline at end of file diff --git a/bruno/ebooks/Get Ebook Rating.bru b/bruno/ebooks/Get Ebook Rating.bru deleted file mode 100644 index bd49c7d..0000000 --- a/bruno/ebooks/Get Ebook Rating.bru +++ /dev/null @@ -1,50 +0,0 @@ -meta { - name: Get Ebook Rating - type: http - seq: 6 -} - -get { - url: {{base_url}}/api/ebooks/{{ebook_id}}/rating - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Get Ebook Rating - - Retrieves the authenticated user's rating for a specific ebook. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Ebook UUID - - **Response when rating exists:** - - `id` (string): Rating UUID - - `ebook_id` (string): Ebook UUID - - `user_id` (string): User UUID - - `rating` (number): Rating value (1-10, where odd numbers = half-stars) - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Response when no rating exists:** - - `ebook_id` (string): Ebook UUID - - `user_id` (string): User UUID - - `rating` (number): Always 0 (indicates no rating) - - **Error Responses:** - - 401: Invalid authentication - - **Rating System:** - - Backend: 1-10 scale (odd numbers = half-stars: 1,3,5,7,9) - - Frontend: 1-5 stars with half-star precision (0.5, 1.5, 2.5, 3.5, 4.5) - - Example: Backend rating 3 = Frontend display 1.5 stars - - **Note:** This endpoint returns HTTP 200 with rating 0 when no rating exists, instead of HTTP 404. -} \ No newline at end of file diff --git a/bruno/ebooks/Get Ebook Ratings.bru b/bruno/ebooks/Get Ebook Ratings.bru deleted file mode 100644 index b0ae7fe..0000000 --- a/bruno/ebooks/Get Ebook Ratings.bru +++ /dev/null @@ -1,44 +0,0 @@ -meta { - name: Get Ebook Ratings - type: http - seq: 9 -} - -get { - url: {{base_url}}/api/ebooks/{{ebook_id}}/ratings - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Get Ebook Ratings - - Retrieves all ratings for a specific ebook, including usernames. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Ebook UUID - - **Response:** Array of rating objects - - `id` (string): Rating UUID - - `ebook_id` (string): Ebook UUID - - `user_id` (string): User UUID - - `rating` (number): Rating value (1-10, where odd numbers = half-stars) - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - `username` (string): Username of the rating user - - **Rating Conversion:** - - Frontend displays: 1-5 stars with half-star precision - - Backend stores: 1-10 (where 1,3,5,7,9 = half-stars) - - **Error Responses:** - - 401: Invalid authentication - - 404: Ebook not found -} \ No newline at end of file diff --git a/bruno/ebooks/Get Ebook.bru b/bruno/ebooks/Get Ebook.bru deleted file mode 100644 index c92eb87..0000000 --- a/bruno/ebooks/Get Ebook.bru +++ /dev/null @@ -1,33 +0,0 @@ -meta { - name: Get Ebook - type: http - seq: 2 -} - -get { - url: {{base_url}}/api/ebooks/{{ebook_id}} - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Get Ebook - - Retrieves details of a specific ebook. - - **Authentication:** Required (Bearer token) - - **Path Parameters:** - - `id` (string): Ebook UUID - - **Response:** Complete ebook object with all metadata - - **Error Responses:** - - 401: Invalid authentication - - 404: Ebook not found -} diff --git a/bruno/ebooks/List Ebooks.bru b/bruno/ebooks/List Ebooks.bru deleted file mode 100644 index 0c7d0b3..0000000 --- a/bruno/ebooks/List Ebooks.bru +++ /dev/null @@ -1,33 +0,0 @@ -meta { - name: List Ebooks - type: http - seq: 1 -} - -get { - url: {{base_url}}/api/ebooks - body: none - auth: inherit -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## List Ebooks - - Retrieves a paginated list of ebooks. - - **Authentication:** Required (Bearer token) - - **Query Parameters:** - - `limit` (number, optional): Number of results (default: 20, max: 100) - - `offset` (number, optional): Pagination offset (default: 0) - - **Response:** Array of ebook objects with id, title, author, etc. - - **Error Responses:** - - 401: Invalid authentication -} diff --git a/bruno/ebooks/Update Ebook.bru b/bruno/ebooks/Update Ebook.bru deleted file mode 100644 index aff0dc9..0000000 --- a/bruno/ebooks/Update Ebook.bru +++ /dev/null @@ -1,78 +0,0 @@ -meta { - name: Update Ebook - type: http - seq: 9 -} - -put { - url: {{base_url}}/api/ebooks/{{ebook_id}} - body: json - auth: inherit -} - -body:json { - { - "title": "Updated Ebook Title", - "author": "Updated Author Name", - "isbn": "978-0123456789", - "description": "Updated ebook description", - "cover_image_path": "/path/to/updated_cover.jpg", - "series": "Updated Series", - "series_number": 2, - "tags": "fiction,adventure,updated", - "asin": "B00EXAMPLE2", - "date_published": "2023-02-15", - "publisher": "Updated Publisher", - "contributors": "Updated Contributor Name" - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Update Ebook - - Updates an existing ebook's metadata. - - **Method:** PUT - - **Endpoint:** /api/ebooks/:id - - **Authentication:** Required (Admin only) - - **Path Parameters:** - - `id` (string): Ebook ID (UUID) - - **Request Body:** JSON object with: - - `title` (string, required): Updated ebook title (1-500 characters) - - `author` (string, optional): Updated author name - - `isbn` (string, optional): Updated ISBN number - - `description` (string, optional): Updated ebook description - - `cover_image_path` (string, optional): Updated path to cover image - - `series` (string, optional): Updated series name - - `series_number` (integer, optional): Updated number in series - - `tags` (string, optional): Updated comma-separated tags - - `asin` (string, optional): Updated Amazon ASIN - - `date_published` (string, optional): Updated publication date (YYYY-MM-DD) - - `publisher` (string, optional): Updated publisher name - - `contributors` (string, optional): Updated contributors list - - **Response:** Updated ebook object with all fields - - **Status Codes:** - - 200: Ebook updated successfully - - 400: Bad request (invalid data or ID) - - 401: Unauthorized - - 403: Forbidden (admin access required) - - 404: Ebook not found - - 500: Internal server error - - **Features:** - - Admin-only endpoint for updating ebook metadata - - Updates only provided fields, preserves others - - Full validation of all fields - - Cannot update file_path, file_size, or mime_type (file properties) -} \ No newline at end of file diff --git a/bruno/environments/Bookmann.bru b/bruno/environments/Bookmann.bru index f4d2363..93ca382 100644 --- a/bruno/environments/Bookmann.bru +++ b/bruno/environments/Bookmann.bru @@ -6,10 +6,11 @@ vars { media_item_id: 550e8400-e29b-41d4-a716-446655440000 highlight_id: 660f9501-f29b-51d4-b716-446655440001 note_id: 7710a602-g29b-61d4-c716-446655440002 - library_id: 8821b703-h29b-71d4-d716-446655440003 + library_id: cc23c3a7-f8fb-451a-a78d-2a16df1b725a job_id: 550e8400-e29b-41d4-a716-446655440000 rating: 5 is_visible: true + library_folder: /app/uploads } vars:secret [ token, diff --git a/bruno/library/Add Library Folder.bru b/bruno/library/Add Library Folder.bru index 4e7d642..1482211 100644 --- a/bruno/library/Add Library Folder.bru +++ b/bruno/library/Add Library Folder.bru @@ -6,8 +6,8 @@ meta { post { url: {{base_url}}/api/libraries/{{library_id}}/folders - auth: inherit body: json + auth: inherit } headers { @@ -16,56 +16,7 @@ headers { body:json { { - "folder_path": "/path/to/library/media" - } -} - -tests { - test_add_library_folder_success(status, headers, body) { - if (status !== 201) { - throw new Error("Expected status 201, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!data || typeof data !== "object") { - throw new Error("Expected response body to be an object"); - } - - // Check for required fields in folder response - if (!data.id) { - throw new Error("Folder response missing required field: id"); - } - - if (!data.library_id) { - throw new Error("Folder response missing required field: library_id"); - } - - if (!data.folder_path) { - throw new Error("Folder response missing required field: folder_path"); - } - - // Validate data types - if (typeof data.id !== "string") { - throw new Error("Folder id must be a string"); - } - - if (typeof data.folder_path !== "string") { - throw new Error("Folder path must be a string"); - } - - return true; + "folder_path": {{library_folder}} } } @@ -111,4 +62,4 @@ docs { - 404: Library not found - 409: Folder already exists for this library - 500: Internal server error -} \ No newline at end of file +} diff --git a/bruno/library/Create Library.bru b/bruno/library/Create Library.bru index 3ca482f..5be9824 100644 --- a/bruno/library/Create Library.bru +++ b/bruno/library/Create Library.bru @@ -22,6 +22,20 @@ body:json { } } +script:post-response { + function onResponse(res) { + let data = res.getBody(); + // If successful registration, set token environment variable + if (res.getStatus() === 201 || res.getStatus() === 200) { + if (data && data.id) { + + return bru.setEnvVar("library_id", data.id, { persist: true }); + } + } + } + onResponse(res); +} + settings { encodeUrl: true timeout: 0 diff --git a/bruno/library/Get Library Folders.bru b/bruno/library/Get Library Folders.bru index 82d893a..7ca5317 100644 --- a/bruno/library/Get Library Folders.bru +++ b/bruno/library/Get Library Folders.bru @@ -6,6 +6,7 @@ meta { get { url: {{base_url}}/api/libraries/{{library_id}}/folders + body: none auth: inherit } @@ -13,52 +14,6 @@ headers { Content-Type: application/json } -tests { - test_get_library_folders_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!Array.isArray(data)) { - throw new Error("Expected response body to be an array"); - } - - // Validate each folder in array - data.forEach((folder, index) => { - if (!folder || typeof folder !== "object") { - throw new Error("Folder at index " + index + " is not an object"); - } - - if (!folder.id) { - throw new Error("Folder at index " + index + " missing required field: id"); - } - - if (!folder.folder_path) { - throw new Error("Folder at index " + index + " missing required field: folder_path"); - } - - if (!folder.library_id) { - throw new Error("Folder at index " + index + " missing required field: library_id"); - } - }); - - return true; - } -} - vars:pre-request { libraryId: "8821b703-h29b-71d4-d716-446655440003" } @@ -97,4 +52,4 @@ docs { - 403: Forbidden (library access denied) - 404: Library not found - 500: Internal server error -} \ No newline at end of file +} diff --git a/bruno/library/Get Library Types.bru b/bruno/library/Get Library Types.bru index f781ad8..7eb01af 100644 --- a/bruno/library/Get Library Types.bru +++ b/bruno/library/Get Library Types.bru @@ -6,6 +6,7 @@ meta { get { url: {{base_url}}/api/libraries/types + body: none auth: inherit } @@ -13,48 +14,6 @@ headers { Content-Type: application/json } -tests { - test_get_library_types_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!Array.isArray(data)) { - throw new Error("Expected response body to be an array"); - } - - // Validate each library type - data.forEach((type, index) => { - if (!type || typeof type !== "object") { - throw new Error("Library type at index " + index + " is not an object"); - } - - if (!type.id) { - throw new Error("Library type at index " + index + " missing required field: id"); - } - - if (!type.name) { - throw new Error("Library type at index " + index + " missing required field: name"); - } - }); - - return true; - } -} - settings { encodeUrl: true timeout: 0 @@ -80,4 +39,4 @@ docs { - 200: Success - 401: Unauthorized - 500: Internal server error -} \ No newline at end of file +} diff --git a/bruno/library/Get Scan Settings.bru b/bruno/library/Get Scan Settings.bru index 028723d..355d211 100644 --- a/bruno/library/Get Scan Settings.bru +++ b/bruno/library/Get Scan Settings.bru @@ -6,6 +6,7 @@ meta { get { url: {{base_url}}/api/library/scan-settings + body: none auth: inherit } @@ -13,42 +14,6 @@ headers { Content-Type: application/json } -tests { - test_get_scan_settings_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!data || typeof data !== "object") { - throw new Error("Expected response body to be an object"); - } - - // Check for required fields - if (typeof data.scan_frequency_minutes !== "number" || data.scan_frequency_minutes < 1) { - throw new Error("Invalid scan_frequency_minutes: " + data.scan_frequency_minutes); - } - - if (typeof data.auto_scan_enabled !== "boolean") { - throw new Error("Invalid auto_scan_enabled: " + data.auto_scan_enabled); - } - - return true; - } -} - settings { encodeUrl: true timeout: 0 @@ -56,25 +21,25 @@ settings { docs { ## Get Scan Settings - + Retrieves the user's current library scanning settings. - + **Method:** GET - + **Endpoint:** /api/library/scan-settings - + **Authentication:** Required (Bearer token) - + **Response:** - `scan_frequency_minutes` (number): Minutes between automatic scans (minimum 1) - `auto_scan_enabled` (boolean): Whether automatic scanning is enabled - `last_scan_at` (string, optional): Timestamp of last scan - `next_scan_at` (string, optional): Timestamp of next scheduled scan - `library_id` (string, optional): Library ID for context - + **Status Codes:** - 200: Success - 401: Unauthorized - 403: Forbidden (access denied) - 500: Internal server error -} \ No newline at end of file +} diff --git a/bruno/library/Get User Visible Libraries.bru b/bruno/library/Get User Visible Libraries.bru index f95784f..72dcdbd 100644 --- a/bruno/library/Get User Visible Libraries.bru +++ b/bruno/library/Get User Visible Libraries.bru @@ -6,6 +6,7 @@ meta { get { url: {{base_url}}/api/libraries/visible + body: none auth: inherit } @@ -13,52 +14,6 @@ headers { Content-Type: application/json } -tests { - test_get_visible_libraries_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!Array.isArray(data)) { - throw new Error("Expected response body to be an array"); - } - - // Validate each library in array (all should be visible) - data.forEach((library, index) => { - if (!library || typeof library !== "object") { - throw new Error("Library at index " + index + " is not an object"); - } - - if (!library.id) { - throw new Error("Library at index " + index + " missing required field: id"); - } - - if (!library.name) { - throw new Error("Library at index " + index + " missing required field: name"); - } - - if (library.is_visible !== true) { - throw new Error("Library at index " + index + " is not marked as visible"); - } - }); - - return true; - } -} - settings { encodeUrl: true timeout: 0 @@ -88,4 +43,4 @@ docs { - 200: Success - 401: Unauthorized - 500: Internal server error -} \ No newline at end of file +} diff --git a/bruno/library/Set Library Visibility.bru b/bruno/library/Set Library Visibility.bru index 972e7f0..9a8bac2 100644 --- a/bruno/library/Set Library Visibility.bru +++ b/bruno/library/Set Library Visibility.bru @@ -6,8 +6,8 @@ meta { post { url: {{base_url}}/api/libraries/visibility - auth: inherit body: json + auth: inherit } headers { @@ -21,42 +21,6 @@ body:json { } } -tests { - test_set_visibility_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!data || typeof data !== "object") { - throw new Error("Expected response body to be an object"); - } - - // Check for required fields in response - if (!data.library_id) { - throw new Error("Response missing required field: library_id"); - } - - if (typeof data.is_visible !== "boolean") { - throw new Error("Response missing required field: is_visible or not boolean"); - } - - return true; - } -} - vars:pre-request { libraryId: "8821b703-h29b-71d4-d716-446655440003", isVisible: true @@ -96,4 +60,4 @@ docs { - 403: Forbidden (admin access required) - 404: Library not found - 500: Internal server error -} \ No newline at end of file +} diff --git a/bruno/library/Update Scan Settings.bru b/bruno/library/Update Scan Settings.bru index 90eb4b3..fda7aed 100644 --- a/bruno/library/Update Scan Settings.bru +++ b/bruno/library/Update Scan Settings.bru @@ -6,8 +6,8 @@ meta { put { url: {{base_url}}/api/library/scan-settings - auth: inherit body: json + auth: inherit } headers { @@ -21,38 +21,6 @@ body:json { } } -tests { - test_update_scan_settings_success(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!data || typeof data !== "object") { - throw new Error("Expected response body to be an object"); - } - - // Check for success message or updated settings - if (!data.message && typeof data.scan_frequency_minutes !== "number") { - throw new Error("Response missing expected fields"); - } - - return true; - } -} - settings { encodeUrl: true timeout: 0 @@ -60,74 +28,24 @@ settings { docs { ## Update Scan Settings - - Updates user's library scanning settings. - - **Method:** PUT - - **Endpoint:** /api/library/scan-settings - - **Authentication:** Required (Bearer token) - - **Request Body:** - - `scan_frequency_minutes` (number, required): Minutes between automatic scans (1-1440) - - `auto_scan_enabled` (boolean, required): Whether automatic scanning is enabled - - `library_id` (string, optional): Specific library ID to update (if not provided, updates global) - - **Response:** Updated scan settings object - - `scan_frequency_minutes` (number): Updated frequency - - `auto_scan_enabled` (boolean): Updated enabled status - - `last_scan_at` (string, optional): Timestamp of last scan - - `next_scan_at` (string, optional): Timestamp of next scheduled scan - - `updated_at` (string): Timestamp of settings update - - **Status Codes:** - - 200: Success - - 400: Invalid settings (frequency out of range) - - 401: Unauthorized - - 403: Forbidden (access denied) - - 404: Library not found (if library_id provided) - - 500: Internal server error -} - -put { - url: {{base_url}}/api/library/scan-settings - body: json - auth: inherit -} - -body { - { - "scan_frequency_minutes": 60, - "auto_scan_enabled": true - } -} - -settings { - encodeUrl: true - timeout: 0 -} - -docs { - ## Update Scan Settings - + Updates the user's ebook scanning settings. - + **Method:** PUT - + **Endpoint:** /api/library/scan-settings - + **Authentication:** Required - + **Request Body:** - `scan_frequency_minutes` (integer, required): Minutes between automatic scans (15-1440) - `auto_scan_enabled` (boolean, required): Whether automatic scanning is enabled - + **Response:** - `message` (string): Success message - + **Status Codes:** - 200: Success - 400: Invalid settings - 401: Unauthorized -} \ No newline at end of file +} diff --git a/bruno/media-items/Create Media Rating.bru b/bruno/media-items/Create Media Rating.bru index 44ddd20..9639e2c 100644 --- a/bruno/media-items/Create Media Rating.bru +++ b/bruno/media-items/Create Media Rating.bru @@ -6,8 +6,8 @@ meta { post { url: {{base_url}}/api/media-items/{{media_item_id}}/rating - auth: inherit body: json + auth: inherit } headers { @@ -20,46 +20,6 @@ body:json { } } -tests { - test_create_rating_success(status, headers, body) { - if (status !== 201) { - throw new Error("Expected status 201, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - // Verify response body is valid JSON and has expected structure - let data; - try { - data = JSON.parse(body); - } catch (e) { - throw new Error("Response body is not valid JSON"); - } - - if (!data || typeof data !== "object") { - throw new Error("Expected response body to be an object"); - } - - // Check for required fields in rating response - if (!data.id) { - throw new Error("Rating response missing required field: id"); - } - - if (!data.media_item_id) { - throw new Error("Rating response missing required field: media_item_id"); - } - - if (typeof data.rating !== "number" || data.rating < 1 || data.rating > 10) { - throw new Error("Invalid rating value: " + data.rating + " (must be 1-10)"); - } - - return true; - } -} - vars:pre-request { mediaItemId: "9932c704-i29b-81d4-e716-446655440004" } @@ -91,7 +51,7 @@ docs { **Example Request:** - `"rating": 7` = 3.5 stars (frontend display) - `"rating": 8` = 4.0 stars (frontend display) - + **Response:** Rating object - `id` (string): Rating UUID - `media_item_id` (string): Media item UUID @@ -99,7 +59,7 @@ docs { - `rating` (number): Rating value (1-10) - `created_at` (string): Creation timestamp - `updated_at` (string): Last update timestamp - + **Status Codes:** - 201: Rating created successfully - 200: Rating updated successfully (if rating already existed) diff --git a/bruno/media-items/Filter Media Items.bru b/bruno/media-items/Filter Media Items.bru index 219b273..a14222b 100644 --- a/bruno/media-items/Filter Media Items.bru +++ b/bruno/media-items/Filter Media Items.bru @@ -6,6 +6,7 @@ meta { get { url: {{base_url}}/api/media-items/filtered?library_id={{library_id}}&genre_filter=Fiction&language_filter=en&year_min=2000&year_max=2024&limit=10&offset=0 + body: none auth: inherit } @@ -13,69 +14,6 @@ headers { Content-Type: application/json } -tests { - test_filter_media_items(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - let data; - try { - const parsed = JSON.parse(body); - data = parsed.data; - } catch (e) { - throw new Error("Response body is not valid JSON: " + e.message); - } - - if (!Array.isArray(data)) { - throw new Error("Expected response data to be an array"); - } - - // Verify filters are applied - data.forEach((item, index) => { - if (!item || typeof item !== "object") { - throw new Error("Media item at index " + index + " is not an object"); - } - - if (!item.id) { - throw new Error("Media item at index " + index + " missing required field: id"); - } - - if (!item.title) { - throw new Error("Media item at index " + index + " missing required field: title"); - } - - if (!item.library_id) { - throw new Error("Media item at index " + index + " missing required field: library_id"); - } - - // Check genre filter if genre is specified - if (item.genre && item.genre !== 'Fiction') { - throw new Error("Item at index " + index + " has incorrect genre: " + item.genre); - } - - // Check language filter if language is specified - if (item.language && item.language !== 'en') { - throw new Error("Item at index " + index + " has incorrect language: " + item.language); - } - - // Check year range if specified - if (item.copyright_year) { - if (item.copyright_year < 2000 || item.copyright_year > 2024) { - throw new Error("Item at index " + index + " has copyright_year outside range: " + item.copyright_year); - } - } - }); - - return true; - } -} - vars:pre-request { genre: "Fiction" language: "en" @@ -90,13 +28,13 @@ settings { docs { ## Filter Media Items - + **Method:** GET - + **Endpoint:** /api/media-items/filtered - + **Authentication:** Required (Bearer token) - + **Query Parameters:** - `library_id` (string, required): UUID of the library - `author_filter` (string, optional): Filter by author (partial match) @@ -109,22 +47,22 @@ docs { - `sort` (string, optional): Sort field and direction (same options as ListMediaItems) - `limit` (integer, optional): Number of items to return (default: 50, max: 1000) - `offset` (integer, optional): Number of items to skip (default: 0) - + **Response:** Object containing array of filtered media items - + **Status Codes:** - 200: Success - 400: Bad request (invalid parameters) - 401: Unauthorized - 500: Internal server error - + **Examples:** - Filter by genre: `/api/media-items/filtered?library_id=xxx&genre_filter=Fiction` - Filter by language: `/api/media-items/filtered?library_id=xxx&language_filter=es` - Filter by year range: `/api/media-items/filtered?library_id=xxx&year_min=2000&year_max=2024` - Filter by cover: `/api/media-items/filtered?library_id=xxx&has_cover=true` - Combine filters: `/api/media-items/filtered?library_id=xxx&genre_filter=Sci-Fi&year_min=2010&language_filter=en` - + **Filter Behavior:** - Multiple filters can be combined (AND logic) - Author and series filters use partial matching (ILIKE) diff --git a/bruno/media-items/List Media Items Sorted.bru b/bruno/media-items/List Media Items Sorted.bru index 9deaf18..83b34be 100644 --- a/bruno/media-items/List Media Items Sorted.bru +++ b/bruno/media-items/List Media Items Sorted.bru @@ -6,6 +6,7 @@ meta { get { url: {{base_url}}/api/media-items?library_id={{library_id}}&sort=title+ASC&limit=10&offset=0 + body: none auth: inherit } @@ -13,64 +14,6 @@ headers { Content-Type: application/json } -tests { - test_list_media_items_sorted(status, headers, body) { - if (status !== 200) { - throw new Error("Expected status 200, got " + status); - } - - const contentType = headers["content-type"]; - if (!contentType || !contentType.includes("application/json")) { - throw new Error("Expected content-type to contain application/json, got " + contentType); - } - - let data; - try { - const parsed = JSON.parse(body); - data = parsed.data; - } catch (e) { - throw new Error("Response body is not valid JSON: " + e.message); - } - - if (!Array.isArray(data)) { - throw new Error("Expected response data to be an array"); - } - - // Verify items are sorted by title ascending - for (let i = 1; i < data.length; i++) { - const prevTitle = data[i - 1].title.toLowerCase(); - const currTitle = data[i].title.toLowerCase(); - if (prevTitle > currTitle) { - throw new Error("Items not sorted by title ASC: " + prevTitle + " should come before " + currTitle); - } - } - - data.forEach((item, index) => { - if (!item || typeof item !== "object") { - throw new Error("Media item at index " + index + " is not an object"); - } - - if (!item.id) { - throw new Error("Media item at index " + index + " missing required field: id"); - } - - if (!item.title) { - throw new Error("Media item at index " + index + " missing required field: title"); - } - - if (!item.library_id) { - throw new Error("Media item at index " + index + " missing required field: library_id"); - } - - if (!item.library_name) { - throw new Error("Media item at index " + index + " missing required field: library_name"); - } - }); - - return true; - } -} - vars:pre-request { sortBy: "title ASC" } @@ -82,13 +25,13 @@ settings { docs { ## List Media Items with Sorting - + **Method:** GET - + **Endpoint:** /api/media-items - + **Authentication:** Required (Bearer token) - + **Query Parameters:** - `library_id` (string, required): UUID of the library - `sort` (string, optional): Sort field and direction @@ -111,20 +54,20 @@ docs { - `genre DESC` - Genre Z-A - `limit` (integer, optional): Number of items to return (default: 50, max: 1000) - `offset` (integer, optional): Number of items to skip (default: 0) - + **Response:** Object containing array of media items - + **Status Codes:** - 200: Success - 400: Bad request (invalid parameters) - 401: Unauthorized - 500: Internal server error - + **Examples:** - Sort by title: `/api/media-items?library_id=xxx&sort=title+ASC` - Sort by author descending: `/api/media-items?library_id=xxx&sort=author+DESC` - Sort by page count: `/api/media-items?library_id=xxx&sort=page_count+ASC` - + **Sorting Behavior:** - All sorts are secondary-sorted by series_number then title for consistency - NULL values are sorted last for ascending, first for descending diff --git a/bruno/progress/Get Reading Progress.bru b/bruno/progress/Get Reading Progress.bru index f629a57..2c1ae90 100644 --- a/bruno/progress/Get Reading Progress.bru +++ b/bruno/progress/Get Reading Progress.bru @@ -5,7 +5,7 @@ meta { } get { - url: {{base_url}}/api/ebooks/{{ebook_id}}/progress + url: {{base_url}}/api/media-items/{{media_item_id}}/progress body: none auth: inherit } @@ -18,15 +18,15 @@ settings { docs { ## Get Reading Progress - Retrieves the authenticated user's reading progress for an ebook. + Retrieves the authenticated user's reading progress for a media item. **Authentication:** Required (Bearer token) **Path Parameters:** - - `id` (string): Ebook UUID + - `id` (string): Media Item UUID **Response:** - - `ebook_id` (string): Ebook UUID + - `id` (string): Media Item UUID - `user_id` (string): User UUID - `current_page` (number): Current page number - `total_pages` (number, nullable): Total pages diff --git a/bruno/progress/Update Reading Progress.bru b/bruno/progress/Update Reading Progress.bru index 142233a..966fa61 100644 --- a/bruno/progress/Update Reading Progress.bru +++ b/bruno/progress/Update Reading Progress.bru @@ -5,7 +5,7 @@ meta { } put { - url: {{base_url}}/api/ebooks/{{ebook_id}}/progress + url: {{base_url}}/api/media-items/{{media_item_id}}/progress body: json auth: inherit } @@ -25,12 +25,12 @@ settings { docs { ## Update Reading Progress - Updates the authenticated user's reading progress for an ebook. + Updates the authenticated user's reading progress for a media item. **Authentication:** Required (Bearer token) **Path Parameters:** - - `id` (string): Ebook UUID + - `id` (string): Media Item UUID **Request Body:** - `current_page` (number, required): Current page number diff --git a/bruno/scanner/Scan Ebooks.bru b/bruno/scanner/Scan Media Items.bru similarity index 81% rename from bruno/scanner/Scan Ebooks.bru rename to bruno/scanner/Scan Media Items.bru index ab8b91d..125736f 100644 --- a/bruno/scanner/Scan Ebooks.bru +++ b/bruno/scanner/Scan Media Items.bru @@ -1,5 +1,5 @@ meta { - name: Scan Ebooks (Background) + name: Scan Media Items (Background) type: http seq: 1 } @@ -22,9 +22,9 @@ body:json { } docs { - ## Scan Ebooks (Background) + ## Scan Media Items (Background) - Triggers an asynchronous ebook scanning operation. The scan runs in the background and can be monitored using the job ID. + Triggers an asynchronous media items scanning operation. The scan runs in the background and can be monitored using the job ID. **Method:** POST @@ -34,7 +34,7 @@ docs { **Request Body:** - `folder_paths` (array of strings, required): List of folder paths to scan - - Example: `["/path/to/ebooks", "/another/path"]` + - Example: `["/path/to/media", "/another/path"]` **Response:** HTTP 202 (Accepted) ```json diff --git a/bruno/scanner/Start Watch Mode.bru b/bruno/scanner/Start Watch Mode.bru index e16cd80..7c92b30 100644 --- a/bruno/scanner/Start Watch Mode.bru +++ b/bruno/scanner/Start Watch Mode.bru @@ -19,7 +19,7 @@ body:json { docs { ## Start Watch Mode - Starts real-time file system monitoring for a specific library. New ebooks will be detected and processed almost instantly. + Starts real-time file system monitoring for a specific library. New media items will be detected and processed almost instantly. **Method:** POST diff --git a/bruno/user/admin/Delete Account.bru b/bruno/user/admin/Delete Account.bru index 075df04..03413f6 100644 --- a/bruno/user/admin/Delete Account.bru +++ b/bruno/user/admin/Delete Account.bru @@ -49,5 +49,5 @@ docs { - Cannot delete the last admin account in the system - Admin role required to use user_id parameter - **Warning:** This action cannot be undone and will permanently delete all user data including ebooks, ratings, and progress. + **Warning:** This action cannot be undone and will permanently delete all user data including media items, ratings, and progress. } \ No newline at end of file diff --git a/bruno/user/auth/Register User.bru b/bruno/user/auth/Register User.bru index a53de49..12cf3b1 100644 --- a/bruno/user/auth/Register User.bru +++ b/bruno/user/auth/Register User.bru @@ -23,11 +23,11 @@ body:json { script:post-response { function onResponse(res) { let data = res.getBody(); - // If successful registration, set token environment variable if (res.getStatus() === 201 || res.getStatus() === 200) { - if (data && data.token) { - return bru.setEnvVar("token", data.token, { persist: true }); + if (data && data.access_token) { + + return bru.setEnvVar("token", data.access_token, { persist: true }); } } }