e76d0b0f072785f1085804d9a5bf4d0d2fd25de3
When the last player clicked ready, toggleReady() published a redundant 'player_ready' event on top of checkAllPlayersReady()'s 'all_ready', and the client reloaded on every message with no guard and without closing the EventSource. Chrome kept the old page's script (and its EventSource) alive across the overlapping reload calls, causing repeated reconnects to the Mercure hub; Firefox apparently tore the page down fast enough to mask it. Skip the redundant publish server-side, and make the client reload idempotent by tracking whether it already fired and closing the EventSource before reloading. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
EscapePage — Online Escape Room
This repository contains a Symfony 7.3 (PHP >= 8.5.1) application for a collaborative online escape room experience.
- Start here: doc/FILES.md — quick file index.
- Development standards and workflows: doc/CONTRIBUTING.md
- All documentation is located under the doc/ directory.
Getting Started (brief)
-
Local machine (no Docker):
- Copy
.envto.env.localand set database andAPP_SECRET. - Install PHP deps:
composer install - Create database and run migrations (when present):
php bin/console doctrine:database:create --if-not-exists && php bin/console doctrine:migrations:migrate -n - Install/import JS deps:
php bin/console importmap:install - Run the server:
symfony server:start -d(or your preferred web server) - Run tests:
vendor/bin/phpunit
- Copy
-
With Docker:
cd docker && docker compose up -d- Install vendors inside the PHP container:
docker compose exec php bashcomposer install
- Initialize DB:
php bin/console doctrine:database:create --if-not-existsphp bin/console doctrine:migrations:migrate -n
- App is at http://localhost:8080
Email (Mailpit in dev, Mailgun for prod)
- Dev: a
mailerservice (Mailpit) runs in Docker.- SMTP DSN in
.env:MAILER_DSN=smtp://mailer:1025 - Mailpit UI: http://localhost:8025 (or mapped port 8025)
- Send a test mail:
php bin/console app:mail:test you@example.com
- SMTP DSN in
- Staging/Prod: use Mailgun.
- Require package (already in composer):
symfony/mailgun-mailer. - Set environment variables (do NOT commit secrets):
MAILER_DSN=mailgun+api://${MAILGUN_API_KEY}:${MAILGUN_DOMAIN}@default?region=euMAILGUN_API_KEY=YOUR_REAL_KEYMAILGUN_DOMAIN=YOUR_SENDING_DOMAIN(e.g.mg.escapepage.nl)- Optional:
MAILER_FROM=no-reply@your-domain.tld
- Drop
region=eu(or useregion=us) depending on which region your Mailgun domain was created in. - Alternatively via SMTP (no extra package):
MAILER_DSN="mailgun+smtp://USERNAME:PASSWORD@default?region=eu"
- Require package (already in composer):
Troubleshooting:
- If emails don’t appear in dev, open Mailpit at http://localhost:8025 and verify messages.
- In prod, check logs for HTTP 2xx responses from Mailgun and verify sender domain is verified (SPF/DKIM) in Mailgun.
Frontend assets with Webpack Encore
We use Webpack Encore to build and minify JS/CSS from the assets/ directory into public/build/.
Install Node dependencies (on your host machine):
npm install
Common commands:
# One-time dev build
npm run dev
# Watch & rebuild on changes
npm run watch
# Production build (minified, versioned filenames)
npm run build
How it’s wired:
- Entry file:
assets/app.js(importsassets/styles/app.css). - Webpack config:
webpack.config.jsoutputs topublic/build/. - Twig template includes built assets via Encore:
- In
templates/base.html.twig:{{ encore_entry_link_tags('app') }}(CSS){{ encore_entry_script_tags('app') }}(JS)
- In
Notes:
- The PHP Docker image does not include Node. Run the build commands on your host, or ask to add a Node build container if you prefer fully containerized builds.
- Built files are ignored by git except for
public/build/.gitignoreto keep the directory.
See doc/CONTRIBUTING.md for code style and more details.
Real‑time updates with Mercure
We use a Mercure hub (Docker service) to push server updates to browsers via Server‑Sent Events (SSE).
Quick start (dev):
- Start Docker stack:
This starts
cd docker && docker compose up -dmercureat http://localhost:8090 and the app at http://localhost:8080. - Install PHP deps inside the PHP container if you haven't yet:
You should see a console log like
docker compose exec php bash composer install[Mercure] Update received: { ... }on the Game Hub page.
Configuration:
- Env vars (defined in
.env, override in.env.localas needed):MERCURE_URL=http://mercure/.well-known/mercure(internal URL from PHP to the hub)MERCURE_PUBLIC_URL=http://localhost:8090/.well-known/mercure(browser URL)MERCURE_JWT_SECRET=!ChangeThisMercureJWT!(dev secret; do not use in prod)MERCURE_TOPIC_BASE=https://escapepage.dev(base topic URL)
- Docker service
mercureis based ondunglas/mercureand allows anonymous subscribers in dev.
Topics:
- Topics must be URLs. We use
MERCURE_TOPIC_BASEto control the domain per environment.- Dev:
.envsetshttps://escapepage.dev - Prod: set
MERCURE_TOPIC_BASE=https://escapepage.com
- Dev:
- The Game Hub demo topic is
${MERCURE_TOPIC_BASE}/game/hub.
Production notes:
- Disable anonymous subscribers and require JWTs for subscriptions (configure the Mercure container accordingly).
- Use a strong, rotated
MERCURE_JWT_SECRETand keep it out of VCS (use real env/secrets). - Serve Mercure over HTTPS and set proper CORS/allowed origins for your production domain(s).
Languages
PHP
64.3%
Twig
25.2%
JavaScript
7.3%
Shell
1.6%
CSS
0.7%
Other
0.9%