Startup
This commit is contained in:
12
.env
12
.env
@@ -25,7 +25,6 @@ APP_SECRET=
|
||||
#
|
||||
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
||||
DATABASE_URL="mysql://app:!ChangeMe!@database:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
@@ -39,4 +38,15 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
|
||||
###> symfony/mailer ###
|
||||
# Development: use Mailpit (docker compose override provides service `mailer` on port 1025)
|
||||
MAILER_DSN=smtp://mailer:1025
|
||||
# Production/Stage (uncomment and set SENDGRID_API_KEY in real env or secrets):
|
||||
# MAILER_DSN=sendgrid+api://%env(SENDGRID_API_KEY)%
|
||||
# Alternatively, via SMTP (no extra package needed):
|
||||
# MAILER_DSN="smtp://apikey:%env(SENDGRID_API_KEY)%@smtp.sendgrid.net:587?encryption=tls"
|
||||
# Optional default sender (used by test command if --from not passed):
|
||||
# MAILER_FROM=no-reply@your-domain.tld
|
||||
# SENDGRID_API_KEY=your_real_key_goes_here # Do NOT commit this; set in .env.local or deployment env
|
||||
###< symfony/mailer ###
|
||||
|
||||
###> symfony/sendgrid-mailer ###
|
||||
# MAILER_DSN=sendgrid://KEY@default
|
||||
###< symfony/sendgrid-mailer ###
|
||||
|
||||
9
.env.prod
Normal file
9
.env.prod
Normal file
@@ -0,0 +1,9 @@
|
||||
### Compiled or real environment variables should be used in production.
|
||||
### Configure MAILER_DSN to use SendGrid API transport.
|
||||
### Prefer storing SENDGRID_API_KEY using Symfony Secrets or real env vars.
|
||||
|
||||
###> symfony/mailer ###
|
||||
# Example using SendGrid API key (replace with real secret via vault/secrets):
|
||||
# SENDGRID_API_KEY=SG.xxxxx
|
||||
MAILER_DSN=sendgrid+api://%env(resolve:SENDGRID_API_KEY)%@default
|
||||
###< symfony/mailer ###
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -18,3 +18,10 @@
|
||||
/public/assets/
|
||||
/assets/vendor/
|
||||
###< symfony/asset-mapper ###
|
||||
|
||||
###> symfony/webpack-encore-bundle ###
|
||||
/node_modules/
|
||||
/public/build/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
###< symfony/webpack-encore-bundle ###
|
||||
|
||||
2
.idea/escapepage.iml
generated
2
.idea/escapepage.iml
generated
@@ -133,6 +133,8 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/extra-bundle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/sendgrid-mailer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/webpack-encore-bundle" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
||||
2
.idea/php.xml
generated
2
.idea/php.xml
generated
@@ -146,6 +146,8 @@
|
||||
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
|
||||
<path value="$PROJECT_DIR$/vendor/masterminds/html5" />
|
||||
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/sendgrid-mailer" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/webpack-encore-bundle" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.2">
|
||||
|
||||
78
README.md
Normal file
78
README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# EscapePage — Online Escape Room
|
||||
|
||||
This repository contains a Symfony 7.3 (PHP >= 8.2) 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):
|
||||
1. Copy `.env` to `.env.local` and set database and `APP_SECRET`.
|
||||
2. Install PHP deps: `composer install`
|
||||
3. Create database and run migrations (when present): `php bin/console doctrine:database:create --if-not-exists && php bin/console doctrine:migrations:migrate -n`
|
||||
4. Install/import JS deps: `php bin/console importmap:install`
|
||||
5. Run the server: `symfony server:start -d` (or your preferred web server)
|
||||
6. Run tests: `vendor/bin/phpunit`
|
||||
|
||||
- With Docker:
|
||||
1. From `docker/`: `docker compose up -d`
|
||||
2. Install vendors inside the PHP container:
|
||||
- `docker compose exec php bash`
|
||||
- `composer install`
|
||||
3. Initialize DB:
|
||||
- `php bin/console doctrine:database:create --if-not-exists`
|
||||
- `php bin/console doctrine:migrations:migrate -n`
|
||||
4. App is at http://localhost:8080
|
||||
|
||||
## Email (Mailpit in dev, SendGrid for prod)
|
||||
- Dev: a `mailer` service (Mailpit) runs in Docker.
|
||||
- SMTP DSN in `.env`: `MAILER_DSN=smtp://mailer:1025`
|
||||
- Mailpit UI: http://localhost:8025
|
||||
- Send a test mail: `php bin/console app:mail:test you@example.com`
|
||||
- Staging/Prod: use SendGrid.
|
||||
- Require package (already in composer): `symfony/sendgrid-mailer`.
|
||||
- Set environment variables (do NOT commit secrets):
|
||||
- `MAILER_DSN=sendgrid+api://%env(SENDGRID_API_KEY)%`
|
||||
- `SENDGRID_API_KEY=YOUR_REAL_KEY`
|
||||
- Optional: `MAILER_FROM=no-reply@your-domain.tld`
|
||||
- Alternatively via SMTP (no extra package):
|
||||
- `MAILER_DSN="smtp://apikey:%env(SENDGRID_API_KEY)%@smtp.sendgrid.net:587?encryption=tls"`
|
||||
|
||||
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 SendGrid and verify sender domain is verified in SendGrid.
|
||||
|
||||
## 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` (imports `assets/styles/app.css`).
|
||||
- Webpack config: `webpack.config.js` outputs to `public/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)
|
||||
|
||||
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/.gitignore` to keep the directory.
|
||||
|
||||
See doc/CONTRIBUTING.md for code style and more details.
|
||||
@@ -1,10 +1,6 @@
|
||||
import './bootstrap.js';
|
||||
/*
|
||||
* Welcome to your app's main JavaScript file!
|
||||
*
|
||||
* This file will be included onto the page via the importmap() Twig function,
|
||||
* which should already be in your base.html.twig.
|
||||
*/
|
||||
import './styles/app.css';
|
||||
|
||||
console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');
|
||||
console.log('This log comes from assets/app.js built by Webpack Encore! 🎉');
|
||||
|
||||
11
assets/game1.js
Normal file
11
assets/game1.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/* Game1 entry point built with Webpack Encore */
|
||||
import './styles/game1.css';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Simple boot log so you can verify it in the browser console
|
||||
// and confirm this specific bundle is loaded on the Game Hub page.
|
||||
console.log('Game1 bundle loaded');
|
||||
|
||||
// Example: add a CSS class to <body> so page-specific styles can apply
|
||||
document.body.classList.add('game1-page');
|
||||
});
|
||||
17
assets/styles/game1.css
Normal file
17
assets/styles/game1.css
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Styles specific to Game1 */
|
||||
|
||||
/* page-level indicator to confirm CSS is loaded */
|
||||
body.game1-page {
|
||||
/* subtle background tint so you can visually confirm on /game */
|
||||
background-color: #f9fbff;
|
||||
}
|
||||
|
||||
/* example component style */
|
||||
.game1-banner {
|
||||
padding: 1rem 1.25rem;
|
||||
border: 1px solid #cfe2ff;
|
||||
background: #e9f2ff;
|
||||
color: #0b5ed7;
|
||||
border-radius: 8px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
0
bin/console
Executable file → Normal file
0
bin/console
Executable file → Normal file
0
bin/phpunit
Executable file → Normal file
0
bin/phpunit
Executable file → Normal file
@@ -26,6 +26,7 @@
|
||||
"symfony/intl": "7.3.*",
|
||||
"symfony/mailer": "7.3.*",
|
||||
"symfony/mime": "7.3.*",
|
||||
"symfony/sendgrid-mailer": "7.3.*",
|
||||
"symfony/monolog-bundle": "^3.0",
|
||||
"symfony/notifier": "7.3.*",
|
||||
"symfony/process": "7.3.*",
|
||||
@@ -43,7 +44,8 @@
|
||||
"symfony/web-link": "7.3.*",
|
||||
"symfony/yaml": "7.3.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
"twig/twig": "^2.12|^3.0",
|
||||
"symfony/webpack-encore-bundle": "^2.1"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
||||
5964
composer.lock
generated
5964
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,4 +13,5 @@ return [
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
@@ -19,8 +19,8 @@ doctrine:
|
||||
App:
|
||||
type: attribute
|
||||
is_bundle: false
|
||||
dir: '%kernel.project_dir%/src/Entity'
|
||||
prefix: 'App\Entity'
|
||||
dir: '%kernel.project_dir%/src'
|
||||
prefix: 'App'
|
||||
alias: App
|
||||
controller_resolver:
|
||||
auto_mapping: false
|
||||
|
||||
@@ -2,6 +2,7 @@ framework:
|
||||
notifier:
|
||||
chatter_transports:
|
||||
texter_transports:
|
||||
sendgrid: '%env(MAILER_DSN)%'
|
||||
channel_policy:
|
||||
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo
|
||||
urgent: ['email']
|
||||
|
||||
45
config/packages/webpack_encore.yaml
Normal file
45
config/packages/webpack_encore.yaml
Normal file
@@ -0,0 +1,45 @@
|
||||
webpack_encore:
|
||||
# The path where Encore is building the assets - i.e. Encore.setOutputPath()
|
||||
output_path: '%kernel.project_dir%/public/build'
|
||||
# If multiple builds are defined (as shown below), you can disable the default build:
|
||||
# output_path: false
|
||||
|
||||
# Set attributes that will be rendered on all script and link tags
|
||||
script_attributes:
|
||||
defer: true
|
||||
# Uncomment (also under link_attributes) if using Turbo Drive
|
||||
# https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change
|
||||
# 'data-turbo-track': reload
|
||||
# link_attributes:
|
||||
# Uncomment if using Turbo Drive
|
||||
# 'data-turbo-track': reload
|
||||
|
||||
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
|
||||
# crossorigin: 'anonymous'
|
||||
|
||||
# Preload all rendered script and link tags automatically via the HTTP/2 Link header
|
||||
# preload: true
|
||||
|
||||
# Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
|
||||
# strict_mode: false
|
||||
|
||||
# If you have multiple builds:
|
||||
# builds:
|
||||
# frontend: '%kernel.project_dir%/public/frontend/build'
|
||||
|
||||
# pass the build name as the 3rd argument to the Twig functions
|
||||
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
|
||||
|
||||
framework:
|
||||
assets:
|
||||
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
|
||||
|
||||
#when@prod:
|
||||
# webpack_encore:
|
||||
# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
|
||||
# # Available in version 1.2
|
||||
# cache: true
|
||||
|
||||
#when@test:
|
||||
# webpack_encore:
|
||||
# strict_mode: false
|
||||
@@ -1,5 +1,23 @@
|
||||
controllers:
|
||||
# Attribute-based routing imports for controllers in subnamespaces
|
||||
|
||||
website_controllers:
|
||||
resource:
|
||||
path: ../src/Controller/
|
||||
namespace: App\Controller
|
||||
path: ../src/Website/Controller/
|
||||
namespace: App\Website\Controller
|
||||
type: attribute
|
||||
|
||||
game_controllers:
|
||||
resource:
|
||||
path: ../src/Game/Controller/
|
||||
namespace: App\Game\Controller
|
||||
type: attribute
|
||||
prefix: /game
|
||||
|
||||
# Uncomment when you add base controllers
|
||||
# base_controllers:
|
||||
# resource:
|
||||
# path: ../src/Base/Controller/
|
||||
# namespace: App\Base\Controller
|
||||
# type: attribute
|
||||
# # Set a prefix if desired, e.g., "/base" or leave empty to mount at root
|
||||
# # prefix: /base
|
||||
|
||||
13
config/routes/app.yaml
Normal file
13
config/routes/app.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
website:
|
||||
resource: ../../src/Website/Controller/
|
||||
type: attribute
|
||||
prefix:
|
||||
en: '/'
|
||||
nl: '/nl'
|
||||
|
||||
game:
|
||||
resource: ../../src/Game/Controller/
|
||||
type: attribute
|
||||
prefix:
|
||||
en: '/game'
|
||||
nl: '/nl/game'
|
||||
138
doc/CONTRIBUTING.md
Normal file
138
doc/CONTRIBUTING.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Contribution & Code Style Guide
|
||||
|
||||
This document is for Junie (and humans) to keep our code style consistent and to quickly find the files we’ll reference during development of the Online Escape Room platform.
|
||||
|
||||
Project type: Symfony 7.3, PHP >= 8.2, Doctrine ORM 3, Twig, Stimulus (UX), Importmap/Asset Mapper, PHPUnit 11.
|
||||
|
||||
|
||||
## 1. Repository Conventions
|
||||
- PHP version: 8.2+ (composer.json enforces ">=8.2").
|
||||
- Framework: Symfony 7.3.* (see composer.json).
|
||||
- Architecture: MVC with Controllers in `src/Controller`, Entities in `src/Entity`, Repositories in `src/Repository`, Templates in `templates`.
|
||||
- Env files: `.env`, `.env.local` (ignored), and environment overrides like `.env.test.local`.
|
||||
- Routes: annotation/attributes in controllers or YAML/PHP in `config/routes`.
|
||||
- Documentation location: All documentation must live under the `doc/` directory. New docs requested by the team will be added there.
|
||||
|
||||
|
||||
## 2. Coding Standards
|
||||
|
||||
### 2.1 PHP
|
||||
- Standard: PSR-12 (line length soft cap 120; prefer multi-line for long signatures/arrays).
|
||||
- Strict types at top of new files: `declare(strict_types=1);`
|
||||
- Type-hint everything (params, returns, properties). Prefer readonly where applicable.
|
||||
- Visibility: declare on all properties and methods.
|
||||
- Exceptions: throw domain-specific exceptions for game logic; do not return null where an exception is appropriate.
|
||||
- Controllers: keep thin; delegate to services. Use DTOs/Form types to validate input.
|
||||
- Dependency Injection: constructor injection; avoid container-aware services.
|
||||
- Naming: Services suffixed with `...Service`, commands with `...Command`, event listeners with `...Listener`/`...Subscriber`.
|
||||
- Logging: use `Psr\Log\LoggerInterface` where meaningful.
|
||||
|
||||
### 2.2 Twig
|
||||
- Keep templates lean; no heavy logic. Use filters/functions and view models if needed.
|
||||
- Use `trans` for strings that will be localized.
|
||||
- Partial templates/components: place in `templates/_partials` or `templates/components`.
|
||||
|
||||
### 2.3 JavaScript (Stimulus / vanilla)
|
||||
- Use ES modules via Importmap/Asset Mapper.
|
||||
- Controllers in `assets/controllers`. Name as `something_controller.js` following Stimulus conventions.
|
||||
- Prefer small, focused controllers. Keep DOM queries scoped to controller element.
|
||||
- Avoid global state; communicate via events or Turbo streams when appropriate.
|
||||
|
||||
### 2.4 Styles (CSS/SCSS)
|
||||
- Keep styles in `assets/styles`. Use BEM naming for classes.
|
||||
- Prefer CSS variables for theme colors, spacing scale, z-index scale.
|
||||
|
||||
### 2.5 YAML / Config
|
||||
- 2-space indentation, no tabs.
|
||||
- Use parameters and env vars (`env()`) instead of hardcoding secrets.
|
||||
|
||||
### 2.6 Git & Commits
|
||||
- Branch naming: `feature/<short-name>`, `fix/<short-name>`, `chore/<short-name>`.
|
||||
- Commit messages:
|
||||
- Conventional commits style: `feat: ...`, `fix: ...`, `chore: ...`, `docs: ...`, `test: ...`, `refactor: ...`.
|
||||
- First line ≤ 72 chars; add body when needed with rationale.
|
||||
|
||||
|
||||
## 3. Linting, Tests, and Quality
|
||||
|
||||
### 3.1 PHP
|
||||
- Use PHP-CS-Fixer or PHP_CodeSniffer (PHPCS) with PSR-12. If not yet installed, proposed composer scripts (to add later):
|
||||
- `composer require --dev friendsofphp/php-cs-fixer`
|
||||
- Script: `php-cs-fixer fix --allow-risky=yes`
|
||||
- Static analysis: PHPStan level 6–8 recommended:
|
||||
- `composer require --dev phpstan/phpstan`
|
||||
- Run: `vendor/bin/phpstan analyse src tests` (configure `phpstan.neon` later)
|
||||
|
||||
### 3.2 Symfony
|
||||
- Cache and debug:
|
||||
- `php bin/console cache:clear`
|
||||
- `php bin/console debug:router`
|
||||
- `php bin/console debug:container`
|
||||
|
||||
### 3.3 Tests
|
||||
- PHPUnit (already required):
|
||||
- Run tests: `vendor/bin/phpunit`
|
||||
- Tests location: `tests/`
|
||||
- Config: `phpunit.dist.xml`
|
||||
|
||||
### 3.4 Frontend
|
||||
- Stimulus/UX: controllers auto-registered via `symfony/stimulus-bundle`.
|
||||
- Asset Mapper/Importmap:
|
||||
- Install deps: `php bin/console importmap:install`
|
||||
- Dev server (if using symfony local server): `symfony server:start -d`
|
||||
|
||||
|
||||
## 4. Project File Map (Quick Reference)
|
||||
- App kernel: `src/Kernel.php`
|
||||
- Controllers: `src/Controller/`
|
||||
- Domain entities: `src/Entity/`
|
||||
- Repositories: `src/Repository/`
|
||||
- Migrations: `migrations/`
|
||||
- Templates: `templates/`
|
||||
- Translations: `translations/`
|
||||
- Assets entry: `assets/app.js`, styles in `assets/styles/`
|
||||
- Stimulus controllers: `assets/controllers/`
|
||||
- Routes: `config/routes/`
|
||||
- Packages config: `config/packages/`
|
||||
- Env vars: `.env` (base), `.env.local` (local overrides)
|
||||
- Public web root: `public/`
|
||||
- Tests: `tests/`
|
||||
|
||||
|
||||
## 5. How We Build Features (Checklist)
|
||||
1) Create branch: `feature/<name>`.
|
||||
2) Describe the task in an issue with acceptance criteria.
|
||||
3) Implement backend (entities/services/controllers) with tests.
|
||||
4) Implement templates and Stimulus controller if interactive.
|
||||
5) Add/adjust routes and translations.
|
||||
6) Run: linters, phpstan, phpunit; ensure green.
|
||||
7) Open PR; request review.
|
||||
|
||||
|
||||
## 6. Escape Room Domain Notes (early)
|
||||
- Core concepts likely: Game, Room, Puzzle, Session, Player, Team, Hint, Timer.
|
||||
- Consider events (Domain Events) for puzzle solved, hint requested, time warnings.
|
||||
- Real-time collaboration options: Symfony Mercure, WebSockets, or Turbo Streams.
|
||||
- Persist immutable audit trail for gameplay.
|
||||
|
||||
|
||||
## 7. Editor Configuration
|
||||
- Recommend EditorConfig. If we add `.editorconfig` later, set:
|
||||
- Indent 4 spaces for PHP, 2 for YAML/Twig/JS/CSS.
|
||||
- LF line endings, UTF-8, insert final newline, trim trailing whitespace.
|
||||
|
||||
|
||||
## 8. Security & Secrets
|
||||
- Never commit secrets. Use environment variables or Symfony Vault.
|
||||
- Review `APP_ENV`, `APP_SECRET`, database DSN in `.env` and `.env.local`.
|
||||
|
||||
|
||||
## 9. Release & Environments
|
||||
- Envs: `dev`, `test`, `prod`.
|
||||
- Build steps: run migrations, warmup cache, compile assets if using.
|
||||
|
||||
|
||||
## 10. References
|
||||
- Symfony Best Practices: https://symfony.com/doc/current/best_practices.html
|
||||
- Doctrine ORM 3 Docs: https://www.doctrine-project.org/projects/doctrine-orm/en/current/
|
||||
- Stimulus: https://stimulus.hotwired.dev/
|
||||
51
doc/FILES.md
Normal file
51
doc/FILES.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Project File Index (Quick Reference)
|
||||
|
||||
Use this index to quickly locate files and directories during development and in discussions.
|
||||
|
||||
## Top-Level
|
||||
- docker/compose.yaml / docker/compose.override.yaml — Docker services.
|
||||
- docker/ — Docker build contexts and configs (php Dockerfile, nginx vhost, compose files).
|
||||
- composer.json / composer.lock — Dependencies and scripts.
|
||||
- importmap.php — Importmap configuration for JS dependencies.
|
||||
- phpunit.dist.xml — PHPUnit configuration.
|
||||
- public/ — Web root (index.php, assets, static files).
|
||||
- var/ — Cache and logs.
|
||||
- vendor/ — Composer dependencies.
|
||||
|
||||
## Application Source (src/)
|
||||
- src/Kernel.php — Symfony Kernel bootstrapping.
|
||||
- src/Website/ — Marketing/public website area (controllers, templates under templates/website/).
|
||||
- src/Game/ — Game area (controllers, templates under templates/game/).
|
||||
- src/Entity/ — Doctrine ORM entities.
|
||||
- src/Repository/ — Doctrine repositories.
|
||||
|
||||
## Configuration (config/)
|
||||
- config/packages/ — Symfony bundles configuration (framework.yaml, cache.yaml, etc.).
|
||||
- config/routes/ — Routing configuration files.
|
||||
- config/routes/app.yaml — Imports attribute routes for both sites (Website and Game).
|
||||
|
||||
## Data & DB
|
||||
- migrations/ — Doctrine migrations.
|
||||
|
||||
## Presentation
|
||||
- templates/ — Twig templates.
|
||||
- templates/website — Views for the public website.
|
||||
- templates/game — Views for the game area.
|
||||
- translations/ — i18n message files.
|
||||
|
||||
## Frontend Assets
|
||||
- assets/app.js — Main JS entry (imports Stimulus, styles, etc.).
|
||||
- assets/controllers/ — Stimulus controllers.
|
||||
- assets/styles/ — Global styles.
|
||||
- assets/vendor/ — Vendor frontend assets if any.
|
||||
|
||||
## Tests
|
||||
- tests/ — Test suites for PHPUnit.
|
||||
|
||||
## Environment
|
||||
- .env — Base environment configuration.
|
||||
- .env.local — Local overrides (ignored).
|
||||
|
||||
## Notes
|
||||
- Follow doc/CONTRIBUTING.md for code style and workflows.
|
||||
- Email setup: see doc/email.md for dev Mailpit and production SendGrid configuration.
|
||||
10
doc/README.md
Normal file
10
doc/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Project Documentation
|
||||
|
||||
This directory contains all project documentation for EscapePage.
|
||||
|
||||
- Code Style & Contribution Guide: CONTRIBUTING.md
|
||||
- Project File Index: FILES.md
|
||||
|
||||
Policy: All new and existing documentation must be placed in this doc/ directory. If you ask Junie to add docs in the future, they will be created under doc/.
|
||||
|
||||
Additional docs can be added here as the project grows (architecture decisions, API docs, gameplay design, onboarding, etc.).
|
||||
60
doc/docker.md
Normal file
60
doc/docker.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Docker Setup
|
||||
|
||||
This app can run fully in Docker using docker compose with PHP-FPM, Nginx and MySQL.
|
||||
|
||||
## Services
|
||||
- php: PHP 8.2 FPM with required extensions and Composer
|
||||
- nginx: Serves the Symfony app from public/ and proxies PHP to php-fpm
|
||||
- database: MySQL 8.0 (data persisted in a volume)
|
||||
- mailer (dev only via compose.override.yaml): Mailpit (SMTP/UI)
|
||||
|
||||
## Prerequisites
|
||||
- Docker and Docker Compose (v2)
|
||||
|
||||
## Usage
|
||||
|
||||
### 1) Build and start
|
||||
```
|
||||
./docker/setup.sh
|
||||
```
|
||||
App will be served at http://localhost:8080
|
||||
|
||||
Alternatively (manual):
|
||||
```
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml up -d --build
|
||||
```
|
||||
|
||||
### 2) Install dependencies
|
||||
The setup script already runs composer install. To run manually:
|
||||
```
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml exec php composer install
|
||||
```
|
||||
|
||||
### 3) Prepare DB
|
||||
The setup script already prepares the DB. To run manually:
|
||||
```
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml exec php php bin/console doctrine:database:create --if-not-exists
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml exec php php bin/console doctrine:migrations:migrate -n
|
||||
```
|
||||
|
||||
### 4) Run tests
|
||||
```
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml exec php vendor/bin/phpunit
|
||||
```
|
||||
|
||||
### 5) Logs
|
||||
```
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml logs -f nginx
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml logs -f php
|
||||
```
|
||||
|
||||
### 6) Stop
|
||||
```
|
||||
docker compose -f docker/compose.yaml -f docker/compose.override.yaml down
|
||||
```
|
||||
|
||||
## Notes
|
||||
- .env already points DATABASE_URL to the `database` service hostname.
|
||||
- Nginx listens on port 8080 (mapped from container 80) to avoid conflicts.
|
||||
- Source is bind-mounted; changes on host reflect inside containers.
|
||||
- For production images, create a separate Dockerfile with build steps (composer install --no-dev, cache warmup) and avoid bind mounts.
|
||||
45
doc/email.md
Normal file
45
doc/email.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Email Delivery: Dev Mailcatcher & Production SendGrid
|
||||
|
||||
This application uses Symfony Mailer. We separate development and production delivery:
|
||||
|
||||
- Development: Mailpit (mailcatcher) via SMTP in Docker.
|
||||
- Production: SendGrid via API transport.
|
||||
|
||||
## Development (Mailpit)
|
||||
|
||||
- Service is defined in `compose.override.yaml` as `mailer` (axllent/mailpit).
|
||||
- Ports:
|
||||
- SMTP: 1025 (mapped to host 1025)
|
||||
- Web UI: 8025 (mapped to host 8025)
|
||||
- Default DSN for dev is set in `.env`:
|
||||
|
||||
```
|
||||
MAILER_DSN=smtp://mailer:1025
|
||||
```
|
||||
|
||||
- Usage:
|
||||
1. Start stack: `docker compose up -d`
|
||||
2. Send an email from the app.
|
||||
3. Open http://localhost:8025 to view captured emails.
|
||||
|
||||
## Production (SendGrid)
|
||||
|
||||
Use the SendGrid API transport. Do not commit secrets.
|
||||
|
||||
- Example configuration is in `.env.prod`:
|
||||
|
||||
```
|
||||
MAILER_DSN=sendgrid+api://%env(resolve:SENDGRID_API_KEY)%@default
|
||||
```
|
||||
|
||||
- Provide `SENDGRID_API_KEY` via:
|
||||
- Real environment variable on the server/container, or
|
||||
- Symfony secrets: `php bin/console secrets:set SENDGRID_API_KEY` (and dump for prod), or
|
||||
- Orchestration secret stores (e.g., Docker/K8s).
|
||||
|
||||
### Notes
|
||||
- No Mailpit container is defined in the base `compose.yaml`, only in `compose.override.yaml`. This ensures it is used in development only.
|
||||
- To test email locally without Docker, you can:
|
||||
- Run Mailpit on your host (ports 1025/8025) and set `MAILER_DSN=smtp://127.0.0.1:1025` in `.env.local`.
|
||||
- If you need to use SendGrid SMTP instead of API, a DSN example:
|
||||
`smtp://apikey:YOUR_SENDGRID_API_KEY@smtp.sendgrid.net:587`.
|
||||
@@ -1,18 +1,25 @@
|
||||
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
php:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: php/Dockerfile
|
||||
container_name: escapepage-php
|
||||
volumes:
|
||||
- ../:/var/www/html:delegated
|
||||
environment:
|
||||
APP_ENV: dev
|
||||
depends_on:
|
||||
- database
|
||||
networks:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27-alpine
|
||||
container_name: escapepage-nginx
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
@@ -20,26 +27,50 @@ services:
|
||||
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- php
|
||||
networks:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
mailer:
|
||||
image: axllent/mailpit:latest
|
||||
container_name: escapepage-mailer
|
||||
ports:
|
||||
- "8025:8025"
|
||||
networks:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
database:
|
||||
image: mysql:8.0
|
||||
container_name: escapepage-db
|
||||
environment:
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-app}
|
||||
MYSQL_USER: ${MYSQL_USER:-app}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-!ChangeMe!}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root}
|
||||
command: ["--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD:-root}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 60s
|
||||
start_period: 30s
|
||||
command: ["--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]
|
||||
volumes:
|
||||
- database_data:/var/lib/mysql:rw
|
||||
# Uncomment the two lines below if you need to access MySQL from your host (workbench, etc.)
|
||||
# ports:
|
||||
# - "3306:3306"
|
||||
networks:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
volumes:
|
||||
###> doctrine/doctrine-bundle ###
|
||||
database_data:
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
networks:
|
||||
backend:
|
||||
driver: bridge
|
||||
|
||||
28
docker/nginx/default.conf
Normal file
28
docker/nginx/default.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /var/www/html/public;
|
||||
index index.php index.html;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
fastcgi_param DOCUMENT_ROOT $realpath_root;
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_read_timeout 120;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
|
||||
client_max_body_size 32m;
|
||||
}
|
||||
21
docker/php/Dockerfile
Normal file
21
docker/php/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM php:8.2-fpm-alpine
|
||||
|
||||
# Install system deps
|
||||
RUN apk add --no-cache bash git icu-dev libzip-dev oniguruma-dev
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-configure intl \
|
||||
&& docker-php-ext-install -j$(nproc) intl pdo pdo_mysql opcache
|
||||
|
||||
# Install composer
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1 \
|
||||
COMPOSER_HOME=/tmp/composer
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Configure PHP
|
||||
COPY php.ini $PHP_INI_DIR/conf.d/zz-custom.ini
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Default command
|
||||
CMD ["php-fpm"]
|
||||
9
docker/php/php.ini
Normal file
9
docker/php/php.ini
Normal file
@@ -0,0 +1,9 @@
|
||||
memory_limit=512M
|
||||
post_max_size=32M
|
||||
upload_max_filesize=32M
|
||||
max_execution_time=60
|
||||
; For Symfony dev
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.validate_timestamps=1
|
||||
opcache.revalidate_freq=0
|
||||
126
docker/setup.sh
Normal file
126
docker/setup.sh
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Simple setup script to bootstrap the Dockerized dev stack for this project.
|
||||
# - Builds and starts containers (php, nginx, mariadb, mailpit via override)
|
||||
# - Installs composer dependencies
|
||||
# - Ensures APP_SECRET is set (generates if empty)
|
||||
# - Creates and migrates the database
|
||||
# - Installs/imports JS dependencies
|
||||
# - Prints helpful info on success
|
||||
|
||||
# Usage:
|
||||
# ./docker/setup.sh # full setup
|
||||
# ./docker/setup.sh --no-build # skip image rebuild
|
||||
# ./docker/setup.sh --down # stop and remove containers (down)
|
||||
# ./docker/setup.sh --recreate # force recreate containers
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "$0")"/.. && pwd)
|
||||
DOCKER_DIR="$ROOT_DIR/docker"
|
||||
# Determine the docker compose command (V2 'docker compose' or V1 'docker-compose')
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
DOCKER_COMPOSE="docker compose"
|
||||
elif command -v docker-compose >/dev/null 2>&1; then
|
||||
DOCKER_COMPOSE="docker-compose"
|
||||
else
|
||||
echo "Error: Neither 'docker compose' nor 'docker-compose' was found. Please install Docker Compose." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Helper to run docker compose from the docker/ directory
|
||||
dc() { (cd "$DOCKER_DIR" && $DOCKER_COMPOSE -f compose.yaml "$@"); }
|
||||
|
||||
REBUILD=1
|
||||
RECREATE=0
|
||||
DOWN_ONLY=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--no-build) REBUILD=0 ;;
|
||||
--recreate) RECREATE=1 ;;
|
||||
--down) DOWN_ONLY=1 ;;
|
||||
*) echo "Unknown option: $arg" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
need() { command -v "$1" >/dev/null 2>&1 || { echo "Error: '$1' is required but not installed." >&2; exit 1; }; }
|
||||
|
||||
need docker
|
||||
|
||||
if [ "$DOWN_ONLY" -eq 1 ]; then
|
||||
dc down
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BUILD_ARGS=()
|
||||
if [ "$REBUILD" -eq 1 ]; then
|
||||
BUILD_ARGS+=("--build")
|
||||
fi
|
||||
if [ "$RECREATE" -eq 1 ]; then
|
||||
BUILD_ARGS+=("--force-recreate")
|
||||
fi
|
||||
|
||||
# Start stack
|
||||
dc up "${BUILD_ARGS[@]}"
|
||||
|
||||
# Helper to run commands in php container
|
||||
pexec() { dc exec -T php "$@"; }
|
||||
|
||||
# Wait for database to be healthy (mariadb)
|
||||
printf "Waiting for database to be healthy..."
|
||||
# Use docker inspect health status
|
||||
DB_HEALTH=""
|
||||
for i in {1..60}; do
|
||||
DB_HEALTH=$(docker inspect -f '{{.State.Health.Status}}' "$(docker ps --filter name=_database_ --format '{{.ID}}' | head -n1)" 2>/dev/null || true)
|
||||
if [ "$DB_HEALTH" = "healthy" ]; then
|
||||
echo " OK"
|
||||
break
|
||||
fi
|
||||
printf "."
|
||||
sleep 2
|
||||
if [ "$i" -eq 60 ]; then
|
||||
echo "\nWarning: database health check not healthy yet, continuing anyway."
|
||||
fi
|
||||
done
|
||||
|
||||
# Ensure composer is available and install dependencies
|
||||
pexec composer install --no-interaction
|
||||
|
||||
# Ensure APP_SECRET is set
|
||||
if grep -q '^APP_SECRET=$' "$ROOT_DIR/.env" 2>/dev/null; then
|
||||
echo "Generating APP_SECRET in .env.local..."
|
||||
mkdir -p "$ROOT_DIR"
|
||||
SECRET=$(openssl rand -hex 16)
|
||||
# Write to .env.local so we don't commit it
|
||||
if [ ! -f "$ROOT_DIR/.env.local" ]; then
|
||||
printf "APP_SECRET=%s\n" "$SECRET" > "$ROOT_DIR/.env.local"
|
||||
elif ! grep -q '^APP_SECRET=' "$ROOT_DIR/.env.local"; then
|
||||
printf "APP_SECRET=%s\n" "$SECRET" >> "$ROOT_DIR/.env.local"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Prepare DB
|
||||
pexec php bin/console doctrine:database:create --if-not-exists || true
|
||||
pexec php bin/console doctrine:migrations:migrate -n || true
|
||||
|
||||
# Import JS deps (Importmap/Asset Mapper)
|
||||
pexec php bin/console importmap:install || true
|
||||
|
||||
APP_URL=http://localhost:8080
|
||||
MAILPIT_URL=http://localhost:8025
|
||||
|
||||
cat <<EOT
|
||||
|
||||
Setup complete!
|
||||
|
||||
Open the app: $APP_URL
|
||||
Mailpit (dev): $MAILPIT_URL
|
||||
|
||||
Common commands:
|
||||
(cd docker && $DOCKER_COMPOSE logs -f nginx)
|
||||
(cd docker && $DOCKER_COMPOSE logs -f php)
|
||||
(cd docker && $DOCKER_COMPOSE exec php bash)
|
||||
(cd docker && $DOCKER_COMPOSE down)
|
||||
|
||||
You can re-run this script any time. Use --no-build to skip rebuilding images.
|
||||
EOT
|
||||
8205
package-lock.json
generated
Normal file
8205
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
Normal file
25
package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "escapepage",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "EscapePage Symfony app assets built with Webpack Encore",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"dev": "encore dev",
|
||||
"watch": "encore dev --watch",
|
||||
"build": "encore production"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.0",
|
||||
"@babel/preset-env": "^7.25.0",
|
||||
"@symfony/webpack-encore": "^4.6.1",
|
||||
"babel-loader": "^9.1.3",
|
||||
"core-js": "^3.37.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-notifier": "^1.15.0"
|
||||
}
|
||||
}
|
||||
55
src/Command/TestEmailCommand.php
Normal file
55
src/Command/TestEmailCommand.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:mail:test',
|
||||
description: 'Sends a simple test email using the configured mail transport (SendGrid in prod).'
|
||||
)]
|
||||
final class TestEmailCommand extends Command
|
||||
{
|
||||
public function __construct(private readonly MailerInterface $mailer)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('to', InputArgument::REQUIRED, 'Recipient email address')
|
||||
->addOption('subject', null, InputOption::VALUE_REQUIRED, 'Email subject', 'EscapePage mailer test')
|
||||
->addOption('from', null, InputOption::VALUE_REQUIRED, 'Sender email address (defaults to MAILER_FROM if set)')
|
||||
->addOption('from-name', null, InputOption::VALUE_REQUIRED, 'Sender name', 'EscapePage');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$to = (string) $input->getArgument('to');
|
||||
$subject = (string) $input->getOption('subject');
|
||||
$fromEmail = (string) ($input->getOption('from') ?? ($_ENV['MAILER_FROM'] ?? $_SERVER['MAILER_FROM'] ?? 'no-reply@example.com'));
|
||||
$fromName = (string) $input->getOption('from-name');
|
||||
|
||||
$email = (new Email())
|
||||
->from(new Address($fromEmail, $fromName))
|
||||
->to($to)
|
||||
->subject($subject)
|
||||
->html('<p>This is a test email sent at ' . date('c') . '.</p><p>If you see this, your mailer setup works.</p>')
|
||||
->text('This is a test email sent at ' . date('c') . ". If you see this, your mailer setup works.");
|
||||
|
||||
$this->mailer->send($email);
|
||||
|
||||
$output->writeln('<info>Test email sent to ' . $to . '.</info>');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
0
src/Controller/.gitignore
vendored
0
src/Controller/.gitignore
vendored
0
src/Entity/.gitignore
vendored
0
src/Entity/.gitignore
vendored
17
src/Game/Controller/HubController.php
Normal file
17
src/Game/Controller/HubController.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
final class HubController extends AbstractController
|
||||
{
|
||||
#[Route(path: '', name: 'game_hub')]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('game/hub/index.html.twig');
|
||||
}
|
||||
}
|
||||
0
src/Repository/.gitignore
vendored
0
src/Repository/.gitignore
vendored
18
src/Website/Controller/HomeController.php
Normal file
18
src/Website/Controller/HomeController.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Website\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
final class HomeController extends AbstractController
|
||||
{
|
||||
#[Route(path: '', name: 'website_home')]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render(
|
||||
'website/home/index.html.twig');
|
||||
}
|
||||
}
|
||||
25
symfony.lock
25
symfony.lock
@@ -229,6 +229,15 @@
|
||||
"config/routes/security.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/sendgrid-mailer": {
|
||||
"version": "7.3",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "4.4",
|
||||
"ref": "224aedffb66812dc2b0965dabc14d5f800941da6"
|
||||
}
|
||||
},
|
||||
"symfony/stimulus-bundle": {
|
||||
"version": "2.30",
|
||||
"recipe": {
|
||||
@@ -319,6 +328,22 @@
|
||||
"config/packages/messenger.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/webpack-encore-bundle": {
|
||||
"version": "2.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "2.0",
|
||||
"ref": "719f6110345acb6495e496601fc1b4977d7102b3"
|
||||
},
|
||||
"files": [
|
||||
"./assets/app.js",
|
||||
"./assets/styles/app.css",
|
||||
"./config/packages/webpack_encore.yaml",
|
||||
"./package.json",
|
||||
"./webpack.config.js"
|
||||
]
|
||||
},
|
||||
"twig/extra-bundle": {
|
||||
"version": "v3.21.0"
|
||||
}
|
||||
|
||||
@@ -4,24 +4,17 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{{ 'site.name'|trans }}{% endblock %}</title>
|
||||
{% block stylesheets %}{% endblock %}
|
||||
{% block stylesheets %}
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
{% set pathinfo = app.request.pathinfo %}
|
||||
{% set is_nl = pathinfo starts with '/nl' %}
|
||||
<a href="{{ is_nl ? '/nl' : '/' }}">{{ 'nav.home'|trans }}</a> |
|
||||
<a href="{{ is_nl ? '/nl/game' : '/game' }}">{{ 'nav.game'|trans }}</a>
|
||||
<span style="margin-left:1rem">
|
||||
{# Language switcher: URL prefix strategy. Our routes have localized prefixes: en (no prefix), nl (/nl). #}
|
||||
{% set pathinfo = app.request.pathinfo %}
|
||||
{% set is_nl = pathinfo starts with '/nl' %}
|
||||
{% set en_url = is_nl ? pathinfo|slice(3) : pathinfo %}
|
||||
{% set nl_url = is_nl ? pathinfo : '/nl' ~ pathinfo %}
|
||||
<a href="{{ en_url ?: '/' }}">EN</a> /
|
||||
<a href="{{ nl_url }}">NL</a>
|
||||
</span>
|
||||
<a href="/">{{ 'nav.home'|trans }}</a> |
|
||||
<a href="/game">{{ 'nav.game'|trans }}</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
@@ -30,6 +23,8 @@
|
||||
<footer>
|
||||
<small>© {{ "now"|date("Y") }} {{ 'site.name'|trans }}</small>
|
||||
</footer>
|
||||
{% block javascripts %}{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ encore_entry_script_tags('app') }}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
22
templates/game/hub/index.html.twig
Normal file
22
templates/game/hub/index.html.twig
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}{{ 'game.title'|trans({'%site%': ('site.name'|trans)}) }}{% endblock %}
|
||||
|
||||
{# Include Game1-specific CSS in addition to the base app assets #}
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('game1') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ 'game.h1'|trans }}</h1>
|
||||
<p>{{ 'game.description'|trans }}</p>
|
||||
<div class="game1-banner">Game 1 assets are active. Enjoy the challenge!</div>
|
||||
<p><a href="{{ path('website_home') }}">{{ 'link.back_to_website'|trans }}</a></p>
|
||||
{% endblock %}
|
||||
|
||||
{# Include Game1-specific JS in addition to the base app assets #}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('game1') }}
|
||||
{% endblock %}
|
||||
9
templates/website/home/index.html.twig
Normal file
9
templates/website/home/index.html.twig
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}{{ 'home.title'|trans({'%site%': ('site.name'|trans)}) }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ 'home.h1'|trans({'%site%': ('site.name'|trans)}) }}</h1>
|
||||
<p>{{ 'home.description'|trans }}</p>
|
||||
<p><a href="{{ path('game_hub') }}">{{ 'link.enter_game'|trans }}</a></p>
|
||||
{% endblock %}
|
||||
16
translations/messages.en.yaml
Normal file
16
translations/messages.en.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# General
|
||||
site.name: EscapePage
|
||||
nav.home: Home
|
||||
nav.game: Game
|
||||
link.enter_game: Enter the Game Area
|
||||
link.back_to_website: Back to Website
|
||||
|
||||
# Home page
|
||||
home.title: "Welcome | EscapePage"
|
||||
home.h1: "Welcome to EscapePage"
|
||||
home.description: "This is the public website. Minimal interaction, information about the game, and links."
|
||||
|
||||
# Game hub
|
||||
game.title: "Game Hub | EscapePage"
|
||||
game.h1: "Game Area"
|
||||
game.description: "This is the game hub. Interactive components will be built here."
|
||||
16
translations/messages.nl.yaml
Normal file
16
translations/messages.nl.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Algemeen
|
||||
site.name: EscapePage
|
||||
nav.home: Start
|
||||
nav.game: Spel
|
||||
link.enter_game: Ga naar het Speelgedeelte
|
||||
link.back_to_website: Terug naar Website
|
||||
|
||||
# Home pagina
|
||||
home.title: "Welkom | %{site}%"
|
||||
home.h1: "Welkom bij %{site}%"
|
||||
home.description: "Dit is de publieke website. Beperkte interactie, informatie over het spel en links."
|
||||
|
||||
# Spel hub
|
||||
game.title: "Spel Hub | %{site}%"
|
||||
game.h1: "Speelgedeelte"
|
||||
game.description: "Dit is de spelhub. Interactieve componenten worden hier later gebouwd."
|
||||
34
webpack.config.js
Normal file
34
webpack.config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const Encore = require('@symfony/webpack-encore');
|
||||
|
||||
if (!Encore.isRuntimeEnvironmentConfigured()) {
|
||||
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
|
||||
}
|
||||
|
||||
Encore
|
||||
// where compiled assets will be stored
|
||||
.setOutputPath('public/build/')
|
||||
// public path used by the web server to access the output path
|
||||
.setPublicPath('/build')
|
||||
// clean output before build
|
||||
.cleanupOutputBeforeBuild()
|
||||
// main entry for your app
|
||||
.addEntry('app', './assets/app.js')
|
||||
.addEntry('game1', './assets/game1.js')
|
||||
// split entry chunks for better caching
|
||||
.splitEntryChunks()
|
||||
// will require an extra script tag for runtime.js
|
||||
.enableSingleRuntimeChunk()
|
||||
// features
|
||||
.enableSourceMaps(!Encore.isProduction())
|
||||
.enableVersioning(Encore.isProduction())
|
||||
// Babel config for wide browser support
|
||||
.configureBabelPresetEnv((options) => {
|
||||
options.useBuiltIns = 'usage';
|
||||
options.corejs = 3;
|
||||
options.bugfixes = true;
|
||||
})
|
||||
.autoProvidejQuery(false)
|
||||
.enableBuildNotifications()
|
||||
;
|
||||
|
||||
module.exports = Encore.getWebpackConfig();
|
||||
Reference in New Issue
Block a user