Update of documentation
This commit is contained in:
263
README.md
263
README.md
@@ -103,14 +103,259 @@ cd backend
|
||||
- `docker-compose logs -f [service]` - View logs
|
||||
- `docker-compose build` - Rebuild containers
|
||||
|
||||
## Features
|
||||
## ✨ Features
|
||||
|
||||
- Story management with HTML content support
|
||||
- Author profiles with ratings and metadata
|
||||
- Tag-based categorization
|
||||
- Full-text search capabilities
|
||||
- Responsive reading interface
|
||||
- JWT-based authentication
|
||||
- Docker-based deployment
|
||||
### 📚 **Story Management**
|
||||
- **Rich Text Support**: HTML content with automatic plain text extraction
|
||||
- **Content Sanitization**: Secure HTML processing with customizable sanitization rules
|
||||
- **Metadata Management**: Title, summary, description, source URL tracking
|
||||
- **Rating System**: 5-star rating system for stories
|
||||
- **Word Count**: Automatic word count calculation
|
||||
- **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
1056
docs/API.md
Normal file
File diff suppressed because it is too large
Load Diff
263
docs/DATA_MODEL.md
Normal file
263
docs/DATA_MODEL.md
Normal 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
147
docs/README.md
Normal 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
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