Files
spotlightcam/docs/TODO.md

44 KiB
Raw Blame History

TODO - spotlight.cam

Active tasks and roadmap - optimized for quick reference


🎯 CURRENT STATUS

Phase: 3 (MVP Finalization) - COMPLETED Previous Phases:

  • Phase 2.5 (WebRTC Implementation) - COMPLETED
  • Phase 2 (Matches & Ratings API) - COMPLETED
  • Phase 1.6 (Competition Heats System) - COMPLETED Progress: 100% MVP complete Status: Ready for production deployment

🔧 CURRENT STATUS - Backend Tests (2025-11-20) - ALL PASSING!

Test Status: 223/223 passing (100%) - ALL TESTS FIXED!

  • Initial state: 145/223 (65%)
  • After cleanup fixes: 189/223 (84.8%)
  • Final state: 223/223 (100%)
  • Total improvement: +78 tests (+35%)

Code Coverage: 71.31% (up from ~45%)

All fixes completed:

  1. Test cleanup - replaced deleteMany({}) with selective deletion:
    • backend/src/__tests__/events.test.js - selective deletion by username/email/slug
    • backend/src/__tests__/matches.test.js - selective deletion by username/email/slug
    • backend/src/__tests__/csrf.test.js - selective deletion by username/email
  2. Cleanup resilience - replaced delete() with deleteMany():
    • backend/src/__tests__/socket.test.js - all afterAll + inline cleanup
    • backend/src/__tests__/matches.test.js - inline cleanup (2 locations)
    • backend/src/__tests__/socket-webrtc.test.js - inline cleanup
  3. Socket tests fixes:
    • Changed eventIdslug in socket event handlers
    • Added EventParticipant creation before join_event_room
    • Added safety checks before creating FK relationships
  4. Auth tests fixes:
    • app.test.js - CORS origin localhost:8080
    • security.js - added localhost:3000 to allowed origins (dev)
    • auth-phase1.5.test.js - fixed error message expectations
  5. Test isolation - unique test data per suite to prevent race conditions:
    • users.test.js - prefixed usernames with users_ (users_john_dancer, users_sarah_swings)
    • matches.test.js - prefixed with matches_ (matches_john_dancer, etc.) + unique event slug
    • events.test.js - prefixed with events_ (events_john_dancer, etc.) + unique slugs + unique worldsdc_id

Commits made:

  • test: fix socket.test.js cleanup and event room parameters
  • test: improve test cleanup - selective deletion instead of wiping tables
  • test: fix test isolation by using unique test data per suite

🔐 SECURITY AUDIT FINDINGS (2025-11-20)

Audit Status: Comprehensive security audit completed by nodejs-security-auditor Overall Security Grade: B+ (pending critical fixes) Production Readiness: NOT READY - 3 critical blockers must be resolved Ready for Production: 95% (after fixing critical issues)

🚨 CRITICAL ISSUES (BLOCKERS - Must Fix Immediately)

Status: All must be resolved before production deployment

  1. HARDCODED AWS CREDENTIALS IN REPOSITORY

    • Severity: CRITICAL (10/10)
    • CWE: CWE-798 (Use of Hard-coded Credentials)
    • File: backend/.env.production (lines with AWS keys)
    • Issue: Live AWS credentials committed to Git repository
    • Impact:
      • Unauthorized AWS account access
      • Financial damage from resource abuse
      • Email spam/phishing using your domain
      • Potential data breach
    • Required Actions:
      # 1. ROTATE CREDENTIALS IMMEDIATELY in AWS Console
      # 2. Remove from Git history:
      git rm --cached backend/.env.production backend/.env.development
      git filter-repo --path backend/.env.production --invert-paths
      # 3. Use environment variables or Docker Secrets instead
      # 4. Never commit .env.production files again
      
    • Location: Backend deployment configuration
    • Priority: IMMEDIATE - These credentials are compromised
  2. WEAK PRODUCTION JWT SECRET

    • Severity: CRITICAL (9/10)
    • CWE: CWE-521 (Weak Password Requirements)
    • File: backend/.env.production (JWT_SECRET line)
    • Issue: JWT_SECRET=production-secret-key-CHANGE-THIS-IN-REAL-PRODUCTION
    • Impact:
      • Attackers can forge valid JWT tokens
      • Complete authentication bypass
      • Account takeover for any user
    • Required Fix:
      # Generate cryptographically secure secret (minimum 256 bits)
      node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
      # Use output as JWT_SECRET in production environment (not in Git!)
      
    • Location: backend/src/utils/auth.js:7 (implementation is correct, secret is weak)
    • Priority: IMMEDIATE - Complete auth bypass possible
  3. DEFAULT DATABASE PASSWORD IN PRODUCTION

    • Severity: CRITICAL (8/10)
    • CWE: CWE-798 (Use of Hard-coded Credentials)
    • File: docker-compose.yml:196 (db-prod service)
    • Issue: POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-spotlightcam123}
    • Impact:
      • Database compromise if default is used
      • Access to all user data (emails, password hashes, messages)
    • Required Fix:
      # Remove default fallback, force explicit password:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set}
      
    • Location: Docker Compose production database service
    • Priority: IMMEDIATE - Database security

🔴 HIGH PRIORITY ISSUES (Fix Before Production)

Status: Should be resolved before public launch

  1. MISSING HTTPS/TLS CONFIGURATION

    • Severity: HIGH (8/10)
    • Files: nginx/conf.d.prod/default.conf, docker-compose.yml
    • Issue: Production nginx only listens on HTTP (port 80), no HTTPS/TLS
    • Impact:
      • All traffic in plaintext (credentials, tokens, videos)
      • JWT tokens exposed to network sniffing
      • Session cookies vulnerable to interception
      • No MITM protection
    • Required Actions:
      # 1. Obtain SSL certificate (Let's Encrypt recommended)
      certbot certonly --webroot -w /var/www/html -d spotlight.cam
      
      # 2. Update nginx config for HTTPS (see audit report for full config)
      # 3. Update CORS_ORIGIN to https://spotlight.cam
      # 4. Update Socket.IO to force secure transports
      
    • Priority: Required for production deployment
  2. MISSING SECURITY HEADERS IN NGINX

    • Severity: HIGH (6/10)
    • File: nginx/conf.d.prod/default.conf
    • Issue: Production nginx missing critical security headers
    • Impact: XSS, clickjacking, MIME sniffing vulnerabilities
    • Required Fix:
      add_header X-Frame-Options "SAMEORIGIN" always;
      add_header X-Content-Type-Options "nosniff" always;
      add_header X-XSS-Protection "1; mode=block" always;
      add_header Referrer-Policy "strict-origin-when-cross-origin" always;
      add_header Content-Security-Policy "default-src 'self'; ..." always;
      # HSTS after HTTPS is working:
      # add_header Strict-Transport-Security "max-age=31536000" always;
      
    • Priority: Must fix before launch
  3. DEPENDENCY VULNERABILITIES

    • Severity: HIGH (frontend), MODERATE (backend)
    • Tool: npm audit
    • Issues:
      • Backend: js-yaml (moderate - prototype pollution)
      • Backend: cookie via csurf (low - out of bounds chars)
      • Frontend: glob (high - command injection CVE, CLI only)
    • Required Actions:
      cd backend && npm audit fix
      cd frontend && npm audit fix
      # Monitor csurf package (maintenance mode) - consider migration
      
    • Priority: Run before launch
  4. EXCESSIVE REQUEST SIZE LIMIT

    • Severity: HIGH (DoS potential)
    • File: nginx/conf.d.prod/default.conf:13
    • Issue: client_max_body_size 500M; - Way too large for API
    • Analysis: WebRTC is P2P (videos don't go through server), API only needs small payloads
    • Required Fix:
      client_max_body_size 10M;  # Reduced from 500M
      location /api { client_max_body_size 1M; }
      location /socket.io { client_max_body_size 64k; }
      
    • Priority: Prevent DoS attacks
  5. SOCKET.IO CORS CONFIGURATION

    • Severity: MEDIUM (6/10)
    • File: backend/src/socket/index.js:21
    • Issue: CORS_ORIGIN is comma-separated string, Socket.IO expects array
    • Required Fix:
      const allowedOrigins = process.env.CORS_ORIGIN
        ? process.env.CORS_ORIGIN.split(',').map(o => o.trim())
        : ['http://localhost:8080'];
      io = new Server(httpServer, { cors: { origin: allowedOrigins } });
      
    • Priority: Important for multi-origin deployments

🟡 MEDIUM PRIORITY RECOMMENDATIONS (Next Sprint)

Status: 🔄 Implement in security hardening sprint

  1. 🔄 BCRYPT COST FACTOR TOO LOW

    • Severity: MEDIUM (3/10)
    • File: backend/src/utils/auth.js:7
    • Issue: bcrypt.genSalt(10) - Cost 10 was standard in 2010, OWASP recommends 12+
    • Recommendation:
      const BCRYPT_ROUNDS = parseInt(process.env.BCRYPT_ROUNDS) || 12;
      const salt = await bcrypt.genSalt(BCRYPT_ROUNDS);
      
    • Trade-off: 4x slower hashing (400ms vs 100ms), negligible UX impact
    • Priority: Next security update
  2. 🔄 ACCOUNT LOCKOUT USER ENUMERATION

    • Severity: MEDIUM (2/10)
    • File: backend/src/controllers/auth.js:108-134
    • Issue: Different responses reveal if account exists (401 vs 423)
    • Recommendation: Use same error message for locked/non-existent accounts
    • Priority: Optional - low practical risk
  3. 🔄 WEAK SESSION TIMEOUT

    • Severity: MEDIUM (4/10)
    • File: backend/src/utils/auth.js
    • Issue: 24-hour JWT tokens cannot be revoked, too long for stolen tokens
    • Recommendation: Implement refresh token pattern (15min access + 7day refresh)
    • Priority: Before scaling, acceptable for MVP
  4. 🔄 NO RATE LIMITING ON SOCKET.IO EVENTS

    • Severity: MEDIUM (4/10)
    • File: backend/src/socket/index.js
    • Issue: Socket.IO events (messages, signaling) have no rate limiting
    • Recommendation: Implement per-socket rate limiter (30 messages/minute)
    • Priority: Before public launch to prevent spam
  5. 🔄 MISSING INPUT SANITIZATION ON CHAT MESSAGES

    • Severity: MEDIUM (4/10)
    • File: backend/src/socket/index.js:212-268
    • Issue: Chat message content not sanitized before database save
    • Recommendation:
    const sanitizedContent = sanitizeHtml(content).slice(0, 2000);
    
  • Note: DOMPurify utility exists in utils/sanitize.js but not used for chat
  • Priority: Before launch (XSS prevention)

🟢 LOW PRIORITY IMPROVEMENTS (Future Hardening)

Status: 📝 Optional enhancements

  1. 📝 PostgreSQL Connection Security

    • Issue: Dev database exposes port 5432 to host
    • Recommendation: Restrict to localhost or use pgAdmin in Docker
    • Status: Acceptable for development
  2. 📝 Docker Container Running as Root

    • File: backend/Dockerfile.prod
    • Issue: Container doesn't specify non-root user
    • Recommendation: Add USER nodejs directive
    • Priority: Best practice, limited impact in containers
  3. 📝 Missing Security Logging

    • Issue: Security events not systematically logged (login failures, lockouts, token failures)
    • Recommendation: Implement structured security logging
    • Priority: Useful for monitoring, not blocking
  4. 📝 Token Verification Timing Attack

    • Severity: LOW (2/10)
    • File: backend/src/controllers/auth.js:305-309
    • Issue: 6-digit verification code comparison not timing-safe
    • Note: timingSafeEqual utility exists, just not used here
    • Priority: Optional - codes expire in 24h with rate limiting

POSITIVE SECURITY FINDINGS

Excellent practices already implemented:

  1. Strong Authentication: JWT, bcrypt, email verification, password reset
  2. Comprehensive Input Validation: express-validator throughout
  3. Security Headers: Helmet.js with CSP, XSS protection, HSTS, noSniff
  4. Rate Limiting: Multiple limiters (API, auth, email)
  5. CORS Configuration: Proper origin validation
  6. Authorization: Socket.IO auth middleware, WebRTC signaling authorization
  7. Database Security: Prisma ORM prevents SQL injection
  8. Error Handling: Generic messages in production, no stack traces
  9. Account Lockout: Implemented after failed attempts
  10. Defense in Depth: Multiple security layers
  11. WebRTC P2P: Videos don't touch server (privacy + security)
  12. Test Coverage: 223/223 tests passing (71% coverage)

📋 SECURITY DEPLOYMENT CHECKLIST

Pre-Deployment (MUST COMPLETE):

  • CRITICAL: Rotate AWS credentials and remove from Git history
  • CRITICAL: Generate strong JWT_SECRET (64+ bytes)
  • CRITICAL: Set strong PostgreSQL password (no fallback)
  • CRITICAL: Remove .env files from Git tracking
  • HIGH: Configure HTTPS/TLS with valid certificate
  • HIGH: Add nginx security headers
  • HIGH: Fix npm audit vulnerabilities
  • HIGH: Reduce nginx body size limit (500M → 10M)
  • MEDIUM: Update Socket.IO CORS to handle array
  • MEDIUM: Implement Socket.IO rate limiting

Post-Deployment (First 30 Days):

  • Monitor security logs for unusual patterns
  • Test account lockout mechanism
  • Verify HTTPS enforcement
  • Test CSRF protection
  • Review and rotate credentials on schedule

Ongoing Maintenance:

  • Monthly: npm audit and dependency updates
  • Quarterly: Rotate JWT_SECRET (requires re-auth)
  • Yearly: Security penetration testing

📊 RISK ASSESSMENT SUMMARY

Vulnerability Likelihood Impact Risk Score Status
AWS Credentials Exposed VERY HIGH CRITICAL 10/10 MUST FIX
Weak JWT Secret HIGH CRITICAL 9/10 MUST FIX
Default DB Password MEDIUM CRITICAL 8/10 MUST FIX
Missing HTTPS HIGH HIGH 8/10 MUST FIX
Missing nginx Headers MEDIUM HIGH 6/10 Should Fix
Excessive Body Size LOW MEDIUM 4/10 Should Fix

Overall Assessment: Application is 95% production-ready from security perspective. The remaining 5% are critical blockers requiring 1-2 days to fix.


🔗 FULL AUDIT REPORT

For complete details including:

  • Specific code examples for each fix
  • Configuration templates
  • OWASP Top 10 coverage analysis
  • Compliance notes (GDPR)
  • Detailed architecture recommendations

See the complete security audit report generated by nodejs-security-auditor agent (available in conversation history).


⚛️ FRONTEND REFACTORING (2025-11-20)

Analysis Status: Comprehensive React architecture review completed Code Analyzed: 27 JSX components, ~4000 lines of code Refactoring Potential: ~35% code reduction possible Recommended Phase: Phase 1 (Quick Wins) - START IMMEDIATELY

📊 Current State Analysis

Strengths:

  • Functional code, all features working
  • Good folder structure (pages, components, hooks)
  • Existing reusable components (Avatar, Layout, HeatsBanner)

Issues:

  • ⚠️ ~40% code duplication (especially in chat components)
  • ⚠️ Very large components (EventChatPage: 761 lines, MatchChatPage: 567 lines, ProfilePage: 558 lines)
  • ⚠️ Lack of abstraction for repeating patterns
  • ⚠️ Inline components and modals polluting main components
  • ⚠️ Duplicated Socket.IO logic across chat pages

Metrics:

Before Refactoring:
- Total LOC: ~4000
- Duplicated code: ~40%
- Largest component: 761 lines
- Reusable components: 8
- Custom hooks: 1

After Refactoring (projected):
- Total LOC: ~2600 (-35%)
- Duplicated code: ~10%
- Largest component: ~350 lines
- Reusable components: 25+
- Custom hooks: 6+

Effort: 1 week (15 hours) Impact: High - reduces codebase by ~30%, eliminates major duplication Priority: HIGH - should be done before adding new features

1. Alert/Message Components (30 min)

Create:

  • components/common/Alert.jsx - Unified success/error/warning/info alerts
  • Replace 10+ inline alert boxes in ProfilePage, RegisterPage, LoginPage, etc.

Files to create:

// components/common/Alert.jsx
const Alert = ({ type = 'info', message, icon: Icon }) => {
  // Unified alert component with 4 variants (success, error, warning, info)
};

Impact: -50 lines of duplicated alert code


2. Loading Components (20 min)

Create:

  • components/common/LoadingSpinner.jsx - Centralized loading spinner
  • components/common/LoadingButton.jsx - Button with loading state

Files to create:

// components/common/LoadingSpinner.jsx
const LoadingSpinner = ({ size = 'md', text, fullPage = false }) => {
  // Unified loading spinner (small, medium, large, full-page variants)
};

// components/common/LoadingButton.jsx
const LoadingButton = ({ loading, children, loadingText, ...props }) => {
  // Button with built-in loading state
};

Impact: -40 lines of duplicated loading UI


3. EventCard Extraction (30 min)

Create:

  • components/events/EventCard.jsx - Extract inline component from EventsPage

Files affected:

  • pages/EventsPage.jsx - Remove inline EventCard (lines 120-192)

Impact: -72 lines, better component organization


4. Form Input Components (2 hours)

Create:

  • components/common/FormInput.jsx - Text/email/password/url inputs with icons
  • components/common/FormSelect.jsx - Select dropdowns with icons

Files affected:

  • pages/ProfilePage.jsx - 15+ input fields → use FormInput/FormSelect
  • pages/RegisterPage.jsx - Registration form inputs
  • pages/LoginPage.jsx - Login form inputs

Example transformation:

// BEFORE (15 lines)
<div>
  <label className="block text-sm font-medium text-gray-700 mb-2">First Name</label>
  <input type="text" name="firstName" value={...} onChange={...} className="w-full px-3 py-2..." />
</div>

// AFTER (5 lines)
<FormInput label="First Name" name="firstName" value={profileData.firstName} onChange={handleProfileChange} />

Impact: -150 lines of repetitive form code


5. Modal Components (2 hours)

Create:

  • components/modals/Modal.jsx - Generic modal wrapper
  • components/modals/ConfirmationModal.jsx - Reusable confirmation dialog

Files affected:

  • pages/EventChatPage.jsx:
    • Leave Event Modal (lines 682-729) → use ConfirmationModal
    • Edit Heats Modal (lines 732-754) → use Modal
  • Future: Any new modals use these components

Example transformation:

// BEFORE (47 lines of inline modal JSX)
{showLeaveModal && (
  <div className="fixed inset-0 bg-black bg-opacity-50...">
    {/* 40+ lines */}
  </div>
)}

// AFTER (8 lines)
<ConfirmationModal
  isOpen={showLeaveModal}
  onClose={() => setShowLeaveModal(false)}
  onConfirm={handleLeaveEvent}
  title="Leave Event?"
  description={`Are you sure you want to leave ${event.name}?`}
  confirmText="Leave Event"
  isLoading={isLeaving}
/>

Impact: -70 lines of modal boilerplate, reusable for future features


6. Chat Components (3 hours)

Create:

  • components/chat/ChatMessageList.jsx - Message container with scroll
  • components/chat/ChatMessage.jsx - Individual message bubble
  • components/chat/ChatInput.jsx - Message input with send button

Files affected:

  • pages/EventChatPage.jsx (lines 594-666) - Use chat components
  • pages/MatchChatPage.jsx (lines 354-549) - Use chat components

Duplication eliminated:

  • Message rendering logic: ~40 lines × 2 = 80 lines
  • Message input form: ~20 lines × 2 = 40 lines
  • Total: ~120 lines of duplicated code

Example transformation:

// BEFORE (66 lines in EventChatPage + 60 lines in MatchChatPage)
<div className="flex-1 overflow-y-auto p-4 space-y-4">
  {messages.map((message) => (
    <div className={...}>
      {/* 30+ lines of message rendering */}
    </div>
  ))}
</div>

// AFTER (3 lines!)
<ChatMessageList
  messages={messages}
  currentUserId={user.id}
  messagesEndRef={messagesEndRef}
/>

Impact: -120 lines of duplicated chat UI


📦 Phase 1 Summary

Total effort: 15 hours (1 week) Lines of code removed: ~450 lines Code reduction: ~30% Components created: 11 new reusable components

Immediate benefits:

  1. Eliminates major code duplication
  2. Reduces ProfilePage from 558 → ~400 lines (-28%)
  3. Reduces EventChatPage from 761 → ~550 lines (-28%)
  4. Reduces MatchChatPage from 567 → ~400 lines (-29%)
  5. Future features faster to implement (use existing components)
  6. Easier to maintain (change in one place affects all uses)
  7. Better testability (small components easier to test)

New folder structure after Phase 1:

frontend/src/
├── components/
│   ├── common/
│   │   ├── Alert.jsx ⭐ NEW
│   │   ├── Avatar.jsx (existing)
│   │   ├── FormInput.jsx ⭐ NEW
│   │   ├── FormSelect.jsx ⭐ NEW
│   │   ├── LoadingSpinner.jsx ⭐ NEW
│   │   ├── LoadingButton.jsx ⭐ NEW
│   │   ├── PasswordStrengthIndicator.jsx (existing)
│   │   └── VerificationBanner.jsx (existing)
│   ├── chat/ ⭐ NEW FOLDER
│   │   ├── ChatMessageList.jsx ⭐ NEW
│   │   ├── ChatMessage.jsx ⭐ NEW
│   │   └── ChatInput.jsx ⭐ NEW
│   ├── events/ ⭐ NEW FOLDER
│   │   └── EventCard.jsx ⭐ NEW
│   ├── modals/ ⭐ NEW FOLDER
│   │   ├── Modal.jsx ⭐ NEW
│   │   └── ConfirmationModal.jsx ⭐ NEW
│   ├── heats/
│   ├── layout/
│   ├── pwa/
│   └── WebRTCWarning.jsx (existing)

🔄 PHASE 2: Custom Hooks (Future - After Phase 1)

Effort: 1 week (12 hours) Impact: High - separates business logic from UI Priority: MEDIUM - do after Phase 1 is complete

Tasks:

  1. useForm Hook (1h) - Generic form state management
  2. useEventChat Hook (3h) - Extract Socket.IO logic from EventChatPage
  3. useMatchChat Hook (2h) - Extract Socket.IO logic from MatchChatPage
  4. useInfiniteScroll Hook (45min) - Reusable infinite scroll logic

Impact: -200 lines, better separation of concerns


🎨 PHASE 3: Advanced Components (Future)

Effort: 1 week (10 hours) Impact: Medium - completes component library Priority: LOW - nice to have, do after Phase 1 & 2

Tasks:

  1. ParticipantsSidebar (2h) - Extract from EventChatPage
  2. UserListItem (1h) - Reusable user list entry
  3. HeatBadges (30min) - Reusable heat display component
  4. WebRTC Components (2h) - File transfer UI components

Impact: -150 lines, fully modular codebase


📋 REFACTORING CHECKLIST - PHASE 1 (Quick Wins)

Week 1 - Day 1-2:

  • Create components/common/Alert.jsx
  • Create components/common/LoadingSpinner.jsx
  • Create components/common/LoadingButton.jsx
  • Replace all inline alerts in ProfilePage, LoginPage, RegisterPage
  • Replace all inline loading spinners across pages
  • Test: Verify all pages still work correctly

Week 1 - Day 3:

  • Create components/events/EventCard.jsx
  • Extract EventCard from EventsPage.jsx
  • Test: Events page renders correctly

Week 1 - Day 4-5:

  • Create components/common/FormInput.jsx
  • Create components/common/FormSelect.jsx
  • Refactor ProfilePage to use FormInput/FormSelect
  • Refactor RegisterPage to use FormInput/FormSelect
  • Refactor LoginPage to use FormInput/FormSelect
  • Test: All forms submit correctly, validation works

Week 1 - Day 6:

  • Create components/modals/Modal.jsx
  • Create components/modals/ConfirmationModal.jsx
  • Refactor EventChatPage modals
  • Test: Modals open/close correctly

Week 1 - Day 7:

  • Create components/chat/ChatMessageList.jsx
  • Create components/chat/ChatMessage.jsx
  • Create components/chat/ChatInput.jsx
  • Refactor EventChatPage to use chat components
  • Refactor MatchChatPage to use chat components
  • Test: Chat functionality works in both pages
  • Final testing: Full regression test of all pages

Completion criteria:

  • All new components created and tested
  • No regressions in existing functionality
  • Code review completed
  • ~450 lines of code removed
  • Commit: "refactor(frontend): Phase 1 - create reusable components and eliminate duplication"

💡 ADDITIONAL RECOMMENDATIONS

Performance Optimizations (Future):

// Memoization for expensive components
const ChatMessage = React.memo(({ message, isOwn }) => { /* ... */ });

// Lazy loading for modals
const EditHeatsModal = React.lazy(() => import('./modals/EditHeatsModal'));

// useMemo for expensive calculations
const filteredUsers = useMemo(() => {
  return users.filter(u => !shouldHideUser(u.userId));
}, [users, hideMyHeats]);

Code Splitting (Future):

// routes/index.jsx - Lazy load heavy pages
const EventChatPage = lazy(() => import('../pages/EventChatPage'));
const MatchChatPage = lazy(() => import('../pages/MatchChatPage'));

TypeScript Migration (Future):

Consider migrating to TypeScript for:

  • Better type safety
  • IntelliSense in IDE
  • Easier refactoring
  • Fewer runtime bugs

📊 ESTIMATED ROI

Investment: 15 hours (Phase 1) Returns:

  • Immediate: 30% less code to maintain (~450 lines removed)
  • Short-term: 3-4 hours saved per new feature (reusable components)
  • Long-term: 50% faster development of chat/form features

Payback period: ~1 week (time saved on next feature) Recommendation: START PHASE 1 IMMEDIATELY

The refactoring will pay for itself after implementing just 1-2 new features, and will make the codebase significantly more maintainable.


📋 FUTURE UX/UI IMPROVEMENTS

Dashboard Page (Post-Login):

  • Create /dashboard route as default landing page after login
  • Show current user information (profile summary, stats)
  • Quick access buttons to active chats (event rooms, match chats)
  • Display ongoing discussions/conversations
  • Show upcoming events user is registered for
  • Recent activity feed (new matches, messages, ratings)
  • Navigation shortcuts to key features

Event Chat Sidebar Enhancements:

  • Add competitor number display for each user in sidebar
  • Show user nationality (flag + country code)
  • Expand user info cards in sidebar:
    • Competition heats (already implemented)
    • Competitor number
    • Country/nationality
    • User role/level
    • Years of experience (if available in profile)
    • Average rating (if rated)
  • Consider expandable user cards (click to see more details)
  • Add filtering options:
    • By nationality
    • By division/level
    • By competitor number range
  • Improve mobile responsiveness for extended sidebar info

Completed

  • Phase 0: Frontend mockup with all views
  • Phase 1: Backend Foundation
    • Node.js + Express API
    • PostgreSQL database with Prisma ORM
    • JWT authentication (register, login)
    • Socket.IO real-time chat (event & match rooms)
    • Comprehensive test coverage (81%+)
  • Phase 1.5: Email & WSDC & Profiles & Security
    • Email verification (AWS SES with link + PIN)
    • Password reset workflow
    • WSDC API integration (auto-fill registration)
    • User profiles (social media links, location)
    • Public profiles (/{username})
    • Event participation tracking (auto-save joined events)
    • Event security (unique slugs, prevent ID enumeration)
    • QR code event check-in system (physical presence required at venue)
  • Phase 1.6: Competition Heats System
    • Database schema (divisions, competition_types, event_user_heats)
    • Backend API (CRUD operations, validation)
    • Frontend components (HeatsBanner, forms, badges)
  • Phase 2: Matches & Ratings API
    • Match requests with CUID slugs
    • Real-time match notifications
    • Ratings system (1-5 stars, comments, preferences)
    • Public profile ratings display
    • Profile links from chat/matches
    • Message history persistence
    • Duplicate rating prevention

See: docs/archive/COMPLETED.md for full list of completed tasks


📌 Phase 1: Backend Foundation - COMPLETED

Completed: 2025-11-12 Time Spent: ~14 hours

Step 1: Backend Setup (COMPLETED)

  • Add backend service to docker-compose.yml
  • Initialize Node.js + Express project
  • Create folder structure (routes, controllers, middleware, etc.)
  • Add healthcheck endpoint: GET /api/health
  • Update nginx config to proxy /api/* to backend
  • Unit tests (7 tests passing)

Step 2: PostgreSQL Setup (COMPLETED)

  • Add db service (PostgreSQL 15) to docker-compose.yml
  • Configure volumes for data persistence
  • Prisma ORM implementation
  • Create database schema (6 tables with relations)
  • Add indexes for performance
  • Create seed data (3 events, 2 users, chat rooms)
  • Fix OpenSSL compatibility issue for Prisma

Step 3: Authentication API (COMPLETED)

  • Install dependencies: bcrypt, jsonwebtoken, express-validator
  • Implement password hashing with bcrypt (10 salt rounds)
  • Implement JWT token generation and verification
  • Create endpoints: register, login, /users/me
  • Create auth middleware for protected routes
  • Update frontend AuthContext to use real API
  • Unit tests (30 tests, 78%+ coverage)

Step 4: WebSocket Chat (COMPLETED)

  • Install Socket.IO 4.8.1 on backend
  • Setup Socket.IO server with Express integration
  • JWT authentication for socket connections
  • Event rooms (join/leave/messages/active users)
  • Match rooms (private 1:1 chat)
  • Install socket.io-client on frontend
  • Update EventChatPage with real-time messaging
  • Update MatchChatPage with real-time chat
  • Unit tests (12 tests, 89% coverage for Socket.IO module)

📌 Phase 1.6: Competition Heats System - COMPLETED

Completed: 2025-11-14 Time Spent: ~8 hours Status: Fully implemented and tested

Business Logic Summary

  • Users must declare their competition heats before matchmaking
  • One user can compete in multiple divisions/heats (e.g., J&J Novice + Strictly Advanced)
  • Constraint: Cannot compete in same role in same division+competition type (e.g., cannot have "J&J Novice Leader" twice)
  • Role is optional (can be NULL = undeclared)
  • Heat numbers: 1-9
  • Format example: "J&J NOV 1 L" (Jack & Jill, Novice, Heat 1, Leader)

Step 1: Database Schema - COMPLETED

  • Create migration for 3 new tables:
    • divisions - Pre-defined competition divisions
      • Columns: id, name (varchar), abbreviation (varchar 3), display_order (int)
      • Seed data: Newcomer (NEW), Novice (NOV), Intermediate (INT), Advanced (ADV), All-Star (ALL), Champion (CHA)
    • competition_types - Pre-defined competition types
      • Columns: id, name (varchar), abbreviation (varchar 3)
      • Seed data: Jack & Jill (J&J), Strictly (STR)
    • event_user_heats - User's declared heats for event
      • Columns: id, user_id, event_id, division_id, competition_type_id, heat_number (1-9), role (enum: Leader/Follower/NULL), created_at, updated_at
      • UNIQUE constraint: (user_id, event_id, division_id, competition_type_id, role)
      • Foreign keys: user_id → users.id, event_id → events.id, division_id → divisions.id, competition_type_id → competition_types.id
      • Indexes: (user_id, event_id), (event_id)
  • Update Prisma schema
  • Run migration
  • Verify seed data

Step 2: Backend API - COMPLETED

  • Create routes and controllers:
    • GET /api/divisions - List all divisions (public)
    • GET /api/competition-types - List all competition types (public)
    • POST /api/events/:slug/heats - Add/update user's heats (authenticated)
      • Input: array of { divisionId, competitionTypeId, heatNumber, role? }
      • Validation: unique constraint, heat number 1-9
      • Replace all existing heats (upsert pattern)
    • GET /api/events/:slug/heats/me - Get current user's heats (authenticated)
    • GET /api/events/:slug/heats/all - Get all users' heats for sidebar (authenticated)
      • Returns: userId, username, avatar, heats[]
    • DELETE /api/events/:slug/heats/:id - Delete specific heat (authenticated)
  • Validation middleware:
    • Heat number 1-9
    • Role enum (Leader/Follower/NULL)
    • Unique constraint enforcement
    • User must be event participant
  • Unit tests (CRUD operations, validation, constraints)

Step 3: Socket.IO Events - COMPLETED

  • Add event: heats_updated - Broadcast when user updates heats
    • Payload: { userId, username, heats[] }
    • Send to all users in event room
  • Update active_users event to include heats data

Step 4: Frontend Components - COMPLETED

  • Create HeatsBanner component (sticky between header and chat) - DONE
    • Show only if user has no heats declared
    • Form with dynamic heat entries (add/remove)
    • Fields per entry: Competition Type (select), Division (select), Heat Number (1-9), Role (optional: Leader/Follower)
    • "Save Heats" button → POST /api/events/:slug/heats
    • On save success: hide banner, show success message
  • Basic heats display in EventChatPage ( Enhanced UI features remain for Phase 4)
  • Create frontend API methods in services/api.js - DONE
    • divisionsAPI.getAll()
    • competitionTypesAPI.getAll()
    • heatsAPI.saveHeats(slug, heats[])
    • heatsAPI.getMyHeats(slug)
    • heatsAPI.getAllHeats(slug)
    • heatsAPI.deleteHeat(slug, heatId)
  • Socket.IO integration - Basic support implemented

Step 4.1: EventChatPage Integration - OPTIONAL ENHANCEMENTS (Phase 4)

What needs to be done:

  1. Add state management for heats:

    const [myHeats, setMyHeats] = useState([]);
    const [userHeats, setUserHeats] = useState(new Map()); // userId → heats[]
    const [showHeatsBanner, setShowHeatsBanner] = useState(false);
    const [hideMyHeats, setHideMyHeats] = useState(false);
    const [showHeatsModal, setShowHeatsModal] = useState(false);
    
  2. Load heats on component mount:

    useEffect(() => {
      const loadHeats = async () => {
        const [myHeatsData, allHeatsData] = await Promise.all([
          heatsAPI.getMyHeats(slug),
          heatsAPI.getAllHeats(slug),
        ]);
        setMyHeats(myHeatsData);
        setShowHeatsBanner(myHeatsData.length === 0);
    
        // Map userHeats
        const heatsMap = new Map();
        allHeatsData.forEach(userHeat => {
          heatsMap.set(userHeat.userId, userHeat.heats);
        });
        setUserHeats(heatsMap);
      };
      loadHeats();
    }, [slug]);
    
  3. Add HeatsBanner before chat:

    {showHeatsBanner && (
      <HeatsBanner
        slug={slug}
        onSave={() => {
          setShowHeatsBanner(false);
          // Reload heats
        }}
      />
    )}
    
  4. Add "Edit Heats" button in header (next to "Leave Event"):

    <button
      onClick={() => setShowHeatsModal(true)}
      className="flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50"
    >
      <Edit size={16} />
      Edit Heats
    </button>
    
  5. Create modal for editing heats (reuse HeatsBanner logic)

  6. Add heat badges to sidebar under username:

    {activeUsers.map(activeUser => {
      const userHeatsForThisUser = userHeats.get(activeUser.userId) || [];
      const hasHeats = userHeatsForThisUser.length > 0;
    
      return (
        <div key={activeUser.userId}>
          <div className="flex items-center">
            <img src={activeUser.avatar} />
            <span>{activeUser.username}</span>
          </div>
    
          {/* Heat badges */}
          <div className="flex flex-wrap gap-1 mt-1">
            {userHeatsForThisUser.slice(0, 3).map(heat => (
              <span key={heat.id} className="text-xs px-2 py-0.5 bg-blue-100 text-blue-700 rounded">
                {heat.competitionType.abbreviation} {heat.division.abbreviation} {heat.heatNumber}
                {heat.role && ` ${heat.role[0]}`}
              </span>
            ))}
            {userHeatsForThisUser.length > 3 && (
              <span className="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded">
                +{userHeatsForThisUser.length - 3}
              </span>
            )}
          </div>
    
          {/* UserPlus button - disabled if no heats */}
          <button
            onClick={() => handleMatchWith(activeUser.userId)}
            disabled={!hasHeats}
            className="p-1 text-primary-600 hover:bg-primary-50 rounded disabled:opacity-30 disabled:cursor-not-allowed"
          >
            <UserPlus className="w-4 h-4" />
          </button>
        </div>
      );
    })}
    
  7. Add filter checkbox above active users:

    <label className="flex items-center gap-2 text-sm text-gray-700 mb-2">
      <input
        type="checkbox"
        checked={hideMyHeats}
        onChange={(e) => setHideMyHeats(e.target.checked)}
        className="rounded border-gray-300"
      />
      Hide users from my heats
    </label>
    
  8. Filter logic:

    const filteredUsers = hideMyHeats
      ? activeUsers.filter(activeUser => {
          const theirHeats = userHeats.get(activeUser.userId) || [];
          return !theirHeats.some(theirHeat =>
            myHeats.some(myHeat =>
              myHeat.divisionId === theirHeat.divisionId &&
              myHeat.competitionTypeId === theirHeat.competitionTypeId &&
              myHeat.heatNumber === theirHeat.heatNumber
            )
          );
        })
      : activeUsers;
    
  9. Socket.IO heats_updated listener:

    useEffect(() => {
      const socket = getSocket();
      if (!socket) return;
    
      socket.on('heats_updated', ({ userId, username, heats }) => {
        setUserHeats(prev => {
          const newMap = new Map(prev);
          newMap.set(userId, heats);
          return newMap;
        });
    
        // If it's current user, update myHeats
        if (userId === user.id) {
          setMyHeats(heats);
          setShowHeatsBanner(heats.length === 0);
        }
      });
    
      return () => {
        socket.off('heats_updated');
      };
    }, [user.id]);
    

Step 5: Styling & UX - BASIC COMPLETE

  • Heat badges design
  • Banner responsive design (mobile + desktop)
  • Loading states for heat operations
  • Error handling & validation messages

Step 6: Testing & Edge Cases - COMPLETED

  • Test unique constraint violation (frontend + backend)
  • Backend unit tests for heats API
  • Test role optional (NULL) handling

Technical Notes

  • Abbreviations:
    • Divisions: NEW, NOV, INT, ADV, ALL, CHA
    • Competition Types: J&J, STR
    • Roles: L (Leader), F (Follower), empty (NULL)
  • Display format: "{CompType} {Div} {Heat} {Role?}" → "J&J NOV 1 L"
  • Future enhancement: When Matches API is implemented, editing heats with active match requires partner confirmation

📌 Phase 2.5: WebRTC P2P File Transfer - COMPLETED

Completed: 2025-11-15 Time Spent: ~10 hours

Step 1: WebRTC Signaling - COMPLETED

  • Socket.IO signaling events (offer, answer, ICE candidates)
  • Frontend WebRTC setup (RTCPeerConnection)
  • STUN server configuration
  • Connection state monitoring
  • Unit tests (7 backend tests passing)

Step 2: WebRTC File Transfer - COMPLETED

  • RTCDataChannel setup (ordered, reliable)
  • File metadata exchange
  • File chunking (16KB chunks)
  • Progress monitoring (sender & receiver)
  • Error handling & reconnection
  • Complete P2P video transfer flow
  • Tested up to 700MB files
  • Fallback: Link sharing UI

🎯 Future Phases (Reference)

Phase 3: MVP Finalization - COMPLETED

  • Security hardening:
    • Rate limiting (express-rate-limit)
    • Input validation & sanitization
    • CORS configuration
    • SQL injection prevention
    • XSS protection
    • CSRF protection
    • Account lockout
  • Testing:
    • Integration tests (API endpoints)
    • WebRTC connection tests
    • 223/223 tests passing (100%)
    • 71% code coverage
  • PWA features:
    • Web app manifest
    • Service worker (offline support)
    • App icons & splash screens
    • Install prompt
    • iOS support
  • Deployment preparation:
    • Production Docker images (Dockerfile.prod)
    • Environment configuration (.env.production)
    • Database backup scripts
    • Health check scripts
    • Monitoring documentation
    • Docker compose profiles (dev/prod)

Note: E2E tests (Playwright/Cypress) and CI/CD pipeline are optional enhancements for Phase 4.

Phase 5: Optional Extensions

  • User badges & trust system
  • Block users
  • Public profiles
  • Push notifications
  • Video compression
  • Multi-file transfer

📝 Active Development Tasks

Documentation

  • README.md
  • docs/QUICKSTART.md
  • docs/CONTEXT.md
  • docs/TODO.md
  • docs/SESSION_CONTEXT.md
  • docs/ARCHITECTURE.md
  • docs/DEPLOYMENT.md
  • docs/MONITORING.md
  • docs/archive/COMPLETED.md
  • docs/archive/RESOURCES.md
  • API documentation (Swagger/OpenAPI) - after backend
  • Architecture diagrams - after backend
  • WebRTC flow diagram - after WebRTC implementation

Infrastructure

  • Docker Compose (nginx, frontend, backend, db)
  • Production Dockerfiles (Dockerfile.prod for frontend & backend)
  • Docker Compose profiles (dev/prod)
  • Production environment configuration
  • Operations scripts (backup, restore, health-check)
  • CI/CD pipeline (GitHub Actions) - Optional
  • HTTPS setup (Let's Encrypt) - Requires server

Testing

  • Backend tests (Jest + Supertest) - 223/223 passing, 71% coverage
  • WebRTC tests - 7 backend tests passing
  • Socket.IO tests - Complete coverage
  • Security tests (CSRF, rate limiting, auth)
  • Frontend tests (Vitest + React Testing Library) - Optional
  • E2E tests (Playwright / Cypress) - Optional

🚀 Quick Commands

Start development:

docker compose up --build

Rebuild after changes:

docker compose down && docker compose up --build

Access:

Git workflow:

git status
git add .
git commit -m "feat: description"

📊 Progress Tracking

Phase Status Progress Time Spent
Phase 0: Frontend Mockup Done 100% ~8h
Phase 1: Backend Foundation Done 100% ~14h
Phase 1.5: Email & WSDC & Profiles Done 100% ~12h
Phase 1.6: Competition Heats Done 100% ~8h
Phase 2: Matches & Ratings API Done 100% ~10h
Phase 2.5: WebRTC Implementation Done 100% ~10h
Phase 3: MVP Finalization Done 100% ~20h
Total MVP Complete 100% ~82h
Phase 4: Extensions Optional - TBD

Overall MVP Progress: 100% Ready for production deployment


📝 Notes

  • All MVP features implemented and tested
  • 223/223 backend tests passing (71% coverage)
  • Production deployment ready (Docker configs, scripts, monitoring)
  • Security hardened (CSRF, rate limiting, account lockout)
  • PWA enabled (offline support, iOS compatible)
  • 🚀 Ready for production deployment (requires infrastructure setup)

For detailed task history: See docs/archive/COMPLETED.md For learning resources: See docs/archive/RESOURCES.md For quick session context: See docs/SESSION_CONTEXT.md For technical details: See docs/ARCHITECTURE.md


Last Updated: 2025-11-20