Update of documentation
This commit is contained in:
583
docs/openapi.yaml
Normal file
583
docs/openapi.yaml
Normal file
@@ -0,0 +1,583 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: StoryCove API
|
||||
description: |
|
||||
StoryCove is a self-hosted web application for storing, organizing, and reading short stories from various internet sources.
|
||||
|
||||
## Features
|
||||
- Story management with HTML content support
|
||||
- Author profiles with ratings and metadata
|
||||
- Tag-based categorization and series organization
|
||||
- Full-text search powered by Typesense
|
||||
- Image upload for covers and avatars
|
||||
- JWT-based authentication
|
||||
|
||||
## Authentication
|
||||
All endpoints (except `/api/auth/login`) require JWT authentication via httpOnly cookies.
|
||||
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: StoryCove
|
||||
license:
|
||||
name: MIT
|
||||
|
||||
servers:
|
||||
- url: http://localhost:6925/api
|
||||
description: Local development server
|
||||
- url: https://storycove.hardegger.io/api
|
||||
description: Production server
|
||||
|
||||
security:
|
||||
- cookieAuth: []
|
||||
|
||||
paths:
|
||||
# Authentication endpoints
|
||||
/auth/login:
|
||||
post:
|
||||
tags: [Authentication]
|
||||
summary: Login with password
|
||||
description: Authenticate with application password and receive JWT token
|
||||
security: [] # No authentication required
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [password]
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
description: Application password
|
||||
responses:
|
||||
'200':
|
||||
description: Authentication successful
|
||||
headers:
|
||||
Set-Cookie:
|
||||
description: JWT token cookie
|
||||
schema:
|
||||
type: string
|
||||
example: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; HttpOnly; Max-Age=86400
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example: Authentication successful
|
||||
token:
|
||||
type: string
|
||||
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/auth/logout:
|
||||
post:
|
||||
tags: [Authentication]
|
||||
summary: Logout and clear token
|
||||
description: Clear authentication token and logout
|
||||
responses:
|
||||
'200':
|
||||
description: Logout successful
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MessageResponse'
|
||||
|
||||
/auth/verify:
|
||||
get:
|
||||
tags: [Authentication]
|
||||
summary: Verify token validity
|
||||
description: Check if current JWT token is valid
|
||||
responses:
|
||||
'200':
|
||||
description: Token is valid
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MessageResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
# Story endpoints
|
||||
/stories:
|
||||
get:
|
||||
tags: [Stories]
|
||||
summary: List all stories
|
||||
description: Get paginated list of all stories
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/PageParam'
|
||||
- $ref: '#/components/parameters/SizeParam'
|
||||
- $ref: '#/components/parameters/SortByParam'
|
||||
- $ref: '#/components/parameters/SortDirParam'
|
||||
responses:
|
||||
'200':
|
||||
description: Stories retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PagedStories'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
post:
|
||||
tags: [Stories]
|
||||
summary: Create new story
|
||||
description: Create a new story with optional author, series, and tags
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateStoryRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: Story created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/StoryDto'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/stories/{id}:
|
||||
get:
|
||||
tags: [Stories]
|
||||
summary: Get story by ID
|
||||
description: Retrieve a specific story by its UUID
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Story UUID
|
||||
responses:
|
||||
'200':
|
||||
description: Story retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/StoryDto'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
put:
|
||||
tags: [Stories]
|
||||
summary: Update story
|
||||
description: Update an existing story
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateStoryRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Story updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/StoryDto'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
delete:
|
||||
tags: [Stories]
|
||||
summary: Delete story
|
||||
description: Delete a story and all its relationships
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: Story deleted successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MessageResponse'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/stories/search:
|
||||
get:
|
||||
tags: [Stories]
|
||||
summary: Search stories
|
||||
description: Search stories using Typesense full-text search
|
||||
parameters:
|
||||
- name: query
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Search query
|
||||
- $ref: '#/components/parameters/PageParam'
|
||||
- $ref: '#/components/parameters/SizeParam'
|
||||
- name: authors
|
||||
in: query
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Filter by author names
|
||||
- name: tags
|
||||
in: query
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Filter by tag names
|
||||
- name: minRating
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
- name: maxRating
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
responses:
|
||||
'200':
|
||||
description: Search results
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SearchResult'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
cookieAuth:
|
||||
type: apiKey
|
||||
in: cookie
|
||||
name: token
|
||||
|
||||
parameters:
|
||||
PageParam:
|
||||
name: page
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
default: 0
|
||||
description: Page number (0-based)
|
||||
|
||||
SizeParam:
|
||||
name: size
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
default: 20
|
||||
description: Page size
|
||||
|
||||
SortByParam:
|
||||
name: sortBy
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
default: createdAt
|
||||
description: Field to sort by
|
||||
|
||||
SortDirParam:
|
||||
name: sortDir
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [asc, desc]
|
||||
default: desc
|
||||
description: Sort direction
|
||||
|
||||
schemas:
|
||||
StoryDto:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
title:
|
||||
type: string
|
||||
summary:
|
||||
type: string
|
||||
nullable: true
|
||||
description:
|
||||
type: string
|
||||
nullable: true
|
||||
contentHtml:
|
||||
type: string
|
||||
nullable: true
|
||||
contentPlain:
|
||||
type: string
|
||||
nullable: true
|
||||
sourceUrl:
|
||||
type: string
|
||||
nullable: true
|
||||
coverPath:
|
||||
type: string
|
||||
nullable: true
|
||||
wordCount:
|
||||
type: integer
|
||||
rating:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
nullable: true
|
||||
volume:
|
||||
type: integer
|
||||
nullable: true
|
||||
authorId:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
authorName:
|
||||
type: string
|
||||
nullable: true
|
||||
seriesId:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
seriesName:
|
||||
type: string
|
||||
nullable: true
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TagDto'
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
CreateStoryRequest:
|
||||
type: object
|
||||
required: [title]
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
maxLength: 255
|
||||
summary:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
maxLength: 1000
|
||||
contentHtml:
|
||||
type: string
|
||||
sourceUrl:
|
||||
type: string
|
||||
volume:
|
||||
type: integer
|
||||
authorId:
|
||||
type: string
|
||||
format: uuid
|
||||
authorName:
|
||||
type: string
|
||||
seriesId:
|
||||
type: string
|
||||
format: uuid
|
||||
seriesName:
|
||||
type: string
|
||||
tagNames:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
UpdateStoryRequest:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
maxLength: 255
|
||||
summary:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
maxLength: 1000
|
||||
contentHtml:
|
||||
type: string
|
||||
sourceUrl:
|
||||
type: string
|
||||
volume:
|
||||
type: integer
|
||||
authorId:
|
||||
type: string
|
||||
format: uuid
|
||||
seriesId:
|
||||
type: string
|
||||
format: uuid
|
||||
tagNames:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
TagDto:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
storyCount:
|
||||
type: integer
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
updatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
PagedStories:
|
||||
type: object
|
||||
properties:
|
||||
content:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/StoryDto'
|
||||
pageable:
|
||||
$ref: '#/components/schemas/Pageable'
|
||||
totalElements:
|
||||
type: integer
|
||||
totalPages:
|
||||
type: integer
|
||||
last:
|
||||
type: boolean
|
||||
first:
|
||||
type: boolean
|
||||
numberOfElements:
|
||||
type: integer
|
||||
size:
|
||||
type: integer
|
||||
number:
|
||||
type: integer
|
||||
empty:
|
||||
type: boolean
|
||||
|
||||
Pageable:
|
||||
type: object
|
||||
properties:
|
||||
sort:
|
||||
$ref: '#/components/schemas/Sort'
|
||||
pageNumber:
|
||||
type: integer
|
||||
pageSize:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
paged:
|
||||
type: boolean
|
||||
unpaged:
|
||||
type: boolean
|
||||
|
||||
Sort:
|
||||
type: object
|
||||
properties:
|
||||
sorted:
|
||||
type: boolean
|
||||
unsorted:
|
||||
type: boolean
|
||||
|
||||
SearchResult:
|
||||
type: object
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/StoryDto'
|
||||
totalHits:
|
||||
type: integer
|
||||
format: int64
|
||||
page:
|
||||
type: integer
|
||||
perPage:
|
||||
type: integer
|
||||
query:
|
||||
type: string
|
||||
searchTimeMs:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
MessageResponse:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
details:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: Bad request - validation error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
Unauthorized:
|
||||
description: Authentication required or invalid token
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
NotFound:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
tags:
|
||||
- name: Authentication
|
||||
description: Authentication endpoints
|
||||
- name: Stories
|
||||
description: Story management endpoints
|
||||
- name: Authors
|
||||
description: Author management endpoints
|
||||
- name: Tags
|
||||
description: Tag management endpoints
|
||||
- name: Series
|
||||
description: Series management endpoints
|
||||
- name: Files
|
||||
description: File upload and management endpoints
|
||||
- name: Search
|
||||
description: Search and indexing endpoints
|
||||
- name: Configuration
|
||||
description: Application configuration endpoints
|
||||
Reference in New Issue
Block a user