1056 lines
21 KiB
Markdown
1056 lines
21 KiB
Markdown
# StoryCove REST API Documentation
|
|
|
|
## Overview
|
|
|
|
StoryCove provides a comprehensive REST API for managing stories, authors, tags, and series. All endpoints use JSON for request and response data, follow RESTful conventions, and require JWT authentication (except for login).
|
|
|
|
**Base URL**: `/api`
|
|
**Authentication**: JWT tokens via httpOnly cookies
|
|
**Content-Type**: `application/json` (unless otherwise specified)
|
|
|
|
## Authentication
|
|
|
|
All API endpoints (except authentication endpoints) require JWT authentication. Tokens are provided via httpOnly cookies and expire after 24 hours.
|
|
|
|
### Authentication Flow
|
|
|
|
1. **Login**: `POST /api/auth/login` with password
|
|
2. **Receive Token**: Server sets httpOnly cookie with JWT
|
|
3. **Access Protected Endpoints**: Cookie automatically sent with requests
|
|
4. **Token Expiry**: 24 hours, requires re-authentication
|
|
|
|
---
|
|
|
|
## Authentication Endpoints
|
|
|
|
### POST /api/auth/login
|
|
|
|
**Description**: Authenticate with application password and receive JWT token.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"password": "string" // Required, application password
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Authentication successful",
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
}
|
|
```
|
|
|
|
**Response (401 Unauthorized)**:
|
|
```json
|
|
{
|
|
"error": "Invalid password"
|
|
}
|
|
```
|
|
|
|
**Side Effects**: Sets httpOnly cookie `token` with 24-hour expiration.
|
|
|
|
### POST /api/auth/logout
|
|
|
|
**Description**: Clear authentication token and logout.
|
|
|
|
**Request Body**: None
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Logged out successfully"
|
|
}
|
|
```
|
|
|
|
**Side Effects**: Clears the `token` cookie.
|
|
|
|
### GET /api/auth/verify
|
|
|
|
**Description**: Verify if current JWT token is valid.
|
|
|
|
**Authentication**: Required
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Token is valid"
|
|
}
|
|
```
|
|
|
|
**Response (401 Unauthorized)**:
|
|
```json
|
|
{
|
|
"error": "Token is invalid or expired"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Story Management Endpoints
|
|
|
|
### GET /api/stories
|
|
|
|
**Description**: Get paginated list of all stories.
|
|
|
|
**Query Parameters**:
|
|
- `page` (integer, default: 0): Page number (0-based)
|
|
- `size` (integer, default: 20): Page size (max: 100)
|
|
- `sortBy` (string, default: "createdAt"): Sort field
|
|
- `sortDir` (string, default: "desc"): Sort direction (asc/desc)
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"content": [StoryDto],
|
|
"pageable": {
|
|
"sort": { "sorted": true, "unsorted": false },
|
|
"pageNumber": 0,
|
|
"pageSize": 20,
|
|
"offset": 0,
|
|
"paged": true,
|
|
"unpaged": false
|
|
},
|
|
"totalElements": 150,
|
|
"totalPages": 8,
|
|
"last": false,
|
|
"first": true,
|
|
"numberOfElements": 20,
|
|
"size": 20,
|
|
"number": 0,
|
|
"sort": { "sorted": true, "unsorted": false },
|
|
"empty": false
|
|
}
|
|
```
|
|
|
|
### GET /api/stories/{id}
|
|
|
|
**Description**: Get specific story by UUID.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
|
|
**Response (200 OK)**: `StoryDto`
|
|
|
|
**Response (404 Not Found)**:
|
|
```json
|
|
{
|
|
"error": "Story not found with id: {id}"
|
|
}
|
|
```
|
|
|
|
### POST /api/stories
|
|
|
|
**Description**: Create a new story.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"title": "string", // Required
|
|
"summary": "string", // Optional
|
|
"description": "string", // Optional
|
|
"contentHtml": "<p>Story content</p>", // Optional
|
|
"sourceUrl": "https://example.com", // Optional
|
|
"volume": 1, // Optional, integer
|
|
"authorId": "uuid", // Optional, use either authorId or authorName
|
|
"authorName": "Author Name", // Optional, creates author if doesn't exist
|
|
"seriesId": "uuid", // Optional, use either seriesId or seriesName
|
|
"seriesName": "Series Name", // Optional, creates series if doesn't exist
|
|
"tagNames": ["tag1", "tag2"] // Optional, creates tags if they don't exist
|
|
}
|
|
```
|
|
|
|
**Response (201 Created)**: `StoryDto`
|
|
|
|
**Response (400 Bad Request)**:
|
|
```json
|
|
{
|
|
"error": "Title is required",
|
|
"details": {
|
|
"title": "must not be blank"
|
|
}
|
|
}
|
|
```
|
|
|
|
### PUT /api/stories/{id}
|
|
|
|
**Description**: Update existing story. All fields are optional.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
|
|
**Request Body**: Same as POST, all fields optional
|
|
|
|
**Response (200 OK)**: `StoryDto`
|
|
|
|
### DELETE /api/stories/{id}
|
|
|
|
**Description**: Delete story and all associated relationships.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Story deleted successfully"
|
|
}
|
|
```
|
|
|
|
### POST /api/stories/{id}/cover
|
|
|
|
**Description**: Upload cover image for story.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
|
|
**Content-Type**: `multipart/form-data`
|
|
|
|
**Form Parameters**:
|
|
- `file` (File): Image file (JPG, PNG, WebP, max 5MB)
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Cover uploaded successfully",
|
|
"coverPath": "covers/story-uuid-cover.jpg",
|
|
"coverUrl": "/api/files/images/covers/story-uuid-cover.jpg"
|
|
}
|
|
```
|
|
|
|
### DELETE /api/stories/{id}/cover
|
|
|
|
**Description**: Remove cover image from story.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Cover removed successfully"
|
|
}
|
|
```
|
|
|
|
### POST /api/stories/{id}/rating
|
|
|
|
**Description**: Set story rating.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"rating": 4 // Integer, 1-5
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**: `StoryDto`
|
|
|
|
### Tag Management for Stories
|
|
|
|
#### POST /api/stories/{id}/tags/{tagId}
|
|
|
|
**Description**: Add existing tag to story.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
- `tagId` (UUID): Tag identifier
|
|
|
|
**Response (200 OK)**: `StoryDto`
|
|
|
|
#### DELETE /api/stories/{id}/tags/{tagId}
|
|
|
|
**Description**: Remove tag from story.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Story identifier
|
|
- `tagId` (UUID): Tag identifier
|
|
|
|
**Response (200 OK)**: `StoryDto`
|
|
|
|
### Story Search and Filtering
|
|
|
|
#### GET /api/stories/search
|
|
|
|
**Description**: Search stories using Typesense full-text search.
|
|
|
|
**Query Parameters**:
|
|
- `query` (string, required): Search query
|
|
- `page` (integer, default: 0): Page number
|
|
- `size` (integer, default: 20): Page size
|
|
- `authors` (string[]): Filter by author names
|
|
- `tags` (string[]): Filter by tag names
|
|
- `minRating` (integer): Minimum rating filter
|
|
- `maxRating` (integer): Maximum rating filter
|
|
- `sortBy` (string): Sort field
|
|
- `sortDir` (string): Sort direction
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"results": [StorySearchDto],
|
|
"totalHits": 42,
|
|
"page": 0,
|
|
"perPage": 20,
|
|
"query": "search terms",
|
|
"searchTimeMs": 15
|
|
}
|
|
```
|
|
|
|
#### GET /api/stories/search/suggestions
|
|
|
|
**Description**: Get search query suggestions.
|
|
|
|
**Query Parameters**:
|
|
- `query` (string, required): Partial search query
|
|
- `limit` (integer, default: 5): Number of suggestions
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
["suggestion 1", "suggestion 2", "suggestion 3"]
|
|
```
|
|
|
|
### Story Collections
|
|
|
|
#### GET /api/stories/author/{authorId}
|
|
|
|
**Description**: Get stories by specific author.
|
|
|
|
**Path Parameters**:
|
|
- `authorId` (UUID): Author identifier
|
|
|
|
**Query Parameters**: Standard pagination parameters
|
|
|
|
**Response (200 OK)**: Paginated `StoryDto` list
|
|
|
|
#### GET /api/stories/series/{seriesId}
|
|
|
|
**Description**: Get stories in series, ordered by volume.
|
|
|
|
**Path Parameters**:
|
|
- `seriesId` (UUID): Series identifier
|
|
|
|
**Response (200 OK)**: `StoryDto[]` (ordered by volume)
|
|
|
|
#### GET /api/stories/tags/{tagName}
|
|
|
|
**Description**: Get stories with specific tag.
|
|
|
|
**Path Parameters**:
|
|
- `tagName` (string): Tag name
|
|
|
|
**Query Parameters**: Standard pagination parameters
|
|
|
|
**Response (200 OK)**: Paginated `StoryDto` list
|
|
|
|
#### GET /api/stories/recent
|
|
|
|
**Description**: Get recently added stories.
|
|
|
|
**Query Parameters**:
|
|
- `limit` (integer, default: 10): Number of stories
|
|
|
|
**Response (200 OK)**: `StoryDto[]`
|
|
|
|
#### GET /api/stories/top-rated
|
|
|
|
**Description**: Get top-rated stories.
|
|
|
|
**Query Parameters**:
|
|
- `limit` (integer, default: 10): Number of stories
|
|
|
|
**Response (200 OK)**: `StoryDto[]`
|
|
|
|
---
|
|
|
|
## Author Management Endpoints
|
|
|
|
### GET /api/authors
|
|
|
|
**Description**: Get paginated list of all authors.
|
|
|
|
**Query Parameters**: Standard pagination and sorting parameters
|
|
|
|
**Response (200 OK)**: Paginated `AuthorDto` list
|
|
|
|
### GET /api/authors/{id}
|
|
|
|
**Description**: Get specific author by UUID.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Author identifier
|
|
|
|
**Response (200 OK)**: `AuthorDto`
|
|
|
|
### POST /api/authors
|
|
|
|
**Description**: Create new author.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Author Name", // Required, unique
|
|
"notes": "Notes about the author", // Optional
|
|
"urls": ["https://example.com"] // Optional, list of URLs
|
|
}
|
|
```
|
|
|
|
**Response (201 Created)**: `AuthorDto`
|
|
|
|
### PUT /api/authors/{id} (JSON)
|
|
|
|
**Description**: Update author with JSON data.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Author identifier
|
|
|
|
**Content-Type**: `application/json`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Updated Name", // Optional
|
|
"notes": "Updated notes", // Optional
|
|
"urls": ["url1", "url2"], // Optional, replaces all URLs
|
|
"rating": 4 // Optional, 1-5
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**: `AuthorDto`
|
|
|
|
### PUT /api/authors/{id} (Multipart)
|
|
|
|
**Description**: Update author with multipart form data (supports avatar upload).
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Author identifier
|
|
|
|
**Content-Type**: `multipart/form-data`
|
|
|
|
**Form Parameters**:
|
|
- `name` (string): Author name
|
|
- `notes` (string): Author notes
|
|
- `urls` (string[]): List of URLs
|
|
- `authorRating` (integer): Author rating
|
|
- `avatar` (File): Avatar image file
|
|
|
|
**Response (200 OK)**: `AuthorDto`
|
|
|
|
### DELETE /api/authors/{id}
|
|
|
|
**Description**: Delete author and all associated data.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Author identifier
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Author deleted successfully"
|
|
}
|
|
```
|
|
|
|
### Author Image Management
|
|
|
|
#### POST /api/authors/{id}/avatar
|
|
|
|
**Description**: Upload avatar image for author.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Author identifier
|
|
|
|
**Content-Type**: `multipart/form-data`
|
|
|
|
**Form Parameters**:
|
|
- `file` (File): Avatar image (JPG, PNG, WebP, max 5MB)
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Avatar uploaded successfully",
|
|
"avatarPath": "avatars/author-uuid-avatar.jpg",
|
|
"avatarUrl": "/api/files/images/avatars/author-uuid-avatar.jpg"
|
|
}
|
|
```
|
|
|
|
#### DELETE /api/authors/{id}/avatar
|
|
|
|
**Description**: Remove avatar image from author.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Avatar removed successfully"
|
|
}
|
|
```
|
|
|
|
### Author URL Management
|
|
|
|
#### POST /api/authors/{id}/urls
|
|
|
|
**Description**: Add URL to author.
|
|
|
|
**Path Parameters**:
|
|
- `id` (UUID): Author identifier
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"url": "https://example.com"
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**: `AuthorDto`
|
|
|
|
#### DELETE /api/authors/{id}/urls
|
|
|
|
**Description**: Remove URL from author.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"url": "https://example.com"
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**: `AuthorDto`
|
|
|
|
### Author Search and Collections
|
|
|
|
#### GET /api/authors/search
|
|
|
|
**Description**: Search authors by name (database search).
|
|
|
|
**Query Parameters**:
|
|
- `query` (string, required): Search query
|
|
- Standard pagination parameters
|
|
|
|
**Response (200 OK)**: Paginated `AuthorDto` list
|
|
|
|
#### GET /api/authors/search-typesense
|
|
|
|
**Description**: Search authors using Typesense.
|
|
|
|
**Query Parameters**:
|
|
- `q` (string, default: "*"): Search query
|
|
- `page`, `size`: Pagination
|
|
- `sortBy` (string, default: "name"): Sort field
|
|
- `sortOrder` (string, default: "asc"): Sort order
|
|
|
|
**Response (200 OK)**: `SearchResultDto<AuthorDto>`
|
|
|
|
#### GET /api/authors/top-rated
|
|
|
|
**Description**: Get top-rated authors.
|
|
|
|
**Query Parameters**:
|
|
- `limit` (integer, default: 10): Number of authors
|
|
|
|
**Response (200 OK)**: `AuthorDto[]`
|
|
|
|
---
|
|
|
|
## Tag Management Endpoints
|
|
|
|
### GET /api/tags
|
|
|
|
**Description**: Get paginated list of all tags.
|
|
|
|
**Query Parameters**: Standard pagination and sorting parameters
|
|
|
|
**Response (200 OK)**: Paginated `TagDto` list
|
|
|
|
### GET /api/tags/{id}
|
|
|
|
**Description**: Get specific tag by UUID.
|
|
|
|
**Response (200 OK)**: `TagDto`
|
|
|
|
### POST /api/tags
|
|
|
|
**Description**: Create new tag.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "tag-name" // Required, max 100 characters, will be lowercased
|
|
}
|
|
```
|
|
|
|
**Response (201 Created)**: `TagDto`
|
|
|
|
### PUT /api/tags/{id}
|
|
|
|
**Description**: Update tag name.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "new-tag-name" // Optional
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**: `TagDto`
|
|
|
|
### DELETE /api/tags/{id}
|
|
|
|
**Description**: Delete tag and remove from all stories.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Tag deleted successfully"
|
|
}
|
|
```
|
|
|
|
### Tag Collections and Search
|
|
|
|
#### GET /api/tags/search
|
|
|
|
**Description**: Search tags by name.
|
|
|
|
**Query Parameters**:
|
|
- `query` (string, required): Search query
|
|
- Standard pagination parameters
|
|
|
|
**Response (200 OK)**: Paginated `TagDto` list
|
|
|
|
#### GET /api/tags/autocomplete
|
|
|
|
**Description**: Get tag suggestions for autocomplete.
|
|
|
|
**Query Parameters**:
|
|
- `query` (string, required): Partial tag name
|
|
- `limit` (integer, default: 10): Number of suggestions
|
|
|
|
**Response (200 OK)**: `TagDto[]`
|
|
|
|
#### GET /api/tags/popular
|
|
|
|
**Description**: Get most used tags.
|
|
|
|
**Query Parameters**:
|
|
- `limit` (integer, default: 20): Number of tags
|
|
|
|
**Response (200 OK)**: `TagDto[]` (ordered by usage count)
|
|
|
|
#### GET /api/tags/unused
|
|
|
|
**Description**: Get tags not used by any stories.
|
|
|
|
**Response (200 OK)**: `TagDto[]`
|
|
|
|
#### GET /api/tags/stats
|
|
|
|
**Description**: Get tag usage statistics.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"totalTags": 150,
|
|
"usedTags": 120,
|
|
"unusedTags": 30
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Series Management Endpoints
|
|
|
|
### GET /api/series
|
|
|
|
**Description**: Get paginated list of all series.
|
|
|
|
**Response (200 OK)**: Paginated `SeriesDto` list
|
|
|
|
### GET /api/series/{id}
|
|
|
|
**Description**: Get specific series by UUID.
|
|
|
|
**Response (200 OK)**: `SeriesDto`
|
|
|
|
### POST /api/series
|
|
|
|
**Description**: Create new series.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Series Name", // Required, max 255 characters
|
|
"description": "Description" // Optional, max 1000 characters
|
|
}
|
|
```
|
|
|
|
**Response (201 Created)**: `SeriesDto`
|
|
|
|
### PUT /api/series/{id}
|
|
|
|
**Description**: Update series.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Updated Name", // Optional
|
|
"description": "Updated desc" // Optional
|
|
}
|
|
```
|
|
|
|
**Response (200 OK)**: `SeriesDto`
|
|
|
|
### DELETE /api/series/{id}
|
|
|
|
**Description**: Delete series and remove series association from stories.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Series deleted successfully"
|
|
}
|
|
```
|
|
|
|
### Series Collections
|
|
|
|
#### GET /api/series/search
|
|
|
|
**Description**: Search series by name.
|
|
|
|
**Query Parameters**:
|
|
- `query` (string, required): Search query
|
|
- Standard pagination parameters
|
|
|
|
**Response (200 OK)**: Paginated `SeriesDto` list
|
|
|
|
#### GET /api/series/with-stories
|
|
|
|
**Description**: Get series that have stories.
|
|
|
|
**Query Parameters**:
|
|
- `limit` (integer, default: 20): Number of series
|
|
|
|
**Response (200 OK)**: `SeriesDto[]`
|
|
|
|
#### GET /api/series/popular
|
|
|
|
**Description**: Get most popular series (by story count).
|
|
|
|
**Query Parameters**:
|
|
- `limit` (integer, default: 10): Number of series
|
|
|
|
**Response (200 OK)**: `SeriesDto[]`
|
|
|
|
#### GET /api/series/empty
|
|
|
|
**Description**: Get series with no stories.
|
|
|
|
**Response (200 OK)**: `SeriesDto[]`
|
|
|
|
#### GET /api/series/stats
|
|
|
|
**Description**: Get series statistics.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"totalSeries": 50,
|
|
"seriesWithStories": 35,
|
|
"emptySeries": 15
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## File Management Endpoints
|
|
|
|
### POST /api/files/upload/cover
|
|
|
|
**Description**: Upload cover image (generic endpoint).
|
|
|
|
**Content-Type**: `multipart/form-data`
|
|
|
|
**Form Parameters**:
|
|
- `file` (File): Image file
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Cover uploaded successfully",
|
|
"path": "covers/filename.jpg",
|
|
"url": "/api/files/images/covers/filename.jpg"
|
|
}
|
|
```
|
|
|
|
### POST /api/files/upload/avatar
|
|
|
|
**Description**: Upload avatar image (generic endpoint).
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Avatar uploaded successfully",
|
|
"path": "avatars/filename.jpg",
|
|
"url": "/api/files/images/avatars/filename.jpg"
|
|
}
|
|
```
|
|
|
|
### GET /api/files/images/**
|
|
|
|
**Description**: Serve image files.
|
|
|
|
**Query Parameters**:
|
|
- `path` (string, required): Image path
|
|
|
|
**Response**: Image file with appropriate Content-Type
|
|
|
|
**Headers**:
|
|
- `Cache-Control: max-age=31536000` (1 year)
|
|
- `Content-Type: image/jpeg|png|webp`
|
|
|
|
### DELETE /api/files/images
|
|
|
|
**Description**: Delete image file.
|
|
|
|
**Query Parameters**:
|
|
- `path` (string, required): Image path to delete
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Image deleted successfully"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Search and Indexing Endpoints
|
|
|
|
### POST /api/search/reindex
|
|
|
|
**Description**: Manually reindex all stories in Typesense.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Successfully reindexed all stories",
|
|
"storiesCount": 1250
|
|
}
|
|
```
|
|
|
|
### GET /api/search/health
|
|
|
|
**Description**: Check search service health status.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"status": "healthy",
|
|
"message": "Typesense is running and responsive"
|
|
}
|
|
```
|
|
|
|
**Response (503 Service Unavailable)**:
|
|
```json
|
|
{
|
|
"status": "unhealthy",
|
|
"message": "Typesense is not responding",
|
|
"error": "Connection refused"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Endpoints
|
|
|
|
### GET /api/config/html-sanitization
|
|
|
|
**Description**: Get HTML sanitization configuration for frontend.
|
|
|
|
**Response (200 OK)**:
|
|
```json
|
|
{
|
|
"allowedTags": [
|
|
"p", "br", "div", "span", "h1", "h2", "h3",
|
|
"b", "strong", "i", "em", "u", "s", "del"
|
|
],
|
|
"allowedAttributes": {
|
|
"p": ["class", "style"],
|
|
"div": ["class", "style"],
|
|
"span": ["class", "style"]
|
|
},
|
|
"allowedCssProperties": [
|
|
"color", "background-color", "font-size",
|
|
"font-weight", "font-style", "text-align"
|
|
],
|
|
"removedAttributes": {
|
|
"a": ["href", "target"]
|
|
},
|
|
"description": "HTML sanitization configuration for StoryCove"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Data Transfer Objects (DTOs)
|
|
|
|
### StoryDto
|
|
|
|
```json
|
|
{
|
|
"id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"title": "Story Title",
|
|
"summary": "Story summary",
|
|
"description": "Story description",
|
|
"contentHtml": "<p>HTML content</p>",
|
|
"contentPlain": "Plain text content",
|
|
"sourceUrl": "https://example.com/story",
|
|
"coverPath": "covers/story-cover.jpg",
|
|
"wordCount": 1500,
|
|
"rating": 4,
|
|
"volume": 1,
|
|
"authorId": "author-uuid",
|
|
"authorName": "Author Name",
|
|
"seriesId": "series-uuid",
|
|
"seriesName": "Series Name",
|
|
"tags": [
|
|
{
|
|
"id": "tag-uuid",
|
|
"name": "tag-name",
|
|
"storyCount": 10,
|
|
"createdAt": "2024-01-15T10:30:00",
|
|
"updatedAt": "2024-01-15T10:30:00"
|
|
}
|
|
],
|
|
"createdAt": "2024-01-15T10:30:00",
|
|
"updatedAt": "2024-01-16T14:20:00"
|
|
}
|
|
```
|
|
|
|
### AuthorDto
|
|
|
|
```json
|
|
{
|
|
"id": "author-uuid",
|
|
"name": "Author Name",
|
|
"notes": "Notes about the author",
|
|
"avatarImagePath": "avatars/author-avatar.jpg",
|
|
"authorRating": 4,
|
|
"averageStoryRating": 3.8,
|
|
"urls": [
|
|
"https://authorwebsite.com",
|
|
"https://twitter.com/author"
|
|
],
|
|
"storyCount": 15,
|
|
"createdAt": "2024-01-10T09:00:00",
|
|
"updatedAt": "2024-01-16T11:00:00"
|
|
}
|
|
```
|
|
|
|
### TagDto
|
|
|
|
```json
|
|
{
|
|
"id": "tag-uuid",
|
|
"name": "fantasy",
|
|
"storyCount": 25,
|
|
"createdAt": "2024-01-05T12:00:00",
|
|
"updatedAt": "2024-01-05T12:00:00"
|
|
}
|
|
```
|
|
|
|
### SeriesDto
|
|
|
|
```json
|
|
{
|
|
"id": "series-uuid",
|
|
"name": "Series Name",
|
|
"description": "Series description",
|
|
"storyCount": 5,
|
|
"createdAt": "2024-01-01T00:00:00"
|
|
}
|
|
```
|
|
|
|
### SearchResultDto<T>
|
|
|
|
```json
|
|
{
|
|
"results": [T], // Array of result objects
|
|
"totalHits": 42,
|
|
"page": 0,
|
|
"perPage": 20,
|
|
"query": "search terms",
|
|
"searchTimeMs": 15
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
### Standard HTTP Status Codes
|
|
|
|
- **200 OK**: Request successful
|
|
- **201 Created**: Resource created successfully
|
|
- **400 Bad Request**: Invalid request data or validation error
|
|
- **401 Unauthorized**: Authentication required or invalid token
|
|
- **403 Forbidden**: Access denied
|
|
- **404 Not Found**: Resource not found
|
|
- **409 Conflict**: Resource conflict (e.g., duplicate name)
|
|
- **413 Payload Too Large**: File upload too large
|
|
- **415 Unsupported Media Type**: Invalid file type
|
|
- **422 Unprocessable Entity**: Validation error
|
|
- **500 Internal Server Error**: Server error
|
|
|
|
### Error Response Format
|
|
|
|
**Single Error**:
|
|
```json
|
|
{
|
|
"error": "Error message"
|
|
}
|
|
```
|
|
|
|
**Validation Errors**:
|
|
```json
|
|
{
|
|
"error": "Validation failed",
|
|
"details": {
|
|
"title": "must not be blank",
|
|
"rating": "must be between 1 and 5"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Field-Specific Error**:
|
|
```json
|
|
{
|
|
"error": "Author not found with id: 123e4567-e89b-12d3-a456-426614174000"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Rate Limiting and Performance
|
|
|
|
### Request Limits
|
|
- No explicit rate limiting implemented
|
|
- File uploads limited to 5MB per file
|
|
- Page size limited to 100 items maximum
|
|
- Search queries have reasonable timeout limits
|
|
|
|
### Performance Considerations
|
|
- Pagination used for all list endpoints
|
|
- Lazy loading for entity relationships
|
|
- Image files cached with 1-year expiration
|
|
- Search powered by Typesense for fast full-text search
|
|
- Database queries optimized with proper indexing
|
|
|
|
### Best Practices
|
|
- Use pagination for large datasets
|
|
- Cache images and static content
|
|
- Use search endpoints for finding content rather than filtering
|
|
- Batch operations when possible
|
|
- Include only necessary fields in responses
|
|
|
|
This API documentation provides comprehensive coverage of all StoryCove endpoints with examples, error handling, and best practices for integration. |