# StoryCove - Software Requirements Specification ## 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.2 Technology Stack - **Frontend**: Next.js - **Backend**: Spring Boot (Java) - **Database**: PostgreSQL - **Search Engine**: Typesense - **Reverse Proxy**: Nginx - **Containerization**: Docker & Docker Compose ## 2. System Architecture ### 2.1 Container Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Nginx (Port 80/443) │ │ Reverse Proxy │ └─────────────┬───────────────────────────┬───────────────────┘ │ │ ▼ ▼ ┌─────────────────────────┐ ┌─────────────────────────────────┐ │ Next.js Frontend │ │ Spring Boot Backend │ │ (Port 3000) │ │ (Port 8080) │ └─────────────────────────┘ └──────────┬──────────┬──────────┘ │ │ ▼ ▼ ┌────────────────┐ ┌─────────────────┐ │ PostgreSQL │ │ Typesense │ │ (Port 5432) │ │ (Port 8108) │ └────────────────┘ └─────────────────┘ ``` ### 2.2 Data Flow 1. User accesses application through Nginx 2. Nginx routes requests to appropriate service 3. Frontend communicates with Backend API 4. Backend manages data in PostgreSQL and syncs search index with Typesense 5. Authentication handled via JWT tokens ## 3. Data Models ### 3.1 Database Schema #### Story Table ```sql CREATE TABLE stories ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(500) NOT NULL, author_id UUID NOT NULL, content_html TEXT NOT NULL, content_plain TEXT NOT NULL, source_url VARCHAR(1000), word_count INTEGER NOT NULL, series_id UUID, volume INTEGER, rating INTEGER CHECK (rating >= 1 AND rating <= 5), cover_image_path VARCHAR(500), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (author_id) REFERENCES authors(id), FOREIGN KEY (series_id) REFERENCES series(id) ); ``` #### Author Table ```sql CREATE TABLE authors ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL UNIQUE, notes TEXT, author_rating INTEGER CHECK (author_rating >= 1 AND author_rating <= 5), avatar_image_path VARCHAR(500), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### Author URLs Table ```sql 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 ); ``` #### Series Table ```sql CREATE TABLE series ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(500) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### Tags Table ```sql CREATE TABLE tags ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### Story Tags Junction Table ```sql CREATE TABLE story_tags ( story_id UUID NOT NULL, tag_id UUID NOT NULL, PRIMARY KEY (story_id, tag_id), FOREIGN KEY (story_id) REFERENCES stories(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE ); ``` ### 3.2 Typesense Schema ```json { "name": "stories", "fields": [ {"name": "id", "type": "string"}, {"name": "title", "type": "string"}, {"name": "author_name", "type": "string"}, {"name": "content", "type": "string"}, {"name": "tags", "type": "string[]"}, {"name": "series_name", "type": "string", "optional": true}, {"name": "word_count", "type": "int32"}, {"name": "rating", "type": "int32", "optional": true}, {"name": "created_at", "type": "int64"} ], "default_sorting_field": "created_at" } ``` ## 4. API Specification ### 4.1 Authentication Endpoints #### POST /api/auth/login ```json Request: { "password": "string" } Response: { "token": "jwt_token_string", "expiresIn": 86400 } ``` ### 4.2 Story Endpoints #### GET /api/stories Query parameters: - `page` (integer): Page number - `limit` (integer): Items per page - `search` (string): Search query - `tags` (string[]): Filter by tags - `authorId` (uuid): Filter by author - `seriesId` (uuid): Filter by series #### POST /api/stories ```json Request (multipart/form-data): { "title": "string", "authorName": "string", "contentHtml": "string", "sourceUrl": "string", "tags": ["string"], "seriesName": "string", "volume": "integer", "coverImage": "file (optional)" } ``` #### POST /api/stories/{id}/cover Upload/update cover image ``` Request: multipart/form-data - coverImage: file Response: { "imagePath": "string" } ``` #### GET /api/stories/{id} Returns full story details including HTML content #### PUT /api/stories/{id} Update story (same structure as POST) #### DELETE /api/stories/{id} Delete a story #### PUT /api/stories/{id}/rating ```json Request: { "rating": 1-5 } ``` ### 4.3 Author Endpoints #### GET /api/authors List all authors with story count and average rating #### GET /api/authors/{id} Get author details with all stories #### PUT /api/authors/{id} ```json Request (multipart/form-data): { "notes": "string", "authorRating": 1-5, "urls": [ { "url": "string", "description": "string" } ], "avatarImage": "file (optional)" } ``` #### POST /api/authors/{id}/avatar Upload/update author avatar ``` Request: multipart/form-data - avatarImage: file Response: { "imagePath": "string" } ``` ### 4.4 Image Endpoints #### GET /api/images/{type}/{filename} Serve images (covers or avatars) - type: "covers" or "avatars" - filename: stored filename #### DELETE /api/stories/{id}/cover Remove cover image from story #### DELETE /api/authors/{id}/avatar Remove avatar from author ### 4.4 Tag Endpoints #### GET /api/tags Returns tag cloud data with usage counts #### GET /api/tags/autocomplete?q={query} Autocomplete for tag input ### 4.5 Series Endpoints #### GET /api/series List all series with story counts #### GET /api/series/{id}/stories Get all stories in a series ordered by volume ## 5. UI/UX Specifications ### 5.1 Main Views #### Story List View - Grid/List toggle - Search bar with real-time results - Tag cloud for filtering - Sort options: Date added, Title, Author, Rating - Pagination - Cover image thumbnails in grid view - Quick actions: Edit, Delete, Read #### Story Create/Edit Form - Title input (required) - Author input with autocomplete - Cover image upload with preview - Rich text editor for content (with HTML source view) - Tag input with autocomplete and chip display - Series dropdown with "Add new" option - Volume number input (shown if series selected) - Source URL input - Save/Cancel buttons #### Reading View - Clean, distraction-free interface - Cover image display at the top (if available) - Responsive typography - Progress indicator - Navigation: Previous/Next in series - Quick access to rate story - Back to library button #### Author View - Author avatar display - Author name and rating display - Editable notes section - URL links management - Story list with cover thumbnails and individual ratings - Average story rating calculation - Edit author details button with avatar upload ### 5.2 Global Settings - Font family selection - Font size adjustment - Theme selection (Light/Dark) - Reading width preference ## 6. Technical Implementation Details ### 6.1 Frontend (Next.js) #### Key Libraries - **UI Framework**: Tailwind CSS or Material-UI - **State Management**: React Context or Zustand - **HTTP Client**: Axios or Fetch API - **HTML Sanitization**: DOMPurify - **Rich Text Editor**: TinyMCE or Quill - **Image Handling**: react-dropzone for uploads #### Authentication Flow ```typescript // JWT stored in httpOnly cookie // Auth context provides user state // Protected routes using middleware ``` #### Image Upload Handling ```typescript // Use multipart/form-data for story and author forms // Image preview before upload // Supported formats: JPG, PNG, WebP // Max file size: 5MB // Automatic image optimization on backend ``` ### 6.2 Backend (Spring Boot) #### Key Dependencies ```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt org.jsoup jsoup org.postgresql postgresql ``` #### HTML Processing ```java // Use Jsoup for HTML sanitization // Whitelist allowed tags: p, br, strong, em, ul, ol, li, h1-h6, blockquote // Strip all JavaScript and unsafe attributes // Generate plain text version for search indexing ``` #### Image Processing ```java // Image upload handling // Supported formats: JPG, PNG, WebP // Max size: 5MB // Automatic resizing: covers to 800x1200 max, avatars to 400x400 // Store in filesystem: /app/images/covers/ and /app/images/avatars/ // Generate unique filenames using UUID // Thumbnail generation for list views ``` ### 6.3 Search Integration #### Typesense Sync Strategy 1. On story create/update: Index immediately 2. On story delete: Remove from index 3. Batch reindex endpoint for maintenance 4. Search includes: title, author, content, tags ### 6.4 Security Considerations 1. **Authentication**: JWT with secure httpOnly cookies 2. **Input Validation**: All inputs validated and sanitized 3. **HTML Sanitization**: Strict whitelist of allowed tags 4. **SQL Injection**: Use parameterized queries 5. **XSS Prevention**: Content Security Policy headers 6. **CORS**: Configured for frontend origin only ## 7. Deployment Configuration ### 7.1 Docker Compose Configuration ```yaml version: '3.8' services: nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf - images_data:/app/images:ro depends_on: - frontend - backend frontend: build: ./frontend environment: - NEXT_PUBLIC_API_URL=http://backend:8080 depends_on: - backend backend: build: ./backend environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/storycove - SPRING_DATASOURCE_USERNAME=storycove - SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD} - JWT_SECRET=${JWT_SECRET} - TYPESENSE_API_KEY=${TYPESENSE_API_KEY} - TYPESENSE_HOST=typesense - TYPESENSE_PORT=8108 - IMAGE_STORAGE_PATH=/app/images volumes: - images_data:/app/images depends_on: - postgres - typesense postgres: image: postgres:15-alpine environment: - POSTGRES_DB=storycove - POSTGRES_USER=storycove - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data typesense: image: typesense/typesense:0.25.0 environment: - TYPESENSE_API_KEY=${TYPESENSE_API_KEY} - TYPESENSE_DATA_DIR=/data volumes: - typesense_data:/data volumes: postgres_data: typesense_data: images_data: ``` ### 7.2 Environment Variables ```env DB_PASSWORD=secure_password_here JWT_SECRET=secure_jwt_secret_here TYPESENSE_API_KEY=secure_api_key_here APP_PASSWORD=application_password_here ``` ## 8. Testing Strategy ### 8.1 Unit Tests - Backend: JUnit 5 for service and controller tests - Frontend: Jest and React Testing Library ### 8.2 Integration Tests - API endpoint testing with MockMvc - Database integration tests with Testcontainers ### 8.3 E2E Tests - Cypress or Playwright for critical user flows ## 9. Phase 2 Roadmap ### 9.1 URL Content Grabbing - Configurable scrapers for specific sites - Site configuration stored in database - Content extraction rules per site - Image download and storage ### 9.2 Image Support - Image storage in filesystem or S3-compatible storage - Image optimization pipeline - Inline image display in stories ### 9.3 Story Collections - Collection management interface - Ordered story lists - Collection sharing (future) ### 9.4 Export Functionality - PDF generation with formatting - EPUB export with metadata - Batch export for collections ## 10. Development Milestones ### Milestone 1: Infrastructure Setup (Week 1) - Docker environment configuration - Database schema implementation - Basic Spring Boot setup with security ### Milestone 2: Core Backend (Week 2-3) - Story CRUD operations - Author management - Tag system - Typesense integration ### Milestone 3: Frontend Foundation (Week 4-5) - Authentication flow - Story list and create forms - Author views - Search interface ### Milestone 4: Reading Experience (Week 6) - Reading view implementation - Settings management - Rating system ### Milestone 5: Polish & Testing (Week 7-8) - UI refinements - Comprehensive testing - Documentation - Deployment scripts