diff --git a/.gitignore b/.gitignore index d18c43e..842d57a 100644 --- a/.gitignore +++ b/.gitignore @@ -188,4 +188,7 @@ coverage/ # Deployment .vercel -.netlify \ No newline at end of file +.netlify + +# Bruno environment folder +#environment/ diff --git a/bruno/auth/Get Profile.bru b/bruno/auth/Get Profile.bru new file mode 100644 index 0000000..56d5ffc --- /dev/null +++ b/bruno/auth/Get Profile.bru @@ -0,0 +1,37 @@ +meta { + name: Get Profile + type: http + seq: 1 +} + +get { + url: {{base_url}}/api/auth/profile + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { + ## Get User Profile + + Retrieves the authenticated user's profile. + + **Method:** GET + + **Endpoint:** /api/auth/profile + + **Authentication:** Required + + **Response:** + - `id` (string): User ID + - `email` (string): Email + - `username` (string): Username + + **Status Codes:** + - 200: Success + - 401: Unauthorized +} diff --git a/bruno/auth/Get Profile.yml b/bruno/auth/Get Profile.yml deleted file mode 100644 index 7dd622d..0000000 --- a/bruno/auth/Get Profile.yml +++ /dev/null @@ -1,23 +0,0 @@ -meta: - name: Get Profile - seq: 3 -http: - method: get - url: "{{base_url}}/api/auth/profile" - auth: - type: bearer -docs: | - ## Get User Profile - - Retrieves the profile of the authenticated user. - - **Authentication:** Required (Bearer token) - - **Response:** - - `id` (string): User UUID - - `email` (string): User's email address - - `username` (string): User's username - - **Error Responses:** - - 401: Invalid or missing JWT token - - 404: User not found \ No newline at end of file diff --git a/bruno/auth/Login User.bru b/bruno/auth/Login User.bru new file mode 100644 index 0000000..973a664 --- /dev/null +++ b/bruno/auth/Login User.bru @@ -0,0 +1,45 @@ +meta { + name: Login User + type: http + seq: 2 +} + +post { + url: {{base_url}}/api/auth/login + body: json + auth: inherit +} + +body:json { + { + "login": "test@example.com", + "password": "password123" + } +} + +docs { + ## Login User + + Authenticates a user with email/username and password. + + **Method:** POST + + **Endpoint:** /api/auth/login + + **Request Body:** + - `login` (string): Email or username + - `password` (string): Password + + **Response:** + - `token` (string): JWT token + - `user` (object): User details + + **Status Codes:** + - 200: Success + - 401: Invalid credentials +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/bruno/auth/Login User.yml b/bruno/auth/Login User.yml deleted file mode 100644 index 2939fbb..0000000 --- a/bruno/auth/Login User.yml +++ /dev/null @@ -1,29 +0,0 @@ -meta: - name: Login User - seq: 2 -http: - method: post - url: "{{base_url}}/api/auth/login" - body: - type: json - data: | - { - "login": "test@example.com", - "password": "password123" - } -docs: | - ## Login User - - Authenticates a user and returns a JWT token. - - **Request Body:** - - `login` (string, required): Email address or username - - `password` (string, required): User's password - - **Response:** - - `token` (string): JWT authentication token (24h expiry) - - `user` (object): User profile with id, email, username - - **Error Responses:** - - 401: Invalid credentials - - 400: Invalid request data \ No newline at end of file diff --git a/bruno/auth/Register User.bru b/bruno/auth/Register User.bru new file mode 100644 index 0000000..8c83883 --- /dev/null +++ b/bruno/auth/Register User.bru @@ -0,0 +1,47 @@ +meta { + name: Register User + type: http + seq: 3 +} + +post { + url: {{base_url}}/api/auth/register + body: json + auth: inherit +} + +body:json { + { + "email": "test@example.com", + "username": "testuser", + "password": "password123" + } +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { + ## Register User + + Creates a new user account. + + **Method:** POST + + **Endpoint:** /api/auth/register + + **Request Body:** + - `email` (string): Email address + - `username` (string): Username + - `password` (string): Password + + **Response:** + - `token` (string): JWT token + - `user` (object): User details + + **Status Codes:** + - 201: Created + - 409: User exists +} diff --git a/bruno/auth/Register User.yml b/bruno/auth/Register User.yml deleted file mode 100644 index 58727e4..0000000 --- a/bruno/auth/Register User.yml +++ /dev/null @@ -1,31 +0,0 @@ -meta: - name: Register User - seq: 1 -http: - method: post - url: "{{base_url}}/api/auth/register" - body: - type: json - data: | - { - "email": "test@example.com", - "username": "testuser", - "password": "password123" - } -docs: | - ## Register User - - Creates a new user account with email, username, and password. - - **Request Body:** - - `email` (string, required): Valid email address - - `username` (string, required): Unique username (3-50 chars) - - `password` (string, required): Password (min 6 chars) - - **Response:** - - `token` (string): JWT authentication token - - `user` (object): User profile with id, email, username - - **Error Responses:** - - 409: Email or username already exists - - 400: Invalid request data \ No newline at end of file diff --git a/bruno/bruno.json b/bruno/bruno.json new file mode 100644 index 0000000..9882668 --- /dev/null +++ b/bruno/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "Bookmann API", + "type": "collection" +} \ No newline at end of file diff --git a/bruno/collection.yml b/bruno/collection.yml deleted file mode 100644 index 6fad735..0000000 --- a/bruno/collection.yml +++ /dev/null @@ -1,4 +0,0 @@ -version: "1" -name: "Bookmann API" -type: collection -items: [] \ No newline at end of file diff --git a/bruno/ebooks/Create Ebook.yml b/bruno/ebooks/Create Ebook.bru similarity index 57% rename from bruno/ebooks/Create Ebook.yml rename to bruno/ebooks/Create Ebook.bru index 7f1f781..5e82ea2 100644 --- a/bruno/ebooks/Create Ebook.yml +++ b/bruno/ebooks/Create Ebook.bru @@ -1,25 +1,34 @@ -meta: +meta { name: Create Ebook + type: http seq: 3 -http: - method: post - url: "{{base_url}}/api/ebooks" - auth: - type: bearer - body: - type: json - data: | - { - "title": "Sample Book", - "author": "Sample Author", - "isbn": "1234567890", - "description": "A sample ebook", - "file_path": "/uploads/sample.epub", - "file_size": 1024000, - "mime_type": "application/epub+zip", - "cover_image_path": "/uploads/cover.jpg" - } -docs: | +} + +post { + url: http://localhost:8765/api/ebooks + body: json + auth: inherit +} + +body:json { + { + "title": "Sample Book", + "author": "Sample Author", + "isbn": "1234567890", + "description": "A sample ebook", + "file_path": "/uploads/sample.epub", + "file_size": 1024000, + "mime_type": "application/epub+zip", + "cover_image_path": "/uploads/cover.jpg" + } +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { ## Create Ebook Creates a new ebook entry in the library. @@ -40,4 +49,5 @@ docs: | **Error Responses:** - 401: Invalid authentication - - 400: Invalid request data \ No newline at end of file + - 400: Invalid request data +} diff --git a/bruno/ebooks/Delete Ebook.yml b/bruno/ebooks/Delete Ebook.bru similarity index 54% rename from bruno/ebooks/Delete Ebook.yml rename to bruno/ebooks/Delete Ebook.bru index 4e54fee..a46a6fc 100644 --- a/bruno/ebooks/Delete Ebook.yml +++ b/bruno/ebooks/Delete Ebook.bru @@ -1,16 +1,24 @@ -meta: +meta { name: Delete Ebook + type: http seq: 5 -http: - method: delete - url: "{{base_url}}/api/ebooks/{{ebook_id}}" - auth: - type: bearer -vars: - pre-request: - - name: ebook_id - value: "123e4567-e89b-12d3-a456-426614174000" -docs: | +} + +delete { + url: http://localhost:8765/api/ebooks/123e4567-e89b-12d3-a456-426614174000 + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} + + + + + +docs { ## Delete Ebook Removes an ebook from the library. @@ -24,4 +32,5 @@ docs: | **Error Responses:** - 401: Invalid authentication - - 404: Ebook not found \ No newline at end of file + - 404: Ebook not found +} \ No newline at end of file diff --git a/bruno/ebooks/Get Ebook.yml b/bruno/ebooks/Get Ebook.bru similarity index 57% rename from bruno/ebooks/Get Ebook.yml rename to bruno/ebooks/Get Ebook.bru index b5f2d34..1cc7b5d 100644 --- a/bruno/ebooks/Get Ebook.yml +++ b/bruno/ebooks/Get Ebook.bru @@ -1,16 +1,24 @@ -meta: +meta { name: Get Ebook + type: http seq: 2 -http: - method: get - url: "{{base_url}}/api/ebooks/{{ebook_id}}" - auth: - type: bearer -vars: - pre-request: - - name: ebook_id - value: "123e4567-e89b-12d3-a456-426614174000" -docs: | +} + +get { + url: http://localhost:8765/api/ebooks/123e4567-e89b-12d3-a456-426614174000 + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} + + + + + +docs { ## Get Ebook Retrieves details of a specific ebook. @@ -24,4 +32,5 @@ docs: | **Error Responses:** - 401: Invalid authentication - - 404: Ebook not found \ No newline at end of file + - 404: Ebook not found +} \ No newline at end of file diff --git a/bruno/ebooks/List Ebooks.yml b/bruno/ebooks/List Ebooks.bru similarity index 69% rename from bruno/ebooks/List Ebooks.yml rename to bruno/ebooks/List Ebooks.bru index f771842..88e2323 100644 --- a/bruno/ebooks/List Ebooks.yml +++ b/bruno/ebooks/List Ebooks.bru @@ -1,12 +1,22 @@ -meta: +meta { name: List Ebooks + type: http seq: 1 -http: - method: get - url: "{{base_url}}/api/ebooks" - auth: - type: bearer -docs: | +} + +get { + url: http://localhost:8765/api/ebooks + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} + + + +docs { ## List Ebooks Retrieves a paginated list of ebooks. @@ -20,4 +30,5 @@ docs: | **Response:** Array of ebook objects with id, title, author, etc. **Error Responses:** - - 401: Invalid authentication \ No newline at end of file + - 401: Invalid authentication +} \ No newline at end of file diff --git a/bruno/ebooks/Update Ebook.yml b/bruno/ebooks/Update Ebook.bru similarity index 58% rename from bruno/ebooks/Update Ebook.yml rename to bruno/ebooks/Update Ebook.bru index f488612..3ecef93 100644 --- a/bruno/ebooks/Update Ebook.yml +++ b/bruno/ebooks/Update Ebook.bru @@ -1,24 +1,29 @@ -meta: +meta { name: Update Ebook + type: http seq: 4 -http: - method: put - url: "{{base_url}}/api/ebooks/{{ebook_id}}" - auth: - type: bearer - body: - type: json - data: | - { - "title": "Updated Book Title", - "author": "Updated Author", - "description": "Updated description" - } -vars: - pre-request: - - name: ebook_id - value: "123e4567-e89b-12d3-a456-426614174000" -docs: | +} + +put { + url: http://localhost:8765/api/ebooks/123e4567-e89b-12d3-a456-426614174000 + body: json + auth: inherit +} + +body:json { + { + "title": "Updated Book Title", + "author": "Updated Author", + "description": "Updated description" + } +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { ## Update Ebook Updates an existing ebook's metadata. @@ -40,4 +45,5 @@ docs: | **Error Responses:** - 401: Invalid authentication - 404: Ebook not found - - 400: Invalid request data \ No newline at end of file + - 400: Invalid request data +} diff --git a/bruno/environments/Bookmann.bru b/bruno/environments/Bookmann.bru new file mode 100644 index 0000000..01ab8e3 --- /dev/null +++ b/bruno/environments/Bookmann.bru @@ -0,0 +1,3 @@ +vars { + base_url: http://localhost:8765 +} diff --git a/bruno/environments/localhost.yml b/bruno/environments/localhost.yml deleted file mode 100644 index 7f0e49e..0000000 --- a/bruno/environments/localhost.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: localhost -variables: - - name: base_url - value: http://localhost:8765 \ No newline at end of file diff --git a/bruno/progress/Get Reading Progress.yml b/bruno/progress/Get Reading Progress.bru similarity index 67% rename from bruno/progress/Get Reading Progress.yml rename to bruno/progress/Get Reading Progress.bru index 64bdc10..e4f9aae 100644 --- a/bruno/progress/Get Reading Progress.yml +++ b/bruno/progress/Get Reading Progress.bru @@ -1,16 +1,20 @@ -meta: +meta { name: Get Reading Progress + type: http seq: 6 -http: - method: get - url: "{{base_url}}/api/ebooks/{{ebook_id}}/progress" - auth: - type: bearer -vars: - pre-request: - - name: ebook_id - value: "123e4567-e89b-12d3-a456-426614174000" -docs: | +} + +get { + url: http://localhost:8765/api/ebooks/123e4567-e89b-12d3-a456-426614174000/progress + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { ## Get Reading Progress Retrieves the authenticated user's reading progress for an ebook. @@ -28,4 +32,5 @@ docs: | - `last_read_at` (string): Last read timestamp **Error Responses:** - - 401: Invalid authentication \ No newline at end of file + - 401: Invalid authentication +} diff --git a/bruno/progress/Update Reading Progress.yml b/bruno/progress/Update Reading Progress.bru similarity index 57% rename from bruno/progress/Update Reading Progress.yml rename to bruno/progress/Update Reading Progress.bru index 4bea3f6..1d450d4 100644 --- a/bruno/progress/Update Reading Progress.yml +++ b/bruno/progress/Update Reading Progress.bru @@ -1,23 +1,28 @@ -meta: +meta { name: Update Reading Progress + type: http seq: 7 -http: - method: put - url: "{{base_url}}/api/ebooks/{{ebook_id}}/progress" - auth: - type: bearer - body: - type: json - data: | - { - "current_page": 45, - "total_pages": 200 - } -vars: - pre-request: - - name: ebook_id - value: "123e4567-e89b-12d3-a456-426614174000" -docs: | +} + +put { + url: http://localhost:8765/api/ebooks/123e4567-e89b-12d3-a456-426614174000/progress + body: json + auth: inherit +} + +body:json { + { + "current_page": 45, + "total_pages": 200 + } +} + +settings { + encodeUrl: true + timeout: 0 +} + +docs { ## Update Reading Progress Updates the authenticated user's reading progress for an ebook. @@ -35,4 +40,5 @@ docs: | **Error Responses:** - 401: Invalid authentication - - 400: Invalid request data \ No newline at end of file + - 400: Invalid request data +} diff --git a/bruno/workspace.yml b/bruno/workspace.yml deleted file mode 100644 index 1d6d461..0000000 --- a/bruno/workspace.yml +++ /dev/null @@ -1,3 +0,0 @@ -version: "1" -name: "Bookmann API Workspace" -type: workspace \ No newline at end of file diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index f0342ca..62be2b9 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -36,15 +36,15 @@

{error}

- {:else if ebooks.length === 0} -
-
-
📖
-

Welcome to your library!

-

You haven't added any ebooks yet. Start building your collection by uploading your favorite books.

- -
-
+ {:else if ebooks.length === 0} +
+
+
📖
+

Welcome to your library!

+

You haven't added any ebooks yet. Start building your collection by uploading your favorite books.

+ Add Your First Ebook +
+
{:else}
{#each ebooks as ebook (ebook.id)} diff --git a/frontend/src/routes/upload/+page.server.ts b/frontend/src/routes/upload/+page.server.ts new file mode 100644 index 0000000..1f462c6 --- /dev/null +++ b/frontend/src/routes/upload/+page.server.ts @@ -0,0 +1,34 @@ +import type { Actions } from './$types'; +import { createEbook } from '$lib/api'; +import { redirect } from '@sveltejs/kit'; + +export const actions: Actions = { + default: async ({ request, fetch }) => { + const data = await request.formData(); + const title = data.get('title') as string; + const author = data.get('author') as string; + const description = data.get('description') as string; + const file = data.get('file') as File; + + if (!title || !file) { + return { success: false, error: 'Title and file are required' }; + } + + try { + // For now, simulate upload - in reality, the backend would handle file upload + // and return the ebook data + const ebookData = { + title, + author: author || null, + description: description || null, + file_path: `/uploads/${file.name}`, // Placeholder + }; + + await createEbook(ebookData); + throw redirect(303, '/'); + } catch (err) { + if (err instanceof Response) throw err; // Redirect + return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }; + } + }, +}; \ No newline at end of file diff --git a/frontend/src/routes/upload/+page.svelte b/frontend/src/routes/upload/+page.svelte new file mode 100644 index 0000000..4db609a --- /dev/null +++ b/frontend/src/routes/upload/+page.svelte @@ -0,0 +1,105 @@ + + + + Upload Ebook - Bookmann + + +
+
+
+
+
📤
+

+ Upload Ebook +

+

Add a new book to your library

+
+
+
{ + loading = true; + return async ({ update }) => { + try { + await update(); + goto('/'); + } catch (err) { + showError('Upload failed'); + } finally { + loading = false; + } + }; + }}> +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
\ No newline at end of file