Implemented host-controlled game start mechanism that generates unique player chips from user names. Added `started_at` timestamp to games and new `game_chips` table to store player identifiers. Chips are created using first name initial plus first two surname letters (e.g., SNE for Sascha Nesterovic) with automatic conflict resolution. Updated frontend to display start button for hosts, show game status, and populate chip selection modal
222 lines
6.1 KiB
Markdown
222 lines
6.1 KiB
Markdown
# Cluedo HP Webapp
|
||
|
||
A small multiplayer web app that acts as a digital note sheet for a Harry Potter-inspired Cluedo game. Several logged-in players can join the same game and manage their personal clues independently.
|
||
|
||
## Features
|
||
|
||
- Login with user and admin roles
|
||
- Admin-managed user creation and deactivation
|
||
- Multiple games per user
|
||
- Join games using a short join code
|
||
- Host-controlled game start with automatic player chips
|
||
- Player chips are generated from the first name initial and first two surname letters, e.g. `SNE` for Sascha Nesterovic
|
||
- Automatic player list with host indication
|
||
- Personal note sheet for each player and game
|
||
- Categories for suspects, items, and locations
|
||
- Entry status tracking: empty, ruled out, present, or maybe
|
||
- Additional notes using `i`, `m`, and `s.<chip>`
|
||
- Winner selection by the game host
|
||
- Winner badge and confetti animation for all players
|
||
- Live updates for new players and winner changes
|
||
- Personal game statistics
|
||
- Password change functionality
|
||
- Harry Potter house themes: Default, Gryffindor, Slytherin, Ravenclaw, and Hufflepuff
|
||
- Installable as a Progressive Web App
|
||
|
||
## Technology
|
||
|
||
### Frontend
|
||
|
||
- React 18
|
||
- Vite
|
||
- Nginx as the production web server
|
||
- `canvas-confetti` for the winner animation
|
||
- `vite-plugin-pwa` for PWA support
|
||
|
||
### Backend
|
||
|
||
- Python 3.12
|
||
- FastAPI
|
||
- SQLAlchemy 2
|
||
- SQLite 3
|
||
- Passlib and bcrypt for password hashing
|
||
|
||
### Deployment
|
||
|
||
- Docker Compose
|
||
- Frontend on port `8081`
|
||
- Backend on port `8080`
|
||
- SQLite database stored in a persistent Docker volume
|
||
|
||
## Quick start with Docker
|
||
|
||
Requirements:
|
||
|
||
- Docker
|
||
- Docker Compose
|
||
|
||
1. Copy `.env.example` to `.env` and set a strong secret:
|
||
|
||
```env
|
||
BACKEND_SECRET_KEY=use-a-long-random-secret-value
|
||
```
|
||
|
||
2. Build and start the containers:
|
||
|
||
```bash
|
||
docker compose up --build
|
||
```
|
||
|
||
3. Open the application:
|
||
|
||
- Frontend: http://localhost:8081
|
||
- Backend/API: http://localhost:8080
|
||
|
||
On the first startup, the application opens a setup screen where you create the first administrator with an email address, display name, and password. The default suspects, items, and locations are seeded automatically as well.
|
||
|
||
The SQLite database is stored in the Docker volume `cluedo-data` and survives normal container restarts.
|
||
|
||
Start the containers in the background:
|
||
|
||
```bash
|
||
docker compose up -d --build
|
||
```
|
||
|
||
View logs:
|
||
|
||
```bash
|
||
docker compose logs -f backend
|
||
```
|
||
|
||
Stop the containers:
|
||
|
||
```bash
|
||
docker compose down
|
||
```
|
||
|
||
## Local development without Docker
|
||
|
||
### Backend
|
||
|
||
```bash
|
||
cd backend
|
||
python -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install -r requirements.txt
|
||
|
||
export DATABASE_URL=sqlite:///./data/cluedo.db
|
||
export SECRET_KEY=use-a-long-random-secret-value
|
||
|
||
uvicorn app.main:app --reload --port 8080
|
||
```
|
||
|
||
On Windows PowerShell, set the environment variables like this:
|
||
|
||
```powershell
|
||
$env:DATABASE_URL = "sqlite:///./data/cluedo.db"
|
||
$env:SECRET_KEY = "use-a-long-random-secret-value"
|
||
uvicorn app.main:app --reload --port 8080
|
||
```
|
||
|
||
### Frontend
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
The frontend is normally available at http://localhost:5173. During development, the API must be reachable under `/api`; the production Docker setup provides this through Nginx.
|
||
|
||
Create a production build:
|
||
|
||
```bash
|
||
npm run build
|
||
```
|
||
|
||
## Project structure
|
||
|
||
```text
|
||
.
|
||
├── backend/
|
||
│ ├── app/
|
||
│ │ ├── main.py # FastAPI app, startup, seed data
|
||
│ │ ├── models.py # SQLAlchemy models
|
||
│ │ ├── db.py # SQLite engine and sessions
|
||
│ │ ├── security.py # Password and cookie logic
|
||
│ │ └── routes/ # Auth, admin, and game APIs
|
||
│ ├── Dockerfile
|
||
│ └── requirements.txt
|
||
├── frontend/
|
||
│ ├── src/
|
||
│ │ ├── App.jsx # Main UI and game flow
|
||
│ │ ├── api/client.js # API client
|
||
│ │ ├── components/ # Pages, cards, and modals
|
||
│ │ ├── styles/ # Themes and inline styles
|
||
│ │ └── utils/ # Small helpers and storage utilities
|
||
│ ├── Dockerfile
|
||
│ └── nginx.conf
|
||
├── docker-compose.yml
|
||
└── .env
|
||
```
|
||
|
||
## Data model
|
||
|
||
- `users`: users, roles, status, display names, and themes
|
||
- `games`: game name, join code, host, and winner
|
||
- `game_members`: assignment of users to games
|
||
- `entries`: seeded suspects, items, and locations
|
||
- `sheet_state`: personal status and notes for an entry
|
||
|
||
Each player has their own set of `sheet_state` records for every game. A player's notes are therefore not visible to other players.
|
||
|
||
## API overview
|
||
|
||
### Authentication
|
||
|
||
- `POST /auth/login`
|
||
- `POST /auth/logout`
|
||
- `GET /auth/me`
|
||
- `PATCH /auth/password`
|
||
- `PATCH /auth/theme`
|
||
- `GET /auth/me/stats`
|
||
|
||
### First-run setup
|
||
|
||
- `GET /setup/status`
|
||
- `POST /setup/admin`
|
||
|
||
The setup endpoint is available only while no administrator exists. After the first administrator has been created, the setup screen is disabled automatically.
|
||
|
||
### Administration
|
||
|
||
- `GET /admin/users`
|
||
- `POST /admin/users`
|
||
- `DELETE /admin/users/{user_id}` – deactivates a user
|
||
|
||
### Games
|
||
|
||
- `GET /games`
|
||
- `POST /games`
|
||
- `POST /games/join`
|
||
- `GET /games/{game_id}`
|
||
- `GET /games/{game_id}/members`
|
||
- `PATCH /games/{game_id}/winner`
|
||
- `POST /games/{game_id}/start`
|
||
- `GET /games/{game_id}/chips`
|
||
- `GET /games/{game_id}/sheet`
|
||
- `PATCH /games/{game_id}/sheet/{entry_id}`
|
||
|
||
## SQLite notes
|
||
|
||
SQLite is a good fit for this application: the data volume is small, the app is intended for internal use, and write operations are short. The database file is stored inside the container at `/app/data/cluedo.db` and persisted through the `cluedo-data` volume.
|
||
|
||
For larger deployments with many concurrent write operations, PostgreSQL would still be the more robust choice. For the intended private or small-group use case, SQLite is sufficient.
|
||
|
||
## Security notes
|
||
|
||
- The values in `.env` are examples and should be changed before production use.
|
||
- When using HTTPS, set `COOKIE_SECURE` to `true`.
|
||
- The application is intended for an internal or small user group.
|
||
- The automatic database migration is intentionally pragmatic and does not replace a migration system such as Alembic.
|