Big commit whatever
This commit is contained in:
@@ -0,0 +1,564 @@
|
||||
# Laravel Jukebox API - Complete Documentation
|
||||
|
||||
## Project Overview
|
||||
- **Framework**: Laravel 13.0
|
||||
- **PHP Version**: ^8.3
|
||||
- **Database**: MySQL (configured via Docker at 172.17.0.1:3306)
|
||||
- **Authentication**: Laravel Sanctum 4.0 (Token-based API authentication)
|
||||
- **Purpose**: Music streaming/management API with role-based access control
|
||||
|
||||
---
|
||||
|
||||
## 1. API ROUTES
|
||||
|
||||
### Location: `/routes/api.php`
|
||||
|
||||
#### Public Routes (No Authentication Required)
|
||||
```
|
||||
POST /register - User registration
|
||||
POST /login - User login
|
||||
```
|
||||
|
||||
#### Authenticated Routes (Requires Sanctum token)
|
||||
|
||||
**Auth Endpoints:**
|
||||
```
|
||||
POST /logout - Logout user
|
||||
GET /me - Get current user info
|
||||
```
|
||||
|
||||
**User Likes (Authenticated):**
|
||||
```
|
||||
GET /me/likes - Get user's liked tracks
|
||||
POST /tracks/{track}/like - Like a track
|
||||
DELETE /tracks/{track}/like - Unlike a track
|
||||
```
|
||||
|
||||
**Browse Routes (Read-only for authenticated users):**
|
||||
```
|
||||
GET /labels - List all labels
|
||||
GET /labels/{label} - Get label details
|
||||
GET /genres - List all genres
|
||||
GET /genres/{genre} - Get genre details
|
||||
GET /artists - List all artists
|
||||
GET /artists/{artist} - Get artist details
|
||||
GET /albums - List all albums
|
||||
GET /albums/{album} - Get album details
|
||||
GET /tracks - List all tracks
|
||||
GET /tracks/{track} - Get track details
|
||||
```
|
||||
|
||||
**Admin-Only Routes (Requires 'admin' role):**
|
||||
```
|
||||
POST /labels - Create label
|
||||
PUT /labels/{label} - Update label
|
||||
DELETE /labels/{label} - Delete label
|
||||
|
||||
POST /genres - Create genre
|
||||
PUT /genres/{genre} - Update genre
|
||||
DELETE /genres/{genre} - Delete genre
|
||||
|
||||
POST /artists - Create artist
|
||||
PUT /artists/{artist} - Update artist
|
||||
DELETE /artists/{artist} - Delete artist
|
||||
|
||||
POST /albums - Create album
|
||||
PUT /albums/{album} - Update album
|
||||
DELETE /albums/{album} - Delete album
|
||||
|
||||
POST /tracks - Create track
|
||||
PUT /tracks/{track} - Update track
|
||||
DELETE /tracks/{track} - Delete track
|
||||
|
||||
GET /users - List all users
|
||||
GET /users/{user} - Get user details
|
||||
PUT /users/{user} - Update user
|
||||
DELETE /users/{user} - Delete user
|
||||
```
|
||||
|
||||
#### Web Routes (`routes/web.php`)
|
||||
- Minimal: Single `GET /` returning welcome view (for blade/HTML views)
|
||||
|
||||
---
|
||||
|
||||
## 2. CONTROLLERS & METHODS
|
||||
|
||||
### AuthController (`app/Http/Controllers/AuthController.php`)
|
||||
**Methods:**
|
||||
- `register(Request $request): JsonResponse` - Handles user registration
|
||||
- Validates: name, email, password (min 8 chars, confirmed)
|
||||
- Creates user with default 'user' role
|
||||
- Returns token + user object
|
||||
- HTTP 201
|
||||
|
||||
- `login(Request $request): JsonResponse` - Handles user login
|
||||
- Validates: email, password
|
||||
- Returns token + user object if credentials valid
|
||||
- Throws ValidationException if invalid
|
||||
|
||||
- `logout(Request $request): JsonResponse` - Deletes current access token
|
||||
- Returns success message
|
||||
|
||||
- `me(Request $request): JsonResponse` - Returns current authenticated user
|
||||
- Loads user role relationship
|
||||
|
||||
---
|
||||
|
||||
### UserController (`app/Http/Controllers/UserController.php`)
|
||||
**Methods:**
|
||||
- `index(): JsonResponse` - List all users with roles
|
||||
- `show(User $user): JsonResponse` - Get single user with role
|
||||
- `update(Request $request, User $user): JsonResponse`
|
||||
- Validates: name, email (unique), password, role_id
|
||||
- Returns updated user with role
|
||||
- `destroy(User $user): JsonResponse` - Delete user
|
||||
- Returns HTTP 204
|
||||
|
||||
---
|
||||
|
||||
### ArtistController (`app/Http/Controllers/ArtistController.php`)
|
||||
**Methods:**
|
||||
- `index(): JsonResponse` - List artists with label relationship
|
||||
- `show(Artist $artist): JsonResponse` - Get artist with label, tracks, genres
|
||||
- `store(Request $request): JsonResponse`
|
||||
- Validates: name, cover_path (nullable), release_date (nullable), label_id, duration
|
||||
- Returns HTTP 201
|
||||
- `update(Request $request, Artist $artist): JsonResponse` - Update artist
|
||||
- `destroy(Artist $artist): JsonResponse` - Delete artist
|
||||
|
||||
---
|
||||
|
||||
### AlbumController (`app/Http/Controllers/AlbumController.php`)
|
||||
**Methods:**
|
||||
- `index(): JsonResponse` - List albums with labels
|
||||
- `show(Album $album): JsonResponse` - Get album with label, tracks, artists, genres
|
||||
- `store(Request $request): JsonResponse`
|
||||
- Validates: title, cover_path, release_date, duration_seconds, type (enum: album|single|ep), label_id
|
||||
- Returns HTTP 201
|
||||
- `update(Request $request, Album $album): JsonResponse` - Update album
|
||||
- `destroy(Album $album): JsonResponse` - Delete album
|
||||
|
||||
---
|
||||
|
||||
### TrackController (`app/Http/Controllers/TrackController.php`)
|
||||
**Methods:**
|
||||
- `index(): JsonResponse` - List tracks with album, artists, genres
|
||||
- `show(Track $track): JsonResponse` - Get track with album.label, artists, genres
|
||||
- `store(Request $request): JsonResponse`
|
||||
- Validates: title, file_path, duration_seconds (nullable), album_id (nullable), artist_ids (array), genre_ids (array)
|
||||
- Syncs artist and genre relationships
|
||||
- Returns HTTP 201
|
||||
- `update(Request $request, Track $track): JsonResponse`
|
||||
- Syncs artist and genre relationships
|
||||
- `destroy(Track $track): JsonResponse` - Delete track
|
||||
|
||||
---
|
||||
|
||||
### LikeController (`app/Http/Controllers/LikeController.php`)
|
||||
**Methods:**
|
||||
- `like(Request $request, Track $track): JsonResponse`
|
||||
- Uses `syncWithoutDetaching()` to add like without removing existing ones
|
||||
- `unlike(Request $request, Track $track): JsonResponse`
|
||||
- Uses `detach()` to remove like
|
||||
- `index(Request $request): JsonResponse`
|
||||
- Returns user's liked tracks with album, artists, genres relationships
|
||||
|
||||
---
|
||||
|
||||
### GenreController (`app/Http/Controllers/GenreController.php`)
|
||||
**Methods:**
|
||||
- `index(): JsonResponse` - List all genres
|
||||
- `show(Genre $genre): JsonResponse` - Get single genre
|
||||
- `store(Request $request): JsonResponse` - Create genre (validates: name, max 100 chars)
|
||||
- `update(Request $request, Genre $genre): JsonResponse` - Update genre
|
||||
- `destroy(Genre $genre): JsonResponse` - Delete genre
|
||||
|
||||
---
|
||||
|
||||
### LabelController (`app/Http/Controllers/LabelController.php`)
|
||||
**Methods:**
|
||||
- `index(): JsonResponse` - List all labels
|
||||
- `show(Label $label): JsonResponse` - Get single label
|
||||
- `store(Request $request): JsonResponse` - Create label (validates: name, max 100 chars)
|
||||
- `update(Request $request, Label $label): JsonResponse` - Update label
|
||||
- `destroy(Label $label): JsonResponse` - Delete label
|
||||
|
||||
---
|
||||
|
||||
## 3. MODELS & RELATIONSHIPS
|
||||
|
||||
### User Model
|
||||
**Attributes:** id, name, email, password (hashed), role_id, timestamps
|
||||
**Fillable:** name, email, password, role_id
|
||||
**Hidden:** password, remember_token
|
||||
**Traits:** HasApiTokens, HasFactory, Notifiable
|
||||
|
||||
**Relationships:**
|
||||
- `role()`: BelongsTo(Role) - User's role
|
||||
- `likes()`: BelongsToMany(Track, 'likes') - Tracks user has liked
|
||||
|
||||
---
|
||||
|
||||
### Role Model
|
||||
**Attributes:** id, name, timestamps
|
||||
**Fillable:** name
|
||||
|
||||
**Relationships:**
|
||||
- `users()`: HasMany(User) - Users with this role
|
||||
|
||||
---
|
||||
|
||||
### Artist Model
|
||||
**Attributes:** id, name, cover_path, release_date, label_id, duration, timestamps
|
||||
**Fillable:** name, cover_path, release_date, label_id, duration
|
||||
|
||||
**Relationships:**
|
||||
- `label()`: BelongsTo(Label) - Artist's label
|
||||
- `tracks()`: BelongsToMany(Track, 'artist_track') - All tracks by artist
|
||||
|
||||
---
|
||||
|
||||
### Album Model
|
||||
**Attributes:** id, title, cover_path, release_date, duration_seconds, type (enum), label_id, timestamps
|
||||
**Fillable:** title, cover_path, release_date, duration_seconds, type, label_id
|
||||
|
||||
**Relationships:**
|
||||
- `label()`: BelongsTo(Label) - Album's label
|
||||
- `tracks()`: HasMany(Track) - All tracks in album
|
||||
|
||||
---
|
||||
|
||||
### Track Model
|
||||
**Attributes:** id, title, file_path, duration_seconds, album_id, timestamps
|
||||
**Fillable:** title, file_path, duration_seconds, album_id
|
||||
|
||||
**Relationships:**
|
||||
- `album()`: BelongsTo(Album) - Album containing track
|
||||
- `artists()`: BelongsToMany(Artist, 'artist_track') - Contributing artists
|
||||
- `genres()`: BelongsToMany(Genre, 'track_genre') - Track genres
|
||||
- `likedBy()`: BelongsToMany(User, 'likes') - Users who liked track
|
||||
|
||||
---
|
||||
|
||||
### Genre Model
|
||||
**Attributes:** id, name, timestamps
|
||||
**Fillable:** name
|
||||
|
||||
**Relationships:**
|
||||
- `tracks()`: BelongsToMany(Track, 'track_genre') - Tracks in genre
|
||||
|
||||
---
|
||||
|
||||
### Label Model
|
||||
**Attributes:** id, name, timestamps
|
||||
**Fillable:** name
|
||||
|
||||
**Relationships:**
|
||||
- `artists()`: HasMany(Artist) - Artists on label
|
||||
- `albums()`: HasMany(Album) - Albums on label
|
||||
|
||||
---
|
||||
|
||||
## 4. DATABASE SCHEMA & MIGRATIONS
|
||||
|
||||
### Tables Created (in order)
|
||||
|
||||
**1. roles**
|
||||
- id (PRIMARY)
|
||||
- name (VARCHAR)
|
||||
- timestamps
|
||||
|
||||
**2. genres**
|
||||
- id (PRIMARY)
|
||||
- name (VARCHAR 100)
|
||||
- timestamps
|
||||
|
||||
**3. labels**
|
||||
- id (PRIMARY)
|
||||
- name (VARCHAR 100)
|
||||
- timestamps
|
||||
|
||||
**4. users**
|
||||
- id (PRIMARY)
|
||||
- name (VARCHAR)
|
||||
- email (VARCHAR)
|
||||
- password (VARCHAR)
|
||||
- role_id (FOREIGN KEY → roles.id)
|
||||
- timestamps
|
||||
|
||||
**5. artists**
|
||||
- id (PRIMARY)
|
||||
- name (VARCHAR 255)
|
||||
- cover_path (VARCHAR 255, nullable)
|
||||
- release_date (DATETIME, nullable)
|
||||
- label_id (FOREIGN KEY → labels.id, nullable)
|
||||
- duration (INTEGER, nullable)
|
||||
- timestamps
|
||||
|
||||
**6. albums**
|
||||
- id (PRIMARY)
|
||||
- title (VARCHAR 255)
|
||||
- cover_path (VARCHAR 255, nullable)
|
||||
- release_date (DATETIME, nullable)
|
||||
- duration_seconds (INTEGER, nullable)
|
||||
- type (ENUM: 'album', 'single', 'ep', default: 'album')
|
||||
- label_id (FOREIGN KEY → labels.id, nullable)
|
||||
- timestamps
|
||||
|
||||
**7. tracks**
|
||||
- id (PRIMARY)
|
||||
- title (VARCHAR 255)
|
||||
- file_path (VARCHAR 255)
|
||||
- duration_seconds (INTEGER, nullable)
|
||||
- album_id (FOREIGN KEY → albums.id, nullable)
|
||||
- timestamps
|
||||
|
||||
**8. artist_track** (pivot table)
|
||||
- artist_id (FOREIGN KEY → artists.id)
|
||||
- track_id (FOREIGN KEY → tracks.id)
|
||||
- PRIMARY KEY: (artist_id, track_id)
|
||||
- timestamps
|
||||
|
||||
**9. track_genre** (pivot table)
|
||||
- track_id (FOREIGN KEY → tracks.id)
|
||||
- genre_id (FOREIGN KEY → genres.id)
|
||||
- PRIMARY KEY: (track_id, genre_id)
|
||||
- timestamps
|
||||
|
||||
**10. likes** (pivot table)
|
||||
- user_id (FOREIGN KEY → users.id)
|
||||
- track_id (FOREIGN KEY → tracks.id)
|
||||
- PRIMARY KEY: (user_id, track_id)
|
||||
- timestamps
|
||||
|
||||
**11. personal_access_tokens** (Sanctum)
|
||||
- id (PRIMARY)
|
||||
- tokenable_id, tokenable_type (polymorphic)
|
||||
- name (TEXT)
|
||||
- token (VARCHAR 64, UNIQUE)
|
||||
- abilities (TEXT, nullable)
|
||||
- last_used_at (TIMESTAMP, nullable)
|
||||
- expires_at (TIMESTAMP, nullable, indexed)
|
||||
- timestamps
|
||||
|
||||
---
|
||||
|
||||
## 5. AUTHENTICATION & AUTHORIZATION
|
||||
|
||||
### Sanctum Configuration (`config/sanctum.php`)
|
||||
|
||||
**Stateful Domains:**
|
||||
- localhost, localhost:3000, 127.0.0.1, 127.0.0.1:8000, ::1
|
||||
- Plus current application URL
|
||||
|
||||
**Guard:** web (session guard, though API uses bearer tokens)
|
||||
|
||||
**Expiration:** null (tokens don't expire automatically)
|
||||
|
||||
**Token Prefix:** env('SANCTUM_TOKEN_PREFIX', '')
|
||||
|
||||
**Middleware:**
|
||||
- authenticate_session: Laravel\Sanctum\Http\Middleware\AuthenticateSession
|
||||
- encrypt_cookies: Illuminate\Cookie\Middleware\EncryptCookies
|
||||
- validate_csrf_token: Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
|
||||
|
||||
### Auth Configuration (`config/auth.php`)
|
||||
|
||||
**Default Guard:** web (session-based)
|
||||
|
||||
**User Provider:** Eloquent (uses App\Models\User)
|
||||
|
||||
**Password Broker:** users (uses users table)
|
||||
|
||||
**Token Expiry:** 60 minutes
|
||||
|
||||
### Custom Middleware
|
||||
|
||||
**EnsureAdmin** (`app/Http/Middleware/EnsureAdmin.php`)
|
||||
- Checks if user's role name is 'admin'
|
||||
- Returns 403 Forbidden if not admin
|
||||
- Applied to routes group via 'admin' alias in `bootstrap/app.php`
|
||||
|
||||
---
|
||||
|
||||
## 6. SEEDERS & FACTORIES
|
||||
|
||||
### DatabaseSeeder (`database/seeders/DatabaseSeeder.php`)
|
||||
Currently creates a single test user:
|
||||
```
|
||||
Name: Test User
|
||||
Email: test@example.com
|
||||
Password: Uses UserFactory default
|
||||
```
|
||||
|
||||
The seeder is commented to create 10 users but currently just creates 1.
|
||||
|
||||
### UserFactory (`database/factories/UserFactory.php`)
|
||||
**Attributes Generated:**
|
||||
- name: fake()->name()
|
||||
- email: fake()->unique()->safeEmail()
|
||||
- email_verified_at: now()
|
||||
- password: Hash::make('password')
|
||||
- remember_token: Str::random(10)
|
||||
|
||||
**Methods:**
|
||||
- `unverified()`: Sets email_verified_at to null
|
||||
|
||||
**Note:** No factory exists for other models (Artist, Album, Track, Genre, Label)
|
||||
|
||||
---
|
||||
|
||||
## 7. CORS CONFIGURATION
|
||||
|
||||
**Status:** No explicit CORS configuration found
|
||||
|
||||
**Implications:**
|
||||
- No `config/cors.php` file
|
||||
- Sanctum handles CORS implicitly via stateful domains configuration
|
||||
- Likely relying on Laravel default CORS handling or missing CORS setup
|
||||
- Frontend on localhost:3000 should work (listed in stateful domains)
|
||||
|
||||
**Recommendation:** May need explicit CORS middleware for production cross-origin requests
|
||||
|
||||
---
|
||||
|
||||
## 8. RESOURCES & TRANSFORMERS
|
||||
|
||||
**Status:** No JSON:API Resources or Transformers found
|
||||
|
||||
**Current Approach:**
|
||||
- Direct Eloquent model casting to JSON
|
||||
- Raw `response()->json()` returns from controllers
|
||||
- Relationships loaded via `load()` and `with()` methods
|
||||
- No API resource classes (Laravel Resource classes) implemented
|
||||
|
||||
**Data Transformation:**
|
||||
- Manual eager loading in controllers
|
||||
- Example: `$artist->load(['label', 'tracks.album', 'tracks.genres'])`
|
||||
|
||||
---
|
||||
|
||||
## 9. ENVIRONMENT CONFIGURATION
|
||||
|
||||
### .env (Development)
|
||||
```
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=172.17.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=jukebox
|
||||
DB_USERNAME=jukebox_admin
|
||||
DB_PASSWORD=Super
|
||||
|
||||
SESSION_DRIVER=database
|
||||
CACHE_STORE=database
|
||||
QUEUE_CONNECTION=database
|
||||
```
|
||||
|
||||
### Key Settings
|
||||
- Debug mode enabled (local)
|
||||
- MySQL via Docker (172.17.0.1)
|
||||
- Database-backed sessions
|
||||
- Database-backed queue
|
||||
- Database-backed cache
|
||||
|
||||
---
|
||||
|
||||
## 10. DEPENDENCIES
|
||||
|
||||
**Core:**
|
||||
- laravel/framework: ^13.0
|
||||
- laravel/sanctum: ^4.0
|
||||
- laravel/tinker: ^3.0
|
||||
|
||||
**Dev:**
|
||||
- fakerphp/faker: ^1.23
|
||||
- laravel/pail: ^1.2.5
|
||||
- laravel/pint: ^1.27
|
||||
- mockery/mockery: ^1.6
|
||||
- nunomaduro/collision: ^8.6
|
||||
- phpunit/phpunit: ^12.5.12
|
||||
|
||||
---
|
||||
|
||||
## 11. KEY OBSERVATIONS & NOTES
|
||||
|
||||
### Strengths
|
||||
1. **Clean Role-Based Architecture** - Admin/User separation via middleware
|
||||
2. **Proper Eloquent Relationships** - All pivot tables correctly implemented
|
||||
3. **Token-Based Auth** - Sanctum for stateless API authentication
|
||||
4. **Comprehensive Data Model** - Well-structured music catalog schema
|
||||
|
||||
### Areas for Enhancement
|
||||
1. **No Resources/Transformers** - Consider Laravel API Resources for consistent responses
|
||||
2. **No CORS Config** - Explicit CORS setup recommended for production
|
||||
3. **Limited Factories** - Only UserFactory; others could enable better testing
|
||||
4. **No API Documentation** - Consider OpenAPI/Swagger docs
|
||||
5. **No Request DTOs** - Could use Form Requests for centralized validation
|
||||
6. **No Response Standardization** - No consistent error/success response format
|
||||
7. **No Pagination** - List endpoints return all results (performance concern)
|
||||
8. **No Query Filtering** - No search/filter capabilities on GET endpoints
|
||||
9. **No Rate Limiting** - No API rate limiting configured
|
||||
10. **Minimal Seeding** - Only test user; could seed sample data
|
||||
|
||||
### Security Considerations
|
||||
1. Password hashing properly handled via Laravel
|
||||
2. CSRF protection configured (though API uses tokens)
|
||||
3. Access control via admin middleware is solid
|
||||
4. Foreign key constraints prevent orphaned records
|
||||
5. Role-based authorization working correctly
|
||||
|
||||
### API Response Format
|
||||
Current format (no wrapper):
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Track Name",
|
||||
"duration_seconds": 240,
|
||||
"created_at": "2026-04-23T...",
|
||||
"updated_at": "2026-04-23T..."
|
||||
}
|
||||
```
|
||||
|
||||
Could benefit from standardized wrapper:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": { ... },
|
||||
"message": "Success"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. TESTING STRUCTURE
|
||||
|
||||
**Test Framework:** PHPUnit 12.5.12
|
||||
|
||||
**Test Directories:**
|
||||
- `tests/Feature/` - Feature tests
|
||||
- `tests/Unit/` - Unit tests
|
||||
- `tests/TestCase.php` - Base test class
|
||||
|
||||
**Example Tests:** ExampleTest.php files (placeholder)
|
||||
|
||||
**No comprehensive tests currently implemented**
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This is a **music streaming/library API** built with Laravel 13, featuring:
|
||||
- ✅ User registration & login with Sanctum tokens
|
||||
- ✅ Role-based access control (admin/user)
|
||||
- ✅ Full CRUD for Artists, Albums, Tracks, Genres, Labels
|
||||
- ✅ User favorites/likes system
|
||||
- ✅ Proper database schema with relationships
|
||||
- ⚠️ Minimal documentation, testing, and optimization
|
||||
- ⚠️ Missing common API best practices
|
||||
|
||||
Ready for development and enhancement with proper testing, documentation, and optimization work.
|
||||
Reference in New Issue
Block a user