project-standalo-sonic-cloud/docs/PROJECT_DOCUMENTATION.md

1462 lines
40 KiB
Markdown

# Sonic Cloud - Project Documentation
**Version**: 0.1.0
**Type**: Music Platform for Musicians and Listeners
---
## Executive Summary
### What is Sonic Cloud?
Sonic Cloud is a modern music platform that connects musicians with listeners. Think of it as a place where independent artists can upload their music, organize it into albums, and share it with the world - while listeners can discover new music, create playlists, and follow their favorite artists.
**Key Capabilities**:
- Musicians upload and manage their songs and albums
- Listeners discover trending music and new releases
- Everyone can create and share custom playlists
- Built-in audio player with waveform visualization
- Search across all songs, artists, and albums
### Who is it for?
**Musicians**: Upload your tracks, organize them into albums, build your artist profile, and track how many plays your music gets.
**Labels**: Manage multiple artists under your label, invite new talent, and view collective statistics.
**Listeners**: Discover new music by genre, create playlists, follow artists, and share your favorite songs.
---
## Quick Start
### Installation
```bash
# Install dependencies
npm install
# Set up database
npx prisma generate
npx prisma db push
# Start development server
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) in your browser.
### First Steps
1. **Register an account** at `/register`
2. **Become an artist** by creating an artist profile
3. **Upload your first song** using the upload form
4. **Create a playlist** to organize music you love
5. **Search and discover** trending songs and new releases
---
## Architecture Overview
### System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ USER BROWSER │
│ (React 19 + Next.js 16) │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP Requests
┌─────────────────────────────────────────────────────────────────┐
│ NEXT.JS APP ROUTER │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Pages (SSR) │ │ API Routes │ │
│ │ - Home │ │ - Auth │ │
│ │ - Artist │ │ - Songs │ │
│ │ - Album │ │ - Playlists │ │
│ │ - Playlist │ │ - Search │ │
│ │ - Profile │ │ - Share │ │
│ └──────────────────┘ └──────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│ Prisma ORM
┌─────────────────────────────────────────────────────────────────┐
│ SQLITE DATABASE │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Users │ │ Artists │ │ Songs │ │ Playlists│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Albums │ │ Labels │ │ Genres │ │ Shares │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### Data Flow
```
User Action → React Component → API Endpoint → Prisma ORM → SQLite
User Sees Update ← React Refresh ← JSON Response ← Data Query ───┘
```
### Technology Stack
| Layer | Technology | Purpose |
|-------|------------|---------|
| **Frontend** | React 19.2.1 | Interactive user interface |
| **Framework** | Next.js 16.0.10 | Server-side rendering, routing, API |
| **Styling** | Tailwind CSS 4 | Responsive, utility-first design |
| **Language** | TypeScript 5 | Type safety and better code quality |
| **Database** | SQLite | Lightweight, file-based storage |
| **ORM** | Prisma 5.22.0 | Type-safe database access |
| **Authentication** | JWT + bcryptjs | Secure user sessions |
<details>
<summary>🔧 Technical Details - Framework Decisions</summary>
**Why Next.js?**
- File-based routing simplifies page creation
- API routes eliminate need for separate backend
- Server-side rendering improves SEO and performance
- React 19 supports latest concurrent features
**Why SQLite?**
- Zero configuration for development
- Single file database for easy deployment
- Sufficient performance for small-to-medium scale
- Easy migration to PostgreSQL/MySQL later if needed
**Why Prisma?**
- Type-safe database queries prevent runtime errors
- Schema-first approach documents data model
- Automatic migration generation
- Built-in connection pooling
</details>
---
## Features
### For Musicians (Artists)
#### Upload and Manage Songs
Upload audio files with metadata like title, genre, cover art, and description. Your songs are stored with unique IDs and can be updated or deleted at any time.
<details>
<summary>🔧 Technical Details - Song Upload Flow</summary>
```typescript
POST /api/songs/upload
Headers: { Authorization: "Bearer <jwt_token>" }
Body: {
title: string,
artistId: string,
albumId?: string,
audioUrl: string, // URL to audio file
coverImageUrl?: string,
duration: number, // in seconds
isPublic: boolean,
genres: string[] // genre slugs
}
```
**Process**:
1. Validate JWT token → get authenticated user
2. Verify user owns the artist profile
3. Create Song record in database
4. Create SongGenre junction records
5. Return song with ID and metadata
**Validation**:
- Duration must be > 0
- Audio URL format validation
- Genre slugs must exist in Genre table
</details>
#### Organize Albums
Group songs into albums, EPs, or singles. Each album has a release date, cover art, and track listing that you control.
<details>
<summary>🔧 Technical Details - Album Structure</summary>
```typescript
// Album model fields
{
id: string, // UUID
artistId: string, // FK to Artist
title: string,
releaseDate: Date,
albumType: "ALBUM" | "EP" | "SINGLE",
coverImageUrl?: string,
description?: string,
songs: Song[] // hasMany relation
}
```
**Album Types**:
- **ALBUM**: 8+ tracks, typically 30-60 minutes
- **EP**: 3-7 tracks, typically 15-30 minutes
- **SINGLE**: 1-2 tracks
</details>
#### Artist Profile
Create a professional profile with bio, avatar, verified badge, and links to your label (if applicable). Track your total plays across all songs.
#### Analytics
See how many times each song has been played. Monitor which songs are trending and which need promotion.
<details>
<summary>🔧 Technical Details - Play Tracking</summary>
```typescript
POST /api/songs/{id}/play
// Updates play count atomically
await prisma.song.update({
where: { id: songId },
data: {
playCount: { increment: 1 }
}
});
```
**Note**: Play count is incremented on each play request. For production, consider:
- IP-based rate limiting to prevent abuse
- Session tracking to count unique plays
- Analytics aggregation for performance
</details>
---
### For Labels
#### Manage Multiple Artists
Create a label profile and invite artists to join your roster. View all artists under your label in one dashboard.
#### Label Statistics
See aggregate stats across all your artists: total songs, albums, and cumulative play counts.
<details>
<summary>🔧 Technical Details - Label Stats API</summary>
```typescript
GET /api/labels/{id}/stats
Response: {
artistCount: number,
songCount: number,
albumCount: number,
totalPlays: number
}
```
**Calculation**:
- Artist count: Direct count of Artist records with labelId
- Song/Album count: Sum across all label artists
- Total plays: Aggregate playCount from all songs by label artists
</details>
#### Invitation System
Send invitations to artists. They can accept or decline. Track invitation status (pending, accepted, declined, expired).
---
### For Listeners
#### Discover Music
Browse trending songs (sorted by play count) and new releases (sorted by upload date). Filter by genre to find music you love.
<details>
<summary>🔧 Technical Details - Discovery Queries</summary>
```typescript
// Trending songs
GET /api/discover/trending?limit=20
// SQL equivalent
SELECT * FROM Song
WHERE isPublic = true
ORDER BY playCount DESC
LIMIT 20;
// New releases
GET /api/discover/new-releases?limit=20
// SQL equivalent
SELECT * FROM Song
WHERE isPublic = true
ORDER BY createdAt DESC
LIMIT 20;
```
</details>
#### Create Playlists
Make custom playlists with any songs on the platform. Control playlist privacy (public or private). Reorder songs by dragging.
#### Search Everything
Type a query and search across songs (title), artists (name), and albums (title) simultaneously.
<details>
<summary>🔧 Technical Details - Search Implementation</summary>
```typescript
GET /api/search?q=rock
// Searches across three tables
Songs: WHERE title LIKE '%rock%' OR description LIKE '%rock%'
Artists: WHERE name LIKE '%rock%' OR bio LIKE '%rock%'
Albums: WHERE title LIKE '%rock%' OR description LIKE '%rock%'
Response: {
songs: Song[],
artists: Artist[],
albums: Album[]
}
```
**Current Limitations**:
- Case-insensitive substring matching only
- No relevance scoring
- No fuzzy matching
**Future Enhancements**:
- Full-text search engine (Algolia, Elasticsearch)
- Relevance ranking by play count and recency
- Typo tolerance and autocomplete
</details>
#### Audio Player
Play songs with a fixed bottom player that follows you across pages. Features include:
- Play/pause, next/previous track
- Visual waveform with seek functionality
- Volume control and mute
- Shuffle and repeat modes
---
### For Everyone
#### Share Content
Generate shareable links for songs, playlists, and albums. Track how many times your share links are clicked.
<details>
<summary>🔧 Technical Details - Share Link Generation</summary>
```typescript
POST /api/share/song/{id}
// Creates Share record
{
id: string,
type: "SONG" | "PLAYLIST" | "ALBUM",
targetId: string, // ID of shared content
token: string, // nanoid(10) - short unique code
userId?: string, // Who created the share
clickCount: number,
createdAt: Date
}
// Returns shareable URL
https://sonic-cloud.app/s/{token}
```
**Share Token Format**: 10-character nanoid (URL-safe, lowercase + numbers)
**Click Tracking**: Each time `/s/{token}` is visited, clickCount increments.
</details>
#### Genre System
Browse music by genre. Each genre has a name, slug (URL-friendly), description, and visual color code.
#### User Profiles
View other users' public playlists and activity. Update your own profile with display name, bio, and avatar.
---
## API Reference
The platform exposes 40 RESTful API endpoints across 8 functional domains.
### Authentication (`/api/auth`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/auth/register` | POST | No | Create new user account |
| `/auth/login` | POST | No | Authenticate and receive JWT |
| `/auth/forgot-password` | POST | No | Request password reset email |
| `/auth/reset-password` | POST | No | Reset password with token |
<details>
<summary>🔧 Technical Details - Authentication Flow</summary>
**Registration**:
```typescript
POST /api/auth/register
Body: {
email: string,
password: string, // Min 8 chars
username: string, // Unique, 3-20 chars
displayName?: string
}
Response: {
user: { id, email, username, role },
token: string // JWT valid for 7 days
}
```
**Login**:
```typescript
POST /api/auth/login
Body: {
email: string,
password: string
}
Response: {
user: { id, email, username, role },
token: string
}
```
**JWT Payload**:
```typescript
{
userId: string,
email: string,
role: "USER" | "ADMIN",
iat: number, // issued at
exp: number // expires at (7 days)
}
```
**Password Hashing**: bcryptjs with 10 salt rounds
**Token Validation**: All protected routes verify JWT via `Authorization: Bearer <token>` header
</details>
---
### Users (`/api/users`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/users/me` | GET | Yes | Get current user profile |
| `/users/me` | PUT | Yes | Update profile (displayName, bio, avatarUrl) |
---
### Artists (`/api/artists`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/artists` | POST | Yes | Create artist profile for current user |
| `/artists/{id}` | GET | No | Get artist details with stats |
| `/artists/{id}` | PUT | Yes | Update artist profile (owner only) |
| `/artists/{id}/songs` | GET | No | Get all public songs by artist |
| `/artists/{id}/albums` | GET | No | Get all albums by artist |
<details>
<summary>🔧 Technical Details - Artist Endpoints</summary>
**Create Artist**:
```typescript
POST /api/artists
Body: {
name: string,
bio?: string,
avatarUrl?: string,
websiteUrl?: string
}
// Automatically generates slug from name
// Associates with authenticated userId
```
**Get Artist**:
```typescript
GET /api/artists/{id}
Response: {
id, name, slug, bio, avatarUrl, verified,
userId, labelId,
_count: {
songs: number,
albums: number
},
label?: { id, name, slug }
}
```
**Authorization**: Only artist owner can update their profile
</details>
---
### Songs (`/api/songs`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/songs/upload` | POST | Yes | Upload new song (artist only) |
| `/songs/{id}` | GET | No | Get song details (respects privacy) |
| `/songs/{id}` | PUT | Yes | Update song metadata (owner only) |
| `/songs/{id}` | DELETE | Yes | Delete song (owner only) |
| `/songs/{id}/play` | POST | No | Increment play count |
---
### Albums (`/api/albums`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/albums` | POST | Yes | Create album (artist only) |
| `/albums/{id}` | GET | No | Get album with all songs |
| `/albums/{id}` | PUT | Yes | Update album (owner only) |
| `/albums/{id}` | DELETE | Yes | Delete album (owner only) |
---
### Playlists (`/api/playlists`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/playlists` | GET | Yes | Get current user's playlists |
| `/playlists` | POST | Yes | Create new playlist |
| `/playlists/{id}` | GET | No | Get playlist (respects privacy) |
| `/playlists/{id}` | PUT | Yes | Update playlist (owner only) |
| `/playlists/{id}` | DELETE | Yes | Delete playlist (owner only) |
| `/playlists/{id}/songs` | POST | Yes | Add song to playlist |
| `/playlists/{id}/reorder` | PUT | Yes | Reorder songs in playlist |
<details>
<summary>🔧 Technical Details - Playlist Reordering</summary>
```typescript
PUT /api/playlists/{id}/reorder
Body: {
songIds: string[] // Ordered array of song IDs
}
// Updates position field for each PlaylistSong junction record
for (let i = 0; i < songIds.length; i++) {
await prisma.playlistSong.updateMany({
where: { playlistId: id, songId: songIds[i] },
data: { position: i }
});
}
```
**Position Indexing**: 0-based integer positions for stable ordering
</details>
---
### Discovery (`/api/discover`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/discover/trending` | GET | No | Get trending songs by play count |
| `/discover/new-releases` | GET | No | Get newest public songs |
| `/discover/genres` | GET | No | Get all genres with song counts |
| `/discover/genres/{slug}` | GET | No | Get songs in specific genre |
---
### Labels (`/api/labels`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/labels` | POST | Yes | Create label profile |
| `/labels/{id}` | GET | No | Get label details with artists |
| `/labels/{id}` | PUT | Yes | Update label (owner only) |
| `/labels/{id}/stats` | GET | No | Get aggregate label statistics |
| `/labels/{id}/artists` | GET | No | Get all artists under label |
---
### Search (`/api/search`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/search?q={query}` | GET | No | Search songs, artists, albums |
---
### Sharing (`/api/share`)
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/share/song/{id}` | POST | No | Create shareable link for song |
| `/share/playlist/{id}` | POST | No | Create shareable link for playlist |
| `/share/album/{id}` | POST | No | Create shareable link for album |
| `/share/{token}` | GET | No | Resolve share link to content |
| `/share/{token}/click` | POST | No | Track share link click |
---
## Data Models
The database uses 10 core tables to represent the music platform.
### User
Base account entity for authentication and profile.
**Key Fields**:
- `id` (UUID): Primary key
- `email` (string): Unique, lowercase, used for login
- `username` (string): Unique, 3-20 characters
- `displayName` (string): Optional public name
- `passwordHash` (string): bcrypt hash
- `role` (enum): USER or ADMIN
**Relationships**:
- Has one Artist
- Has one Label
- Has many Playlists
- Has many Shares
<details>
<summary>🔧 Technical Details - User Schema</summary>
```prisma
model User {
id String @id @default(uuid())
email String @unique
username String @unique
displayName String?
passwordHash String
role Role @default(USER)
avatarUrl String?
bio String?
websiteUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
artist Artist?
label Label?
playlists Playlist[]
shares Share[]
}
enum Role {
USER
ADMIN
}
```
</details>
---
### Artist
Extended profile for musicians who upload content.
**Key Fields**:
- `id` (UUID): Primary key
- `userId` (UUID): Foreign key to User (one-to-one)
- `name` (string): Artist name
- `slug` (string): URL-friendly identifier
- `verified` (boolean): Official verification badge
- `labelId` (UUID): Optional foreign key to Label
**Relationships**:
- Belongs to User
- Belongs to Label (optional)
- Has many Songs
- Has many Albums
<details>
<summary>🔧 Technical Details - Artist Schema</summary>
```prisma
model Artist {
id String @id @default(uuid())
userId String @unique
name String
slug String @unique
bio String?
avatarUrl String?
verified Boolean @default(false)
websiteUrl String?
labelId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
label Label? @relation(fields: [labelId], references: [id])
songs Song[]
albums Album[]
@@index([slug])
@@index([labelId])
}
```
**Slug Generation**: Lowercase, hyphens for spaces, alphanumeric only
</details>
---
### Label
Organization that manages multiple artists.
**Key Fields**:
- `id` (UUID): Primary key
- `userId` (UUID): Foreign key to User (owner)
- `name` (string): Label name
- `slug` (string): URL-friendly identifier
- `description` (string): About the label
**Relationships**:
- Belongs to User (owner)
- Has many Artists
- Has many LabelInvitations
---
### Genre
Music category tags for organization and discovery.
**Key Fields**:
- `id` (UUID): Primary key
- `name` (string): Genre name
- `slug` (string): URL-friendly identifier
- `description` (string): Genre description
- `color` (string): Hex color code for UI
**Relationships**:
- Has many SongGenre (junction)
**Example Genres**: Rock, Jazz, Electronic, Hip-Hop, Classical, Pop
---
### Album
Collection of songs released together.
**Key Fields**:
- `id` (UUID): Primary key
- `artistId` (UUID): Foreign key to Artist
- `title` (string): Album title
- `releaseDate` (DateTime): Release date
- `albumType` (enum): ALBUM, EP, or SINGLE
- `coverImageUrl` (string): Album artwork URL
**Relationships**:
- Belongs to Artist
- Has many Songs
<details>
<summary>🔧 Technical Details - Album Schema</summary>
```prisma
model Album {
id String @id @default(uuid())
artistId String
title String
releaseDate DateTime
albumType AlbumType @default(ALBUM)
coverImageUrl String?
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
artist Artist @relation(fields: [artistId], references: [id])
songs Song[]
@@index([artistId])
@@index([releaseDate])
}
enum AlbumType {
ALBUM
EP
SINGLE
}
```
</details>
---
### Song
Individual audio track with metadata and analytics.
**Key Fields**:
- `id` (UUID): Primary key
- `artistId` (UUID): Foreign key to Artist
- `albumId` (UUID): Optional foreign key to Album
- `title` (string): Song title
- `audioUrl` (string): URL to audio file
- `duration` (number): Length in seconds
- `playCount` (number): Total plays across platform
- `isPublic` (boolean): Visibility control
**Relationships**:
- Belongs to Artist
- Belongs to Album (optional)
- Has many SongGenre (junction)
- Has many PlaylistSong (junction)
<details>
<summary>🔧 Technical Details - Song Schema</summary>
```prisma
model Song {
id String @id @default(uuid())
artistId String
albumId String?
title String
audioUrl String
coverImageUrl String?
duration Int // seconds
playCount Int @default(0)
isPublic Boolean @default(true)
description String?
lyrics String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
artist Artist @relation(fields: [artistId], references: [id])
album Album? @relation(fields: [albumId], references: [id])
genres SongGenre[]
playlists PlaylistSong[]
@@index([artistId])
@@index([albumId])
@@index([playCount])
@@index([createdAt])
}
```
**Indexes**: Optimized for queries on artist, album, play count (trending), and creation date (new releases)
</details>
---
### Playlist
User-created collection of songs.
**Key Fields**:
- `id` (UUID): Primary key
- `userId` (UUID): Foreign key to User (owner)
- `title` (string): Playlist name
- `description` (string): Optional description
- `isPublic` (boolean): Privacy control
- `coverImageUrl` (string): Playlist artwork URL
**Relationships**:
- Belongs to User
- Has many PlaylistSong (junction)
<details>
<summary>🔧 Technical Details - Playlist Schema</summary>
```prisma
model Playlist {
id String @id @default(uuid())
userId String
title String
description String?
isPublic Boolean @default(false)
coverImageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
songs PlaylistSong[]
@@index([userId])
}
model PlaylistSong {
id String @id @default(uuid())
playlistId String
songId String
position Int // For ordering
addedAt DateTime @default(now())
playlist Playlist @relation(fields: [playlistId], references: [id])
song Song @relation(fields: [songId], references: [id])
@@unique([playlistId, songId])
@@index([playlistId, position])
}
```
**Position**: 0-based integer for stable song ordering within playlist
</details>
---
### LabelInvitation
Invitation workflow for labels recruiting artists.
**Key Fields**:
- `id` (UUID): Primary key
- `labelId` (UUID): Foreign key to Label
- `artistId` (UUID): Foreign key to Artist
- `status` (enum): PENDING, ACCEPTED, DECLINED, EXPIRED
- `expiresAt` (DateTime): Invitation expiration
**Relationships**:
- Belongs to Label
- Belongs to Artist
<details>
<summary>🔧 Technical Details - Invitation Lifecycle</summary>
**States**:
1. **PENDING**: Initial state after creation
2. **ACCEPTED**: Artist accepts → artistId gets labelId assigned
3. **DECLINED**: Artist rejects invitation
4. **EXPIRED**: expiresAt passes without action
**Expiration**: Default 7 days from creation
</details>
---
### Share
Shareable content links with analytics.
**Key Fields**:
- `id` (UUID): Primary key
- `type` (enum): SONG, PLAYLIST, or ALBUM
- `targetId` (UUID): ID of shared content
- `token` (string): Unique 10-character code
- `clickCount` (number): Tracking metric
- `userId` (UUID): Optional creator
**Relationships**:
- Belongs to User (optional)
<details>
<summary>🔧 Technical Details - Share Schema</summary>
```prisma
model Share {
id String @id @default(uuid())
type ShareType
targetId String
token String @unique
userId String?
clickCount Int @default(0)
createdAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id])
@@index([token])
@@index([targetId])
}
enum ShareType {
SONG
PLAYLIST
ALBUM
}
```
**Token Generation**: `nanoid(10)` - URL-safe, lowercase + numbers
</details>
---
## Entity Relationship Diagram
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ USER │──────<│ ARTIST │──────<│ SONG │
│ │ 1:1 │ │ 1:N │ │
│ - id │ │ - id │ │ - id │
│ - email │ │ - userId │ │ - artistId │
│ - username │ │ - name │ │ - title │
│ - password │ │ - slug │ │ - audioUrl │
│ - role │ │ - verified │ │ - playCount │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ 1:1 │ N:1 │ N:M
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ LABEL │ │ ALBUM │ │ SONGGENRE │
│ │ │ │ │ (junction) │
│ - id │ │ - id │ │ │
│ - userId │ │ - artistId │ │ - songId │
│ - name │──────<│ - title │ │ - genreId │
│ - slug │ 1:N │ - releaseDate│ └──────┬───────┘
└──────┬───────┘ └──────┬───────┘ │ N:1
│ │ │
│ 1:N │ 1:N ▼
│ │ ┌──────────────┐
▼ │ │ GENRE │
┌──────────────┐ │ │ │
│ LABELINVITE │ │ │ - id │
│ │ │ │ - name │
│ - labelId │ │ │ - slug │
│ - artistId │ │ │ - color │
│ - status │ │ └──────────────┘
└──────────────┘ │
┌──────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PLAYLIST │──────<│ PLAYLISTSONG │>──────│ SONG │
│ │ 1:N │ (junction) │ N:1 │ (see above) │
│ - id │ │ │ │ │
│ - userId │ │ - playlistId │ └──────────────┘
│ - title │ │ - songId │
│ - isPublic │ │ - position │
└──────────────┘ └──────────────┘
┌──────────────┐
│ SHARE │
│ │
│ - id │
│ - type │──── References SONG | PLAYLIST | ALBUM
│ - targetId │
│ - token │
│ - clickCount │
└──────────────┘
```
---
## Components
The UI is built from 20 reusable React components organized by function.
### Audio Components
#### AudioPlayer
Fixed bottom player that follows across all pages.
**Features**:
- Song info display (title, artist, cover)
- Play/pause, next/previous buttons
- Progress bar with seek functionality
- Volume slider and mute toggle
- Current time / total duration display
**Location**: `components/AudioPlayer.tsx`
<details>
<summary>🔧 Technical Details - AudioPlayer Implementation</summary>
**State Management**:
```typescript
interface PlayerState {
currentSong: Song | null;
isPlaying: boolean;
currentTime: number;
duration: number;
volume: number;
isMuted: boolean;
queue: Song[];
queueIndex: number;
repeatMode: "off" | "one" | "all";
isShuffled: boolean;
}
```
**Audio Element**: HTML5 `<audio>` element controlled by React state
**Time Updates**: `ontimeupdate` event updates progress bar every ~250ms
</details>
---
#### PlayerControls
Reusable control buttons for audio playback.
**Buttons**:
- Play/pause toggle
- Previous track
- Next track
- Shuffle toggle
- Repeat cycle (off → all → one)
**Location**: `components/PlayerControls.tsx`
---
#### WaveformDisplay
Visual audio waveform with interactive seeking.
**Features**:
- Canvas-based rendering
- Progress indicator overlay
- Click-to-seek functionality
- Responsive width
**Location**: `components/WaveformDisplay.tsx`
<details>
<summary>🔧 Technical Details - Waveform Rendering</summary>
**Canvas Drawing**:
1. Parse audio amplitude data (Web Audio API)
2. Draw vertical bars for each sample
3. Color code: played (blue), unplayed (gray)
4. Update on time change
**Performance**: Debounced redraw to prevent excessive rendering
</details>
---
### Card Components
Display content previews in grid layouts.
#### SongCard
Shows song thumbnail, title, artist, duration, play count.
#### AlbumCard
Shows album art, title, artist, release year, track count.
#### ArtistCard
Shows circular avatar, artist name, verified badge.
#### PlaylistCard
Shows playlist cover, title, song count, privacy indicator.
#### LabelCard
Shows label logo, name, artist count.
#### GenreBadge
Clickable colored badge with genre name.
**All card locations**: `components/*.tsx`
---
### Form Components
#### AuthForm
Unified form for login, register, password reset modes.
**Fields**:
- Email (all modes)
- Password (login, register)
- Username (register only)
- Display name (register, optional)
**Location**: `components/AuthForm.tsx`
---
#### UploadForm
Multi-field form for song uploads.
**Fields**:
- Title (required)
- Audio file URL (required)
- Cover image URL (optional)
- Duration in seconds (required)
- Album selection (dropdown)
- Genre tags (multi-select)
- Description (textarea)
- Lyrics (textarea)
- Public/private toggle
**Location**: `components/UploadForm.tsx`
---
#### ProfileForm
Edit user profile data.
**Fields**:
- Display name
- Email (read-only)
- Bio (textarea)
- Website URL
- Avatar URL
**Location**: `components/ProfileForm.tsx`
---
#### CreatePlaylistModal
Modal dialog for new playlist creation.
**Fields**:
- Title (required)
- Description (optional)
- Public/private toggle
- Cover image URL (optional)
**Location**: `components/CreatePlaylistModal.tsx`
---
### Navigation Components
#### Header
Fixed top navigation bar.
**Elements**:
- Logo (link to home)
- Nav links (Discover, Genres, Artists, Labels)
- Search bar
- User menu (if logged in)
- Login button (if logged out)
- Mobile menu toggle
**Location**: `components/Header.tsx`
---
#### NavLink
Navigation link with active state.
**Features**:
- Highlights current page
- Uses Next.js router for path matching
**Location**: `components/NavLink.tsx`
---
#### UserMenu
Dropdown menu for authenticated users.
**Links**:
- Profile
- Upload Song
- My Playlists
- Logout
**Location**: `components/UserMenu.tsx`
---
#### SearchBar
Real-time search input with suggestions.
**Features**:
- Debounced input (300ms)
- Dropdown with results preview
- Loading indicator
- Keyboard navigation (arrow keys, enter)
**Location**: `components/SearchBar.tsx`
<details>
<summary>🔧 Technical Details - Search Debouncing</summary>
```typescript
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
// Debounce search API calls
useEffect(() => {
const timer = setTimeout(async () => {
if (query.length >= 2) {
const data = await fetch(`/api/search?q=${query}`);
setResults(data);
}
}, 300);
return () => clearTimeout(timer);
}, [query]);
```
</details>
---
### Sharing Components
#### ShareButton
Button that generates shareable links.
**Actions**:
1. Click button
2. Generate share token (POST to `/api/share/*`)
3. Open ShareModal with link
**Location**: `components/ShareButton.tsx`
---
#### ShareModal
Modal displaying shareable link.
**Features**:
- Copy-to-clipboard button
- QR code (optional)
- Social media share buttons
- Click count display
**Location**: `components/ShareModal.tsx`
---
### Display Components
#### TrackList
Scrollable list of songs with metadata.
**Columns**:
- Position number
- Cover image
- Title
- Artist name
- Duration
**Actions**:
- Click to play
- Right-click context menu (add to playlist, share)
**Location**: `components/TrackList.tsx`
---
## Glossary
**API Endpoint**: A specific URL where a program can request data or send commands. Like an address where your app talks to the server.
**Album**: A collection of songs released together, like a CD or digital release.
**Artist**: A musician or band that creates and uploads music to the platform.
**Authentication**: The process of verifying who you are (usually with email and password).
**bcryptjs**: A library that scrambles passwords so they can't be read if the database is stolen.
**Cover Image**: The picture displayed for a song, album, or playlist.
**Duration**: How long a song is in minutes and seconds.
**EP**: Extended Play - a short music release with 3-7 tracks (less than an album, more than a single).
**Genre**: A category of music like Rock, Jazz, Hip-Hop, or Electronic.
**JWT (JSON Web Token)**: A secure way to remember that you're logged in without storing passwords. Like a temporary pass that expires after 7 days.
**Label**: A music organization that manages multiple artists (like a record company).
**ORM (Object-Relational Mapping)**: A tool that lets programmers work with databases using normal code instead of SQL queries. Prisma is the ORM used here.
**Play Count**: The total number of times a song has been played by all users.
**Playlist**: A custom collection of songs you create (like making a mixtape).
**Prisma**: The database tool used to manage data in this project. It ensures type safety and generates database queries automatically.
**Public/Private**: Public content is visible to everyone; private content is only visible to you.
**REST API**: A standard way for programs to communicate over the internet using HTTP requests (GET, POST, PUT, DELETE).
**Share Token**: A unique short code (like `aBc123XyZ`) used to create shareable links.
**Slug**: A URL-friendly version of a name. For example, "The Beatles" becomes "the-beatles" in URLs.
**SQLite**: A lightweight database stored in a single file. Good for development and small-to-medium projects.
**TypeScript**: A version of JavaScript that checks for errors before the code runs, making development safer.
**UUID (Universally Unique Identifier)**: A long random ID like `550e8400-e29b-41d4-a716-446655440000` that ensures every record is unique.
**Verified Badge**: A checkmark showing an artist is officially recognized (like Twitter's blue checkmark).
**Waveform**: A visual representation of audio that shows the loudness of a song over time. Looks like a series of vertical bars.
---
## Project Statistics
| Metric | Count |
|--------|-------|
| Total Files | 107 |
| TypeScript Files | 107 |
| API Endpoints | 40 |
| Database Models | 10 |
| Pages | 13 |
| UI Components | 20 |
| Type Definition Files | 4 |
| Utility Library Files | 3 |
---
## Development Workflow
This project uses the **Guardrail Workflow System** to enforce a structured development process.
### Workflow Phases
1. **Design Phase**: Plan changes and update manifest
2. **Approval Gate**: Review and approve design
3. **Implementation Phase**: Write code following approved design
4. **Review Phase**: Quality checks and validation
5. **Approval Gate**: Final approval before completion
### Quick Commands
```bash
# Start a new feature workflow
/workflow:spawn add user notifications
# Check current workflow status
/workflow:status
# Approve current phase
/workflow:approve
# Generate documentation
/eureka:index
```
See `CLAUDE.md` for full workflow documentation.
---
## Next Steps
### For New Users
1. Explore the codebase structure in the sections above
2. Run the development server and test the UI
3. Read API endpoint documentation for integration
4. Review data models to understand relationships
### For Contributors
1. Read the workflow documentation in `CLAUDE.md`
2. Use `/workflow:spawn` to start new features
3. Follow the design-approve-implement cycle
4. Run `/workflow:review` before submitting changes
### For Operators
1. Review deployment requirements
2. Configure environment variables (JWT secret, database URL)
3. Set up file storage for audio uploads
4. Consider PostgreSQL migration for production scale
---
**Generated by Eureka Documentation System**
**Last Updated**: 2025-12-18