Fantasy Premier League analytics app with:
- PHP API (
api/) - React + TypeScript frontend (
frontend/) - Dockerized caddy/php/cron/redis local stack
- Backend: PHP 8.2, SQLite, PHPUnit
- Frontend: React, Vite, TanStack Query, Playwright
- Infra (local): Docker Compose (
caddy,php,cron,redis)
- Docker + Docker Compose
- Node.js 22.12.0+ and npm
If you use fnm, make sure shell integration is enabled so node actually switches per repo:
eval "$(fnm env --use-on-cd --shell zsh)"npm run setup
npm run up:buildThis will:
- Build/start Docker services on the API side
- Start frontend dev server
App URLs:
- Frontend:
http://localhost:5173 - API (via Caddy):
http://localhost:8080/api
Optional local env (for odds sync):
export ODDS_API_KEY=your_key_herenpm run up # start API stack + frontend dev server
npm run up:build # rebuild containers + start
npm run down # stop containersnpm run api # start docker services only
npm run api:logs # tail docker logs
npm run dev # frontend dev server onlynpm run build # frontend build
npm run test:frontend # frontend unit tests
npm run test:e2e # Playwright e2e
cd api && ./vendor/bin/phpunit testsManual sync entrypoints:
npm run sync:pre
npm run sync:post
npm run sync:slow
npm run sync:samples
npm run sync:all
npm run sync:snapshotConfigured in deploy/crontab and run by the cron service:
01:00daily:php cron/fixtures-refresh.phpHH:05hourly:php cron/fixtures-post-deadline-refresh.php(runs once per GW, 1h after deadline)09:00daily:php cron/sync-all.php --phase=pre-deadline23:59daily:php cron/sync-all.php --phase=post-gameweek04:00Monday:php cron/sync-all.php --phase=slow12:00Saturday:php cron/sync-all.php --phase=samples
Important: after changing deploy/crontab, rebuild/restart cron:
docker compose build cron
docker compose up -d cronUse .env.example as the source-of-truth template for all required variables.
For production, set these in .env.production:
SUPERFPL_APP_ENV=productionSUPERFPL_DEBUG=0SUPERFPL_CORS_ALLOWED_ORIGINS=https://superfpl.com,https://www.superfpl.comSUPERFPL_ADMIN_TOKEN=<long-random-token>(recommended)REDIS_PASSWORD=<strong-password>(recommended)SUPERFPL_FPL_CONNECT_TIMEOUT=8SUPERFPL_FPL_REQUEST_TIMEOUT=15
Admin access now uses secure cookies (via /api/admin/login) rather than browser local storage.
- FPL client cache: file cache under
api/cache - API response cache: Redis-backed (with fallback bypass), keying includes DB mtime and sync version
- Cache status header for supported GET endpoints:
X-Response-Cache: HIT|MISS|BYPASS - Bypass per request: append
?nocache=1