Migration to last.fm, removed old spotify api client. FOSRestBundle API serving data driven front end (JWT).

This commit is contained in:
boris committed 2026-02-27 16:52:57 +00:00
1 parent 391ecf1732
commit 56dea097d6
51 files changed
+1749 -844

No files matched your search

+2 -2
View File
@@ -23,7 +23,7 @@
## Site settings (ROLE_ADMIN)
- URL: `/admin/settings`
- Form persists Spotify Client ID/Secret in the DB (no restart needed).
- Form persists Last.fm API key in the DB (no restart needed).
- Toggle “Allow self-service registration” to pause public sign-ups while keeping `/admin/users` creation available to staff.
- The setting syncs with the `APP_ALLOW_REGISTRATION` environment variable each time Symfony boots (change the env value and restart to enforce). UI changes persist while the process runs.
- CSRF + role guards prevent unauthorized updates.
@@ -44,5 +44,5 @@ docker compose exec php php bin/console app:promote-moderator user@example.com
## Useful tips
- Registration toggle can be locked by environment (`APP_ALLOW_REGISTRATION`), in which case the UI explains that the value is immutable.
- Changing Spotify credentials in settings is effective immediately; no restart is required.
- Changing Last.fm credentials in settings is effective immediately; no restart is required.
- Admin UI actions are CSRF‑protected and role‑checked; if a button appears disabled, hover for a tooltip explanation.
+12 -14
View File
@@ -6,19 +6,19 @@ This project follows a conventional Symfony architecture with clear separation o
- **Classes**
- **Controllers** end with `Controller` (e.g. `AlbumController`) and expose HTTP‑oriented actions with verb‑based method names (`search`, `show`, `edit`, `delete`).
- **Services** are named by capability, not by caller, using nouns or noun‑phrases (e.g. `AlbumSearchService`, `ConsoleCommandRunner`, `RegistrationToggle`). When a service is tightly scoped to a third‑party, the integration appears in the name (e.g. `SpotifyClient`, `SpotifyMetadataRefresher`).
- **Services** are named by capability, not by caller, using nouns or noun‑phrases (e.g. `AlbumSearchService`, `ConsoleCommandRunner`, `RegistrationToggle`). When a service is tightly scoped to a third‑party, the integration appears in the name (e.g. `LastFmClient`, `LastFmMetadataRefresher`).
- **Entities** are singular domain nouns (`Album`, `Review`, `User`) and avoid transport or UI details.
- **Commands** describe what they do and the environment they are meant for (e.g. `SeedDemoUsersCommand`, `PromoteAdminCommand`).
- **Methods**
- Use **verb‑based, intention‑revealing names** that describe *what* the method does, not *how* it is used (e.g. `refreshAllSpotifyAlbums()`, `resetCatalog()`, `runConsoleCommand()`, `isEnabled()`, `findAlbumByPublicId()`).
- Accessors start with `get*`, `set*`, `is*` / `has*` for booleans (e.g. `getEnvOverride()`, `isSpotifyConfigured()`).
- Use **verb‑based, intention‑revealing names** that describe *what* the method does, not *how* it is used (e.g. `refreshAllExternalAlbums()`, `resetCatalog()`, `runConsoleCommand()`, `isEnabled()`, `findAlbumByPublicId()`).
- Accessors start with `get*`, `set*`, `is*` / `has*` for booleans (e.g. `getEnvOverride()`, `isExternalConfigured()`).
- Avoid ambiguous names like `run()`, `handle()`, or `process()` without a clear domain object; prefer `runConsoleCommand()`, `handleAlbumCoverUpload()`, etc.
- **Variables & parameters**
- Use **descriptive, domain‑level names** (e.g. `$albumRepository`, `$reviewCount`, `$spotifyAlbumPayload`) and avoid unclear abbreviations (`$em` is acceptable for `EntityManagerInterface` in local scope, but prefer full names for properties).
- Booleans read naturally (`$isEnabled`, `$shouldQuerySpotify`, `$needsSync`).
- Collections are pluralized (`$albums`, `$userReviews`, `$spotifyIds`).
- Use **descriptive, domain‑level names** (e.g. `$albumRepository`, `$reviewCount`, `$lastfmAlbumPayload`) and avoid unclear abbreviations (`$em` is acceptable for `EntityManagerInterface` in local scope, but prefer full names for properties).
- Booleans read naturally (`$isEnabled`, `$shouldQueryLast.fm`, `$needsSync`).
- Collections are pluralized (`$albums`, `$userReviews`, `$lastfmIds`).
- **Files & namespaces**
- File names match their primary class name and follow PSR‑4 (e.g. `src/Service/AlbumSearchService.php` for `App\Service\AlbumSearchService`).
@@ -27,10 +27,10 @@ This project follows a conventional Symfony architecture with clear separation o
These conventions should be followed for all new PHP code and when refactoring existing classes to keep the codebase reusable and self‑documenting.
## High-level flow
1. Visitors search for albums (Spotify) and view an album page
1. Visitors search for albums (Last.fm) and view an album page
2. Logged‑in users can write, edit, and delete reviews
3. Moderators and admins can moderate content and manage users
4. Admins configure site settings (Spotify credentials, registration toggle)
4. Admins configure site settings (Last.fm credentials, registration toggle)
## Layers & components
@@ -45,7 +45,7 @@ These conventions should be followed for all new PHP code and when refactoring e
- `User` — authentication principal and roles
- `Album`, `AlbumTrack` — normalized album metadata and track list
- `Review` — user‑authored review with rating and timestamps
- `Setting` — key/value store for site configuration (e.g., Spotify credentials)
- `Setting` — key/value store for site configuration (e.g., Last.fm credentials)
### Repositories (`src/Repository/*`)
- Doctrine repositories for querying by domain (albums, tracks, reviews, settings, users)
@@ -55,10 +55,10 @@ These conventions should be followed for all new PHP code and when refactoring e
- Leverage Symfony validation constraints for robust server‑side validation
### Services (`src/Service/*`)
- `SpotifyClient` — Client Credentials token management (cached) and API calls
- `SpotifyMetadataRefresher`, `SpotifyGenreResolver` — helpers for richer album data
- `LastFmClient` — Last.fm API integration with caching
- `LastFmMetadataRefresher`, `LastFmTagResolver` — helpers for richer album data
- `CatalogResetService` — admin action to reset/sync catalog state safely
- `ImageStorage` — avatar uploads and related image handling
- `UploadStorage` — avatar/album image uploads and related file handling
- `RegistrationToggle` — DB‑backed registration flag with env override
### Security (`config/packages/security.yaml`, `src/Security/*`)
@@ -86,5 +86,3 @@ These conventions should be followed for all new PHP code and when refactoring e
## Testing & tooling
- PHPUnit setup in `composer.json` (`phpunit/phpunit`), BrowserKit & CSS Selector for functional coverage
- Web Profiler enabled in dev
+2 -4
View File
@@ -18,8 +18,7 @@ docker run -d \
-p 8080:8080 \
-e APP_ENV=prod \
-e APP_SECRET=change_me \
-e SPOTIFY_CLIENT_ID=your_client_id \
-e SPOTIFY_CLIENT_SECRET=your_client_secret \
-e LASTFM_API_KEY=your_api_key \
tonehaus-app:latest
```
@@ -51,7 +50,7 @@ docker exec tonehaus php bin/console doctrine:migrations:migrate --no-interactio
## Environment variables
- `APP_ENV` (`prod` recommended in production)
- `APP_SECRET` (required; random string)
- `SPOTIFY_CLIENT_ID`, `SPOTIFY_CLIENT_SECRET`
- `LASTFM_API_KEY`
- `APP_ALLOW_REGISTRATION` (env override for public registration)
- `DATABASE_DRIVER` (`sqlite` default, or `postgres`)
- `DATABASE_URL` (when using Postgres)
@@ -67,4 +66,3 @@ docker exec tonehaus php bin/console doctrine:migrations:migrate --no-interactio
- Build then run a new container alongside the old one, switch traffic at the proxy
- Keep SQLite on a named volume, or use Postgres for shared state across replicas
+2 -2
View File
@@ -1,7 +1,7 @@
# Features
## Albums & Reviews
- Spotify album search with advanced filters (album, artist, year range)
- Last.fm album search with advanced filters (album, artist, year range)
- Album page: cover art, metadata, full tracklist (when available)
- Reviews list (newest first) and inline new review form (logged-in)
- Rating slider (1–10) with live badge
@@ -16,7 +16,7 @@
## Administration
- Dashboard: latest reviews/albums and key counts (moderator+)
- Users: create/delete users, promote/demote moderators (admin constraints)
- Settings: manage Spotify credentials, toggle public registration (admin)
- Settings: manage Last.fm credentials, toggle public registration (admin)
## Design & UX
- Responsive Bootstrap UI
+26
View File
@@ -0,0 +1,26 @@
# Last.fm Integration
## Credentials
- Preferred: manage the Last.fm API key in `/admin/settings` (persisted in DB; no restart required).
- Env fallback: `LASTFM_API_KEY`.
## API Client
- `src/Service/LastFmClient.php` (uses Guzzle)
- `searchAlbums(query, limit)`
- `getAlbum(id)` / `getAlbums([ids])`
- `getAlbumWithTracks(id)`
- `getAlbumTracks(id)`
## Caching & Limits
- Last.fm responses are cached in Symfony cache pools to reduce repeated calls.
- Search/detail payloads are normalized to one internal album shape before persistence.
## Ingestion Flow
1. Search Last.fm
2. Resolve tags/genres via `LastFmTagResolver`
3. Persist or update album with `AlbumRepository::upsertFromExternalAlbum()`
4. Persist tracks with `AlbumTrackRepository::replaceAlbumTracks()`
## Admin Settings
- Set the Last.fm API key in `/admin/settings`.
- Key changes apply immediately.
+1 -1
View File
@@ -2,7 +2,7 @@
## Album page
- Artwork, metadata, average rating, and review count
- Full Spotify tracklist when available
- Full Last.fm tracklist when available
- Reviews list (newest first)
- Inline new review form for logged‑in users
+12 -7
View File
@@ -2,7 +2,7 @@
## Prerequisites
- Docker + Docker Compose
- Spotify Developer account (Client ID/Secret)
- Last.fm API key
- A unique `APP_SECRET` value in your environment (for prod builds)
## 1) Start the stack
@@ -18,15 +18,19 @@ Health: `http://localhost:8085/healthz`
docker compose exec tonehaus php bin/console app:promote-admin you@example.com
```
## 3) Configure Spotify
- Preferred: open `/admin/settings` and enter your Client ID/Secret (stored in DB)
## 3) Configure Last.fm
- Preferred: open `/admin/settings` and enter your Last.fm API key (stored in DB)
- Env fallback (in `.env` or your shell):
```bash
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
LASTFM_API_KEY=your_api_key
```
## 4) (Optional) Seed demo data
## 4) JWT auth keys
- API login endpoint: `POST /api/v1/auth/login`
- JWT keys are generated automatically on container start into `config/jwt/`.
- Set `JWT_PASSPHRASE` in `.env` to your production secret.
## 5) (Optional) Seed demo data
```bash
docker compose exec tonehaus php bin/console app:seed-demo-users --count=50
docker compose exec tonehaus php bin/console app:seed-demo-albums --count=40 --attach-users
@@ -44,7 +48,8 @@ docker compose exec tonehaus php bin/console app:seed-demo-reviews --cover-perce
## Environment variables
- `APP_ENV=dev|prod`
- `APP_SECRET=<random_string>`
- `SPOTIFY_CLIENT_ID`, `SPOTIFY_CLIENT_SECRET`
- `LASTFM_API_KEY`
- `JWT_PASSPHRASE=<random_string>`
- `APP_ALLOW_REGISTRATION=1|0` (env can override DB setting)
- `DATABASE_DRIVER=sqlite|postgres`
- `DATABASE_SQLITE_PATH` (optional)
-30
View File
@@ -1,30 +0,0 @@
# Spotify Integration
## Credentials
- Preferred: Manage in `/admin/settings` (persisted in DB; no restart required)
- Env fallback: `SPOTIFY_CLIENT_ID`, `SPOTIFY_CLIENT_SECRET`
## API Client
- `src/Service/SpotifyClient.php`
- Client Credentials token fetch with caching
- `searchAlbums(q, limit)` — album search endpoint
- `getAlbum(id)` / `getAlbums([ids])` — metadata fetch
- `getAlbumWithTracks(id)` — metadata + hydrated tracklist
- `getAlbumTracks(id)` — raw paginated tracks (when needed)
### Caching & Rate Limits
- Access tokens are cached until expiry to avoid unnecessary auth calls.
- Downstream requests should be mindful of Spotify rate limits; user actions are debounced in the UI and server calls are focused on album/track data needed by the current page.
## Advanced search syntax
- Fielded queries are composed as:
- `album:"..."`, `artist:"..."`, `year:YYYY` or `year:YYYY-YYYY`
- Optional free text is appended to the query
- Examples:
- `album:"in rainbows" artist:"radiohead"`
- `year:1999-2004 post rock`
## Admin settings
- Update credentials in `/admin/settings`
- Settings are stored in the database; `APP_ENV` reload or container restart is not required
+3 -4
View File
@@ -29,9 +29,8 @@
postgresql://symfony:symfony@db:5432/symfony?serverVersion=16&charset=utf8
```
## Spotify errors
- Verify credentials in `/admin/settings` or env vars `SPOTIFY_CLIENT_ID` / `SPOTIFY_CLIENT_SECRET`.
- Client Credentials tokens are cached; if revoked, wait for expiry or restart the container.
## Last.fm errors
- Verify credentials in `/admin/settings` or env var `LASTFM_API_KEY`.
## ARM64 Build
@@ -43,4 +42,4 @@ sudo docker buildx build \
-f docker/php/Dockerfile \
. \
--load
```
```