The backend API for the VATUSA (VATSIM US Division) website,
served at api.vatusa.net. It runs in parallel with the main site
(vatusa/current) and is the legacy/current API that
the newer Go (cobalt) and Rust (mithril) backends are intended to extend and eventually
replace.
Status: Legacy. This is a long-lived Laravel application in maintenance mode. Prefer making new functionality in the newer backends where possible; changes here should be targeted and well-scoped.
| Area | Technology |
|---|---|
| Language / runtime | PHP 8.1+ |
| Framework | Laravel 10 |
| Database | MySQL (via PDO). Multiple connections: mysql (primary), plus optional forum, email, moodle |
| Cache / queue | Redis (predis) |
| Auth | JWT (tymon/jwt-auth), OAuth2 (VATSIM Connect via league/oauth2-client), API keys, plus web-token/jwt-* for RFC7519 |
| API docs | OpenAPI / Swagger UI (darkaonline/l5-swagger, zircote/swagger-php) |
| HTML sanitizing | mews/purifier |
| SMTP / AWS SES | |
| File storage | AWS S3 (aws/aws-sdk-php-laravel) |
| Academy | Moodle REST (llagerlof/moodlerest) |
| Error tracking | Sentry (sentry/sentry-laravel) |
| Tests | PHPUnit 10 |
| Container | php:8.1-fpm-alpine + nginx + supervisord |
| CI/CD | GitHub Actions → Docker Hub → ArgoCD/Kustomize (see gitops) |
app/
*.php Eloquent models live at the TOP LEVEL (User.php, Facility.php, …)
— this is the old Laravel 5 convention, not app/Models/.
Http/Controllers/ Controllers, mostly under API/v2/
Http/Middleware/ Auth/CORS tiers (see "Authentication" below)
Jobs/, Mail/ Queued work and mailers
Classes/, Helpers/ app/Helpers/helpers.php is globally autoloaded
Console/ Artisan commands
config/ Standard Laravel config (note the extra DB connections + sso/oauth)
database/migrations/ 66 migrations; database/seeds/ for seeders
routes/
api.php Entry point; wires up v2 routing + middleware
api-v2.php The bulk of the API surface
login.php VATSIM SSO login flow
web.php, channels.php, console.php
resources/
views/emails/ Blade email templates
docker/ nginx / php-fpm / supervisord config baked into the prod image
tests/ PHPUnit (Unit + Feature)
Auth is multi-layered. Routes are grouped by middleware that enforces different access:
auth:jwt,web— session authentication only; no CORS checks.private— internal calls only; includes CORS checks.semiprivate— tries both session auth and API key.public— readability marker only, being phased out.APIKey— legacy v1; checks the API key in the URL path.apikeyv2— validates anapikeyquery/body parameter (v2).
See app/Http/Kernel.php for how these map to middleware classes.
You'll need either Docker (recommended — no local PHP required) or a local PHP 8.1+ toolchain with Composer.
Brings up MySQL + Redis + the API together. Source is bind-mounted, so edits are live.
docker compose -f compose.dev.yml up --buildThe first time (and after adding migrations), run them in another terminal:
docker compose -f compose.dev.yml exec app php artisan migrate
# optional sample data:
docker compose -f compose.dev.yml exec app php artisan db:seedThe API is then at http://localhost:5002 (Swagger docs at /docs).
Many endpoints require real VATSIM SSO / API credentials to do anything useful — fill in the relevant values in
.env(created automatically from.env.exampleon first boot).
cp .env.example .env
composer install
php artisan key:generate
php artisan jwt:secret # if using HMAC JWTs
# point .env at a MySQL + Redis you control, then:
php artisan migrate
php artisan serve # http://localhost:8000php artisan migrate # run DB migrations
php artisan db:seed # seed data
php artisan l5-swagger:generate # regenerate OpenAPI docs
php artisan tinker # REPL
./vendor/bin/phpunit # run testsTests use PHPUnit 10. The committed suite is currently a small set of smoke tests that verify the framework boots and helpers behave — they run against in-memory SQLite and need no external services:
./vendor/bin/phpunitFull DB-backed feature testing is not yet wired up: the app targets MySQL and relies on
several connections (forum, email, moodle), so it can't simply run against SQLite.
Building that out (a MySQL service + a trimmed test schema/factories) is the main thing
needed to make integration tests possible.
Style is checked with Laravel Pint (config in pint.json,
laravel preset). Install it once and run:
composer global require laravel/pint # one-time
pint # fix style in place
pint --test # check onlyThe codebase is not yet fully Pint-clean, so there is no CI style job yet. To add an
enforced one later: run pint, commit the result, then add a pint --test job to
.github/workflows/ci.yml.
Two GitHub Actions workflows:
.github/workflows/ci.yml(PR Checks) — runs on every pull request (and pushes tomaster/dev): PHP syntax lint,composer validate, and the PHPUnit smoke tests. This validates changes without needing a full database..github/workflows/ci-master.yml(CI to Docker Hub) — runs on pushes tomaster/devonly: builds the production Docker image, pushes it to Docker Hub tagged with the commit SHA, then bumps the image tag in thegitopsrepo for ArgoCD to deploy.
Dockerfilebuilds the production image (php-fpm+ nginx + supervisord). Config lives underresources/docker/.build.shis the container entrypoint: sets upstorage/logsand, onprod/livedev/staging, runsphp artisan migrateand fixes purifier permissions before startingsupervisord.deploy.shis a legacy server-sidegit pull+composer install.- Cluster deployment (Kubernetes/ArgoCD manifests) lives in the
gitopsrepo, not here.
These are worth addressing as the codebase is maintained:
composer.jsonrough edges:"minimum-stability": "dev"is risky;mockery/mockeryis pinned to the ancient0.9.*(predates PHPUnit 10) and should be upgraded or removed;doctrine/dbal ^2.5andguzzlehttp/psr7 ^1.5are old major versions.- No real test coverage: only smoke tests exist (see Testing).
- Secure randomness:
randomPassword()inapp/Helpers/helpers.phpusesrand(), which is not cryptographically secure — preferrandom_int()/Str::random(). - Commented-out middleware: CSRF (
VerifyCsrfToken) andEncryptCookiesare disabled inapp/Http/Kernel.php; confirm this is intentional for an API. - Broken
web.phproute:/returnsview('welcome'), but nowelcomeview exists. - Stale dependency PRs: several open Dependabot branches; review and merge or close.