Update of documentation

This commit is contained in:
Stefan Hardegger
2025-07-24 08:51:45 +02:00
parent 77ad643eac
commit 8580d660e9
5 changed files with 2303 additions and 9 deletions

263
README.md
View File

@@ -103,14 +103,259 @@ cd backend
- `docker-compose logs -f [service]` - View logs - `docker-compose logs -f [service]` - View logs
- `docker-compose build` - Rebuild containers - `docker-compose build` - Rebuild containers
## Features ## Features
- Story management with HTML content support ### 📚 **Story Management**
- Author profiles with ratings and metadata - **Rich Text Support**: HTML content with automatic plain text extraction
- Tag-based categorization - **Content Sanitization**: Secure HTML processing with customizable sanitization rules
- Full-text search capabilities - **Metadata Management**: Title, summary, description, source URL tracking
- Responsive reading interface - **Rating System**: 5-star rating system for stories
- JWT-based authentication - **Word Count**: Automatic word count calculation
- Docker-based deployment - **Cover Images**: Upload and manage story cover images
- **Series Support**: Organize stories into series with volume numbers
For detailed specifications, see `storycove-spec.md`. ### 👤 **Author Management**
- **Author Profiles**: Comprehensive author information with notes
- **Avatar Images**: Upload and manage author profile pictures
- **URL Collections**: Track multiple URLs per author (websites, social media, etc.)
- **Author Ratings**: Rate authors with 5-star system
- **Statistics**: View author statistics and story counts
### 🏷️ **Organization & Discovery**
- **Tag System**: Flexible tagging with autocomplete
- **Series Organization**: Group related stories with volume ordering
- **Search & Filter**: Full-text search powered by Typesense
- **Advanced Filtering**: Filter by author, tags, ratings, and more
- **Smart Suggestions**: Auto-complete for tags and search queries
### 🎨 **User Experience**
- **Dark/Light Mode**: Automatic theme switching with system preference detection
- **Responsive Design**: Optimized for desktop, tablet, and mobile
- **Reading Mode**: Distraction-free reading interface
- **Keyboard Navigation**: Full keyboard accessibility
- **Rich Text Editor**: Visual and source editing modes for story content
### 🔒 **Security & Administration**
- **JWT Authentication**: Secure token-based authentication
- **Single Password**: Simplified access control
- **HTML Sanitization**: Prevent XSS attacks with configurable sanitization
- **CORS Configuration**: Environment-specific CORS settings
- **File Upload Security**: Secure image upload with validation
### 🚀 **Technical Features**
- **Full-Text Search**: Powered by Typesense search engine
- **RESTful API**: Comprehensive REST API for all operations
- **Database Optimization**: PostgreSQL with proper indexing
- **Image Processing**: Automatic image optimization and storage
- **Environment Configuration**: Multi-environment deployment support
- **Docker Deployment**: Complete containerized deployment
### 📊 **Analytics & Statistics**
- **Usage Statistics**: Track story counts, tag usage, and more
- **Rating Analytics**: View average ratings and distributions
- **Search Analytics**: Monitor search performance and suggestions
- **Storage Metrics**: Track image storage and database usage
## 📖 Documentation
- **[API Documentation](docs/API.md)**: Complete REST API reference with examples
- **[Data Model](docs/DATA_MODEL.md)**: Detailed database schema and relationships
- **[Technical Specification](storycove-spec.md)**: Comprehensive technical specification
- **Environment Configuration**: Multi-environment deployment setup (see above)
- **Development Setup**: Local development environment setup (see below)
## 🗄️ Data Model
StoryCove uses a PostgreSQL database with the following core entities:
### **Stories**
- **Primary Key**: UUID
- **Fields**: title, summary, description, content_html, content_plain, source_url, word_count, rating, volume, cover_path
- **Relationships**: Many-to-One with Author, Many-to-One with Series, Many-to-Many with Tags
- **Features**: Automatic word count calculation, HTML sanitization, plain text extraction
### **Authors**
- **Primary Key**: UUID
- **Fields**: name, notes, author_rating, avatar_image_path
- **Relationships**: One-to-Many with Stories, One-to-Many with Author URLs
- **Features**: URL collection storage, rating system, statistics calculation
### **Series**
- **Primary Key**: UUID
- **Fields**: name, description
- **Relationships**: One-to-Many with Stories (ordered by volume)
- **Features**: Volume-based story ordering, navigation methods
### **Tags**
- **Primary Key**: UUID
- **Fields**: name (unique)
- **Relationships**: Many-to-Many with Stories
- **Features**: Autocomplete support, usage statistics
### **Join Tables**
- **story_tags**: Links stories to tags
- **author_urls**: Stores multiple URLs per author
## 🔌 REST API Reference
### **Authentication** (`/api/auth`)
- `POST /login` - Authenticate with password
- `POST /logout` - Clear authentication token
- `GET /verify` - Verify token validity
### **Stories** (`/api/stories`)
- `GET /` - List stories (paginated)
- `GET /{id}` - Get specific story
- `POST /` - Create new story
- `PUT /{id}` - Update story
- `DELETE /{id}` - Delete story
- `POST /{id}/cover` - Upload cover image
- `DELETE /{id}/cover` - Remove cover image
- `POST /{id}/rating` - Set story rating
- `POST /{id}/tags/{tagId}` - Add tag to story
- `DELETE /{id}/tags/{tagId}` - Remove tag from story
- `GET /search` - Search stories (Typesense)
- `GET /search/suggestions` - Get search suggestions
- `GET /author/{authorId}` - Stories by author
- `GET /series/{seriesId}` - Stories in series
- `GET /tags/{tagName}` - Stories with tag
- `GET /recent` - Recent stories
- `GET /top-rated` - Top-rated stories
### **Authors** (`/api/authors`)
- `GET /` - List authors (paginated)
- `GET /{id}` - Get specific author
- `POST /` - Create new author
- `PUT /{id}` - Update author (JSON or multipart)
- `DELETE /{id}` - Delete author
- `POST /{id}/avatar` - Upload avatar image
- `DELETE /{id}/avatar` - Remove avatar image
- `POST /{id}/rating` - Set author rating
- `POST /{id}/urls` - Add URL to author
- `DELETE /{id}/urls` - Remove URL from author
- `GET /search` - Search authors
- `GET /search-typesense` - Advanced author search
- `GET /top-rated` - Top-rated authors
### **Tags** (`/api/tags`)
- `GET /` - List tags (paginated)
- `GET /{id}` - Get specific tag
- `POST /` - Create new tag
- `PUT /{id}` - Update tag
- `DELETE /{id}` - Delete tag
- `GET /search` - Search tags
- `GET /autocomplete` - Tag autocomplete
- `GET /popular` - Most used tags
- `GET /unused` - Unused tags
- `GET /stats` - Tag statistics
### **Series** (`/api/series`)
- `GET /` - List series (paginated)
- `GET /{id}` - Get specific series
- `POST /` - Create new series
- `PUT /{id}` - Update series
- `DELETE /{id}` - Delete series
- `GET /search` - Search series
- `GET /with-stories` - Series with stories
- `GET /popular` - Popular series
- `GET /empty` - Empty series
- `GET /stats` - Series statistics
### **Files** (`/api/files`)
- `POST /upload/cover` - Upload cover image
- `POST /upload/avatar` - Upload avatar image
- `GET /images/**` - Serve image files
- `DELETE /images` - Delete image file
### **Search** (`/api/search`)
- `POST /reindex` - Reindex all content
- `GET /health` - Search service health
### **Configuration** (`/api/config`)
- `GET /html-sanitization` - Get HTML sanitization rules
### **Request/Response Format**
All API endpoints use JSON format with proper HTTP status codes:
- **200**: Success
- **201**: Created
- **400**: Bad Request (validation errors)
- **401**: Unauthorized
- **404**: Not Found
- **500**: Internal Server Error
### **Authentication**
- JWT tokens provided via httpOnly cookies
- All endpoints (except `/api/auth/login`) require authentication
- Tokens expire after 24 hours
## 🔧 Development
### **Technology Stack**
- **Frontend**: Next.js 14, TypeScript, Tailwind CSS, React
- **Backend**: Spring Boot 3, Java 21, PostgreSQL, Typesense
- **Infrastructure**: Docker, Docker Compose, Nginx
- **Security**: JWT authentication, HTML sanitization, CORS
### **Local Development Setup**
1. **Prerequisites**:
```bash
# Required
- Docker & Docker Compose
- Node.js 18+ (for frontend development)
- Java 21+ (for backend development)
```
2. **Environment Setup**:
```bash
# Copy environment template
cp .env.example .env
# Edit environment variables
vim .env
```
3. **Database Setup**:
```bash
# Start database only
docker-compose up -d postgres
```
4. **Backend Development**:
```bash
cd backend
./mvnw spring-boot:run
```
5. **Frontend Development**:
```bash
cd frontend
npm install
npm run dev
```
6. **Full Stack Development**:
```bash
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
```
### **Testing**
```bash
# Backend tests
cd backend && ./mvnw test
# Frontend build
cd frontend && npm run build
# Frontend linting
cd frontend && npm run lint
```
### **Database Migrations**
The application uses Hibernate with `ddl-auto: update` for schema management. For production deployments, consider using Flyway or Liquibase for controlled migrations.
For detailed technical specifications, see `storycove-spec.md`.

1056
docs/API.md Normal file

File diff suppressed because it is too large Load Diff

263
docs/DATA_MODEL.md Normal file
View File

@@ -0,0 +1,263 @@
# StoryCove Data Model Documentation
## Overview
StoryCove uses PostgreSQL as its primary database with UUID-based primary keys throughout. The data model is designed to support a personal library of short stories with rich metadata, author information, and flexible organization through tags and series.
## Entity Relationship Diagram
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Authors │────│ Stories │────│ Series │
│ │ │ │ │ │
│ - id (PK) │ │ - id (PK) │ │ - id (PK) │
│ - name │ │ - title │ │ - name │
│ - notes │ │ - content* │ │ - desc │
│ - rating │ │ - rating │ │ │
│ - avatar │ │ - volume │ │ │
└─────────────┘ │ - cover │ └─────────────┘
│ │ - word_count │
│ │ - source_url │
│ │ - timestamps │
│ └──────────────┘
│ │
│ │
┌─────────────┐ │ ┌─────────────┐
│ Author_URLs │ │ │ Tags │
│ │ │ │ │
│ - author_id │ │ │ - id (PK) │
│ - url │ │ │ - name │
└─────────────┘ │ └─────────────┘
│ │
│ │
┌─────────────┐ │
│ Story_Tags │─────────┘
│ │
│ - story_id │
│ - tag_id │
└─────────────┘
```
## Detailed Entity Specifications
### Stories Table
**Table Name**: `stories`
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | UUID | PRIMARY KEY, NOT NULL | Unique identifier |
| title | VARCHAR(255) | NOT NULL | Story title |
| summary | TEXT | NULL | Optional story summary |
| description | VARCHAR(1000) | NULL | Optional description |
| content_html | TEXT | NULL | HTML content of the story |
| content_plain | TEXT | NULL | Plain text version (auto-generated) |
| source_url | VARCHAR(255) | NULL | Source URL where story was found |
| cover_path | VARCHAR(255) | NULL | Path to cover image file |
| word_count | INTEGER | NOT NULL, DEFAULT 0 | Word count (auto-calculated) |
| rating | INTEGER | NULL, CHECK (rating >= 1 AND rating <= 5) | Story rating |
| volume | INTEGER | NULL | Volume number if part of series |
| author_id | UUID | FOREIGN KEY | Reference to authors table |
| series_id | UUID | FOREIGN KEY, NULL | Reference to series table |
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Creation timestamp |
| updated_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Last update timestamp |
**Indexes:**
- Primary key on `id`
- Foreign key index on `author_id`
- Foreign key index on `series_id`
- Index on `created_at` for recent stories queries
- Index on `rating` for top-rated queries
- Unique constraint on `source_url` where not null
**Business Rules:**
- Word count is automatically calculated from `content_plain` or `content_html`
- Plain text content is automatically extracted from HTML content using Jsoup
- Volume is only meaningful when series_id is set
- Rating must be between 1-5 if provided
### Authors Table
**Table Name**: `authors`
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | UUID | PRIMARY KEY, NOT NULL | Unique identifier |
| name | VARCHAR(255) | NOT NULL, UNIQUE | Author name |
| notes | TEXT | NULL | Notes about the author |
| author_rating | INTEGER | NULL, CHECK (author_rating >= 1 AND author_rating <= 5) | Author rating |
| avatar_image_path | VARCHAR(255) | NULL | Path to avatar image |
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Creation timestamp |
| updated_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Last update timestamp |
**Indexes:**
- Primary key on `id`
- Unique index on `name`
- Index on `author_rating` for top-rated queries
**Business Rules:**
- Author names must be unique across the system
- Rating must be between 1-5 if provided
- Author statistics (story count, average rating) are calculated dynamically
### Author URLs Table
**Table Name**: `author_urls`
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| author_id | UUID | FOREIGN KEY, NOT NULL | Reference to authors table |
| url | VARCHAR(255) | NOT NULL | URL associated with author |
**Indexes:**
- Foreign key index on `author_id`
- Composite index on `(author_id, url)` for uniqueness
**Business Rules:**
- One author can have multiple URLs
- URLs are stored as simple strings without validation
- Duplicate URLs for the same author are prevented by application logic
### Series Table
**Table Name**: `series`
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | UUID | PRIMARY KEY, NOT NULL | Unique identifier |
| name | VARCHAR(255) | NOT NULL, UNIQUE | Series name |
| description | VARCHAR(1000) | NULL | Series description |
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Creation timestamp |
**Indexes:**
- Primary key on `id`
- Unique index on `name`
**Business Rules:**
- Series names must be unique
- Stories in a series are ordered by volume number
- Series without stories are allowed (placeholder series)
### Tags Table
**Table Name**: `tags`
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | UUID | PRIMARY KEY, NOT NULL | Unique identifier |
| name | VARCHAR(100) | NOT NULL, UNIQUE | Tag name |
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Creation timestamp |
**Indexes:**
- Primary key on `id`
- Unique index on `name`
- Index on `name` for autocomplete queries
**Business Rules:**
- Tag names must be unique and are stored in lowercase
- Tags are created automatically when referenced by stories
- Tag usage statistics are calculated dynamically
### Story Tags Junction Table
**Table Name**: `story_tags`
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| story_id | UUID | FOREIGN KEY, NOT NULL | Reference to stories table |
| tag_id | UUID | FOREIGN KEY, NOT NULL | Reference to tags table |
**Constraints:**
- Primary key on `(story_id, tag_id)`
- Foreign key to `stories(id)` with CASCADE DELETE
- Foreign key to `tags(id)` with CASCADE DELETE
**Indexes:**
- Composite primary key index
- Index on `tag_id` for reverse lookups
## Data Types and Conventions
### UUID Strategy
- All primary keys use UUID (Universally Unique Identifier)
- Generated using `GenerationType.UUID` in Hibernate
- Provides natural uniqueness across distributed systems
- 36-character string representation (e.g., `123e4567-e89b-12d3-a456-426614174000`)
### Timestamp Management
- All entities have `created_at` timestamp
- Stories and Authors have `updated_at` timestamp (automatically updated)
- Series and Tags only have `created_at` (they're rarely modified)
- All timestamps use `LocalDateTime` in Java, stored as `TIMESTAMP` in PostgreSQL
### Text Fields
- **VARCHAR(n)**: For constrained text fields (names, paths, URLs)
- **TEXT**: For unlimited text content (story content, notes, descriptions)
- **HTML Content**: Stored as-is but sanitized on input and output
- **Plain Text**: Automatically extracted from HTML using Jsoup
### Validation Rules
- **Required Fields**: Entity names/titles are always required
- **Length Limits**: Names limited to 255 characters, descriptions to 1000
- **Rating Range**: All ratings constrained to 1-5 range
- **URL Format**: No format validation at database level
- **Uniqueness**: Names are unique within their entity type
## Relationships and Cascading
### One-to-Many Relationships
- **Author → Stories**: Lazy loaded, cascade ALL operations
- **Series → Stories**: Lazy loaded, ordered by volume, cascade ALL
- **Author → Author URLs**: Eager loaded via `@ElementCollection`
### Many-to-Many Relationships
- **Stories ↔ Tags**: Via `story_tags` junction table
- Managed bidirectionally with helper methods
- Cascade DELETE on both sides
### Foreign Key Constraints
- All foreign keys have proper referential integrity
- DELETE operations cascade appropriately
- No orphaned records are allowed
## Performance Considerations
### Indexing Strategy
- Primary keys automatically indexed
- Foreign keys have dedicated indexes
- Frequently queried fields (rating, created_at) are indexed
- Unique constraints automatically create indexes
### Query Optimization
- Lazy loading prevents N+1 queries
- Pagination used for large result sets
- Specialized queries for common access patterns
- Typesense search engine for full-text search (separate from PostgreSQL)
### Data Volume Estimates
- **Stories**: Expected 1K-10K records per user
- **Authors**: Expected 100-1K records per user
- **Tags**: Expected 50-500 records per user
- **Series**: Expected 10-100 records per user
- **Join Tables**: Scale with story count and tagging usage
## Backup and Migration Considerations
### Schema Evolution
- Uses Hibernate `ddl-auto: update` for development
- Production should use controlled migration tools (Flyway/Liquibase)
- UUID keys allow safe data migration between environments
### Data Integrity
- Foreign key constraints ensure referential integrity
- Check constraints validate data ranges
- Application-level validation provides user-friendly error messages
- Unique constraints prevent duplicate data
### Backup Strategy
- Full PostgreSQL dumps for complete backup
- Image files stored separately in filesystem
- Consider incremental backups for large installations
- Test restore procedures regularly
This data model provides a solid foundation for personal story library management with room for future enhancements while maintaining data integrity and performance.

147
docs/README.md Normal file
View File

@@ -0,0 +1,147 @@
# StoryCove Documentation
Welcome to the StoryCove documentation! This directory contains comprehensive documentation for the StoryCove application.
## 📚 Documentation Files
### **[API Documentation](API.md)**
Complete REST API reference with detailed endpoint descriptions, request/response examples, authentication details, and error handling.
**Contents:**
- Authentication endpoints
- Story management (CRUD, search, images)
- Author management (profiles, avatars, URLs)
- Tag system (creation, autocomplete, statistics)
- Series management (organization, navigation)
- File upload/download (images, validation)
- Search and indexing (Typesense integration)
- Configuration endpoints
- Data Transfer Objects (DTOs)
- Error handling and status codes
### **[Data Model Documentation](DATA_MODEL.md)**
Detailed database schema documentation including entity relationships, constraints, indexes, and business rules.
**Contents:**
- Entity Relationship Diagram
- Detailed table specifications
- Field types and constraints
- Relationship mappings
- Performance considerations
- Backup and migration strategy
### **[OpenAPI Specification](openapi.yaml)**
Machine-readable API specification in OpenAPI 3.0 format for API testing tools, code generation, and integration.
**Usage:**
- Import into Postman, Insomnia, or similar tools
- Generate client SDKs
- API documentation hosting (Swagger UI)
- Contract testing
## 🛠️ How to Use This Documentation
### **For Developers**
1. Start with the [API Documentation](API.md) for endpoint details
2. Reference [Data Model](DATA_MODEL.md) for database understanding
3. Use [OpenAPI spec](openapi.yaml) for testing and integration
4. See main [README](../README.md) for setup instructions
### **For API Integration**
1. Review authentication flow in [API docs](API.md)
2. Import [OpenAPI spec](openapi.yaml) into your API client
3. Test endpoints using the provided examples
4. Implement error handling based on documented responses
### **For Database Work**
1. Study [Data Model](DATA_MODEL.md) for schema understanding
2. Review relationship constraints and business rules
3. Use entity diagrams for system architecture planning
4. Follow migration guidelines for schema changes
### **For System Administration**
1. Reference main [README](../README.md) for deployment instructions
2. Use environment configuration examples
3. Review backup strategies in [Data Model](DATA_MODEL.md)
4. Monitor API health using documented endpoints
## 🔗 External Resources
### **Related Documentation**
- **[Main README](../README.md)**: Project overview, setup, and deployment
- **[Technical Specification](../storycove-spec.md)**: Comprehensive technical specification
- **Environment Files**: `.env.example`, `.env.development`, `.env.production`
### **Technology Documentation**
- **[Spring Boot](https://spring.io/projects/spring-boot)**: Backend framework
- **[Next.js](https://nextjs.org/)**: Frontend framework
- **[PostgreSQL](https://www.postgresql.org/docs/)**: Database system
- **[Typesense](https://typesense.org/docs/)**: Search engine
- **[Docker](https://docs.docker.com/)**: Containerization
## 📝 Documentation Standards
### **API Documentation**
- All endpoints documented with examples
- Request/response schemas included
- Error scenarios covered
- Authentication requirements specified
### **Code Examples**
- JSON examples use realistic data
- UUIDs formatted correctly
- Timestamps in ISO 8601 format
- Error responses include helpful messages
### **Data Model**
- All tables and relationships documented
- Constraints and validation rules specified
- Performance implications noted
- Migration considerations included
## 🤝 Contributing to Documentation
When updating the application:
1. **API Changes**: Update [API.md](API.md) and [openapi.yaml](openapi.yaml)
2. **Database Changes**: Update [DATA_MODEL.md](DATA_MODEL.md)
3. **Feature Changes**: Update main [README](../README.md) features section
4. **Deployment Changes**: Update environment configuration examples
### **Documentation Checklist**
- [ ] API endpoints documented with examples
- [ ] Database schema changes reflected
- [ ] OpenAPI specification updated
- [ ] Error handling documented
- [ ] Authentication requirements specified
- [ ] Performance implications noted
- [ ] Migration steps documented
## 📋 Quick Reference
### **Base URLs**
- **Development**: `http://localhost:6925/api`
- **Production**: `https://yourdomain.com/api`
### **Authentication**
- **Method**: JWT tokens via httpOnly cookies
- **Login**: `POST /api/auth/login`
- **Token Expiry**: 24 hours
### **Key Endpoints**
- **Stories**: `/api/stories`
- **Authors**: `/api/authors`
- **Tags**: `/api/tags`
- **Series**: `/api/series`
- **Search**: `/api/stories/search`
- **Files**: `/api/files`
### **Common Response Codes**
- **200**: Success
- **201**: Created
- **400**: Bad Request
- **401**: Unauthorized
- **404**: Not Found
- **500**: Server Error
This documentation is maintained alongside the codebase to ensure accuracy and completeness. For questions or clarifications, please refer to the appropriate documentation file or create an issue in the project repository.

583
docs/openapi.yaml Normal file
View 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