Flesh out README with architecture, security, and Ubuntu+nginx deploy guide

This commit is contained in:
2026-06-03 10:01:31 +10:00
parent feb2247cd4
commit 0d30ca1bea
+154 -2
View File
@@ -1,3 +1,155 @@
# hidden-spectre-server # HIDDEN SPECTRE — server edition 👻
Node/Express backend + admin panel for Hidden Spectre AR ghost hunt. GIF ghost management, JWT auth, QR-based set recognition. Fan-made tribute, not affiliated with the LEGO Group. An augmented-reality ghost-hunting web app — a fan-made tribute to the AR play concept of the LEGO® Hidden Side™ app. This version adds a **Node/Express backend** with a secure admin panel for managing ghost GIFs and QR-based set recognition.
> **Fan-made. Not affiliated with, sponsored by, or endorsed by the LEGO Group.** LEGO® and Hidden Side™ are trademarks of the LEGO Group. This project does not use LEGO logos or artwork.
## What's here
- **Game** (`public/`) — WebXR world-tracked AR with a camera+gyro fallback. Ghosts roam and you trap them by holding the reticle on them. Ghosts render as **animated GIF billboards** when a set is scanned, or as procedural wisps in free-hunt mode.
- **QR set recognition** — scan a set's QR code; the game asks the API which ghosts that set unlocks and spawns them (weighted by rarity).
- **Admin panel** (`/admin`) — JWT-protected login to upload/enable/disable/delete ghost GIFs and create/manage sets and their QR codes.
- **Backend** (`server/`) — Express 5, SQLite (`better-sqlite3`), bcrypt password hashing, JWT auth, image-only uploads via multer.
## Architecture
```
Browser ──► nginx (HTTPS) ──► Node/Express (localhost:3000)
├─ / game (static)
├─ /admin admin UI (static)
├─ /api/login bcrypt + JWT
├─ /api/ghosts CRUD + GIF upload (auth)
├─ /api/sets CRUD + ghost links (auth)
├─ /api/scan/:code public — game reads roster
└─ /uploads/* served GIFs
└─ SQLite: server/data/spectre.db
```
## Security notes
- Admin writes require a valid JWT (8h expiry). The public game only hits the read-only `/api/scan/:code`.
- Passwords are bcrypt-hashed (cost 12). The first admin is created by the seed script from `.env`.
- `JWT_SECRET` **must** be set or the server refuses to start.
- Uploads are restricted to GIF/PNG/WebP, max 8 MB, stored with random filenames.
- `.env`, the database, and uploaded files are git-ignored.
---
## Deploy on Ubuntu behind nginx
### 1. Install Node + build tools
`better-sqlite3` is a native module and needs a compiler toolchain to build:
```bash
sudo apt update
sudo apt install -y nodejs npm build-essential python3
# (or install Node 20+ from NodeSource for a newer runtime)
```
### 2. Get the code and install
```bash
git clone <this-repo-url> /opt/hidden-spectre
cd /opt/hidden-spectre
npm install # compiles better-sqlite3 — needs build-essential
```
### 3. Configure secrets
```bash
cp .env.example .env
# generate a strong secret:
node -e "console.log('JWT_SECRET=' + require('crypto').randomBytes(48).toString('hex'))" >> .env
# then edit .env to set ADMIN_USER / ADMIN_PASS (used once for seeding)
nano .env
```
### 4. Create the first admin
```bash
npm run seed # reads ADMIN_USER / ADMIN_PASS from .env
```
### 5. Run it as a service
Create `/etc/systemd/system/hidden-spectre.service`:
```ini
[Unit]
Description=Hidden Spectre server
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/hidden-spectre
ExecStart=/usr/bin/node server/index.js
Restart=on-failure
Environment=NODE_ENV=production
User=www-data
[Install]
WantedBy=multi-user.target
```
```bash
sudo chown -R www-data:www-data /opt/hidden-spectre
sudo systemctl daemon-reload
sudo systemctl enable --now hidden-spectre
```
### 6. Reverse-proxy with nginx
In your existing HTTPS server block:
```nginx
server {
listen 443 ssl;
server_name spectre.hideawaygaming.com.au;
ssl_certificate /etc/letsencrypt/live/spectre.hideawaygaming.com.au/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/spectre.hideawaygaming.com.au/privkey.pem;
client_max_body_size 10M; # allow GIF uploads
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
```bash
sudo nginx -t && sudo systemctl reload nginx
```
HTTPS is required — the camera, gyro, and WebXR all refuse to run on an insecure origin.
## Using it
1. Go to `https://your-host/admin`, log in, upload a few ghost GIFs.
2. Create a set with a code like `SET-GRAVEYARD` and tick which ghosts it unlocks.
3. Make a QR code whose payload is exactly that code (any QR generator works) and print/display it.
4. On a phone, open `https://your-host/`, tap **Scan a Set**, scan the QR — those ghosts spawn. Or tap **Free hunt** for procedural ghosts with no set.
## Asset tips for ghost GIFs
- Transparent background (GIF or, better, animated WebP/PNG for alpha) reads best floating in space.
- Roughly square or portrait framing; the billboard auto-fits the image aspect.
- Keep file size modest (a few hundred KB) so they load fast on mobile data.
- `.obj` 3D minifigure ghosts are a planned future mode; this version is GIF/image billboards.
## Files
```
server/index.js Express app + all routes
server/db.js SQLite schema
server/auth.js JWT issue/verify
server/upload.js multer image upload config
server/seed.js first-admin creation
public/ the game + /admin panel
public/js/qr.js QR scanning (BarcodeDetector + manual fallback)
```