Change GET /sexy/models/:slug endpoint to query u.slug directly
instead of concatenating LOWER(first_name || ' ' || last_name).
This matches the actual slug field in the directus_users table.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend Changes:
- Added original_recording_id field to sexy_recordings table to track duplicates
- Added indexes for original_recording_id and public fields
- Implemented /sexy/community-recordings endpoint to list public shared recordings
- Implemented /sexy/recordings/:id/duplicate endpoint to duplicate community recordings
- Community recordings filtered by status=published AND public=true
- Duplication creates a private draft copy for the current user
Frontend Changes:
- Added leaderboard and profile quick links to play view header
- Added navigation buttons for better UX on play page
- Added translations: my_profile, anonymous, load_more
Database Schema:
- ALTER TABLE sexy_recordings ADD COLUMN original_recording_id uuid
- Created foreign key and indexes for efficient queries
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed complex JSON parsing logic for tags field. Now that sexy_recordings.tags
is a json column type (matching sexy_videos), Directus/Knex handles the conversion
automatically. Simple `tags || []` is sufficient.
Related to gamification implementation where sexy_recordings.tags was changed
from text[] to json type to match videos table implementation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Handle tags field when sent as JSON string by parsing it into
an array before insertion. Fixes "malformed array literal" error
when saving recordings with tags.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added Directus hooks for automatic point awards:
- Recording creation/publishing (50 points)
- Recording featured status (100 points bonus)
- Comments on recordings (5 points)
- Created /leaderboard route with full UI
- Server-side data loading with authentication guard
- Responsive design with medal emojis for top 3
- User stats display (recordings, plays, achievements)
- Pagination support
- "How It Works" info section
- Added comprehensive gamification translations
- Time-weighted scoring displayed for rankings
- Automatic achievement checking on point awards
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Database schema with 5 new tables:
- sexy_recording_plays: Track recording playback
- sexy_user_points: Individual point actions
- sexy_achievements: Predefined achievement definitions
- sexy_user_achievements: User progress tracking
- sexy_user_stats: Cached statistics for leaderboards
- Seeded 17 achievements across 4 categories
- Backend gamification helper functions with time-weighted scoring
- Three new API endpoints:
- GET /sexy/gamification/leaderboard
- GET /sexy/gamification/user/:id
- GET /sexy/gamification/achievements
- Recording play endpoints with automatic point awards
- Time-decay formula (λ=0.005) for balanced rankings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The directus_users table doesn't have a 'featured' column, causing
SQL errors when the /sexy/models endpoint tried to filter by it.
Removed the featured parameter check to fix 500 errors on homepage.
Error was: "column u.featured does not exist"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit completes the migration of all API calls from direct Directus
SDK calls to custom bundle endpoints, bypassing Directus permissions using
direct database queries via Knex.
Backend changes (packages/bundle/src/endpoint/index.ts):
- Added /sexy/models endpoint with optional featured and limit filters
- Added /sexy/models/:slug endpoint for single model lookup
- Added /sexy/videos endpoint with optional model_id filter
- Added /sexy/articles endpoint with optional featured filter
- All endpoints use Knex for direct database access, bypassing permissions
- Endpoints handle nested relationships (photos, banner, models, movie, author)
Frontend changes (packages/frontend/src/lib/services.ts):
- Updated getVideos() to use /sexy/videos custom endpoint
- Updated getVideosForModel() to use /sexy/videos with model_id query param
- Updated getFeaturedVideos() to use /sexy/videos with limit param
- Updated getArticles() to use /sexy/articles custom endpoint
- Updated getModelBySlug() to use /sexy/models/:slug custom endpoint
- Simplified service layer by moving filtering logic to backend
Benefits:
- Complete bypass of Directus permissions layer
- Consistent API pattern across all endpoints
- Centralized business logic in backend
- Cleaner frontend service code
- All endpoints tested and working
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use Knex queries directly to bypass Directus permissions
- Query directus_users with role=Model using database directly
- Fetch related photos and banner using joins
- Successfully returns empty array when no models exist
- Resolves permissions issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add /sexy/models endpoint in bundle with accountability: null
- Update getModels() and getFeaturedModels() to use custom endpoint
- Still experiencing permissions issues with field access
- Need to configure Directus public role permissions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend changes:
- Added /sexy/analytics endpoint to fetch detailed creator analytics
- Calculates total likes, plays, completion rates, and avg watch times
- Groups analytics by date for timeline visualization
- Provides video-specific performance metrics
Frontend changes:
- Added Analytics TypeScript types and service function
- Created Analytics tab in /me dashboard (visible only for Models)
- Displays overview stats: total videos, likes, and plays
- Added detailed video performance table with:
- Individual video metrics
- Color-coded completion rates (green >70%, yellow >40%, red <40%)
- Average watch time per video
- Links to video pages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend (Database & API):
- Add likes_count, plays_count, views_count to sexy_videos table
- Create sexy_video_likes junction table for user-video likes
- Create sexy_video_plays table for analytics tracking
- Add POST /sexy/videos/:id/like endpoint
- Add DELETE /sexy/videos/:id/like endpoint
- Add GET /sexy/videos/:id/like-status endpoint
- Add POST /sexy/videos/:id/play endpoint
- Add PATCH /sexy/videos/:id/play/:playId endpoint
Frontend (Types & Services):
- Update Video interface with counter fields
- Add VideoLikeStatus, VideoLikeResponse, VideoPlayResponse types
- Add likeVideo() service function
- Add unlikeVideo() service function
- Add getVideoLikeStatus() service function
- Add recordVideoPlay() service function
- Add updateVideoPlay() service function
Next: Implement UI components for like button and play count display
- Fix artist_name null handling in header component with email fallback
- Fix authentication in recording endpoints to use req.accountability
- Change duration field type from integer to double precision for millisecond precision
- Add createRecording service function with proper authentication
- Update play page to use fetch API for recording saves
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented complete infrastructure for recording, saving, and managing
buttplug device patterns with precise event timing.
**Phase 1: Backend & Infrastructure**
- Added Directus schema for sexy_recordings collection with all fields
(id, status, user_created, title, description, slug, duration, events,
device_info, tags, linked_video, featured, public)
- Created REST API endpoints in bundle extension:
* GET /sexy/recordings - list user recordings with filtering
* GET /sexy/recordings/:id - get single recording
* POST /sexy/recordings - create new recording with validation
* PATCH /sexy/recordings/:id - update recording (owner only)
* DELETE /sexy/recordings/:id - soft delete by archiving
- Added TypeScript types: RecordedEvent, DeviceInfo, Recording
- Created frontend services: getRecordings(), deleteRecording()
- Built RecordingCard component with stats, device info, and actions
- Added Recordings tab to /me dashboard page with grid layout
- Added i18n translations for recordings UI
**Phase 2: Recording Capture**
- Implemented recording state management in /play page
- Added Start/Stop Recording buttons with visual indicators
- Capture device events with precise timestamps during recording
- Normalize actuator values (0-100) for cross-device compatibility
- Created RecordingSaveDialog component with:
* Recording stats display (duration, events, devices)
* Form inputs (title, description, tags)
* Device information preview
- Integrated save recording API call from play page
- Added success/error toast notifications
- Automatic event filtering during recording
**Technical Details**
- Events stored as JSON array with timestamp, deviceIndex, deviceName,
actuatorIndex, actuatorType, and normalized value
- Device metadata includes name, index, and capability list
- Slug auto-generated from title for SEO-friendly URLs
- Status workflow: draft → published → archived
- Permission checks: users can only access own recordings or public ones
- Frontend uses performance.now() for millisecond precision timing
**User Flow**
1. User scans and connects devices on /play page
2. Clicks "Start Recording" to begin capturing events
3. Manipulates device sliders - all changes are recorded
4. Clicks "Stop Recording" to end capture
5. Save dialog appears with recording preview and form
6. User enters title, description, tags and saves
7. Recording appears in dashboard /me Recordings tab
8. Can play back, edit, or delete recordings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>