diff --git a/Makefile b/Makefile index 7957c92..7547c03 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help start start-build stop build down ps logs prune-everything db-reset +.PHONY: help start start-build stop build down ps logs prune-everything db-reset mercure-jwt .DEFAULT_GOAL := help @@ -38,6 +38,9 @@ prune-everything: fi docker compose down -v --rmi all --remove-orphans +mercure-jwt: + @php bin/generate-mercure-jwt.php + db-reset: @echo "WARNING: This will DROP and RECREATE the database!" @read -p "Type 'yes' to confirm: " confirm; \ diff --git a/README.md b/README.md index 59b830f..464983a 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,143 @@ -mineseeker -========= +# MineSeeker -A Symfony project created on September 22, 2016, 13:56 pm. +A real-time **1v1 multiplayer minesweeper** game played in the browser. +Two players race on the same hidden minefield — uncover safe cells to score points, but hit a mine and you hand the advantage to your opponent. +Games are live and synchronised instantly via a Mercure hub; no page reloads, no polling. -PROJECT VERSION 1.1.0-20191026 +Created by [SplendidBear](https://www.splendidbear.org). -This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket. +--- -#### Must installed modules w/ npm are in package.json + to global: +## Features - $ npm install webpack -g +- **Real-time 1v1 gameplay** — moves broadcast instantly over Mercure (server-sent events) +- **Guest & registered play** — jump in anonymously or create an account for stats and history +- **Full authentication stack** — email/password, passkeys (WebAuthn), TOTP 2FA with backup codes +- **Player profiles** — win/loss/draw stats, per-month charts, recent battle history with shareable replay links +- **Profile pictures** — uploaded to MinIO object storage, thumbnails generated on-the-fly by LiipImagine +- **Battle replay sharing** — share a direct link to any finished game +- **Docker-ready** — single `make start-build` brings up the full production-like stack - You will need a - .babelrc file w/ the presets - webpack.config.js - https://webpack.github.io/docs/webpack-for-browserify-users.html - same as dir where the package.json!! +--- -__(!) Tutorial: https://egghead.io/lessons/react-introduction-to-properties__ +## Tech stack -#### Backend WebSocket server start as daemon - GeniusesOfSymfony/WebSocketBundle +| Layer | Technology | +|---|---| +| Backend | PHP 8.5, Symfony 7.4, Doctrine ORM | +| Frontend | React 19, Vite, MUI, SCSS | +| Database | PostgreSQL 18 | +| Real-time | Mercure (built into FrankenPHP / Caddy) | +| File storage | MinIO (S3-compatible) | +| Image processing | LiipImagine + Flysystem | +| Server | FrankenPHP (Caddy + PHP in one binary) | +| Auth | Symfony Security, Scheb 2FA, web-auth/webauthn-framework | - $ nohup bin/console gos:websocket:server & +--- -#### React JS WebPack watch generator w/ babel presets: es2015, react +## Requirements - $ webpack -p --config=webpack-prod.config.js +### Bare-metal development +- PHP >= 8.5 with extensions: `pdo_pgsql`, `gd`, `intl`, `zip`, `sodium` +- Composer 2 +- Node.js 22 + Bun +- PostgreSQL 18 +- Caddy with FrankenPHP and the Mercure module +- MailHog (or any SMTP server on port 1025) +- MinIO (listening on port 9000) - PROD +### Docker +- Docker Engine 24+ +- Docker Compose v2 +- Bun (for building frontend assets before `make start-build`) +- Composer (for `make start-build`) - $ webpack --progress --colors --watch -d +--- - DEV +## Installation - -d --> Debugger; If you write this line somewhere: debugger; - The browser will stop the code here!!! +### 1. Clone the repository -#### Connect to Prod +```bash +git clone https://github.com/splendidbear/mineseeker.git +cd mineseeker +``` - ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang +### 2. Configure environment + +```bash +cp .env.dist .env +``` + +Edit `.env` and fill in every value. Key ones: + +| Variable | What to set | +|---|---| +| `APP_SECRET` | Random 32-byte hex: `openssl rand -hex 32` | +| `POSTGRES_USER/PASSWORD/DB` | Your PostgreSQL credentials | +| `MINIO_ROOT_USER/PASSWORD` | MinIO admin credentials | +| `MINIO_ENDPOINT` | `http://localhost:9000` (bare-metal) | +| `MINIO_PUBLIC_URL` | Public URL browsers use to reach MinIO | +| `RECAPTCHA_SITE_KEY/SECRET_KEY` | Google reCAPTCHA v3 keys for your domain | +| `MERCURE_JWT_SECRET` | Random secret (generated in step 3) | +| `MERCURE_JWT_TOKEN` | Signed publisher JWT (generated in step 3) | +| `MERCURE_SUBSCRIBER_JWT` | Signed subscriber JWT (generated in step 3) | +| `MAILER_DSN` | `smtp://localhost:1025` for MailHog in dev | + +### 3. Generate Mercure JWT tokens + +```bash +composer install +make mercure-jwt +``` + +Copy the printed values into `.env` and into the `publisher_jwt` / `subscriber_jwt` lines of your Caddy Mercure block, then reload Caddy: + +```bash +sudo systemctl reload caddy +``` + +### 4a. Run with Docker + +```bash +make start-build +``` + +This installs PHP dependencies, builds the frontend assets with Bun, builds the Docker image, and starts all services (`app`, `db`, `mail`, `minio`, `minio_init`). + +The app is available at `http://localhost:10080` (or the domain set in `APP_PUBLIC_HOSTNAME`). + +To apply any code changes later, run the same command again. + +### 4b. Run bare-metal (development) + +```bash +composer install +bun install +bun run dev # Vite dev server with hot-reload +php bin/console doctrine:migrations:migrate --no-interaction +``` + +Start MinIO and MailHog, then open the URL configured in your Caddy vhost. + +### 5. MinIO bucket setup + +**Docker** — handled automatically on first start by the `minio_init` service. No action needed. + +**Bare-metal** — run once after MinIO is up: + +```bash +mc alias set local http://localhost:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD +mc mb local/mineseeker +echo '' | mc pipe local/mineseeker/media/.keep +echo '' | mc pipe local/mineseeker/cache/.keep +# Apply public-read policy for media/ and cache/ — see docker/minio-init.sh for the JSON +``` + +--- + +## License + +LGPL-3.0 — see [LICENSE](LICENSE) for details. + +© 2026 [SplendidBear](https://www.splendidbear.org) diff --git a/bin/generate-mercure-jwt.php b/bin/generate-mercure-jwt.php new file mode 100755 index 0000000..2f6906d --- /dev/null +++ b/bin/generate-mercure-jwt.php @@ -0,0 +1,31 @@ +#!/usr/bin/env php + ['publish' => ['*']]], + $secret, + 'HS256' +); + +$subscriberToken = JWT::encode( + ['mercure' => ['subscribe' => ['*']]], + $secret, + 'HS256' +); + +echo PHP_EOL; +echo "# ── .env ──────────────────────────────────────────────────────────────" . PHP_EOL; +echo "MERCURE_JWT_SECRET=\"{$secret}\"" . PHP_EOL; +echo "MERCURE_JWT_TOKEN={$publisherToken}" . PHP_EOL; +echo "MERCURE_SUBSCRIBER_JWT={$subscriberToken}" . PHP_EOL; +echo PHP_EOL; +echo "# ── /etc/caddy/conf.d/mine.caddy (inside the mercure {} block) ───────" . PHP_EOL; +echo "publisher_jwt {$secret} HS256" . PHP_EOL; +echo "subscriber_jwt {$secret} HS256" . PHP_EOL; +echo PHP_EOL;