Files
spotlightcam/docs/TODO.md
Radosław Gierwiało 1051cc6754 feat(admin): implement activity logs frontend page (Phase 6-7)
Complete implementation of admin activity logs dashboard with real-time
streaming capabilities. Admin users can now monitor all system activity
through a comprehensive web interface.

Features:
- Stats dashboard with 4 key metrics (total logs, unique users, failures, 24h activity)
- Category breakdown visualization with color-coded badges
- Advanced filtering (date range, category, action type, username, success/failure)
- Paginated log table (50 entries per page) with sort by timestamp
- Real-time streaming toggle using Socket.IO
- Color-coded action badges (blue=auth, green=event, purple=match, red=admin, yellow=chat)
- Admin-only access with automatic redirect for non-admin users
- Responsive design for mobile and desktop

Frontend Changes:
- Created ActivityLogsPage.jsx (600+ lines) with complete UI implementation
- Added 3 admin API methods to api.js (getActivityLogs, getActivityLogActions, getActivityLogStats)
- Added /admin/activity-logs route to App.jsx
- Added admin navigation link to Navbar (desktop & mobile) with Shield icon
- Only visible to users with isAdmin flag

Implementation Details:
- Uses getSocket() from socket service for real-time updates
- Joins 'admin_activity_logs' Socket.IO room on streaming enable
- Receives 'activity_log_entry' events and prepends to table (first page only)
- Comprehensive error handling and loading states
- Empty states for no data
- Clean disconnect handling when streaming disabled

Testing:
- Build successful (no errors)
- Ready for manual testing and verification

Phase 8 (Testing) remains for manual verification of all features.
2025-12-02 23:17:19 +01:00

23 KiB

TODO - spotlight.cam

Active tasks and roadmap


Current Status

Phase: MVP Complete - Production Ready Tests: 350/350 passing - 100% (73% coverage) Recent Work:

  • 2025-11-30: Matching runs audit, ratings & stats system, comprehensive test suite
  • 2025-11-30: Rate limiting & spam protection, socket notifications for suggestions

Full implementation history: See docs/archive/COMPLETED.md


Activity Log System (Complete )

Status: Phase 8/8 Complete - Ready for Testing Started: 2025-12-02 Commits: f9cdf2a (Ph1), c9beee9 (Ph2), d83e416 (Ph3), 4dd6603 (Ph4), d641e3f (Ph5) Admin User: spotlight@radziel.com (password: Dance123!)

Purpose

Comprehensive activity logging system for admin monitoring with real-time streaming dashboard.

  • Track all user actions (auth, events, matches, chat, admin)
  • Real-time WebSocket streaming (like tail -f)
  • Filter by time range, action type, username
  • Admin-only access with requireAdmin middleware

Completed

Phase 1: Database Schema

  • ActivityLog model with indexes (43 lines)
  • User.isAdmin flag for access control
  • Admin user created: spotlight@radziel.com
  • Files: backend/prisma/schema.prisma

Phase 2: Backend Services

  • ActivityLog service (300+ lines) - centralized logging, fire-and-forget pattern
    • 18 action constants (AUTH_LOGIN, MATCH_CREATE, etc.)
    • Query interface with filtering
    • Socket.IO emission for real-time
    • Statistics and action types endpoints
  • Request utility - IP extraction (X-Forwarded-For support)
  • Admin middleware - requireAdmin() protects admin routes
  • Files:
    • backend/src/services/activityLog.js
    • backend/src/utils/request.js
    • backend/src/middleware/admin.js

Phase 3: Logging Integration (14 actions)

  • Auth controller: register, login, verify email (token & code), password reset (4 actions)
  • Events routes: checkin, leave event (2 actions)
  • Socket handlers: join event chat, leave event chat, join match room (3 actions)
  • Matches routes: create, accept, reject match (3 actions)
  • Admin routes: matching run + secured all routes with requireAdmin (1 action)
  • Files:
    • backend/src/controllers/auth.js
    • backend/src/routes/events.js
    • backend/src/socket/index.js
    • backend/src/routes/matches.js
    • backend/src/routes/admin.js

Phase 4: Admin API Endpoints

  • GET /api/admin/activity-logs - Query logs with filters (date range, action, category, username, success, pagination)
  • GET /api/admin/activity-logs/actions - Get unique action types
  • GET /api/admin/activity-logs/stats - Statistics dashboard (total, failures, by category, 24h activity)
  • ADMIN_VIEW_LOGS action logging
  • File: backend/src/routes/admin.js

Phase 5: Socket.IO Real-Time Streaming

  • join_admin_activity_logs handler with admin verification
  • leave_admin_activity_logs handler
  • Emits activity_log_entry to admin room (already in Phase 2 service)
  • Fresh DB check for admin status on join
  • File: backend/src/socket/index.js

Phase 6-7: Frontend Admin Page

  • Created frontend/src/pages/admin/ActivityLogsPage.jsx (600+ lines)
  • Stats dashboard (total logs, unique users, failures, 24h activity)
  • Category breakdown visualization
  • Filter UI (date range, category dropdown, action dropdown, username, status)
  • Log table with pagination (50 per page)
  • Real-time streaming toggle with Socket.IO
  • Added admin API methods to frontend/src/services/api.js
  • Added route /admin/activity-logs to App.jsx
  • Added admin navigation link (desktop & mobile) with Shield icon
  • Build successful (no errors)
  • Files:
    • frontend/src/pages/admin/ActivityLogsPage.jsx
    • frontend/src/services/api.js
    • frontend/src/App.jsx
    • frontend/src/components/layout/Navbar.jsx

Phase 8: Testing & Manual Verification

  • Test all 14 action logging points
  • Test admin-only access enforcement
  • Test real-time streaming with multiple admins
  • Test filtering combinations
  • Test pagination
  • Mobile responsive design verification

Implementation Notes

  • Fire-and-forget: Logging never blocks requests or crashes app
  • Denormalized: Username stored to avoid JOINs
  • Scalability: Partitioning after 10M+ rows
  • Security: Admin-only with fresh DB checks

Matching System - Comprehensive Test Scenarios

Last Updated: 2025-11-30 Status: Documented for team discussion

Implementation Status

Implemented Scenarios (See docs/archive/COMPLETED.md for details)

  • S1-S3: Basic flow, collision detection, limits - 19 integration tests
  • S7.1-7.2: Manual match blocks auto suggestions - Implemented & tested
  • S10: Ratings & Stats System - 9 E2E tests (atomic updates, race prevention)
  • S11.3-11.4: Matching Run Details API - Admin endpoints with filtering
  • S12: Multi-heat collision detection - Covered in matching algorithm tests
  • S14.1: Only recorder can accept/reject - Implemented in MVP
  • S15.1-15.2: Rate Limiting & Spam Protection - 8 comprehensive tests
    • Max 20 pending outgoing match requests
    • Rate limit: 10 match requests per minute
  • S16.1: Socket Notifications - Real-time notification when new suggestion created
  • Matching Runs Audit: 6 comprehensive tests (origin_run_id tracking)

🔴 Critical Gaps (P0 - Before Production)

  1. S14.2: Admin Middleware - SECURITY

    • Admin endpoints not protected: /admin/events/:slug/run-now, /admin/matching-runs
    • Need: requireAdmin middleware
  2. S14.3: Event Participant Validation - SECURITY

    • Inconsistent checks across endpoints
    • Need: Audit all suggestion/match endpoints for participant validation

⚠️ High Priority (P1 - First Month)

  1. E9/S13.2: Manual match created AFTER auto suggestion
    • Current: Manual blocks only NEW auto suggestions, old pending remain
    • Need: Cleanup conflicting pending auto suggestions when manual match created

📋 Medium Priority (P2 - Q1 2025)

  1. S15.3: Zombie Matches Cleanup

    • Auto-cancel pending matches older than 30 days
  2. S16.3: Email Reminders

    • Reminder before event for accepted recording assignments

Test Scenarios by Category

S1: BASIC FLOW Implemented

S1.1: Happy path - basic assignment

  • Given: Event with deadline, user A has heat H1, user B no conflict
  • When: Matching runs after deadline
  • Then: Creates suggestion B records A in H1 (pending)

S1.2: Recorder accepts suggestion

  • Given: Pending suggestion: B records A
  • When: B clicks Accept
  • Then: Status→accepted, creates Match (type: auto), both see on /matches

S1.3: Recorder rejects suggestion

  • Given: Pending suggestion: B records A
  • When: B clicks Reject
  • Then: Status→rejected, A sees read-only, next run may assign different recorder
S2: TIME COLLISION DETECTION Implemented

S2.1: Can't record while dancing

  • Given: A has heat H1, B has heat H1 (same slot)
  • Then: B NOT candidate to record A in H1

S2.2: Buffer before - preparation time

  • Given: A has heat H5, B has heat H6 (next)
  • When: HEAT_BUFFER_BEFORE = 1
  • Then: B NOT candidate (needs H5 for prep)

S2.3: Buffer after - rest time

  • Given: A has heat H5, B has heat H4 (previous)
  • When: HEAT_BUFFER_AFTER = 1
  • Then: B NOT candidate (needs H5 for rest)

S2.4: Schedule config - divisions in same slot

  • Given: Schedule config: slot 1 = [Open, Newcomer]; A has Open H3, B has Newcomer H3
  • Then: B can't record A (same slot despite different divisions)
S3: LIMITS & CONSTRAINTS Implemented

S3.1: Max 3 recordings per person

  • Given: B already assigned to record 3 others
  • Then: B NOT candidate for dancer A

S3.2: Opt-out from recording

  • Given: B checked "I don't want to record others"
  • Then: B completely excluded as potential recorder

S3.3: No self-recording

  • Given: A has heat H1
  • Then: A can't be recorder for themselves
S4: FAIRNESS & TIER SYSTEM Implemented (but stats not updating!)

S4.1: BASIC user - normal frequency

  • Given: C (BASIC): done=5, received=0; A,B (BASIC): done=0, received=0
  • Then: C has priority (fairness debt = -5)

S4.2: SUPPORTER user - reduced frequency (-10)

  • Given: A (SUPPORTER) vs B (BASIC), both 0/0
  • Then: B has priority (A has -10 penalty)

S4.3: COMFORT user - significantly reduced (-50)

  • Given: A (COMFORT), B (SUPPORTER), C (BASIC), all 0/0
  • Then: Priority: C > B > A

S4.4: Location preference

  • Given: Dancer A in LA,US; B (LA,US), C (NYC,US), D (London,UK)
  • Then: Priority: B > C > D
S5: INCREMENTAL MATCHING Implemented

S5.1: Accepted suggestions preserved

  • Given: Run 1 created suggestion B→A (pending), B accepted
  • When: Run 2
  • Then: A-B suggestion NOT deleted or overwritten

S5.2: Pending suggestions regenerated

  • Given: Run 1 created B→A (pending), B didn't accept
  • When: Run 2 (new users joined)
  • Then: Pending A-B deleted, new suggestion may assign C→A

S5.3: Accepted suggestions block slots

  • Given: B accepted recording A in heat H5
  • When: Run 2 for dancer D (also has H5)
  • Then: B NOT candidate (busy in H5)
S6: SCHEDULER Implemented

S6.1: Waits until deadline

  • Given: Event has registrationDeadline in 2 hours
  • Then: Scheduler does NOT run matching (waits)

S6.2: Runs after deadline

  • Given: registrationDeadline passed 1 minute ago
  • Then: Runs matching (run 1/5)

S6.3: 5 runs at 5-minute intervals

  • Then: Run 1 (immediately), Run 2 (+5min), Run 3 (+10min), Run 4 (+15min), Run 5 (+20min), STOP

S6.4: Audit trail

  • Given: Matching ran
  • Then: Admin sees table of runs with timestamps and stats (total/pending/accepted/rejected/not_found)
S7: MANUAL vs AUTO MATCHES S7.1-7.4 Implemented

S7.1: Manual match blocks auto suggestion

  • Given: A sent manual match request to B (pending)
  • When: Matching algorithm runs
  • Then: Does NOT create auto suggestion A↔B or B↔A

S7.2: All manual statuses block

  • Given: Manual match A↔B in status: pending/accepted/completed
  • Then: All cases block auto suggestion

S7.3: /matches shows both types with badges

  • Given: A has manual match with B (pending), auto suggestion with C (pending - C records A)
  • Then: A sees 2 items: B (badge "Manual"), C (badge "Auto")

S7.4: Auto pending - redirect to Records

  • Given: A has auto suggestion with C (pending, C records A)
  • Then: Button "Go to Records" → /events/X/chat?tab=records (no Accept/Reject for dancer)
S10: RATINGS & STATS IMPLEMENTED (2025-11-30)

S10.1: Auto match completed → stats updated exactly once

  • Given: Auto Match A↔B (source='auto', statsApplied=false), both rated
  • When: Second rating submitted
  • Then:
    • recordingsDone++ for recorder (user2)
    • recordingsReceived++ for dancer (user1)
    • match.status = 'completed'
    • match.statsApplied = true
  • Implementation: backend/src/routes/matches.js:961-995 (atomic check-and-set)
  • Tests: backend/src/__tests__/ratings-stats-flow.test.js (STEP 6b)

S10.2: Only one rated → no stats

  • Given: Auto Match A↔B, only A rated
  • Then: statsApplied stays false, stats don't change
  • Tests: backend/src/__tests__/ratings-stats-flow.test.js (STEP 6a)

S10.3: Manual match completion → no stats update

  • Given: Match A↔B (source='manual'), both rated
  • Then: Stats don't change (manual matches don't affect fairness)
  • Implementation: backend/src/services/matching.js:682 (early return if source !== 'auto')
  • Tests: backend/src/__tests__/ratings-stats-flow.test.js (STEP 8)

S10.4: Rating edit → no double counting

  • Given: User tries to rate same match twice
  • Then: 400 error "already rated", stats unchanged
  • Implementation: Unique constraint: (matchId, raterId, ratedId)
  • Tests: backend/src/__tests__/ratings-stats-flow.test.js (STEP 7)

Implementation:

// Match model (Prisma schema)
source: String // 'auto' | 'manual'
statsApplied: Boolean @default(false)
suggestionId: Int? // null for manual matches

// Stats application (backend/src/services/matching.js:679-701)
async function applyRecordingStatsForMatch(match) {
  if (match.source !== 'auto') return; // Manual matches ignored

  await prisma.$transaction([
    prisma.user.update({
      where: { id: match.user2Id }, // recorder
      data: { recordingsDone: { increment: 1 } }
    }),
    prisma.user.update({
      where: { id: match.user1Id }, // dancer
      data: { recordingsReceived: { increment: 1 } }
    })
  ]);
}
S11: AUDIT & origin_run_id Partially Implemented

S11.1: New suggestions get correct origin_run_id

  • Given: Run #5 runs matching
  • Then: Each new recording_suggestion has origin_run_id = 5

S11.2: Existing origin_run_id preserved

  • Given: Suggestion S1 from run #3 (accepted/completed)
  • When: Run #4
  • Then: S1 keeps origin_run_id=3 (not deleted)

S11.3: Get suggestions by run 🔴 NOT IMPLEMENTED

  • Need: GET /matching-runs/:id/suggestions
  • Then: Returns only suggestions with origin_run_id=:id

S11.4: Filters work correctly 🔴 NOT IMPLEMENTED

  • Need: Query params onlyAssigned, includeNotFound
  • Then: Filters recorder presence and NOT_FOUND status
S12: MULTIPLE HEATS & COMPLEX COLLISIONS Implemented

S12.1: Same dancer in multiple heats

  • Given: Dancer A in H10 and H12 (Novice J&J), B no conflict
  • Then: B can record A in both heats (if under limit 3)

S12.2: Recorder records multiple dancers in different slots

  • Given: Recorder B has zero own heats, A has H5 (slot 2), C has H8 (slot 3)
  • Then: B can be assigned to both A and C (different slots)

S12.3: Same recorder can't record two people in same slot

  • Given: B accepted recording A in Novice H10 (slot 2), D has Intermediate H10 (also slot 2)
  • When: Next matching run for D
  • Then: B excluded (busySlot recording A)
S13: MANUAL vs AUTO - FURTHER INTERACTIONS ⚠️ Edge Case

S13.1: Manual before matching DONE

  • See S7.1

S13.2: Manual created AFTER auto suggestion ⚠️ EDGE CASE

  • Given: Run #1 created auto suggestion A↔B (pending), then user A creates manual match with B
  • When: Run #2
  • Then:
    • Current behavior: Manual blocks NEW auto suggestions, old pending remains
    • Recommended: Cleanup conflicting pending auto suggestions when manual created

Recommended implementation:

// routes/matches.js - POST /
// After creating manual match:
await prisma.recordingSuggestion.deleteMany({
  where: {
    eventId,
    OR: [
      { heatId: { in: user1HeatIds }, recorderId: user2Id },
      { heatId: { in: user2HeatIds }, recorderId: user1Id },
    ],
    status: 'pending', // only pending, don't touch accepted
  }
});
S14: PERMISSIONS & VISIBILITY ⚠️ Partially Implemented

S14.1: Only recorder can update status

  • Given: Suggestion B records A (pending), A tries PUT /suggestions/:id/status
  • Then: 403 Forbidden

S14.2: Admin-only endpoints 🔴 NOT SECURE

  • Given: Regular user tries POST /admin/events/:slug/run-now or GET /admin/matching-runs
  • Then: Should get 403, but middleware missing!

Need to implement:

// middleware/auth.js
const requireAdmin = (req, res, next) => {
  if (req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  next();
};

S14.3: Event participant visibility ⚠️ INCONSISTENT

  • Need to audit: Do all suggestion/match endpoints verify user is event participant?
  • Locations to check:
    • GET /api/matches?eventSlug=X
    • GET /api/events/:slug/matching/suggestions
    • Frontend: /events/:slug/chat?tab=records
S15: SECURITY & ABUSE PREVENTION 🔴 NOT IMPLEMENTED

S15.1: Rate limiting on manual match requests

  • Given: User A sends 100 manual requests in 1 minute
  • Then: 429 Too Many Requests after 10 requests/minute

S15.2: Max pending requests limit

  • Given: User A has 20 pending outgoing requests
  • Then: Can't send more (limit 20)

S15.3: Zombie matches cleanup

  • Given: Pending match older than 30 days
  • Then: Auto-cancelled by cron job
S16: NOTIFICATIONS & REAL-TIME 🔴 NOT IMPLEMENTED

S16.1: Socket event for new suggestion

  • Given: Matching created suggestion B records A, B is online
  • Then: B receives socket event → toast notification

S16.2: Push notification for offline users

  • Given: B offline when suggestion created
  • Then: Email/push: "You have a new recording assignment"

S16.3: Reminder before event

  • Given: B accepted recording A, event in 2 days
  • Then: Email: "Don't forget to record @userA on Saturday"

Edge Cases for Discussion

E1: User has 10 heats, no one can record

  • Question: Show all as NOT_FOUND? Prioritize which heats are most important?

E2: Recorder wants to un-accept

  • Current: No "un-accept" option
  • Question: Add cancel feature for accepted suggestions?

E3: Manual match rejected, then matching runs

  • Current: Rejected manual still blocks auto suggestions
  • Question: Should rejected manual stop blocking?

E4: User joins AFTER all 5 scheduler runs

  • Current: Won't get auto suggestions (scheduler stopped)
  • Question: Allow admin to manually trigger additional run?

E5: Timezone for deadline

  • Current: Server timezone
  • Question: Use event/organizer timezone?

E6: Load balancing - one recorder has best scores

  • Current: Can get max 3 assignments
  • Question: Distribute more evenly even if scores worse?

E7: Dancer rejects auto suggestion

  • Current: Dancer CAN'T reject (only recorder in MVP)
  • Question: Allow dancers to reject in future?

E8: Event without schedule config

  • Current: Fallback to division-based slots
  • Question: Show admin warning that matching may be less efficient?

E9: Manual match created AFTER auto suggestion exists

  • See S13.2 above

E10: User joins BETWEEN scheduler runs

  • Current: Works - each run regenerates pending suggestions
  • Status: No issue

E11: Recorder accepted but didn't actually record

  • Question: Timeout (7 days after event → expired)? "Report issue" button? Admin override?

Priority Ranking

P0 - CRITICAL (Before Production)

  1. Manual blocks auto suggestions (DONE 2025-11-30)
  2. 🔴 Implement ratings and stats system (S10 - fairness broken without this!)
  3. 🔴 Admin middleware (S14.2 - security)
  4. 🔴 Event participant validation (S14.3 - security)

P1 - HIGH (First Month of Production)

  1. ⚠️ Manual match cleanup on conflict (S13.2 / E9)
  2. ⚠️ Rate limiting and spam protection (S15.1, S15.2)
  3. ⚠️ Socket notifications for new suggestions (S16.1)

P2 - MEDIUM (Q1 2025)

  1. 📋 Endpoint /matching-runs/:id/suggestions with filters (S11.3, S11.4)
  2. 📋 Zombie matches cleanup (S15.3)
  3. 📋 Email reminders (S16.3)

P3 - LOW (Nice to Have)

  1. 📋 Report issue for dancers (E11)
  2. 📋 Admin manual override for suggestions
  3. 📋 Analytics dashboard (success rate, acceptance rate)

Questions for Team

  1. Is ratings system in roadmap? If yes - THIS IS THE MOST IMPORTANT (fairness is broken without it)
  2. Do we have existing admin middleware? If not - must add ASAP for security
  3. Which edge case (E1-E11) is most likely in production?
  4. Should we implement S13.2 cleanup before launch? (manual match after auto suggestion)

Security Audit Findings

Critical Issues (Must Fix Before Production)

Issue Severity File Action
AWS Credentials in Git 10/10 .env.production Rotate & remove from history
Weak JWT Secret 9/10 .env.production Generate 64+ byte secret
Default DB Password 8/10 docker-compose.yml Remove default fallback

High Priority Issues

Issue Severity Action
Missing HTTPS/TLS 8/10 Configure SSL certificate
Missing nginx security headers 6/10 Add X-Frame-Options, CSP, etc.
Dependency vulnerabilities HIGH Run npm audit fix
Excessive body size (500M) 4/10 Reduce to 10M

Positive Security Findings

  • Strong authentication (JWT, bcrypt, email verification)
  • Input validation (express-validator)
  • Security headers (Helmet.js)
  • Rate limiting implemented
  • CORS configured
  • SQL injection prevention (Prisma)
  • Account lockout implemented
  • WebRTC P2P (videos don't touch server)

Pre-Deployment Checklist

  • Rotate AWS credentials, remove from Git history
  • Generate strong JWT_SECRET (64+ bytes)
  • Set strong PostgreSQL password
  • Configure HTTPS/TLS
  • Add nginx security headers
  • Run npm audit fix
  • Reduce nginx body size limit

Future Improvements (Optional)

UX Enhancements

  • Activity Feed (timeline of user activities)
  • Smart sort order (unread first, pending ratings)
  • User statistics (total matches, average rating)
  • Sidebar filters (by nationality, division)

Security Hardening

  • Increase bcrypt rounds (10 → 12)
  • Implement refresh token pattern
  • Add Socket.IO rate limiting
  • Sanitize chat messages

Infrastructure

  • CI/CD pipeline (GitHub Actions)
  • E2E tests (Playwright)
  • Security logging

Progress Summary

Phase Status Time
Phase 0: Frontend Mockup Done ~8h
Phase 1: Backend Foundation Done ~14h
Phase 1.5: Email & WSDC & Profiles Done ~12h
Phase 1.6: Competition Heats Done ~8h
Phase 2: Matches & Ratings Done ~10h
Phase 2.5: WebRTC P2P Done ~10h
Phase 3: MVP Finalization Done ~20h
Total MVP Complete ~82h

Quick Commands

# Development
docker compose up --build

# Tests
docker compose exec backend npm test

# Access
http://localhost:8080

Last Updated: 2025-11-29 Full Details: See docs/archive/COMPLETED.md for implementation details