Files
jd-book-uploader-backend/README.md
ianshaloom ebeae34e01 Add initial project structure with core functionality for book and stationery uploads
- Created main application entry point in `main.go`.
- Added configuration management in `config/config.go` and tests in `config/config_test.go`.
- Implemented handlers for book and stationery uploads in `handlers/book.go` and `handlers/stationery.go`, including validation logic.
- Established database connection and services in `services/database.go` and `services/book_service.go`.
- Defined models for books and stationery in `models/book.go` and `models/stationery.go`.
- Set up Firebase integration for image uploads in `services/firebase.go`.
- Created migration scripts for database schema in `migrations/001_create_tables.sql` and subsequent updates.
- Added CORS and error handling middleware.
- Included comprehensive tests for handlers, services, and utilities.
- Documented API endpoints and usage in `README.md` and migration instructions in `migrations/README.md`.
- Introduced `.gitignore` to exclude unnecessary files and directories.
- Added Go module support with `go.mod` and `go.sum` files.
- Implemented utility functions for slug generation and validation in `utils/slug.go` and `utils/validation.go`.
2025-11-21 08:50:27 +03:00

491 lines
13 KiB
Markdown

# JD Book Uploader - Backend API Documentation
## Overview
Backend API for uploading book and stationery products with images to Firebase Storage and storing metadata in PostgreSQL.
## Base URL
- **Development**: `http://localhost:8080`
- **Production**: Configure via `FRONTEND_URL` environment variable
---
## API Endpoints
### 1. Health Check
**Endpoint:** `GET /api/health`
**Description:** Check the health status of the API and its dependencies (database and Firebase).
**Request:**
- **Method:** `GET`
- **Path:** `/api/health`
- **Headers:** None required
- **Body:** None
**Response:**
**Success (200 OK):**
```json
{
"status": "ok",
"database": "connected",
"firebase": "connected"
}
```
**Service Unavailable (503):**
```json
{
"status": "degraded",
"database": "disconnected",
"firebase": "connected"
}
```
**Response Fields:**
- `status` (string): `"ok"` if all services connected, `"degraded"` if any service unavailable
- `database` (string): `"connected"`, `"disconnected"`, or `"not_initialized"`
- `firebase` (string): `"connected"`, `"disconnected"`, or `"not_initialized"`
---
### 2. Upload Book
**Endpoint:** `POST /api/books`
**Description:** Upload a book product with cover image. Image is uploaded to Firebase Storage and book metadata is stored in PostgreSQL.
**Request:**
- **Method:** `POST`
- **Path:** `/api/books`
- **Content-Type:** `multipart/form-data`
- **Body:** FormData with the following fields:
**Required Parameters:**
- `image` (File) - Book cover image file
- **Type:** File (multipart/form-data)
- **Allowed types:** `image/png`, `image/jpeg`, `image/jpg`, `image/webp`
- **Max size:** 10MB
- **Required:** Yes
- `book_name` (string) - Book name
- **Type:** String
- **Max length:** 200 characters
- **Required:** Yes
- **Validation:** Not empty
- `cost` (number) - Cost price
- **Type:** Number (decimal)
- **Required:** Yes
- **Validation:** Must be a positive number
- `price` (number) - Selling price
- **Type:** Number (decimal)
- **Required:** Yes
- **Validation:** Must be a positive number
- `quantity` (integer) - Stock quantity
- **Type:** Integer
- **Required:** Yes
- **Validation:** Must be a positive integer
- `publisher_author` (string) - Publisher or author name
- **Type:** String
- **Max length:** 200 characters
- **Required:** Yes
- **Validation:** Not empty
- `category` (string) - Book category
- **Type:** String
- **Max length:** 100 characters
- **Required:** Yes
- **Validation:** Not empty
**Optional Parameters:**
- `discount` (number) - Discount amount
- **Type:** Number (decimal)
- **Default:** 0
- **Required:** No
- **Validation:** Must be non-negative (>= 0)
- `description` (string) - Book description
- **Type:** String
- **Max length:** 1000 characters
- **Required:** No
**Request Body Format:**
```
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...
------WebKitFormBoundary...
Content-Disposition: form-data; name="image"; filename="book-cover.png"
Content-Type: image/png
[binary image data]
------WebKitFormBoundary...
Content-Disposition: form-data; name="book_name"
The Great Gatsby
------WebKitFormBoundary...
Content-Disposition: form-data; name="cost"
15.50
------WebKitFormBoundary...
Content-Disposition: form-data; name="price"
24.99
------WebKitFormBoundary...
Content-Disposition: form-data; name="discount"
0
------WebKitFormBoundary...
Content-Disposition: form-data; name="quantity"
100
------WebKitFormBoundary...
Content-Disposition: form-data; name="publisher_author"
F. Scott Fitzgerald
------WebKitFormBoundary...
Content-Disposition: form-data; name="category"
Fiction
------WebKitFormBoundary...
Content-Disposition: form-data; name="description"
A classic American novel
------WebKitFormBoundary...--
```
**Success Response (201 Created):**
```json
{
"success": true,
"data": {
"book_code": "550e8400-e29b-41d4-a716-446655440000",
"book_name": "The Great Gatsby",
"image_url": "https://storage.googleapis.com/download/storage/v1/b/kaisa-341a6.appspot.com/o/images%2F2025%2F11%2F1732123456_abc123.png?generation=1732123456789&alt=media",
"slug": "the-great-gatsby",
"created_at": "2025-11-20T17:30:00Z"
}
}
```
**Response Fields:**
- `success` (boolean): Always `true` on success
- `data` (object): Contains the created book data
- `book_code` (string): UUID generated for the book
- `book_name` (string): Book name as provided
- `image_url` (string): Public URL of uploaded image in Firebase Storage
- `slug` (string): URL-friendly slug generated from book name
- `created_at` (string): ISO 8601 timestamp of creation
**Error Responses:**
**400 Bad Request** - Validation error:
```json
{
"success": false,
"error": "book_name is required"
}
```
**413 Payload Too Large** - File size exceeds limit:
```json
{
"success": false,
"error": "File size exceeds 10MB limit"
}
```
**500 Internal Server Error** - Server error:
```json
{
"success": false,
"error": "Failed to upload image"
}
```
**Common Error Messages:**
- `"Image file is required"` - No image file provided
- `"Invalid file type. Only PNG, JPEG, and WEBP are allowed"` - Unsupported file type
- `"File size exceeds 10MB limit"` - File too large
- `"book_name is required"` - Missing required field
- `"Invalid cost value"` - Invalid number format or negative value
- `"Failed to parse form data"` - Malformed request
- `"Failed to upload image"` - Firebase upload error
- `"Failed to create book"` - Database insert error
---
### 3. Upload Stationery
**Endpoint:** `POST /api/stationery`
**Description:** Upload a stationery product with image. Image is uploaded to Firebase Storage and stationery metadata is stored in PostgreSQL.
**Request:**
- **Method:** `POST`
- **Path:** `/api/stationery`
- **Content-Type:** `multipart/form-data`
- **Body:** FormData with the following fields:
**Required Parameters:**
- `image` (File) - Stationery image file
- **Type:** File (multipart/form-data)
- **Allowed types:** `image/png`, `image/jpeg`, `image/jpg`, `image/webp`
- **Max size:** 10MB
- **Required:** Yes
- `stationery_name` (string) - Stationery name
- **Type:** String
- **Max length:** 200 characters
- **Required:** Yes
- **Validation:** Not empty
- `cost` (number) - Cost price
- **Type:** Number (decimal)
- **Required:** Yes
- **Validation:** Must be a positive number
- `price` (number) - Selling price
- **Type:** Number (decimal)
- **Required:** Yes
- **Validation:** Must be a positive number
- `quantity` (integer) - Stock quantity
- **Type:** Integer
- **Required:** Yes
- **Validation:** Must be a positive integer
- `category` (string) - Stationery category
- **Type:** String
- **Max length:** 100 characters
- **Required:** Yes
- **Validation:** Not empty
**Optional Parameters:**
- `discount` (number) - Discount amount
- **Type:** Number (decimal)
- **Default:** 0
- **Required:** No
- **Validation:** Must be non-negative (>= 0)
- `color` (string) - Product color
- **Type:** String
- **Required:** No
- `material` (string) - Product material
- **Type:** String
- **Required:** No
- `dimensions` (string) - Product dimensions
- **Type:** String
- **Required:** No
- `description` (string) - Product description
- **Type:** String
- **Max length:** 1000 characters
- **Required:** No
**Request Body Format:**
```
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...
------WebKitFormBoundary...
Content-Disposition: form-data; name="image"; filename="pen.png"
Content-Type: image/png
[binary image data]
------WebKitFormBoundary...
Content-Disposition: form-data; name="stationery_name"
Blue Ballpoint Pen
------WebKitFormBoundary...
Content-Disposition: form-data; name="cost"
2.50
------WebKitFormBoundary...
Content-Disposition: form-data; name="price"
5.99
------WebKitFormBoundary...
Content-Disposition: form-data; name="discount"
0
------WebKitFormBoundary...
Content-Disposition: form-data; name="quantity"
200
------WebKitFormBoundary...
Content-Disposition: form-data; name="category"
Writing
------WebKitFormBoundary...
Content-Disposition: form-data; name="color"
Blue
------WebKitFormBoundary...
Content-Disposition: form-data; name="material"
Plastic
------WebKitFormBoundary...
Content-Disposition: form-data; name="dimensions"
15cm
------WebKitFormBoundary...
Content-Disposition: form-data; name="description"
Smooth writing ballpoint pen
------WebKitFormBoundary...--
```
**Success Response (201 Created):**
```json
{
"success": true,
"data": {
"stationery_code": "550e8400-e29b-41d4-a716-446655440001",
"stationery_name": "Blue Ballpoint Pen",
"image_url": "https://storage.googleapis.com/download/storage/v1/b/kaisa-341a6.appspot.com/o/images%2F2025%2F11%2F1732123456_def456.png?generation=1732123456789&alt=media",
"slug": "blue-ballpoint-pen",
"created_at": "2025-11-20T17:30:00Z"
}
}
```
**Response Fields:**
- `success` (boolean): Always `true` on success
- `data` (object): Contains the created stationery data
- `stationery_code` (string): UUID generated for the stationery item
- `stationery_name` (string): Stationery name as provided
- `image_url` (string): Public URL of uploaded image in Firebase Storage
- `slug` (string): URL-friendly slug generated from stationery name
- `created_at` (string): ISO 8601 timestamp of creation
**Error Responses:**
**400 Bad Request** - Validation error:
```json
{
"success": false,
"error": "stationery_name is required"
}
```
**413 Payload Too Large** - File size exceeds limit:
```json
{
"success": false,
"error": "File size exceeds 10MB limit"
}
```
**500 Internal Server Error** - Server error:
```json
{
"success": false,
"error": "Failed to upload image"
}
```
**Common Error Messages:**
- `"Image file is required"` - No image file provided
- `"Invalid file type. Only PNG, JPEG, and WEBP are allowed"` - Unsupported file type
- `"File size exceeds 10MB limit"` - File too large
- `"stationery_name is required"` - Missing required field
- `"Invalid cost value"` - Invalid number format or negative value
- `"Failed to parse form data"` - Malformed request
- `"Failed to upload image"` - Firebase upload error
- `"Failed to create stationery"` - Database insert error
---
## Response Format
All API responses follow a consistent structure:
**Success Response:**
```json
{
"success": true,
"data": { ... }
}
```
**Error Response:**
```json
{
"success": false,
"error": "Error message"
}
```
---
## HTTP Status Codes
- **200 OK** - Health check successful
- **201 Created** - Resource created successfully (book/stationery uploaded)
- **400 Bad Request** - Validation error or invalid input
- **413 Payload Too Large** - File size exceeds 10MB limit
- **500 Internal Server Error** - Server error (database, Firebase, or internal error)
- **503 Service Unavailable** - Service dependencies (database or Firebase) unavailable
---
## Field Validation Summary
### Book Upload Fields
| Field | Type | Required | Max Length | Validation Rules |
|-------|------|----------|------------|------------------|
| `image` | File | Yes | 10MB | PNG, JPEG, or WEBP only |
| `book_name` | string | Yes | 200 | Not empty |
| `cost` | number | Yes | - | Positive number (> 0) |
| `price` | number | Yes | - | Positive number (> 0) |
| `discount` | number | No | - | Non-negative (>= 0), default: 0 |
| `quantity` | integer | Yes | - | Positive integer (> 0) |
| `publisher_author` | string | Yes | 200 | Not empty |
| `category` | string | Yes | 100 | Not empty |
| `description` | string | No | 1000 | - |
### Stationery Upload Fields
| Field | Type | Required | Max Length | Validation Rules |
|-------|------|----------|------------|------------------|
| `image` | File | Yes | 10MB | PNG, JPEG, or WEBP only |
| `stationery_name` | string | Yes | 200 | Not empty |
| `cost` | number | Yes | - | Positive number (> 0) |
| `price` | number | Yes | - | Positive number (> 0) |
| `discount` | number | No | - | Non-negative (>= 0), default: 0 |
| `quantity` | integer | Yes | - | Positive integer (> 0) |
| `category` | string | Yes | 100 | Not empty |
| `color` | string | No | - | - |
| `material` | string | No | - | - |
| `dimensions` | string | No | - | - |
| `description` | string | No | 1000 | - |
---
## Notes
1. **Content-Type Header**: For file uploads, use `multipart/form-data`. Do NOT manually set the `Content-Type` header - let the browser/client set it automatically with the proper boundary.
2. **Image Processing**: The backend expects images to be processed on the frontend (resized to 1000x1000, background removed) before upload. Upload the processed PNG image.
3. **Auto-Generated Fields**: The following fields are automatically generated by the backend:
- `book_code` / `stationery_code`: UUID v4
- `slug`: Generated from product name (lowercase, spaces to hyphens, special characters removed)
- `created_at`: Current timestamp
- `updated_at`: Current timestamp
4. **File Upload**: Only one image file per request. The file field name must be `image`.
5. **Error Messages**: Always check the `error` field in error responses for user-friendly error messages.
6. **CORS**: The backend is configured to accept requests from the frontend URL specified in `FRONTEND_URL` environment variable (default: `http://localhost:5173`).