Files
root bc03225e47 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>
2026-01-20 09:48:17 +00:00

385 lines
12 KiB
Markdown

# 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