# Dashboard Aggregation Endpoint Contract **Date**: 2025-01-27 **Action**: 2.1.1.1 - Design dashboard endpoint contract **Status**: ✅ Complete ## Overview This document defines the contract for a new aggregated dashboard endpoint that consolidates multiple API calls into a single request, reducing over-fetching and improving performance. ## Current Implementation The dashboard currently makes **4+ separate API calls**: 1. **GET /audit/stats** - Dashboard statistics 2. **GET /audit/activity** - Recent activity logs 3. **Social feed API** - Recent posts/feed items 4. **GET /tracks** (with limit: 5) - Library preview **Problems**: - Multiple round trips increase latency - Over-fetching of data - Race conditions possible - No atomic data consistency ## Proposed Endpoint ### Endpoint Specification **Method**: `GET` **Path**: `/api/v1/dashboard` **Authentication**: Required (Bearer token) **Response Format**: Wrapped format `{ success: true, data: DashboardResponse }` ### Request Parameters **Query Parameters** (all optional): - `activity_limit` (integer, default: 10) - Number of recent activity items to return - `library_limit` (integer, default: 5) - Number of library items to return in preview - `stats_period` (string, default: "30d") - Time period for statistics ("7d", "30d", "90d", "all") ### Response Structure ```typescript interface DashboardResponse { stats: DashboardStats; recent_activity: RecentActivity[]; library_preview: LibraryPreview; } interface DashboardStats { tracks_played: number; messages_sent: number; favorites: number; active_friends: number; tracks_played_change?: string; // Percentage change (e.g., "+12%", "-5%") messages_sent_change?: string; favorites_change?: string; active_friends_change?: string; period: string; // "7d", "30d", "90d", "all" } interface RecentActivity { id: string; // UUID type: 'track_upload' | 'message_received' | 'favorite_added' | 'playlist_created' | 'comment_added' | 'post'; title: string; // Human-readable title description?: string; // Optional description/details timestamp: string; // ISO8601 timestamp icon?: string; // Optional icon URL or identifier metadata?: Record; // Additional context (track_id, user_id, etc.) } interface LibraryPreview { items: TrackPreview[]; total_count: number; // Total tracks in user's library has_more: boolean; // Whether there are more items beyond limit } interface TrackPreview { id: string; // UUID title: string; artist: string; duration: number; // Seconds cover_art_path?: string; play_count: number; like_count: number; created_at: string; // ISO8601 timestamp } ``` ### Response Example ```json { "success": true, "data": { "stats": { "tracks_played": 1234, "messages_sent": 567, "favorites": 89, "active_friends": 45, "tracks_played_change": "+12%", "messages_sent_change": "+8%", "favorites_change": "+23%", "active_friends_change": "+5%", "period": "30d" }, "recent_activity": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "type": "track_upload", "title": "Nouvelle piste ajoutée", "description": "My New Track.mp3", "timestamp": "2025-01-27T10:30:00Z", "metadata": { "track_id": "660e8400-e29b-41d4-a716-446655440001", "file_name": "My New Track.mp3" } }, { "id": "550e8400-e29b-41d4-a716-446655440002", "type": "message_received", "title": "Message reçu de @username", "timestamp": "2025-01-27T09:15:00Z", "metadata": { "conversation_id": "770e8400-e29b-41d4-a716-446655440003", "from_user_id": "880e8400-e29b-41d4-a716-446655440004" } } ], "library_preview": { "items": [ { "id": "990e8400-e29b-41d4-a716-446655440005", "title": "My Favorite Track", "artist": "Artist Name", "duration": 240, "cover_art_path": "/covers/track-123.jpg", "play_count": 42, "like_count": 7, "created_at": "2025-01-20T14:30:00Z" } ], "total_count": 156, "has_more": true } } } ``` ## Data Sources ### Stats Calculation **tracks_played**: Sum of play actions from audit logs + track play counts **messages_sent**: Count of message actions from audit logs **favorites**: Count of favorite/like actions from audit logs **active_friends**: Count of unique users interacted with in period **Change percentages**: Compare current period to previous period (e.g., last 30d vs previous 30d) ### Recent Activity Combines: 1. **Audit logs** (`/audit/activity`) - User actions (uploads, plays, favorites, etc.) 2. **Social feed** - Recent posts from followed users 3. **Chat messages** - Recent messages received 4. **Comments** - Comments on user's tracks/playlists Sorted by `timestamp` descending, limited by `activity_limit`. ### Library Preview Returns user's most recently added or most played tracks, limited by `library_limit`. ## Error Handling **401 Unauthorized**: User not authenticated **500 Internal Server Error**: Backend error during aggregation Error responses follow standard format: ```json { "success": false, "error": { "code": 500, "message": "Failed to fetch dashboard data", "timestamp": "2025-01-27T10:30:00Z" } } ``` ## Performance Considerations - **Caching**: Response can be cached for 30-60 seconds (user-specific) - **Database queries**: Use efficient joins and indexes - **Parallel fetching**: Fetch stats, activity, and library in parallel - **Timeouts**: Aggregate endpoint should complete within 2 seconds ## Migration Strategy 1. **Phase 1**: Implement backend endpoint (Action 2.1.1.2) 2. **Phase 2**: Update frontend to use new endpoint (Action 2.1.1.3) 3. **Phase 3**: Remove old API calls (Actions 2.1.1.4-2.1.1.6) 4. **Phase 4**: Add caching (Action 2.1.1.7) ## Validation ✅ Contract documented ✅ Response structure defined ✅ Data sources identified ✅ Error handling specified ✅ Performance considerations noted ⏭️ Next: Action 2.1.1.2 - Implement backend dashboard handler