Files
storycove/storycove-spec.md
2025-07-22 21:49:40 +02:00

17 KiB

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

CREATE TABLE stories (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    title VARCHAR(500) NOT NULL,
    summary TEXT,
    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), -- Phase 2: Consider storing base filename without size suffix
    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

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), -- Phase 2: Consider storing base filename without size suffix
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Author URLs Table

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

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

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

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

{
  "name": "stories",
  "fields": [
    {"name": "id", "type": "string"},
    {"name": "title", "type": "string"},
    {"name": "summary", "type": "string", "optional": true},
    {"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

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

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

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}

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

Phase 2 Enhancement: Support for size variants

  • GET /api/images/{type}/{filename}?size=thumb|medium|full
  • Default to 'full' if no size specified
  • Return appropriate resized version

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

5.3 Color Specifications

Light Mode

  • Background: Off-White (#FAFAF8)
  • Primary Text: Charcoal (#2C3E50)
  • Headers: Deep Navy (#0A1628)
  • Accents: Teal Blue (#2A4D5C)

Dark Mode

  • Background: Deep Navy (#0A1628)
  • Primary Text: Warm Cream (#F5E6D3)
  • Headers: Warm Cream (#F5E6D3)
  • Accents: Golden Amber (#D4A574)

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

// JWT stored in httpOnly cookie
// Auth context provides user state
// Protected routes using middleware

Image Upload Handling

// 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

Theme Implementation

// CSS Variables approach for theme switching
// Light Mode:
--color-background: #FAFAF8;
--color-text-primary: #2C3E50;
--color-text-header: #0A1628;
--color-accent: #2A4D5C;

// Dark Mode:
--color-background: #0A1628;
--color-text-primary: #F5E6D3;
--color-text-header: #F5E6D3;
--color-accent: #D4A574;

// Theme preference stored in localStorage
// Respects system preference on first visit

6.2 Backend (Spring Boot)

Key Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
    </dependency>
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
    </dependency>
</dependencies>

HTML Processing

// 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

// 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
// Current: Single size per image type

// PHASE 2 ENHANCEMENT: Multi-size generation during upload
// Generate multiple sizes for optimal performance:
// - Cover images: thumbnail (200x300), medium (400x600), full (800x1200)
// - Avatar images: small (64x64), medium (200x200), full (400x400)
// Store with naming convention: {uuid}_thumb.jpg, {uuid}_medium.jpg, {uuid}_full.jpg
// Frontend selects appropriate size based on usage context
// Significant bandwidth and loading time improvements

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

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

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 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)
    • Automatic format optimization (WebP when supported)
    • Progressive JPEG for faster loading
  • Smart image serving
    • Context-aware size selection in frontend
    • Responsive images with srcset support
    • Lazy loading implementation
  • Storage optimization
    • Image compression with quality settings
    • Optional cloud storage integration (S3-compatible)
    • Automatic cleanup of unused images
  • Advanced features
    • Image metadata extraction (dimensions, EXIF)
    • Batch image processing tools
    • Image quality assessment and warnings
    • Inline image display in stories (future)

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