- Completed Action 2.1.1.1: Designed dashboard endpoint contract - Created DASHBOARD_ENDPOINT_CONTRACT.md with complete specification - Defined GET /api/v1/dashboard endpoint consolidating 4+ API calls - Response structure: stats, recent_activity, library_preview - Query parameters: activity_limit, library_limit, stats_period - Documented data sources, error handling, performance considerations - Migration strategy outlined for phased rollout - Ready for backend implementation (Action 2.1.1.2)
215 lines
6.3 KiB
Markdown
215 lines
6.3 KiB
Markdown
# 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<string, any>; // 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
|