5b31d3079bccb2e33cb7541a8ecc23183253b2c8
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 29s
Loot Hunt
A digital alternate reality game — find and scan hidden QR codes in real life to earn points and climb the leaderboard.
How It Works
- Admins create hunts, manage all content, assign roles, and reset passwords
- Organizers create and manage their own hunts with printable QR code cards
- Players scan hidden QR codes to earn points — first find gets the most points
- Leaderboards track top players per hunt and globally
Point System
| Scan Order | Points |
|---|---|
| 1st scan | 500 |
| 2nd scan | 250 |
| 3rd scan | 100 |
| 4th+ | 50 |
Players earn points only once per package. Re-scanning lets you update the package hint.
Roles
| Capability | Admin | Organizer | Player |
|---|---|---|---|
| Create hunts | Yes | Yes | No |
| Manage/edit/delete own hunts | Yes | Yes | No |
| Manage other users' hunts | Yes | No | No |
| Download QR code PDFs | Yes | Own hunts | No |
| Password reset | Yes | No | No |
| Delete any image / clear hints | Yes | No | No |
| Assign organizer role | Yes | No | No |
Features
- QR Code Cards — Printable Avery 5371 format PDFs with double-sided backs
- First Finder Photos — First scanner can upload/replace an image per package
- Hints — Most recent scanner can leave a message for the next finder
- Player Profiles — Stats, rank, hunt breakdown, and recent activity
- Dark Mode — Toggle with system preference detection and localStorage persistence
- Relative Timestamps — "3h ago" format with full date on hover
- Flash Messages — Feedback on actions (create, edit, delete, upload, etc.)
- Paginated Leaderboards — 25 per page with navigation controls
- Admin Dashboard — Hunt stats, top finders, discovery rate, recent scans, role management
Tech Stack
- Node.js + Express
- SQLite via sql.js (WASM)
- EJS templates
- PDFKit + qrcode for printable QR sheets
- Docker deployment via Portainer
Quick Start (Development)
cp .env.example .env
npm install
node src/setup-admin.js admin yourpassword
npm run dev
Visit http://localhost:3000
Docker Deployment
The project deploys via Gitea Actions → Docker build → Portainer stack update.
# Build locally
docker build -t loot-hunt .
# Run
docker run -p 3000:3000 -v loot-data:/app/data \
-e SESSION_SECRET=your-secret \
-e BASE_URL=https://loot-hunt.com \
loot-hunt
# Create admin user inside container
docker exec -it loot-hunt node src/setup-admin.js admin yourpassword
Environment Variables
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3000 |
NODE_ENV |
Environment | production |
BASE_URL |
Public URL (for QR codes) | http://localhost:3000 |
SESSION_SECRET |
Session encryption key | (required) |
DB_PATH |
SQLite database path | ./data/loot-hunt.db |
UPLOADS_DIR |
Image uploads directory | ./data/uploads |
TRUST_PROXY |
Trust reverse proxy headers | false |
Project Structure
src/
├── app.js # Express application entry point
├── setup-admin.js # CLI tool to create/promote admin users
├── config/
│ └── database.js # SQLite (sql.js) initialization, schema & migrations
├── middleware/
│ └── auth.js # Auth, admin & organizer middleware
├── models/
│ └── index.js # All database operations
├── routes/
│ ├── auth.js # Login/register/logout/password reset
│ ├── admin.js # Hunt management, PDF download, roles, password reset
│ ├── loot.js # QR scan handling, image upload, hints
│ └── hunts.js # Public hunt profiles, leaderboards, player profiles
├── utils/
│ └── pdf.js # Avery 5371 QR code PDF generation
└── views/ # EJS templates
public/
├── css/
│ └── style.css # Styles with dark mode support
└── js/
└── timeago.js # Relative timestamp formatting
Description
Languages
EJS
46.3%
JavaScript
43.2%
CSS
10.4%
Dockerfile
0.1%