74f19f331273910f408bf3271615c4fc928ace0b
Real-Time Collaboration (WebSocket): - WebSocket server with JWT auth and trip-based rooms - Live sync for all CRUD operations (places, assignments, days, notes, budget, packing, reservations, files) - Socket-based exclusion to prevent duplicate updates - Auto-reconnect with exponential backoff - Assignment move sync between days Performance: - 16 database indexes on all foreign key columns - N+1 query fix in places, assignments and days endpoints - Marker clustering (react-leaflet-cluster) with configurable radius - List virtualization (react-window) for places sidebar - useMemo for filtered places - SQLite WAL mode + busy_timeout for concurrent writes - Weather API: server-side cache (1h forecast, 15min current) + client sessionStorage - Google Places photos: persisted to DB after first fetch - Google Details: 3-tier cache (memory → sessionStorage → API) Security: - CORS auto-configuration (production: same-origin, dev: open) - API keys removed from /auth/me response - Admin-only endpoint for reading API keys - Path traversal prevention in cover image deletion - JWT secret persisted to file (survives restarts) - Avatar upload file extension whitelist - API key fallback: normal users use admin's key without exposure - Case-insensitive email login Dark Mode: - Fixed hardcoded colors across PackingList, Budget, ReservationModal, ReservationsPanel - Mobile map buttons and sidebar sheets respect dark mode - Cluster markers always dark UI/UX: - Redesigned login page with animated planes, stars and feature cards - Admin: create user functionality with CustomSelect - Mobile: day-picker popup for assigning places to days - Mobile: touch-friendly reorder buttons (32px targets) - Mobile: responsive text (shorter labels on small screens) - Packing list: index-based category colors - i18n: translated date picker placeholder, fixed German labels - Default map tile: CartoDB Light
NOMAD
Navigation Organizer for Maps, Activities & Destinations
A self-hosted travel planner for organizing trips, places, budgets, packing lists, and more.
Features
- Drag & Drop Planner — Organize places into day plans with drag & drop reordering
- Interactive Map — Leaflet map with place markers, route visualization, and customizable tile sources
- Google Places Search — Search and auto-fill place details including ratings, reviews, and opening hours (requires API key)
- Weather Forecasts — Current weather and 5-day forecasts for your destinations (requires API key)
- Budget Tracking — Category-based expenses with pie chart, per-person/per-day splitting, and multi-currency support
- Packing Lists — Grouped checklists with progress tracking, drag & drop reordering, and smart suggestions
- Reservations — Track booking status, confirmation numbers, and reservation details
- File Storage — Attach documents, tickets, and PDFs to your trips (up to 50 MB per file)
- Photo Gallery — Upload and manage trip photos with lightbox view
- PDF Export — Export complete trip plans as PDF
- Multi-User — Invite members to collaborate on shared trips
- Day Notes — Add notes to individual days
- Admin Panel — User management, global categories, API key configuration, and backups
- Auto-Backups — Scheduled backups with configurable interval and retention
- Dark Mode — Full light and dark theme support
- Multilingual — English and German (i18n)
- Customizable — Temperature units, time format (12h/24h), map tile sources, default coordinates
Tech Stack
- Backend: Node.js 22 + Express + SQLite (
node:sqlite) - Frontend: React 18 + Vite + Tailwind CSS
- State: Zustand
- Auth: JWT
- Maps: Leaflet + Google Places API (optional)
- Weather: OpenWeatherMap API (optional)
- Icons: lucide-react
Quick Start
docker run -d -p 3000:3000 -v ./data:/app/data -v ./uploads:/app/uploads mauriceboe/nomad
The app runs on port 3000. The first user to register becomes the admin.
Docker Compose (recommended for production)
services:
app:
image: mauriceboe/nomad:latest
container_name: nomad
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
# - JWT_SECRET=optional-set-for-persistent-sessions
volumes:
- ./data:/app/data
- ./uploads:/app/uploads
restart: unless-stopped
docker compose up -d
Updating
docker pull mauriceboe/nomad
docker rm -f nomad
docker run -d --name nomad -p 3000:3000 -v /your/data:/app/data -v /your/uploads:/app/uploads --restart unless-stopped mauriceboe/nomad
Or with Docker Compose: docker compose pull && docker compose up -d
Your data is persisted in the mounted data and uploads volumes.
Reverse Proxy (recommended)
For production, put NOMAD behind a reverse proxy with HTTPS (e.g. Nginx, Caddy, Traefik).
Example with Caddy:
nomad.yourdomain.com {
reverse_proxy localhost:3000
}
Optionally restrict CORS by adding to environment:
- ALLOWED_ORIGINS=https://nomad.yourdomain.com
Optional API Keys
API keys are configured in the Admin Panel after login.
Google Maps (Place Search)
- Go to Google Cloud Console
- Create a project and enable the Places API (New)
- Create an API key under Credentials
- In NOMAD: Admin Panel → API Keys → Google Maps
OpenWeatherMap (Weather Forecasts)
- Sign up at OpenWeatherMap
- Get a free API key
- In NOMAD: Admin Panel → API Keys → OpenWeatherMap
Building from Source
git clone https://github.com/mauriceboe/NOMAD.git
cd NOMAD
docker build -t nomad .
Data & Backups
- Database: SQLite, stored in
./data/travel.db - Uploads: Stored in
./uploads/ - Backups: Create and restore via Admin Panel
- Auto-Backups: Configurable schedule and retention in Admin Panel
License
Description
Languages
TypeScript
64.7%
JavaScript
35%
CSS
0.2%