Various improvements

This commit is contained in:
Stefan Hardegger
2025-08-21 13:55:38 +02:00
parent 35a5825e76
commit a660056003
11 changed files with 829 additions and 55 deletions

View File

@@ -1,17 +1,35 @@
# StoryCove - Software Requirements Specification
## Table of Contents
1. [Executive Summary](#1-executive-summary)
2. [System Architecture](#2-system-architecture)
3. [Data Models](#3-data-models)
4. [API Specification](#4-api-specification)
5. [UI/UX Specifications](#5-uiux-specifications)
6. [Security Requirements](#6-security-requirements)
7. [Deployment](#7-deployment)
8. [Testing Strategy](#8-testing-strategy)
9. [Advanced Features (✅ IMPLEMENTED)](#9-advanced-features--implemented)
- [9.1 Collections Feature](#91-collections-feature--completed)
- [9.2 Enhanced Tag System](#92-enhanced-tag-system--completed)
- [9.3 EPUB Import/Export System](#93-epub-importexport-system--completed)
10. [Future Roadmap](#10-future-roadmap)
## 1. Executive Summary
StoryCove is a self-hosted web application designed to store, organize, and read short stories collected from various internet sources. The application provides a clean, responsive interface for managing a personal library of stories with advanced search capabilities, author management, and rating systems.
### 1.1 Key Features (MVP)
- 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
### 1.1 Key Features (✅ IMPLEMENTED)
- **Story Management**: Complete CRUD operations with HTML content support
- **Author Profiles**: Full author management with ratings, metadata, and URL collections
- **Enhanced Tag System**: Color tags, aliases, merging, and AI-powered suggestions
- **Collections**: Organized story lists with reading flow and EPUB export
- **EPUB Import/Export**: Full EPUB support with metadata and reading position preservation
- **Advanced Search**: Typesense-powered full-text search with faceting
- **Reading Position Tracking**: Character-level position tracking with EPUB CFI support
- **Responsive Reading Interface**: Distraction-free reading with progress tracking
- **JWT Authentication**: Secure single-password authentication system
- **Docker Deployment**: Complete containerized deployment with Docker Compose
### 1.2 Technology Stack
- **Frontend**: Next.js
@@ -58,21 +76,23 @@ StoryCove is a self-hosted web application designed to store, organize, and read
```sql
CREATE TABLE stories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR(500) NOT NULL,
title VARCHAR(255) NOT NULL,
summary TEXT,
description VARCHAR(1000),
author_id UUID NOT NULL,
content_html TEXT NOT NULL,
content_plain TEXT NOT NULL,
source_url VARCHAR(1000),
word_count INTEGER NOT NULL,
word_count INTEGER DEFAULT 0,
series_id UUID,
volume INTEGER,
rating INTEGER CHECK (rating >= 1 AND rating <= 5),
cover_image_path VARCHAR(500), -- Phase 2: Consider storing base filename without size suffix
cover_path VARCHAR(500), -- Updated field name
is_read BOOLEAN DEFAULT FALSE, -- Reading status
reading_position INTEGER DEFAULT 0, -- Character position for reading progress
last_read_at TIMESTAMP, -- Last time story was accessed
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (author_id) REFERENCES authors(id),
FOREIGN KEY (series_id) REFERENCES series(id)
);
@@ -91,13 +111,13 @@ CREATE TABLE authors (
);
```
#### Author URLs Table
#### Author URLs Collection Table
```sql
-- Note: In the actual implementation, this is managed as an @ElementCollection
-- This table is created automatically by Hibernate
CREATE TABLE author_urls (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_id UUID NOT NULL,
url VARCHAR(1000) NOT NULL,
description VARCHAR(255),
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE
);
```
@@ -116,7 +136,98 @@ CREATE TABLE series (
CREATE TABLE tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
color VARCHAR(7), -- Hex color code like #3B82F6
description VARCHAR(500), -- Tag description
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
```
#### Collections Table
```sql
CREATE TABLE collections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(500) NOT NULL,
description TEXT,
rating INTEGER CHECK (rating >= 1 AND rating <= 5),
cover_image_path VARCHAR(500),
is_archived BOOLEAN DEFAULT FALSE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE INDEX idx_collections_archived ON collections(is_archived);
```
#### Collection Stories Junction Table
```sql
CREATE TABLE collection_stories (
collection_id UUID NOT NULL,
story_id UUID NOT NULL,
position INTEGER NOT NULL,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (collection_id, story_id),
FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE,
FOREIGN KEY (story_id) REFERENCES stories(id) ON DELETE CASCADE,
UNIQUE(collection_id, position)
);
CREATE INDEX idx_collection_stories_position ON collection_stories(collection_id, position);
```
#### Collection Tags Junction Table
```sql
CREATE TABLE collection_tags (
collection_id UUID NOT NULL,
tag_id UUID NOT NULL,
PRIMARY KEY (collection_id, tag_id),
FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);
```
#### Tag Aliases Table
```sql
CREATE TABLE tag_aliases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
alias_name VARCHAR(100) UNIQUE NOT NULL,
canonical_tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
created_from_merge BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE INDEX idx_tag_aliases_name ON tag_aliases(alias_name);
```
#### Reading Positions Table
```sql
CREATE TABLE reading_positions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
story_id UUID NOT NULL,
chapter_index INTEGER,
chapter_title VARCHAR(255),
word_position INTEGER,
character_position INTEGER,
percentage_complete DECIMAL(5,2),
epub_cfi TEXT,
context_before VARCHAR(500),
context_after VARCHAR(500),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (story_id) REFERENCES stories(id) ON DELETE CASCADE
);
CREATE INDEX idx_reading_position_story ON reading_positions(story_id);
```
#### Libraries Table
```sql
CREATE TABLE libraries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
```
@@ -241,6 +352,108 @@ Response:
}
```
#### POST /api/stories/{id}/reading-status
```json
Request:
{
"isRead": true
}
Response:
{
"id": "uuid",
"isRead": true,
"readingPosition": 0,
"lastReadAt": "2024-01-01T12:00:00Z"
}
```
#### GET /api/stories/{id}/read
Returns story content optimized for reading interface
#### GET /api/stories/random
Returns a random story with optional filtering
Query parameters:
- `searchQuery` (string): Filter search
- `tags` (string[]): Filter by tags
#### GET /api/stories/check-duplicate
Check for potential duplicate stories
Query parameters:
- `title` (string): Story title to check
- `authorName` (string): Author name to check
#### GET /api/stories/{id}/collections
Get all collections containing this story
### 4.2.1 EPUB Import/Export Endpoints
#### POST /api/stories/epub/import
Import story from EPUB file
```json
Request (multipart/form-data):
{
"file": "epub file",
"authorId": "uuid (optional)",
"authorName": "string (optional)",
"seriesId": "uuid (optional)",
"seriesName": "string (optional)",
"seriesVolume": "integer (optional)",
"tags": ["string"],
"preserveReadingPosition": "boolean",
"overwriteExisting": "boolean",
"createMissingAuthor": "boolean",
"createMissingSeries": "boolean"
}
Response:
{
"success": true,
"storyId": "uuid",
"storyTitle": "string",
"message": "EPUB imported successfully",
"extractedData": {
"chapterCount": 12,
"wordCount": 45000,
"hasCovers": true
}
}
```
#### GET /api/stories/{id}/epub
Export story as EPUB (simple download)
#### POST /api/stories/epub/export
Export story as EPUB with custom options
```json
Request:
{
"storyId": "uuid",
"includeReadingPosition": true,
"includeCoverImage": true,
"includeMetadata": true
}
Response: EPUB file download
```
#### POST /api/stories/epub/validate
Validate EPUB file before import
```json
Request (multipart/form-data):
{
"file": "epub file"
}
Response:
{
"valid": true,
"errors": [],
"filename": "story.epub",
"size": 1024000
}
```
### 4.3 Author Endpoints
#### GET /api/authors
@@ -298,12 +511,261 @@ Remove avatar from author
### 4.4 Tag Endpoints
#### GET /api/tags
Returns tag cloud data with usage counts
Returns paginated list of tags with enhanced features
Query parameters:
- `page` (integer): Page number
- `size` (integer): Items per page
- `sortBy` (string): Sort field (name, usage)
- `sortDir` (string): Sort direction (asc, desc)
#### GET /api/tags/autocomplete?q={query}
Autocomplete for tag input
#### GET /api/tags/{id}
Get specific tag with full details including aliases
### 4.5 Series Endpoints
#### POST /api/tags
Create new tag with color and description
```json
Request:
{
"name": "string",
"color": "#3B82F6",
"description": "string"
}
```
#### PUT /api/tags/{id}
Update tag properties
```json
Request:
{
"name": "string",
"color": "#3B82F6",
"description": "string"
}
```
#### DELETE /api/tags/{id}
Delete tag with safety checks
#### GET /api/tags/autocomplete?query={query}
Enhanced autocomplete with alias resolution
#### GET /api/tags/popular
Most used tags
#### GET /api/tags/unused
Tags not assigned to any stories
#### GET /api/tags/stats
Tag usage statistics
#### GET /api/tags/collections
Tags used by collections
#### GET /api/tags/resolve/{name}
Resolve tag name through alias system
### 4.4.1 Tag Alias Management
#### POST /api/tags/{tagId}/aliases
Add alias to tag
```json
Request:
{
"aliasName": "string"
}
```
#### DELETE /api/tags/{tagId}/aliases/{aliasId}
Remove alias from tag
### 4.4.2 Tag Management Operations
#### POST /api/tags/merge
Merge multiple tags into one
```json
Request:
{
"sourceTagIds": ["uuid1", "uuid2"],
"targetTagId": "uuid3"
}
Response:
{
"resultTag": "TagDto",
"aliasesCreated": ["alias1", "alias2"]
}
```
#### POST /api/tags/merge/preview
Preview tag merge operation without executing
#### POST /api/tags/suggest
AI-powered tag suggestions for content
```json
Request:
{
"title": "string",
"content": "string",
"summary": "string",
"limit": 10
}
Response:
[
{
"tagName": "fantasy",
"confidence": 0.85,
"reason": "Content contains magical elements"
}
]
```
### 4.5 Collections Endpoints
#### GET /api/collections
Search and list collections with pagination (Typesense-powered)
Query parameters:
- `page` (integer): Page number
- `limit` (integer): Items per page
- `search` (string): Search query
- `tags` (string[]): Filter by tags
- `archived` (boolean): Include archived collections
#### GET /api/collections/{id}
Get collection details with story list
#### POST /api/collections
Create new collection
```json
Request (JSON or multipart/form-data):
{
"name": "string",
"description": "string",
"tagNames": ["string"],
"storyIds": ["uuid"],
"coverImage": "file (optional for multipart)"
}
```
#### PUT /api/collections/{id}
Update collection metadata
```json
Request:
{
"name": "string",
"description": "string",
"tagNames": ["string"],
"rating": 1-5
}
```
#### DELETE /api/collections/{id}
Delete collection (stories remain in system)
#### PUT /api/collections/{id}/archive
Archive or unarchive collection
```json
Request:
{
"archived": true
}
```
#### POST /api/collections/{id}/cover
Upload collection cover image
#### DELETE /api/collections/{id}/cover
Remove collection cover image
#### GET /api/collections/{id}/stats
Get detailed collection statistics
```json
Response:
{
"totalStories": 15,
"totalWordCount": 125000,
"estimatedReadingTime": 625,
"averageStoryRating": 4.2,
"tagFrequency": {
"fantasy": 12,
"adventure": 8
},
"authorDistribution": [
{"authorName": "string", "storyCount": 5}
]
}
```
### 4.5.1 Collection Story Management
#### POST /api/collections/{id}/stories
Add stories to collection with optional positioning
```json
Request:
{
"storyIds": ["uuid"],
"position": 3
}
Response:
{
"added": 3,
"skipped": 1,
"totalStories": 15
}
```
#### DELETE /api/collections/{id}/stories/{storyId}
Remove story from collection
#### PUT /api/collections/{id}/stories/order
Reorder stories in collection
```json
Request:
{
"storyOrders": [
{"storyId": "uuid", "position": 1},
{"storyId": "uuid", "position": 2}
]
}
```
#### GET /api/collections/{id}/read/{storyId}
Get story content with collection navigation context
```json
Response:
{
"story": { /* full story data */ },
"collection": {
"id": "uuid",
"name": "string",
"currentPosition": 3,
"totalStories": 10,
"previousStoryId": "uuid",
"nextStoryId": "uuid"
}
}
```
### 4.5.2 Collection EPUB Export
#### GET /api/collections/{id}/epub
Export collection as EPUB file
#### POST /api/collections/{id}/epub
Export collection as EPUB with custom options
```json
Request:
{
"includeCoverImage": true,
"includeMetadata": true,
"includeTableOfContents": true
}
Response: EPUB file download
```
### 4.6 Series Endpoints
#### GET /api/series
List all series with story counts
@@ -587,15 +1049,130 @@ APP_PASSWORD=application_password_here
### 8.3 E2E Tests
- Cypress or Playwright for critical user flows
## 9. Phase 2 Roadmap
## 9. Advanced Features (✅ IMPLEMENTED)
### 9.1 URL Content GrabbingIMPLEMENTED
### 9.1 Collections FeatureCOMPLETED
Story Collections allow users to organize stories into ordered lists for better content management and reading workflows. Collections support custom ordering, metadata, and provide an enhanced reading experience for grouped content.
#### Core Capabilities
- Create and manage ordered lists of stories
- Stories can belong to multiple collections
- Drag-and-drop reordering with gap-based positioning
- Collection-level metadata and ratings
- Dedicated collection reading flow with navigation
- Batch operations on collection contents
- EPUB export for entire collections
- Archive functionality for collection management
#### Collection Data Model
Collections use a junction table approach with position-based ordering:
- **Collections Table**: Core collection metadata with archiving support
- **collection_stories**: Junction table with gap-based positioning (1000, 2000, 3000...)
- **collection_tags**: Many-to-many relationship with tags for categorization
#### Collection Reading Flow
Enhanced reading experience with collection context:
- Navigate between stories in collection order
- Show position within collection (Story 3 of 10)
- Previous/Next story navigation
- Collection-specific reading statistics
#### Collection EPUB Export
Export entire collections as single EPUB files:
- Maintains story order from collection
- Includes collection metadata as book metadata
- Generates table of contents with story titles
- Preserves individual story formatting and structure
### 9.2 Enhanced Tag System ✅ COMPLETED
Comprehensive enhancement of the tagging functionality including color tags, tag deletion, merging, aliases, and AI-powered suggestions.
#### Color Tags
- **Visual Organization**: Assign hex colors to tags for better visual distinction
- **Predefined Palette**: Theme-compatible color selection
- **Custom Colors**: Full color picker for advanced customization
- **Accessibility**: All colors ensure sufficient contrast ratios
#### Tag Aliases System
- **Automatic Resolution**: Users type "magictf" and get "magic tf" automatically
- **Transparent Integration**: Aliases work seamlessly in search and autocomplete
- **Merge Integration**: Automatically created when tags are merged
- **Hover Display**: Show all aliases when hovering over canonical tags
#### Tag Merging
Sophisticated tag merging with automatic aliasing:
1. Select multiple tags to merge
2. Choose canonical tag name
3. Preview merge impact (story counts)
4. All associations transfer to canonical tag
5. Source tags automatically become aliases
#### AI-Powered Tag Suggestions
- **Content Analysis**: Analyze story title, content, and summary
- **Confidence Scoring**: Each suggestion includes confidence level
- **Contextual Reasoning**: Explanations for why tags were suggested
- **Integration**: Available during story creation and editing
#### Tag Management Interface
Dedicated tag maintenance accessible through Settings:
- Searchable and filterable tag list
- Sortable by name, usage count, creation date
- Bulk selection for merge/delete operations
- Visual indicators for colors and alias counts
- Tag statistics and usage analytics
### 9.3 EPUB Import/Export System ✅ COMPLETED
Full-featured EPUB import and export system with advanced metadata handling and reading position preservation.
#### EPUB Import Features
- **Format Support**: EPUB 2.0 and 3.x formats, DRM-free
- **Validation**: Comprehensive file format and structure validation
- **Metadata Extraction**: Title, author, description, subjects/tags, cover images
- **Content Processing**: Multi-chapter combination with proper HTML sanitization
- **Duplicate Detection**: Smart detection of potentially duplicate content
- **Batch Import**: Support for importing multiple EPUB files
#### EPUB Export Features
- **Individual Stories**: Export single stories as properly formatted EPUB files
- **Collection Export**: Export entire collections as single EPUB with TOC
- **Metadata Preservation**: Complete metadata including tags, ratings, series info
- **Cover Images**: Automatic inclusion and optimization of cover artwork
- **Reading Positions**: EPUB CFI standard support for position preservation
#### Advanced Metadata Handling
Comprehensive extraction and preservation of:
- **Basic Metadata**: Title, author, description, language
- **Publication Data**: Publisher, publication date, identifiers (ISBN, etc.)
- **Categorization**: Subjects converted to tags automatically
- **Cover Processing**: Automatic extraction, validation, and optimization
- **Custom Metadata**: StoryCove-specific fields for ratings and series
#### Reading Position Integration
- **EPUB CFI Support**: Standard-compliant reading position tracking
- **Cross-Format Preservation**: Maintain positions when importing/exporting
- **Chapter Awareness**: Chapter-level position tracking with context
- **Percentage Calculation**: Accurate progress percentage based on content length
- **Context Preservation**: Before/after text context for position recovery
#### File Handling & Validation
- **Size Limits**: 50MB maximum file size with configurable limits
- **Security**: Comprehensive validation to prevent malicious content
- **Error Recovery**: Graceful handling of corrupted or invalid EPUB files
- **Progress Feedback**: Real-time progress indicators during import/export
- **Batch Operations**: Support for processing multiple files simultaneously
## 10. Future Roadmap
### 10.1 URL Content Grabbing ✅ IMPLEMENTED
- Configurable scrapers for specific sites
- Site configuration stored in JSON files
- Content extraction rules per site (DeviantArt support added)
- Adaptive content extraction for varying HTML structures
### 9.2 Enhanced Image Processing & Optimization
### 10.2 Enhanced Image Processing & Optimization
- **Multi-size generation during upload**
- Cover images: thumbnail (200x300), medium (400x600), full (800x1200)
- Avatar images: small (64x64), medium (200x200), full (400x400)