- Export getPostDanceBufferSlots and HEAT_BUFFER_AFTER for testing
- Add direct tests for getPreDanceBufferSlots (4 tests)
- Add direct tests for getPostDanceBufferSlots (2 tests)
- Add test for HEAT_BUFFER_AFTER constant
- Add edge case for getCoverableHeats with multiple recorder heats
- Add edge case for hasCollision with multi-heat scenarios
- Total: 39/39 tests passing (9 new tests added)
- Add dual buffer system: BEFORE (prep) and AFTER (rest) dancing
- Track recording assignments to prevent double-booking recorders
- Fix sorting priority: location score takes precedence over load balancing
- Simplify opt-out logic with complete exclusion from matching pool
- Buffers apply only to dancing heats, not recording assignments
- Improve documentation clarity for algorithm constraints
Implemented display of country flags and competitor numbers in event chat messages:
- Country flags displayed as emoji (🇸🇪, 🇵🇱, etc.) with proper emoji font support
- Competitor numbers shown in #123 format next to usernames
- Normalized data architecture with user and participant caches on frontend
- User data (username, avatar, country) and participant data (competitorNumber) cached separately
- Messages store only core data (id, content, userId, createdAt)
- Prevents data inconsistency when users update profile information
- Fixed duplicate message keys React warning with deduplication logic
- Backend sends nested user/participant objects for cache population
- Auto-updates across all messages when user changes avatar or country
Backend changes:
- Socket.IO event_message and message_history include nested user/participant data
- API /events/:slug/messages endpoint restructured with same nested format
- Batch lookup of competitor numbers for efficiency
Frontend changes:
- useEventChat hook maintains userCache and participantCache
- ChatMessage component accepts separate user/participant props
- ChatMessageList performs cache lookups during render
- Emoji font family support for cross-platform flag rendering
Add test-bot.js script that simulates a user participating in event chat for testing purposes.
Features:
- Authenticates as user via API
- Checks in to event using QR code token
- Connects to Socket.IO and joins event room
- Sends random messages at configurable intervals
- Auto-accepts recording suggestions
Usage from container:
docker compose exec backend sh -c 'API_URL=http://localhost:3000 node scripts/test-bot.js --email user@example.com --password pass --slug event-slug --interval 10'
- Add security headers to nginx (X-Frame-Options, CSP, etc.)
- Reduce client_max_body_size from 500M to 10M
- Add npm overrides to fix cookie vulnerability in csurf
- Make navbar sticky with full width
Both users.test.js and auth-phase1.5.test.js were failing due to
unique constraint violations on wsdc_id field when running after
other test suites. Added wsdcId to cleanup queries and proper
deletion of related data (messages, matches, eventParticipants).
- Create constants/statuses.js with MATCH_STATUS, SUGGESTION_STATUS
- Update routes/dashboard.js to use MATCH_STATUS
- Update routes/matches.js to use MATCH_STATUS
- Update routes/events.js to use SUGGESTION_STATUS
- Update services/matching.js to use SUGGESTION_STATUS
- Update tests to use constants
Allow event organizers to configure which divisions run in parallel
(same time slot) for accurate collision detection in the auto-matching
algorithm. Divisions in the same slot will collide with each other.
- Add scheduleConfig JSON field to Event model
- Add PUT /events/:slug/schedule-config API endpoint
- Update matching algorithm to use slot-based collision detection
- Add UI in EventDetailsPage for managing division slots
- Add unit tests for schedule-based collision detection
Add frontend components for auto-matching recording partners:
- RecordingTab component with suggestions list and opt-out toggle
- Tab navigation in EventChatPage (Chat, Uczestnicy, Nagrywanie)
- Matching configuration in EventDetailsPage (deadline, run matching)
- matchingAPI functions in api.js
- Return registrationDeadline and matchingRunAt in GET /events/:slug/details
UI allows users to:
- View who will record their heats
- View heats they need to record
- Accept/reject suggestions
- Opt-out from being a recorder
- Set registration deadline (admin)
- Manually trigger matching (admin)
Implement algorithm to match dancers with recorders based on:
- Heat collision avoidance (division + competitionType + heatNumber)
- Buffer time (1 heat after dancing before can record)
- Location preference (same city > same country > anyone)
- Max 3 recordings per person
- Opt-out support (falls to bottom of queue)
New API endpoints:
- PUT /events/:slug/registration-deadline
- PUT /events/:slug/recorder-opt-out
- POST /events/:slug/run-matching
- GET /events/:slug/match-suggestions
- PUT /events/:slug/match-suggestions/:id/status
Database changes:
- Event: registrationDeadline, matchingRunAt
- EventParticipant: recorderOptOut
- RecordingSuggestion: new model for match suggestions
Allow participants to set their bib/competitor number per event.
Display as badge next to username in participant lists.
- Add competitorNumber field to EventParticipant model
- Add PUT /events/:slug/competitor-number endpoint
- Include competitorNumber in heats/me and heats/all responses
- Add input field in HeatsBanner component
- Display badge in UserListItem component
- Add unit tests for competitor number feature
Track unread messages in match chats and display count badge:
- Schema: Add user1LastReadAt/user2LastReadAt to Match model
- Backend: Calculate unreadCount in dashboard API
- Socket: Update lastReadAt when user joins match room
- Frontend: Display red badge with unread count on match avatar
Show real-time count of users currently in each event chat room.
- Backend: Export getEventsOnlineCounts from socket module
- Dashboard API: Include onlineCount for each active event
- Frontend: Display online count with animated green dot indicator
- Add GET /api/dashboard endpoint for authenticated users
- Returns active events with user heats
- Returns accepted matches with partner info
- Detects video exchange status from message parsing
- Tracks rating completion status (rated by me/partner)
- Returns incoming/outgoing pending match requests
- Add comprehensive test suite (12 tests, 93% coverage)
- Add DASHBOARD_PLAN.md with full design documentation
Problem:
- User got "Chat room not found" error when trying to send messages
- Event ChatRooms were only created by seed script, not for manually
created events
- Event "Another Dance Event" (ID: 420) was missing its ChatRoom
Root Cause:
- Seed script (seed.js:179-188) correctly creates ChatRooms for events
- But events created outside of seed (CLI, manual DB insert) didn't
create ChatRooms
- Socket handler requires ChatRoom to exist before accepting messages
Solution:
1. Added defensive check in check-in handler (POST /api/events/checkin/:token)
2. Automatically creates ChatRoom if missing when first user checks in
3. Logs creation for debugging: "Created missing chat room for event: {slug}"
Impact:
- Existing events without ChatRooms will get them on next check-in
- Future manually-created events will work correctly
- No breaking changes - all 223 tests pass
Changes:
- backend/src/routes/events.js: Added ChatRoom existence check and
auto-creation logic (lines 256-272)
Note: Manually created ChatRoom for event ID 420 to fix immediate issue
- Replace deleteMany({}) with selective deletion by username/email/slug in:
- events.test.js (target specific test users/events only)
- matches.test.js (target specific test users/events only)
- csrf.test.js (target csrftest user only)
- Replace delete() with deleteMany() for resilient cleanup:
- matches.test.js (2 inline cleanups)
- socket-webrtc.test.js (1 inline cleanup)
- Update TODO.md with test status and future UX/UI improvements
Test improvement: 189/223 passing (84.8%), up from 145/223 (65%)
- Replace delete() with deleteMany() in all afterAll hooks (more resilient)
- Add checks for testUser existence before creating EventParticipant
- Add .catch() handlers to ignore duplicate or foreign key errors
- Add conditional checks with ?. before cleanup operations
Changes improve test isolation and prevent foreign key constraint
violations when tests run together. All socket tests now pass
individually (12/12 socket.test.js, 7/7 socket-webrtc.test.js).
- Fixed CORS test in app.test.js to use allowed origin
- Updated auth-phase1.5.test.js to match actual error messages
- Fixed socket.test.js to use slug parameter instead of eventId
- Added EventParticipant records for socket event room tests
- Updated security config to allow both frontend origins in dev
All socket tests now passing (12/12). Test changes ensure proper
cleanup and prevent database conflicts by using selective deletion
instead of wiping entire tables.
Replace deleteMany({}) with selective cleanup targeting only test data:
- events.test.js: Delete only test users (john_dancer, sarah_swings, mike_blues)
and test events (test-dance-festival-2025) before creating new ones
- matches.test.js: Clean up john_dancer, sarah_swings, mike_moves and
test-dance-festival slug specifically
- users.test.js: Remove only john_dancer and sarah_swings test users
in both beforeAll and afterAll hooks
- auth.test.js: Target specific test usernames/emails (testuser, newuser,
lockouttest, etc.) instead of all users
- auth-phase1.5.test.js: Clean up 12 specific test users by username/email
- socket.test.js: Add beforeAll cleanup for sockettest user to prevent
conflicts from previous test runs
- socket-webrtc.test.js: Clean up webrtc_user1 and webrtc_user2 before
creating them
Fix CORS configuration for tests:
- security.js: Add http://localhost:3000 to allowed origins in development
mode to fix app.test.js CORS test (was failing with 500 error)
Results: Improved from 125/223 passing to 137/223 passing (12 more tests fixed)
All test data cleanup now uses WHERE clauses with specific usernames/emails/slugs
instead of wiping entire tables with deleteMany({})
- Update auth.test.js to match current API error messages
- Registration success message includes email verification notice
- Duplicate credentials use generic message to prevent user enumeration
- Add test commands to Makefile
- make test: run all backend tests
- make test-watch: run tests in watch mode
- make test-coverage: run tests with coverage report
All auth tests now pass (19/19 ✓)
- Rename .env.example to .env.development.example
- Update docker-compose.yml to use env_file for dev backend
- Update .gitignore to ignore .env.development and .env.production
- Remove generic .env and .env.example in favor of:
* .env.development (dev) + .env.development.example (template)
* .env.production (prod) + .env.production.example (template)
- Add production Dockerfiles for frontend and backend
* Frontend: multi-stage build with nginx serving static files
* Backend: multi-stage build with Prisma generation
- Create production nginx configuration (nginx/conf.d.prod/)
* Routes to frontend-prod:80 and backend-prod:3000
* Supports WebSocket connections for Socket.IO
- Update docker-compose.yml to use production config
* Add env_file support for backend-prod
* Mount production nginx config directory
- Add .env.production.example template for deployment
Add complete WebRTC peer-to-peer file transfer functionality:
Backend changes:
- Add WebRTC signaling events to Socket.IO (offer, answer, ICE candidates)
- Implement authorization checks for match participants
- Add signaling relay between matched users
Frontend changes:
- Create useWebRTC hook for RTCPeerConnection management
- Implement RTCDataChannel with 16KB chunking for large files
- Add real-time progress monitoring for sender and receiver
- Implement automatic file download on receiver side
- Add connection state tracking and error handling
- Integrate WebRTC with MatchChatPage (replace mockup)
Configuration:
- Add Vite allowed hosts configuration via VITE_ALLOWED_HOSTS env var
- Support comma-separated host list or 'all' for development
- Add .env.example with configuration examples
- Update docker-compose.yml with default allowed hosts
Documentation:
- Add comprehensive WebRTC testing guide with troubleshooting
- Add quick test checklist for manual testing
- Document WebRTC flow, requirements, and success criteria
Features:
- End-to-end encrypted P2P transfer (DTLS)
- 16KB chunk size optimized for DataChannel
- Buffer management to prevent overflow
- Automatic connection establishment with 30s timeout
- Support for files of any size
- Real-time progress tracking
- Clean connection lifecycle management
- Created users.test.js with 25 tests covering all 4 endpoints:
* GET /api/users/me - get current user profile
* PATCH /api/users/me - update profile (all fields)
* PATCH /api/users/me/password - change password
* GET /api/users/:username - get public profile
* GET /api/users/:username/ratings - get user ratings
- All 25 tests passing (100%)
- controllers/user.js coverage: 90.16% (up from 8.19%)
- routes/users.js coverage: 81.81% (up from 27.27%)
- Tested email change with verification
- Tested password security and validation
- Created events.test.js with 34 tests covering all 10 endpoints:
* GET /api/events - list events with join status
* GET /api/events/:slug - event details
* GET /api/events/:slug/messages - event chat messages
* POST /api/events/checkin/:token - QR code checkin
* GET /api/events/:slug/details - detailed event info
* DELETE /api/events/:slug/leave - leave event
* POST /api/events/:slug/heats - add/update user heats
* GET /api/events/:slug/heats/me - get user's heats
* GET /api/events/:slug/heats/all - get all heats
* DELETE /api/events/:slug/heats/:id - delete heat
- All 34 tests passing (100%)
- events.js coverage: 82.02% (up from 8.98%)
- Branch coverage: 75%
- Function coverage: 88.23%
- Changed expected status from 404 to 403 for non-participant access
- Fixed rating response structure (data directly, not data.rating)
- Added testUser3 to setup to avoid duplicate match constraints
- Updated tests to use different user combinations to avoid conflicts
All 24 tests now passing (100%)
Coverage: matches.js improved to 76.58% statement coverage
- Created matches.test.js with 24 tests covering:
* Match creation and validation
* Match listing and filtering
* Match acceptance workflow
* Match deletion
* Rating creation and validation
* User ratings display
- Fixed Jest ES module issues:
* Added mock for jsdom to bypass parse5 compatibility
* Added mock for dompurify for test environment
* Updated package.json with moduleNameMapper
Test results: 19/24 passing (79%)
Remaining: 5 tests need investigation
Complete the match lifecycle with partner rating functionality.
Backend changes:
- Add POST /api/matches/:slug/ratings endpoint to create ratings
* Validate score range (1-5)
* Prevent duplicate ratings (unique constraint per match+rater+rated)
* Auto-complete match when both users have rated
* Return detailed rating data with user and event info
- Add GET /api/users/:username/ratings endpoint to fetch user ratings
* Calculate and return average rating
* Include rater details and event context for each rating
* Limit to last 50 ratings
- Add hasRated field to GET /api/matches/:slug response
* Check if current user has already rated the match
* Enable frontend to prevent duplicate rating attempts
Frontend changes:
- Update RatePartnerPage to use real API instead of mocks
* Load match data and partner info
* Submit ratings with score, comment, and wouldCollaborateAgain
* Check hasRated flag and redirect if already rated
* Validate match status before allowing rating
* Show loading state and proper error handling
- Update MatchChatPage to show rating status
* Replace "Rate Partner" button with "✓ Rated" badge when user has rated
* Improve button text from "End & rate" to "Rate Partner"
- Add ratings API functions
* matchesAPI.createRating(slug, ratingData)
* ratingsAPI.getUserRatings(username)
User flow:
1. After match is accepted, users can rate each other
2. Click "Rate Partner" in chat to navigate to rating page
3. Submit 1-5 star rating with optional comment
4. Rating saved and user redirected to matches list
5. Chat shows "✓ Rated" badge instead of rating button
6. Match marked as 'completed' when both users have rated
7. Users cannot rate the same match twice
Security improvements:
- Add random CUID slugs to Match model to prevent ID enumeration attacks
- Update all match URLs from /matches/:id to /matches/:slug
- Keep numeric IDs for internal Socket.IO operations only
Backend changes:
- Add slug field to matches table with unique index
- Update all match endpoints to use slug-based lookups (GET, PUT, DELETE)
- Add GET /api/matches/:slug/messages endpoint to fetch message history
- Include matchSlug in all Socket.IO notifications
Frontend changes:
- Update all match routes to use slug parameter
- Update MatchesPage to use slug for accept/reject/navigate operations
- Update MatchChatPage to fetch match data by slug and load message history
- Update RatePartnerPage to use slug parameter
- Add matchesAPI.getMatchMessages() function
Bug fixes:
- Fix MatchChatPage not loading message history from database on mount
- Messages now persist and display correctly when users reconnect
Backend changes:
- Add matches API routes (POST, GET, PUT, DELETE)
- Create/accept/reject match requests
- Auto-create private chat rooms on match acceptance
- Socket.IO notifications for match events (received, accepted, cancelled)
- Users join personal rooms (user_{id}) for notifications
Frontend changes:
- Add MatchesPage component with inbox UI
- Matches navigation link with notification badge
- Real-time match request count updates
- Accept/reject match functionality
- Filter matches by status (all/pending/accepted)
- Integrate match requests in EventChatPage (UserPlus button)
Features:
- Send match requests to event participants
- Accept incoming match requests
- Real-time notifications via Socket.IO
- Automatic private chat room creation
- Match status tracking (pending/accepted/completed)
- Authorization checks (only participants can match)
- Duplicate match prevention
Security improvements:
- Add @default(cuid()) to Event.slug for auto-generated random slugs
- Prevent ID enumeration attacks (no more predictable slugs like "warsaw-dance-festival-2025")
- Event slugs now generated as secure cuid strings (e.g., "cmhz3lcgb00018vbn34v4phoi")
Test accounts:
- Add 3 test users to seed (john_dancer, sarah_swings, mike_blues)
- All users checked in to Warsaw Dance Festival 2025
- Pre-configured heats for testing matchmaking system
- Full profiles with WSDC IDs, social media, and locations
Seed improvements:
- Add bcryptjs for password hashing
- Add Prisma seed configuration to package.json
- Use worldsdcId for event upsert (instead of slug)
- Auto-generate event slugs via Prisma default
Documentation:
- Add test account credentials to SESSION_CONTEXT.md
- Document event slug security model
- Include sample heats for each test user
- Export getIO function from socket module
- Broadcast heats_updated event when user updates their heats
- Event includes userId, username, and updated heats array
- Non-blocking broadcast (won't fail request if socket fails)
- Add 3 new database tables: divisions, competition_types, event_user_heats
- Add seed data for 6 divisions (NEW, NOV, INT, ADV, ALL, CHA) and 2 competition types (J&J, STR)
- Add API endpoints for divisions and competition types
- Add heats management endpoints in events route (POST/GET/DELETE)
- Implement unique constraint: cannot have same role in same division+competition type
- Add participant verification before allowing heats management
- Support heat numbers 1-9 with optional Leader/Follower role
Users could gain unauthorized access to event chats by refreshing the page after leaving an event. The socket handler was automatically creating participation records when users joined rooms, completely bypassing the QR code check-in requirement. This fix verifies that users have legitimately checked in before allowing socket room access.
Backend:
- Use _count.participants for accurate real-time participant count
- Remove reliance on stale participantsCount column
Frontend:
- Show check-in requirement message for non-joined events
- Display "Open chat" button only for joined events
- Add dev-only "View details" button for QR code access during testing
- Improve visual feedback with amber-colored check-in notice
This ensures the participant count reflects actual checked-in users
and prevents unauthorized access to QR codes in production while
maintaining developer convenience in development mode.
Backend:
- Add event_checkin_tokens table with unique tokens per event
- Implement GET /api/events/:slug/details endpoint (on-demand token generation)
- Implement POST /api/events/checkin/:token endpoint (date validation only in production)
- Implement DELETE /api/events/:slug/leave endpoint
- Add comprehensive test suite for check-in endpoints
Frontend:
- Add EventDetailsPage with QR code display, participant list, and stats
- Add EventCheckinPage with success/error screens
- Add "Leave Event" button with confirmation modal to EventChatPage
- Install qrcode.react library for QR code generation
- Update routing and API client with new endpoints
Features:
- QR codes valid from (startDate-1d) to (endDate+1d)
- Development mode bypasses date validation for testing
- Automatic participant count tracking
- Duplicate check-in prevention
- Token reuse for same event (generated once, cached)
- Add backend entrypoint with automated Prisma migrations and client regeneration
- Add frontend entrypoint with dependency management
- Update Dockerfiles to use entrypoint scripts
- Ensures database schema stays in sync with Prisma Client after migrations
Replace sequential event IDs in URLs with unique alphanumeric slugs to prevent enumeration attacks. Event URLs now use format /events/{slug}/chat instead of /events/{id}/chat.
Backend changes:
- Add slug field (VARCHAR 50, unique) to Event model
- Create migration with auto-generated 12-char MD5-based slugs for existing events
- Update GET /api/events/:slug endpoint (changed from :id)
- Update GET /api/events/:slug/messages endpoint (changed from :eventId)
- Modify Socket.IO join_event_room to accept slug parameter
- Update send_event_message to use stored event context instead of passing eventId
Frontend changes:
- Update eventsAPI.getBySlug() method (changed from getById)
- Update eventsAPI.getMessages() to use slug parameter
- Change route from /events/:eventId/chat to /events/:slug/chat
- Update EventsPage to navigate using event.slug
- Update EventChatPage to fetch event data via slug and use slug in socket events
Security impact: Prevents attackers from discovering all events by iterating sequential IDs.