- TypeScript 97.7%
- Shell 1.3%
- Dockerfile 0.8%
| backend | ||
| docker | ||
| docs | ||
| frontend | ||
| .dockerignore | ||
| .env.example | ||
| .env.production.example | ||
| .gitignore | ||
| AGENTS.md | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| encrypt_work_checklist.md | ||
| init.sql | ||
| LICENSE | ||
| product_requirements.md | ||
| README.md | ||
| social_sharing_build_plan.md | ||
| start.sh | ||
Privacy-First Custom Jigsaw Puzzle Platform
A privacy-focused web application that lets you create custom jigsaw puzzles from your own photos. Built with zero-knowledge encryption to ensure your photos remain private.
Features
- Custom Puzzles: Create jigsaw puzzles from your own photos with adjustable difficulty (4-5000 pieces)
- Zero-Knowledge Encryption: Optional client-side encryption ensures only you can view your encrypted photos
- Progress Saving: Auto-saves puzzle progress every 15 seconds
- Upload Progress Tracking: Real-time progress feedback with speed and ETA during photo uploads
- Full-Board Image Display: Puzzle image fills the entire board area for an immersive experience
- Guide Overlay: Optional ghost image overlay to help with piece placement (adjustable opacity)
- Responsive Design: Works seamlessly on desktop and mobile devices with automatic tray repositioning on orientation change
- Full Screen Mode: Immersive fullscreen puzzle solving with touch-optimized controls
- Dark Mode: Full dark mode support
- Admin Dashboard: User management, site settings, and IP blocking
- Wall of Glory: Showcase completed puzzles in a customizable gallery with piece outlines, drag-and-drop positioning, and resize handles
- Social Sharing: Share completed puzzles and galleries with public links, with optional gallery access control
- Photo Encryption: Full encryption/decryption flow with passphrase management and re-encryption support
- Photo Usage Badges: Puzzle-piece and checkmark count badges on photos that have been used to create puzzles, shown on both the upload page and puzzle creation page
- Piece Groups: Adjoining pieces lock together wherever you place them, move as a single unit, and tray as one compact block — "Tray All Unlocked" and drag-over-tray keep groups intact. A group only locks in place after it snaps as a whole unit into its solved position; an off-target group always stays draggable
- Zoom & Pan: Zoom grows both the puzzle and the tray 1:1 up to 6x (tray capped at 25% of the canvas width, oversized trays fit-to-tray). Mouse wheel, trackpad pinch (ctrl+wheel), and multi-touch two-finger pinch are all anchored at the pointer; locked pieces and piece groups are never disturbed by zooming or panning. The tray also captures wheel/scroll input in a small margin to its left, so aiming a few pixels off the tray still scrolls the tray instead of zooming the board
- Audit Logging: Track all user and admin actions
Tech Stack
Frontend
- Next.js 16 (App Router)
- TypeScript
- Tailwind CSS
- PixiJS (puzzle canvas rendering)
Backend
- Node.js + Express
- TypeScript
- Lucia Auth (authentication)
- PostgreSQL (database)
- MinIO (S3-compatible object storage)
- ClamAV (virus scanning)
Infrastructure
- Docker Compose
- Zero-knowledge encryption (Web Crypto API)
Quick Start
Prerequisites
- Docker and Docker Compose
- Node.js 20+ (for development)
- npm or yarn (for development)
Option 1: Docker Hub Image (Easiest)
Pull and run the pre-built image with only the infrastructure services:
- Create a project directory and add the production compose file:
mkdir puzzle && cd puzzle
curl -sO https://raw.githubusercontent.com/<repo>/main/docker-compose.prod.yml
curl -sO https://raw.githubusercontent.com/<repo>/main/.env.production.example
cp .env.production.example .env
# Edit .env with your secure passwords
- Start everything:
docker compose -f docker-compose.prod.yml up -d
- Open http://localhost:8080 and register your first user (becomes admin automatically).
Services:
- App (frontend + backend + nginx): http://localhost:8080
Option 2: Build from Source (Docker Compose)
Build and run the full stack locally with Docker:
- Clone the repository:
git clone <repository-url>
cd get_puzzles
- Start all services (builds backend and frontend images, starts Postgres, MinIO, and ClamAV):
docker compose up -d --build
-
Open http://localhost:3002 in your browser
-
Register your first user - they will automatically become an admin
Services:
- Frontend: http://localhost:3002
- Backend API: http://localhost:3001
- MinIO Console: http://localhost:9001 (credentials:
minio_admin/minio_password)
Stop all services:
docker compose down
Stop and remove data volumes:
docker compose down -v
Option 3: Development Setup
For local development with hot reload:
- Clone the repository:
git clone <repository-url>
cd get_puzzles
- Copy the backend environment file:
cp .env.example backend/.env
-
Update environment variables in
backend/.envas needed. -
Start infrastructure services (Postgres, MinIO, ClamAV):
docker compose up -d postgres minio clamav
- Install and start the backend:
cd backend
npm install
npm run dev
- Install and start the frontend (in a new terminal):
cd frontend
npm install
npm run dev
-
Open http://localhost:3000 in your browser
-
Register your first user - they will automatically become an admin
View logs (production):
docker compose -f docker-compose.prod.yml logs -f
Stop all services:
docker compose -f docker-compose.prod.yml down
Stop and remove volumes:
docker compose -f docker-compose.prod.yml down -v
Documentation
- Setup Guide - Detailed setup and configuration
- API Documentation - REST API endpoints
- Security Model - Encryption and privacy details
- Architecture - System architecture overview
- Deployment - Production deployment guide
Testing
Unit tests (136 backend tests):
cd backend
npm test
Backend E2E tests (31 tests — requires the full stack running on http://localhost:3002):
cd backend
npx playwright test
Frontend E2E tests (49 tests x 5 browsers, 245 total — starts its own dev server):
cd frontend
npx playwright test
The project includes:
- 89 unit tests covering auth, uploads, puzzle creation, hints, admin, user settings, gallery, social sharing, photo usage badges, piece groups, and zoom/tray layout math (including the tray capture margin so wheel-scroll a few px left of the tray still scrolls the tray)
- E2E tests covering registration, login, admin settings, photo upload, puzzle creation, thumbnails, user settings, and photo usage badges
- Frontend E2E tests covering puzzle layout, tray behavior, piece-group behavior (group formation, tray-all-unlocked unit behavior, drag-over-tray grouping, group solve-snap/lock correctness), zoom controls (tray growth, locked/group invariance, zoom-aware dragging, fit-on-reload, tray pieces staying in the tray across a reload while zoomed), and mobile responsiveness
Security Features
- Zero-Knowledge Encryption: Photos can be encrypted client-side before upload
- Virus Scanning: All uploads scanned with ClamAV
- Audit Logging: All actions logged for security review
- IP Blocking: Admin can block suspicious IP addresses
- Password Hashing: Argon2id password hashing
- Session Management: Secure session handling with Lucia Auth
Project Structure
get_puzzles/
├── backend/ # Express API server
│ ├── src/
│ │ ├── controllers/ # Request handlers
│ │ ├── services/ # Business logic
│ │ ├── middleware/ # Express middleware
│ │ └── config/ # Configuration
│ ├── migrations/ # Database migration scripts
│ └── e2e/ # Playwright E2E tests
├── frontend/ # Next.js application
│ ├── app/ # App router pages
│ ├── components/ # React components
│ └── lib/ # Utilities and API client
├── docker/ # Docker support files
│ ├── nginx.conf # Reverse proxy config
│ ├── entrypoint.sh # Container startup script
│ └── run-migrations.sh # Automatic migration runner
├── docs/ # Documentation
├── Dockerfile # Multi-stage production build
├── docker-compose.yml # Development compose
├── docker-compose.prod.yml # Production compose
├── init.sql # Database schema
└── .env.production.example
Development Phases
- ✅ Phase 1: Authentication (Login/Registration)
- ✅ Phase 2: Photo Upload (with virus scanning)
- ✅ Phase 3: Puzzle Creation (jigsaw cut algorithm)
- ✅ Phase 4: Puzzle Solving (drag-and-drop, hints)
- ✅ Phase 5: Admin Dashboard (user management, settings)
- ✅ Phase 6: User Settings (profile, password, encryption)
- ✅ Phase 7: Wall of Glory (gallery showcase with piece outlines)
- ✅ Phase 8: Social Sharing (share puzzles and galleries publicly)
- ✅ Phase 9: Photo Encryption (full encryption/decryption flow)
Database Migrations
The application includes an automatic migration system that runs on container startup:
- The base schema (
users,sessions,uploaded_photos,puzzles, ...) lives ininit.sqland is applied idempotently on every boot bydocker/run-migrations.sh— it does not depend on the Postgres volume being fresh (Docker's/docker-entrypoint-initdb.dhook runs only once on an empty data volume) - Migration changes are stored in
backend/migrations/ - The
docker/run-migrations.shscript tracks applied migrations in aschema_migrationstable - Migrations are applied in alphabetical order and are idempotent
- No manual intervention required - just pull the new image and restart
To create a new migration:
- Add a new
.sqlfile tobackend/migrations/with a numbered prefix (e.g.,010_add_feature.sql) - Use
IF NOT EXISTSorIF EXISTSclauses to make migrations idempotent - The migration will run automatically on next container startup
Contributing
Contributions are welcome! Please ensure:
- All tests pass (
npm test) - Code follows existing patterns
- New features include tests
- Documentation is updated
License
MIT License - see LICENSE file for details
Support
For issues and feature requests, please use the GitHub issue tracker.