docs: Improve CLAUDE.md with project documentation
Add comprehensive guidance for Claude Code working in this repository: - Alembic config location clarification - Docker Compose scope (database services only) - Frontend serving explanation (FastAPI root route) - Tailwind config location (in HTML script tag) - Pytest async configuration details Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -0,0 +1,384 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**AudiOhm** is a music streaming application (Spotify alternative) with YouTube audio streaming. The architecture consists of:
|
||||
|
||||
- **Backend**: FastAPI (Python 3.13+) with async SQLAlchemy, PostgreSQL, and Redis
|
||||
- **Frontend**: Pure HTML/JavaScript with Tailwind CSS (no framework - served directly by FastAPI)
|
||||
- **Streaming**: YouTube audio via yt-dlp
|
||||
- **Authentication**: JWT-based auth
|
||||
|
||||
The project uses a service-oriented architecture with clear separation between API routes, business logic (services), data models, and schemas.
|
||||
|
||||
### File Locations
|
||||
|
||||
- **Backend code**: `/opt/audiOhm/backend/app/`
|
||||
- **Frontend code**: `/opt/audiOhm/backend/app/static/` and `/opt/audiOhm/backend/app/templates/`
|
||||
- **Tests**: `/opt/audiOhm/backend/tests/`
|
||||
- **Migrations**: `/opt/audiOhm/backend/alembic/` (config at `/opt/audiOhm/backend/alembic.ini`)
|
||||
- **Working directory for all commands**: `/opt/audiOhm/backend/`
|
||||
|
||||
---
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Backend Development
|
||||
|
||||
```bash
|
||||
# Start backend with hot-reload (from /opt/audiOhm/backend)
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
|
||||
# Or using the module directly
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
```
|
||||
|
||||
### Database Operations
|
||||
|
||||
```bash
|
||||
# From /opt/audiOhm/backend
|
||||
|
||||
# Run migrations
|
||||
alembic upgrade head
|
||||
|
||||
# Create a new migration
|
||||
alembic revision --autogenerate -m "description"
|
||||
|
||||
# Rollback one migration
|
||||
alembic downgrade -1
|
||||
|
||||
# View migration history
|
||||
alembic history
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# From /opt/audiOhm/backend
|
||||
|
||||
# Run all tests
|
||||
pytest
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/api/test_auth.py
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=app tests/
|
||||
|
||||
# Run specific test function
|
||||
pytest tests/api/test_library.py::test_get_liked_tracks -v
|
||||
```
|
||||
|
||||
### Docker Services (PostgreSQL + Redis)
|
||||
|
||||
**Note**: Docker Compose is only for database services (PostgreSQL + Redis), not the FastAPI backend.
|
||||
|
||||
```bash
|
||||
# From /opt/audiOhm (not /opt/audiOhm/backend)
|
||||
docker-compose -f docker/docker-compose.yml up -d
|
||||
|
||||
# Stop services
|
||||
docker-compose -f docker/docker-compose.yml down
|
||||
|
||||
# View logs
|
||||
docker-compose -f docker/docker-compose.yml logs -f postgres
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
# From /opt/audiOhm/backend
|
||||
|
||||
# Format code
|
||||
black app/ tests/
|
||||
|
||||
# Lint code
|
||||
ruff check app/ tests/
|
||||
|
||||
# Type checking
|
||||
mypy app/
|
||||
```
|
||||
|
||||
### Frontend Development
|
||||
|
||||
The frontend is a single-page application (SPA) with no build process:
|
||||
|
||||
- **HTML**: `app/templates/index.html` - All views in one file, served directly by FastAPI at root route `/`
|
||||
- **JavaScript**: `app/static/js/app.js` - All frontend logic
|
||||
- **CSS**: `app/static/css/style.css` - Minimal custom CSS (Tailwind is primary)
|
||||
- **Tailwind Config**: Defined in `<script>` tag within `index.html` (no separate config file)
|
||||
- **No build step**: Edit files directly and refresh browser
|
||||
|
||||
After editing frontend files:
|
||||
- Hard refresh browser: `Ctrl+Shift+R` (Linux/Windows) or `Cmd+Shift+R` (Mac)
|
||||
- Clear browser cache if needed
|
||||
- Check browser console for errors
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Backend Structure
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── api/
|
||||
│ ├── dependencies.py # FastAPI dependencies (DB session, auth)
|
||||
│ └── v1/ # API route handlers (auth, music, playlists, library)
|
||||
├── core/
|
||||
│ ├── config.py # Pydantic Settings with environment variables
|
||||
│ ├── database.py # SQLAlchemy async engine and session management
|
||||
│ ├── security.py # JWT, password hashing
|
||||
│ └── rate_limiter.py # Rate limiting with slowapi
|
||||
├── models/ # SQLAlchemy ORM models (user, track, artist, album, playlist, etc.)
|
||||
├── schemas/ # Pydantic schemas for request/response validation
|
||||
├── services/ # Business logic layer
|
||||
│ ├── auth_service.py
|
||||
│ ├── music_service.py
|
||||
│ ├── youtube_service.py
|
||||
│ ├── playlist_service.py
|
||||
│ └── library_service.py
|
||||
├── static/
|
||||
│ ├── css/style.css # Minimal custom CSS (Tailwind is primary)
|
||||
│ └── js/app.js # All frontend JavaScript (SPA functionality)
|
||||
└── templates/
|
||||
└── index.html # Single-page app HTML with Tailwind classes
|
||||
```
|
||||
|
||||
### Key Architecture Patterns
|
||||
|
||||
1. **Dependency Injection**: FastAPI's `Depends()` for database sessions (`DBSession`) and authentication (`CurrentUser`)
|
||||
2. **Service Layer**: All business logic in `/services` - API routes are thin, delegating to services
|
||||
3. **Repository Pattern**: Services handle database operations using async SQLAlchemy
|
||||
4. **Async/Await**: All database operations and external API calls are async
|
||||
5. **JWT Authentication**: Token-based auth with `get_current_user` dependency
|
||||
|
||||
### Type Aliases (from `app/api/dependencies.py`)
|
||||
|
||||
Important type aliases used throughout the codebase:
|
||||
- `DBSession` = `Annotated[AsyncSession, Depends(get_db)]` - Inject database session
|
||||
- `CurrentUser` = `Annotated[User, Depends(get_current_user)]` - Inject authenticated user
|
||||
- `CurrentUserOptional` = `Annotated[Optional[User], Depends(get_current_user_optional)]` - Inject user if authenticated
|
||||
- `AuthServiceDep` = `Annotated[AuthService, Depends(get_auth_service)]` - Inject auth service
|
||||
|
||||
Use these in route signatures for clean dependency injection:
|
||||
```python
|
||||
async def my_endpoint(
|
||||
db: DBSession,
|
||||
current_user: CurrentUser,
|
||||
) -> dict:
|
||||
# db and current_user are automatically injected
|
||||
```
|
||||
|
||||
### Database Models
|
||||
|
||||
Key relationships:
|
||||
- `User` → `Playlist` (one-to-many)
|
||||
- `User` → `LikedTrack` (one-to-many)
|
||||
- `User` → `ListeningHistory` (one-to-many)
|
||||
- `Playlist` → `PlaylistTrack` → `Track` (many-to-many through join table)
|
||||
- `Track` → `Artist` (many-to-one)
|
||||
- `Track` → `Album` (many-to-one)
|
||||
|
||||
### Frontend Architecture
|
||||
|
||||
- **Single Page Application**: All views are in `index.html`, shown/hidden via JavaScript
|
||||
- **State Management**: `AppState` object in `app.js` holds current state (track, queue, user, etc.)
|
||||
- **DOM Elements**: `DOM` object caches all DOM references for performance
|
||||
- **API Communication**: `fetch()` calls to `/api/v1/*` endpoints
|
||||
- **Styling**: Tailwind CSS utility classes with custom theme extension (defined in `<script>` in `index.html`)
|
||||
- **No Framework**: Pure vanilla JavaScript with DOM manipulation
|
||||
- **Window Assignment**: All functions called from HTML `onclick` handlers must be assigned to `window` object
|
||||
|
||||
Key frontend functions:
|
||||
- `showPage(pageId)` - Switch between views (home, search, library, etc.)
|
||||
- `updatePlayerUI()` - Update player display
|
||||
- `showToast(message, type)` - Show notification to user
|
||||
- All functions that interact with `AppState`
|
||||
|
||||
---
|
||||
|
||||
## Important Implementation Details
|
||||
|
||||
### Database Session Management
|
||||
|
||||
All database sessions are async and automatically committed/rolled back:
|
||||
|
||||
```python
|
||||
# In API routes
|
||||
async def my_endpoint(db: DBSession):
|
||||
result = await db.execute(select(User))
|
||||
return result.scalars().all()
|
||||
# Session auto-commits after successful handler
|
||||
```
|
||||
|
||||
### Authentication Flow
|
||||
|
||||
1. User receives JWT token on login (`/api/v1/auth/login`)
|
||||
2. Token stored in `localStorage` (`authToken`)
|
||||
3. All API requests include `Authorization: Bearer <token>` header
|
||||
4. `get_current_user` dependency validates token and injects user
|
||||
|
||||
### YouTube Integration
|
||||
|
||||
- `YouTubeService` handles YouTube search and stream URL extraction
|
||||
- Audio streamed via `/api/v1/music/youtube/{video_id}/stream` endpoint
|
||||
- Uses yt-dlp for URL extraction (not downloading)
|
||||
|
||||
### Frontend Routing
|
||||
|
||||
Client-side routing in `app.js`:
|
||||
- `showPage(pageId)` function switches between views (home, search, library, etc.)
|
||||
- No server-side routing for pages (only API endpoints)
|
||||
- State persists across navigation
|
||||
|
||||
**How the SPA is served**: FastAPI's root route `/` (in `app/main.py`) serves `index.html`, which contains the entire single-page application. All other routes are API endpoints under `/api/v1/`.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables (.env in backend/)
|
||||
|
||||
Key variables (see `app/core/config.py` for full list):
|
||||
|
||||
```bash
|
||||
# Database
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_USER=spotify
|
||||
POSTGRES_PASSWORD=spotify_password
|
||||
POSTGRES_DB=spotify_le_2
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
|
||||
# JWT
|
||||
SECRET_KEY=change-this-in-production
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=15
|
||||
|
||||
# Application
|
||||
DEBUG=true
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
### Tailwind CSS Configuration
|
||||
|
||||
Defined in `<script>` tag in `index.html`:
|
||||
- Primary colors: Cyan/blue (#0ea5e9)
|
||||
- Accent colors: Pink/magenta (#ec4899)
|
||||
- Dark mode enabled by default (`class="dark"` on `<html>`)
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Creating a New API Endpoint
|
||||
|
||||
1. Add Pydantic schemas in `app/schemas/`
|
||||
2. Add business logic in `app/services/`
|
||||
3. Add route handler in `app/api/v1/`
|
||||
4. Register router in `app/main.py`
|
||||
|
||||
Example:
|
||||
```python
|
||||
# app/api/v1/my_feature.py
|
||||
from fastapi import APIRouter, Depends
|
||||
from app.api.dependencies import DBSession, CurrentUser
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/my-endpoint")
|
||||
async def get_my_data(
|
||||
db: DBSession,
|
||||
current_user: CurrentUser,
|
||||
) -> dict:
|
||||
# Your logic here - typically delegate to a service
|
||||
return {"data": "response"}
|
||||
```
|
||||
|
||||
### Adding Database Fields
|
||||
|
||||
1. Update model in `app/models/`
|
||||
2. Create migration: `alembic revision --autogenerate -m "description"`
|
||||
3. Review generated migration in `alembic/versions/`
|
||||
4. Apply migration: `alembic upgrade head`
|
||||
5. Update Pydantic schemas
|
||||
|
||||
**Important**: Always review auto-generated migrations before applying!
|
||||
|
||||
### Frontend State Updates
|
||||
|
||||
When modifying frontend behavior:
|
||||
1. Modify `AppState` object for state changes
|
||||
2. Update DOM elements cached in `DOM` object
|
||||
3. Call `updatePlayerUI()` or `renderTracks()` as needed
|
||||
4. Use `showToast(message, type)` for user feedback
|
||||
5. **CRITICAL**: Assign new functions to `window` if called from HTML `onclick`
|
||||
|
||||
Example:
|
||||
```javascript
|
||||
// Define function
|
||||
function myNewFunction() {
|
||||
// Your logic
|
||||
}
|
||||
|
||||
// MUST assign to window if called from HTML
|
||||
window.myNewFunction = myNewFunction;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- **Unit tests**: Test services and business logic
|
||||
- **API tests**: Test endpoints with test database
|
||||
- **Fixtures**: `conftest.py` provides test DB and auth setup
|
||||
- Test files in `backend/tests/api/` mirror API structure
|
||||
|
||||
**Pytest Configuration** (`pytest.ini`):
|
||||
- `asyncio_mode = auto` - Required for async test functions
|
||||
- `asyncio_default_fixture_loop_scope = function` - Isolated event loop per test
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Database Connection Issues
|
||||
- Ensure Docker services are running: `docker-compose -f docker/docker-compose.yml ps`
|
||||
- Check connection string in `.env` matches Docker config
|
||||
- Run migrations: `alembic upgrade head`
|
||||
|
||||
### Authentication Errors (401)
|
||||
- Check `SECRET_KEY` is set in `.env`
|
||||
- Verify token format: `Authorization: Bearer <token>`
|
||||
- Frontend: Check `localStorage.getItem('authToken')`
|
||||
|
||||
### YouTube Streaming Issues
|
||||
- yt-dlp may need updates: `pip install --upgrade yt-dlp`
|
||||
- Some videos may be region-restricted or unavailable
|
||||
|
||||
### Frontend Not Updating
|
||||
- Hard refresh browser: Ctrl+Shift+R
|
||||
- Check browser console for JavaScript errors
|
||||
- Verify `app.js` functions are assigned to `window` object (for HTML onclick handlers)
|
||||
|
||||
---
|
||||
|
||||
## Default Credentials
|
||||
|
||||
Admin user (created on first run):
|
||||
- Email: `admin@example.com`
|
||||
- Password: `admin123`
|
||||
|
||||
---
|
||||
|
||||
## Documentation References
|
||||
|
||||
- **Tailwind refactor info**: `/opt/audiOhm/TAILWIND_REFACTOR.md`
|
||||
- **Quick styling reference**: `/opt/audiOhm/QUICK_REFERENCE.md`
|
||||
- **Installation guide**: `/opt/audiOhm/INSTALLATION.md`
|
||||
- **API docs**: Available at `http://localhost:8000/api/docs` (Swagger UI) when backend is running
|
||||
Reference in New Issue
Block a user