mad-lawsuit/DESIGN_SPECIFICATION.md

510 lines
19 KiB
Markdown

# Eliza Kragh v. MAD Court Docket Website - Design Specification
## Project Overview
A public-facing website to display court documents and filings for the ongoing lawsuit: **Eliza Kragh v. Montana Association of the Deaf**. The system provides public transparency for court proceedings while allowing administrative management of documents and user notifications.
## Technical Architecture
### System Components
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Next.js 15.4 │ │ Express.js 6 │ │ PostgreSQL 17.5 │
│ Frontend │◄──►│ Backend API │◄──►│ Database │
│ (Port 3000) │ │ (Port 3001) │ │ (Port 5432) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ ┌─────────────────┐ │
│ │ Redis │ │
└──────────────►│ Cache/Session │◄─────────────┘
│ (Port 6379) │
└─────────────────┘
┌─────────────────┐
│ File System │
│ /uploads/ │
└─────────────────┘
```
**Technology Stack (June 2025 Latest Versions):**
- **Frontend**: Next.js 15.4.0 with React 19, TypeScript 6.0, Tailwind CSS
- **Backend**: Node.js 22 LTS, Express.js 6.0, TypeScript 6.0, Prisma 5.0
- **Database**: PostgreSQL 17.5 with Redis caching
- **Infrastructure**: Docker Engine 28.1.1, Nginx reverse proxy
## Database Schema
### Tables Structure
```sql
-- Docket Entries (main court filings)
CREATE TABLE docket_entries (
id SERIAL PRIMARY KEY,
date DATE NOT NULL,
document_type VARCHAR(100) NOT NULL,
summary TEXT NOT NULL,
court_notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Documents (PDFs associated with entries)
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
docket_entry_id INTEGER REFERENCES docket_entries(id) ON DELETE CASCADE,
original_filename VARCHAR(255) NOT NULL,
stored_filename VARCHAR(255) NOT NULL UNIQUE,
file_path VARCHAR(500) NOT NULL,
title VARCHAR(255) NOT NULL,
summary TEXT,
notes TEXT,
is_primary BOOLEAN DEFAULT FALSE,
file_size INTEGER NOT NULL,
display_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Email Subscriptions
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
unsubscribe_token VARCHAR(255) UNIQUE
);
-- Admin Users
CREATE TABLE admin_users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
);
-- Indexes for performance
CREATE INDEX idx_docket_entries_date ON docket_entries(date DESC);
CREATE INDEX idx_documents_docket_entry ON documents(docket_entry_id);
CREATE INDEX idx_documents_display_order ON documents(docket_entry_id, display_order);
CREATE INDEX idx_subscriptions_active ON subscriptions(is_active);
```
## API Endpoints
### Public Endpoints
```
GET /api/docket-entries
Response: Array of docket entries with associated documents
{
"entries": [
{
"id": 1,
"date": "2024-12-15",
"document_type": "Motion to Dismiss",
"summary": "Defendant's motion...",
"court_notes": "Motion filed within deadline...",
"documents": [
{
"id": 1,
"title": "Main Document",
"summary": "Primary motion document",
"notes": "Filed by defendant's counsel",
"is_primary": true,
"display_order": 0
}
]
}
]
}
GET /api/documents/:id/download
Response: PDF file stream
Headers: Content-Type: application/pdf
POST /api/subscribe
Body: { "email": "user@example.com" }
Response: { "success": true, "message": "Subscribed successfully" }
GET /api/unsubscribe/:token
Response: Unsubscribe confirmation page
```
### Admin Endpoints (Protected)
```
POST /api/auth/login
Body: { "username": "admin", "password": "password" }
Response: { "token": "jwt-token", "expires": "timestamp" }
POST /api/docket-entries
Body: {
"date": "2024-12-15",
"document_type": "Motion",
"summary": "Entry summary",
"court_notes": "Court notes",
"documents": [
{
"title": "Main Document",
"summary": "Document summary",
"notes": "Document notes",
"is_primary": true,
"file": "base64-encoded-pdf"
}
]
}
PUT /api/docket-entries/:id
Body: Updated entry data
Response: Updated entry object
DELETE /api/docket-entries/:id
Response: { "success": true }
POST /api/documents
Body: FormData with file and metadata
Response: Created document object
PUT /api/documents/:id
Body: Updated document metadata
Response: Updated document object
PUT /api/documents/:id/replace
Body: FormData with new file
Response: Updated document object
DELETE /api/documents/:id
Response: { "success": true }
```
## File Management System
### UUID-Based Naming Scheme
```
Format: {entry-id}_{document-type}_{uuid}.pdf
Examples:
001_main_a7b3c9d2-4e5f-6789-abcd-ef0123456789.pdf
001_exhibit_b8c4d0e3-5f60-789a-bcde-f01234567890.pdf
002_main_c9d5e1f4-6071-890b-cdef-012345678901.pdf
```
### Directory Structure
```
uploads/
├── 2024/
│ ├── 12/
│ │ ├── 001_main_a7b3c9d2-4e5f-6789-abcd-ef0123456789.pdf
│ │ ├── 001_exhibit_b8c4d0e3-5f60-789a-bcde-f01234567890.pdf
│ │ └── 002_main_c9d5e1f4-6071-890b-cdef-012345678901.pdf
│ └── 11/
│ └── [previous month files]
└── 2025/
└── [future files]
```
### File Upload Process
1. Validate file type (PDF only) and size (max 10MB)
2. Generate UUID for filename
3. Create year/month directory structure
4. Save file with UUID-based name
5. Store metadata in database
6. Return success response with document ID
## Frontend Components
### Public Interface Components
```
components/
├── Layout/
│ ├── Header.tsx # Site header with title and subscribe button
│ ├── Footer.tsx # Site footer
│ └── Layout.tsx # Main layout wrapper
├── Docket/
│ ├── DocketList.tsx # List of all docket entries
│ ├── DocketEntry.tsx # Individual entry display
│ ├── DocumentList.tsx # Documents within an entry
│ └── DocumentViewer.tsx # PDF viewing modal
├── Subscription/
│ ├── SubscribeForm.tsx # Email subscription form
│ └── SubscribeModal.tsx # Subscription confirmation modal
└── Common/
├── LoadingSpinner.tsx # Loading states
├── ErrorMessage.tsx # Error display
└── Button.tsx # Reusable button component
```
### Admin Interface Components
```
components/admin/
├── Auth/
│ └── LoginForm.tsx # Admin login form
├── Dashboard/
│ ├── Dashboard.tsx # Main admin dashboard
│ ├── EntryList.tsx # Manage existing entries
│ └── Stats.tsx # Basic statistics
├── Entry/
│ ├── CreateEntry.tsx # New docket entry form
│ ├── EditEntry.tsx # Edit existing entry
│ └── DeleteEntry.tsx # Delete confirmation
├── Document/
│ ├── DocumentUpload.tsx # Multi-file upload component
│ ├── DocumentEdit.tsx # Edit document metadata
│ └── DocumentReplace.tsx # Replace PDF file
└── Layout/
├── AdminLayout.tsx # Admin layout wrapper
└── AdminNav.tsx # Admin navigation
```
## User Interface Layouts
### Public Docket Page
```
┌─────────────────────────────────────────────────────────────┐
│ ELIZA KRAGH v. MAD DOCKET │
│ [Subscribe] │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Case Information │
│ ───────────────── │
│ Case: Eliza Kragh v. Montana Association of the Deaf │
│ Status: Ongoing │
│ Last Updated: December 15, 2024 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Docket Entries │
│ ───────────────── │
│ [Date] | [Document Type] | [Summary] | [View] │
│ ──────────────────────────────────────────────────────── │
│ 12/15/24 | Motion to Dismiss | Defendant's motion... |[+] │
│ └─ 📄 Main Document │
│ └─ 📄 Exhibit A - Financial Records │
│ └─ 📄 Exhibit B - Email Correspondence │
│ ──────────────────────────────────────────────────────── │
│ 12/10/24 | Complaint | Plaintiff's initial filing... |[+] │
│ └─ 📄 Main Document │
│ └─ 📄 Exhibit 1 - Contract Agreement │
│ ──────────────────────────────────────────────────────── │
│ [More entries...] │
└─────────────────────────────────────────────────────────────┘
```
### Admin Dashboard
```
┌─────────────────────────────────────────────────────────────┐
│ ADMIN DASHBOARD [Logout] │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ [+ New Docket Entry] │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Manage Existing Entries │
│ ────────────────────── │
│ 12/15/24 | Motion to Dismiss | [Edit] [Delete] │
│ └─ 📄 Main Document [Edit] [Replace] [Del] │
│ └─ 📄 Exhibit A [Edit] [Replace] [Del] │
│ └─ 📄 Exhibit B [Edit] [Replace] [Del] │
│ └─ [+ Add Document to Entry] │
│ ──────────────────────────────────────────────────────── │
│ 12/10/24 | Complaint | [Edit] [Delete] │
│ └─ 📄 Main Document [Edit] [Replace] [Del] │
│ └─ 📄 Exhibit 1 [Edit] [Replace] [Del] │
│ └─ [+ Add Document to Entry] │
└─────────────────────────────────────────────────────────────┘
```
## Docker Configuration
### docker-compose.yml
```yaml
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://backend:3001/api
depends_on:
- backend
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "3001:3001"
environment:
- DATABASE_URL=postgresql://docket_user:docket_pass@postgres:5432/docket_db
- REDIS_URL=redis://redis:6379
- JWT_SECRET=your-secret-key
- UPLOAD_DIR=/app/uploads
volumes:
- uploads:/app/uploads
depends_on:
- postgres
- redis
postgres:
image: postgres:15
environment:
- POSTGRES_DB=docket_db
- POSTGRES_USER=docket_user
- POSTGRES_PASSWORD=docket_pass
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- frontend
- backend
volumes:
postgres_data:
uploads:
```
## Security Implementation
### Authentication & Authorization
- JWT tokens for admin authentication
- Token expiration and refresh mechanism
- Protected routes middleware
- Role-based access control
### File Security
- Files served through API endpoints only
- No direct file system access
- File type validation (PDF only)
- File size limits (10MB max)
- UUID-based naming prevents enumeration
### Input Validation
- All API inputs validated with Joi/Zod
- SQL injection prevention with Prisma ORM
- XSS protection with input sanitization
- CSRF protection for admin forms
### Rate Limiting
- API endpoint rate limiting
- File upload rate limiting
- Login attempt limiting
- Email subscription rate limiting
## Email Notification System
### Subscription Process
1. User enters email on public site
2. Email validated and stored in database
3. Confirmation email sent with unsubscribe token
4. User added to active subscribers list
### Notification Trigger
1. Admin uploads new docket entry
2. System checks for active subscribers
3. Email notification queued for each subscriber
4. Background job processes email queue
5. Emails sent with entry summary and link
### Email Template
```
Subject: New Filing in Eliza Kragh v. Montana Association of the Deaf
Dear Subscriber,
A new document has been filed in the case of Eliza Kragh v. Montana Association of the Deaf:
Date: December 15, 2024
Document Type: Motion to Dismiss
Summary: Defendant's motion to dismiss based on lack of jurisdiction...
View the complete filing and all documents at:
https://docket-site.com
To unsubscribe from these notifications, click here:
https://docket-site.com/unsubscribe/[token]
Best regards,
Court Docket Notification System
```
## Performance Optimization
### Frontend Optimization
- Next.js static generation for public pages
- Image optimization for any graphics
- Code splitting and lazy loading
- PDF.js worker for document rendering
- Caching strategies for API responses
### Backend Optimization
- Database query optimization with indexes
- Redis caching for frequently accessed data
- File streaming for PDF downloads
- Connection pooling for database
- Compression middleware for API responses
### Infrastructure Optimization
- Nginx reverse proxy with caching
- Gzip compression for static assets
- CDN integration for file serving
- Database connection pooling
- Redis session storage
## Deployment Strategy
### Development Environment
- Docker Compose for local development
- Hot reloading for both frontend and backend
- Development database with sample data
- Email testing with local SMTP server
### Production Environment
- Docker Swarm or Kubernetes deployment
- SSL/TLS certificates with Let's Encrypt
- Production database with backups
- Real SMTP service for email notifications
- Monitoring and logging with ELK stack
### CI/CD Pipeline
- GitHub Actions for automated testing
- Docker image building and pushing
- Automated deployment to staging
- Manual approval for production deployment
- Database migration automation
## Testing Strategy
### Unit Testing
- Backend API endpoints with Jest
- Frontend components with React Testing Library
- Database operations with test database
- File upload functionality testing
### Integration Testing
- End-to-end user workflows with Cypress
- API integration testing
- Email notification testing
- File serving and download testing
### Security Testing
- Authentication and authorization testing
- Input validation testing
- File upload security testing
- SQL injection prevention testing
This comprehensive design specification serves as the complete technical reference for implementing the Eliza Kragh v. MAD court docket website.