- GET /api/directions proxies OSRM (OSRM_URL env, default public demo server) with validation, Leaflet-order geometry, 24h capped cache - Map draws straight lines first, then upgrades ground legs to road-following polylines with routed km (marked road) as directions arrive; silent fallback to great-circle on failure; air legs unchanged - README documents OSRM_URL
122 lines
5.5 KiB
Markdown
122 lines
5.5 KiB
Markdown
# Trip Plan
|
|
|
|
A self-hosted, multi-user web tool for collaboratively planning trips. Multiple people log in to the same instance, create trips with a name and date range, and fill in a day-by-day calendar together — flights (with multi-leg connections), hotels, border crossings, drives, rental cars, and activities. Each trip is drawn on an interactive map with the route between stops, per-leg distances, cost splitting between members, and an overall summary. It runs as a single Docker container with a SQLite database.
|
|
|
|
## Features
|
|
|
|
- **Accounts without passwords** — Mullvad-style: one click generates a random account number (shown once, stored only as a hash). No email, no password, no personal data.
|
|
- **Sharing via join codes** — every trip has a short code; anyone with an account enters it to become a co-editor. The owner can regenerate the code to stop further joins.
|
|
- **Day-by-day calendar** — a real calendar grid; click a day to add any number of entries (activity, hotel, travel, flight, rental car, immigration, note).
|
|
- **Multi-leg flights** — type `CNX-BKK-DXB-FRA` and it expands into segments with per-leg flight number and times; every airport becomes a stop on the map (bundled offline IATA airport database).
|
|
- **Rental cars** — brand/model/type, booking ref, pickup/dropoff with dates and times, and included km — compared against the trip's estimated driven km.
|
|
- **Locations** — geocoded via OpenStreetMap (Nominatim, English results); lat/lng stored per entry.
|
|
- **Map** — Leaflet + OpenStreetMap; stops plotted in order, air legs dashed / ground legs solid, per-leg great-circle km.
|
|
- **Costs & splitting** — price per entry with "split equally", "everyone pays their own", or "payer's expense"; per-person share/paid/net table and minimal settle-up transfers, in the trip's currency.
|
|
- **Summary** — days/nights, entry counts, total km, **≈ km driven** (rental-car figure) vs km flown, and locations visited.
|
|
|
|
## Deployment (Docker + external Caddy for TLS)
|
|
|
|
The app serves plain HTTP on port 3000 and is designed to sit behind a reverse
|
|
proxy that terminates TLS — e.g. an external [Caddy](https://caddyserver.com/).
|
|
|
|
### 1. Run the container
|
|
|
|
Using the prebuilt image from the registry:
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
tripplan:
|
|
image: git.b4l.co.th/grabowski/trip-plan:latest
|
|
restart: unless-stopped
|
|
environment:
|
|
SESSION_SECRET: "<long random string — e.g. openssl rand -hex 32>"
|
|
volumes:
|
|
- ./data:/app/data # database lives next to this compose file
|
|
# No public port mapping needed when Caddy shares a Docker network with
|
|
# the app (recommended). For a host-level Caddy, map localhost only:
|
|
ports:
|
|
- "127.0.0.1:3000:3000"
|
|
```
|
|
|
|
The entrypoint chowns `./data` to the app's unprivileged `node` user on
|
|
startup, so a root-created bind-mount directory works out of the box. (Images
|
|
older than this note need a one-time `sudo chown -R 1000:1000 ./data`.)
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
(Or build locally instead of pulling: clone this repo and use `build: .` — the
|
|
checked-in `docker-compose.yml` does exactly that.)
|
|
|
|
### 2. Point Caddy at it
|
|
|
|
If Caddy runs on the same host (host-level Caddy, app bound to `127.0.0.1:3000`):
|
|
|
|
```caddyfile
|
|
trips.example.com {
|
|
reverse_proxy 127.0.0.1:3000
|
|
}
|
|
```
|
|
|
|
If Caddy runs as a container, attach both services to a shared Docker network
|
|
and proxy by service name instead:
|
|
|
|
```caddyfile
|
|
trips.example.com {
|
|
reverse_proxy tripplan:3000
|
|
}
|
|
```
|
|
|
|
That's all — Caddy obtains and renews the certificate automatically; the app
|
|
needs no TLS configuration of its own. Session cookies are `SameSite=Lax` and
|
|
`httpOnly`, and work as-is behind the proxy.
|
|
|
|
### 3. Updating
|
|
|
|
```bash
|
|
docker compose pull && docker compose up -d
|
|
```
|
|
|
|
Schema migrations run automatically at startup; existing data in `./data` is
|
|
preserved (back up that directory to back up all trips).
|
|
|
|
## Quick start (local, no proxy)
|
|
|
|
```bash
|
|
docker compose up -d # builds the image from source
|
|
# open http://localhost:3000 and create the first account
|
|
```
|
|
|
|
> Before exposing the app beyond localhost, set your own random `SESSION_SECRET`
|
|
> in `docker-compose.yml`.
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
npm install
|
|
npm start # http://localhost:3000
|
|
npm test # API tests (node:test + supertest)
|
|
```
|
|
|
|
`npm run dev` starts the server with `--watch` for auto-reload.
|
|
|
|
## Environment variables
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `PORT` | `3000` | HTTP port the server listens on. |
|
|
| `DATA_DIR` | `./data` (`/app/data` in Docker) | Directory holding the SQLite database file. |
|
|
| `SESSION_SECRET` | dev value (warns) | Secret used to sign session cookies. **Set this in production.** |
|
|
| `OSRM_URL` | `https://router.project-osrm.org` | Base URL for the driving-directions proxy (`/api/directions`). The default is the public OSRM demo server — fine for light personal use. Self-hosters can point this at their own OSRM instance. |
|
|
|
|
## Data
|
|
|
|
All state lives in a single SQLite file under `DATA_DIR`. In Docker this is `/app/data`, bind-mounted from `./data` next to the compose file — back up that directory to back up all trips. No external database or services are required (geocoding calls OpenStreetMap's Nominatim, which needs no API key; airport lookups use a bundled offline dataset; road directions call the public OSRM demo server, also no API key).
|
|
|
|
## Documentation
|
|
|
|
- [docs/PROJECT_OVERVIEW.md](docs/PROJECT_OVERVIEW.md) — architecture, data model, and design decisions.
|
|
- [docs/API.md](docs/API.md) — the REST API contract.
|