40 KiB
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
# Install dependencies
npm install
# Set up database
npx prisma generate
npx prisma db push
# Start development server
npm run dev
Open http://localhost:3000 in your browser.
First Steps
- Register an account at
/register - Become an artist by creating an artist profile
- Upload your first song using the upload form
- Create a playlist to organize music you love
- 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 |
🔧 Technical Details - Framework Decisions
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
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.
🔧 Technical Details - Song Upload Flow
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:
- Validate JWT token → get authenticated user
- Verify user owns the artist profile
- Create Song record in database
- Create SongGenre junction records
- Return song with ID and metadata
Validation:
- Duration must be > 0
- Audio URL format validation
- Genre slugs must exist in Genre table
Organize Albums
Group songs into albums, EPs, or singles. Each album has a release date, cover art, and track listing that you control.
🔧 Technical Details - Album Structure
// 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
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.
🔧 Technical Details - Play Tracking
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
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.
🔧 Technical Details - Label Stats API
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
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.
🔧 Technical Details - Discovery Queries
// 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;
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.
🔧 Technical Details - Search Implementation
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
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.
🔧 Technical Details - Share Link Generation
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.
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 |
🔧 Technical Details - Authentication Flow
Registration:
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:
POST /api/auth/login
Body: {
email: string,
password: string
}
Response: {
user: { id, email, username, role },
token: string
}
JWT Payload:
{
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
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 |
🔧 Technical Details - Artist Endpoints
Create Artist:
POST /api/artists
Body: {
name: string,
bio?: string,
avatarUrl?: string,
websiteUrl?: string
}
// Automatically generates slug from name
// Associates with authenticated userId
Get Artist:
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
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 |
🔧 Technical Details - Playlist Reordering
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
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 keyemail(string): Unique, lowercase, used for loginusername(string): Unique, 3-20 charactersdisplayName(string): Optional public namepasswordHash(string): bcrypt hashrole(enum): USER or ADMIN
Relationships:
- Has one Artist
- Has one Label
- Has many Playlists
- Has many Shares
🔧 Technical Details - User Schema
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
}
Artist
Extended profile for musicians who upload content.
Key Fields:
id(UUID): Primary keyuserId(UUID): Foreign key to User (one-to-one)name(string): Artist nameslug(string): URL-friendly identifierverified(boolean): Official verification badgelabelId(UUID): Optional foreign key to Label
Relationships:
- Belongs to User
- Belongs to Label (optional)
- Has many Songs
- Has many Albums
🔧 Technical Details - Artist Schema
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
Label
Organization that manages multiple artists.
Key Fields:
id(UUID): Primary keyuserId(UUID): Foreign key to User (owner)name(string): Label nameslug(string): URL-friendly identifierdescription(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 keyname(string): Genre nameslug(string): URL-friendly identifierdescription(string): Genre descriptioncolor(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 keyartistId(UUID): Foreign key to Artisttitle(string): Album titlereleaseDate(DateTime): Release datealbumType(enum): ALBUM, EP, or SINGLEcoverImageUrl(string): Album artwork URL
Relationships:
- Belongs to Artist
- Has many Songs
🔧 Technical Details - Album Schema
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
}
Song
Individual audio track with metadata and analytics.
Key Fields:
id(UUID): Primary keyartistId(UUID): Foreign key to ArtistalbumId(UUID): Optional foreign key to Albumtitle(string): Song titleaudioUrl(string): URL to audio fileduration(number): Length in secondsplayCount(number): Total plays across platformisPublic(boolean): Visibility control
Relationships:
- Belongs to Artist
- Belongs to Album (optional)
- Has many SongGenre (junction)
- Has many PlaylistSong (junction)
🔧 Technical Details - Song Schema
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)
Playlist
User-created collection of songs.
Key Fields:
id(UUID): Primary keyuserId(UUID): Foreign key to User (owner)title(string): Playlist namedescription(string): Optional descriptionisPublic(boolean): Privacy controlcoverImageUrl(string): Playlist artwork URL
Relationships:
- Belongs to User
- Has many PlaylistSong (junction)
🔧 Technical Details - Playlist Schema
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
LabelInvitation
Invitation workflow for labels recruiting artists.
Key Fields:
id(UUID): Primary keylabelId(UUID): Foreign key to LabelartistId(UUID): Foreign key to Artiststatus(enum): PENDING, ACCEPTED, DECLINED, EXPIREDexpiresAt(DateTime): Invitation expiration
Relationships:
- Belongs to Label
- Belongs to Artist
🔧 Technical Details - Invitation Lifecycle
States:
- PENDING: Initial state after creation
- ACCEPTED: Artist accepts → artistId gets labelId assigned
- DECLINED: Artist rejects invitation
- EXPIRED: expiresAt passes without action
Expiration: Default 7 days from creation
Share
Shareable content links with analytics.
Key Fields:
id(UUID): Primary keytype(enum): SONG, PLAYLIST, or ALBUMtargetId(UUID): ID of shared contenttoken(string): Unique 10-character codeclickCount(number): Tracking metricuserId(UUID): Optional creator
Relationships:
- Belongs to User (optional)
🔧 Technical Details - Share Schema
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
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
🔧 Technical Details - AudioPlayer Implementation
State Management:
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
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
🔧 Technical Details - Waveform Rendering
Canvas Drawing:
- Parse audio amplitude data (Web Audio API)
- Draw vertical bars for each sample
- Color code: played (blue), unplayed (gray)
- Update on time change
Performance: Debounced redraw to prevent excessive rendering
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
🔧 Technical Details - Search Debouncing
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]);
Sharing Components
ShareButton
Button that generates shareable links.
Actions:
- Click button
- Generate share token (POST to
/api/share/*) - 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
- Design Phase: Plan changes and update manifest
- Approval Gate: Review and approve design
- Implementation Phase: Write code following approved design
- Review Phase: Quality checks and validation
- Approval Gate: Final approval before completion
Quick Commands
# 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
- Explore the codebase structure in the sections above
- Run the development server and test the UI
- Read API endpoint documentation for integration
- Review data models to understand relationships
For Contributors
- Read the workflow documentation in
CLAUDE.md - Use
/workflow:spawnto start new features - Follow the design-approve-implement cycle
- Run
/workflow:reviewbefore submitting changes
For Operators
- Review deployment requirements
- Configure environment variables (JWT secret, database URL)
- Set up file storage for audio uploads
- Consider PostgreSQL migration for production scale
Generated by Eureka Documentation System Last Updated: 2025-12-18