259 lines
8.5 KiB
Markdown
259 lines
8.5 KiB
Markdown
# Sonic Cloud - Quick Reference Card
|
|
|
|
## Commands
|
|
|
|
| Command | Purpose |
|
|
|---------|---------|
|
|
| `npm run dev` | Start development server (port 3000) |
|
|
| `npm run build` | Build production bundle |
|
|
| `npm start` | Start production server |
|
|
| `npm run lint` | Run ESLint code checks |
|
|
| `npx prisma studio` | Open database GUI |
|
|
| `npx prisma migrate dev` | Create and apply migrations |
|
|
| `npx prisma generate` | Regenerate Prisma client |
|
|
| `npx prisma db push` | Push schema to database |
|
|
|
|
## Tech Stack
|
|
|
|
| Layer | Technology |
|
|
|-------|-----------|
|
|
| Language | TypeScript 5 |
|
|
| Framework | Next.js 16.0.10 (App Router) |
|
|
| UI Library | React 19.2.1 |
|
|
| Styling | Tailwind CSS 4 |
|
|
| Database | SQLite (Prisma ORM 5.22.0) |
|
|
| Authentication | JWT + bcryptjs |
|
|
|
|
## Key Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `app/layout.tsx` | Root layout with Header, AudioPlayer |
|
|
| `app/page.tsx` | Homepage with trending/new releases |
|
|
| `lib/auth.ts` | JWT utilities (verifyToken, hashPassword) |
|
|
| `lib/db.ts` | Prisma client singleton |
|
|
| `lib/sharing.ts` | Share link generation |
|
|
| `prisma/schema.prisma` | Database schema (10 models) |
|
|
| `types/index.ts` | Core TypeScript types |
|
|
| `project_manifest.json` | Guardrail entity definitions |
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Required | Purpose |
|
|
|----------|----------|---------|
|
|
| `DATABASE_URL` | Yes | SQLite file path |
|
|
| `JWT_SECRET` | Yes | Token signing secret |
|
|
| `NEXT_PUBLIC_API_URL` | Optional | API base URL |
|
|
|
|
## API Endpoints (40 total)
|
|
|
|
### Authentication
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| POST | `/api/auth/register` | No | Register new user |
|
|
| POST | `/api/auth/login` | No | Login and get token |
|
|
| POST | `/api/auth/forgot-password` | No | Request reset token |
|
|
| POST | `/api/auth/reset-password` | No | Reset with token |
|
|
|
|
### Users
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| GET | `/api/users/me` | Yes | Get current user |
|
|
| PUT | `/api/users/me` | Yes | Update profile |
|
|
|
|
### Artists
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| POST | `/api/artists` | Yes | Create artist |
|
|
| GET | `/api/artists/{id}` | No | Get artist details |
|
|
| PUT | `/api/artists/{id}` | Yes | Update artist (owner) |
|
|
| GET | `/api/artists/{id}/songs` | No | List artist songs |
|
|
| GET | `/api/artists/{id}/albums` | No | List artist albums |
|
|
|
|
### Songs
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| POST | `/api/songs/upload` | Yes | Upload song (artist) |
|
|
| GET | `/api/songs/{id}` | No | Get song details |
|
|
| PUT | `/api/songs/{id}` | Yes | Update song (owner) |
|
|
| DELETE | `/api/songs/{id}` | Yes | Delete song (owner) |
|
|
| POST | `/api/songs/{id}/play` | No | Increment play count |
|
|
|
|
### Albums
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| POST | `/api/albums` | Yes | Create album (artist) |
|
|
| GET | `/api/albums/{id}` | No | Get album with songs |
|
|
| PUT | `/api/albums/{id}` | Yes | Update album (owner) |
|
|
| DELETE | `/api/albums/{id}` | Yes | Delete album (owner) |
|
|
|
|
### Playlists
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| GET | `/api/playlists` | Yes | List user playlists |
|
|
| POST | `/api/playlists` | Yes | Create playlist |
|
|
| GET | `/api/playlists/{id}` | No | Get playlist (respects privacy) |
|
|
| PUT | `/api/playlists/{id}` | Yes | Update playlist (owner) |
|
|
| DELETE | `/api/playlists/{id}` | Yes | Delete playlist (owner) |
|
|
| POST | `/api/playlists/{id}/songs` | Yes | Add song to playlist |
|
|
| PUT | `/api/playlists/{id}/reorder` | Yes | Reorder songs |
|
|
|
|
### Discovery
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| GET | `/api/discover/trending` | No | Trending songs (by plays) |
|
|
| GET | `/api/discover/new-releases` | No | Recent songs (by date) |
|
|
| GET | `/api/discover/genres` | No | All genres with counts |
|
|
| GET | `/api/discover/genres/{slug}` | No | Songs by genre |
|
|
|
|
### Labels
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| POST | `/api/labels` | Yes | Create label |
|
|
| GET | `/api/labels/{id}` | No | Get label details |
|
|
| PUT | `/api/labels/{id}` | Yes | Update label (owner) |
|
|
| GET | `/api/labels/{id}/stats` | No | Label statistics |
|
|
| GET | `/api/labels/{id}/artists` | No | Label artists |
|
|
|
|
### Search & Sharing
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| GET | `/api/search` | No | Search songs/artists/albums |
|
|
| POST | `/api/share/song/{id}` | No | Create song share link |
|
|
| POST | `/api/share/playlist/{id}` | No | Create playlist share link |
|
|
| POST | `/api/share/album/{id}` | No | Create album share link |
|
|
| GET | `/api/share/{token}` | No | Resolve share link |
|
|
| POST | `/api/share/{token}/click` | No | Track share clicks |
|
|
|
|
## Common Patterns
|
|
|
|
### Authentication Flow
|
|
```typescript
|
|
// 1. Login → get token
|
|
const res = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ email, password })
|
|
});
|
|
const { token } = await res.json();
|
|
|
|
// 2. Store token
|
|
localStorage.setItem('auth_token', token);
|
|
|
|
// 3. Use in authenticated requests
|
|
fetch('/api/users/me', {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
});
|
|
```
|
|
|
|
### Data Fetching (Client Component)
|
|
```typescript
|
|
// Using fetch with auth
|
|
const [data, setData] = useState(null);
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem('auth_token');
|
|
fetch('/api/endpoint', {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => res.json())
|
|
.then(setData);
|
|
}, []);
|
|
```
|
|
|
|
### Error Handling
|
|
```typescript
|
|
// API returns { error: "message" } or { data: ... }
|
|
const res = await fetch('/api/endpoint');
|
|
const json = await res.json();
|
|
|
|
if (!res.ok || json.error) {
|
|
// Handle error
|
|
console.error(json.error);
|
|
return;
|
|
}
|
|
|
|
// Use json.data
|
|
```
|
|
|
|
### Ownership Checks (Backend)
|
|
```typescript
|
|
// lib/auth.ts exports verifyToken(request)
|
|
const user = verifyToken(request);
|
|
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
|
|
// Check ownership
|
|
const entity = await prisma.entity.findUnique({ where: { id } });
|
|
if (entity.userId !== user.id) {
|
|
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
|
}
|
|
```
|
|
|
|
## Database Models (10)
|
|
|
|
| Model | Key Relations | Auth |
|
|
|-------|---------------|------|
|
|
| User | → Artist (1), Label (1), Playlist (*), Share (*) | Base |
|
|
| Artist | → User (1), Label (1), Song (*), Album (*) | User role |
|
|
| Label | → User (1), Artist (*), LabelInvitation (*) | User role |
|
|
| Song | → Artist (1), Album (1), Genre (*), Playlist (*) | Public/Private |
|
|
| Album | → Artist (1), Song (*) | Public |
|
|
| Playlist | → User (1), Song (*) | Public/Private |
|
|
| Genre | → Song (*) | Public |
|
|
| Share | → User (1) | Token-based |
|
|
| LabelInvitation | → Label (1), Artist (1) | Status-based |
|
|
| SongGenre | → Song (1), Genre (1) | Join table |
|
|
|
|
## Component Categories
|
|
|
|
| Category | Components | Count |
|
|
|----------|-----------|-------|
|
|
| Audio | AudioPlayer, PlayerControls, WaveformDisplay | 3 |
|
|
| Cards | SongCard, AlbumCard, ArtistCard, PlaylistCard, LabelCard, GenreBadge | 6 |
|
|
| Forms | AuthForm, UploadForm, ProfileForm, CreatePlaylistModal | 4 |
|
|
| Navigation | Header, NavLink, UserMenu, SearchBar | 4 |
|
|
| Sharing | ShareButton, ShareModal | 2 |
|
|
| Display | TrackList | 1 |
|
|
|
|
## Workflow Commands
|
|
|
|
| Command | Purpose |
|
|
|---------|---------|
|
|
| `/workflow:spawn` | Start automated workflow |
|
|
| `/workflow:status` | Check current state |
|
|
| `/workflow:approve` | Approve current gate |
|
|
| `/workflow:reject` | Reject with feedback |
|
|
| `/workflow:resume` | Resume interrupted workflow |
|
|
| `/guardrail:init` | Initialize manifest (manual) |
|
|
| `/guardrail:design` | Add entities (manual) |
|
|
| `/guardrail:implement` | Implement entities (manual) |
|
|
| `/eureka:index` | Generate documentation |
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
sonic-cloud/
|
|
├── app/ # Next.js App Router (pages + API)
|
|
│ ├── api/ # REST API endpoints (30 files)
|
|
│ └── [pages]/ # Frontend pages (13 pages)
|
|
├── components/ # React components (36 files)
|
|
├── lib/ # Utilities (auth, db, sharing)
|
|
├── types/ # TypeScript definitions (4 files)
|
|
├── prisma/ # Database schema
|
|
├── .workflow/ # Guardrail workflow state
|
|
├── .claude/ # Claude commands
|
|
└── skills/ # Custom skills (orchestrator, docs)
|
|
```
|
|
|
|
## Statistics
|
|
|
|
- **Total Files**: 107 TypeScript files
|
|
- **API Endpoints**: 40 (REST)
|
|
- **Database Models**: 10 (Prisma)
|
|
- **Pages**: 13 (App Router)
|
|
- **Components**: 20 (reusable)
|
|
- **LOC**: ~15,000 lines
|
|
|
|
---
|
|
|
|
**Generated by Eureka Documentation Pipeline** | Sonic Cloud v0.1.0
|