https://github.com/Abrechen2/TravStats
Self-hosted travel logbook.
https://github.com/Abrechen2/TravStats
aviation deck-gl docker flight-tracker nodejs postgis react self-hosted typescript unraid
Last synced: 11 days ago
JSON representation
Self-hosted travel logbook.
- Host: GitHub
- URL: https://github.com/Abrechen2/TravStats
- Owner: Abrechen2
- License: other
- Created: 2025-11-20T15:21:08.000Z (7 months ago)
- Default Branch: Main
- Last Pushed: 2026-05-10T14:24:08.000Z (24 days ago)
- Last Synced: 2026-05-10T16:28:05.912Z (24 days ago)
- Topics: aviation, deck-gl, docker, flight-tracker, nodejs, postgis, react, self-hosted, typescript, unraid
- Language: TypeScript
- Homepage: https://github.com/Abrechen2/TravStats
- Size: 74 MB
- Stars: 34
- Watchers: 0
- Forks: 2
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README

# TravStats
**Self-hosted travel logbook for small households and groups (1β10 users).**
It's a logbook, not a live tracker β you record trips manually, scan a boarding pass, or import a confirmation email, and TravStats turns them into history, stats and maps.
[](https://www.gnu.org/licenses/agpl-3.0)
[](https://github.com/Abrechen2/TravStats/releases)
[](https://github.com/Abrechen2/TravStats/pkgs/container/travstats)
[](https://hub.docker.com/r/abrechen2/travstats)
[](https://github.com/Abrechen2/TravStats/actions/workflows/ci.yml)
---
## Why TravStats
Log every flight you take (cruises landing in v2), visualise your routes
on interactive 2D and 3D maps, collect 101 achievements, and import flights
from boarding passes (QR / PDF417 / OCR), confirmation emails, or
Excel/CSV β all on your own server, no cloud, no telemetry.
It's a logbook, not a live tracker β you record trips manually, scan a
boarding pass, or import a confirmation email, and TravStats turns them
into history, stats and maps. Flight data is optionally enriched from
AirLabs / Aviationstack / OpenSky, but everything you record lives in
your own PostgreSQL. No accounts on someone else's servers, no analytics,
no ads.
- πΊοΈ **Six map modes** β Routes, Heatmap, Hexagon, 3D columns, animated Trips, 3D Globe
- π **Year-over-year statistics** across flights, distance, seats, classes, routes
- π **101 Battlefield-style achievements** across five categories
- π« **Boarding-pass scanner** β QR / barcode / OCR
- π§ **Email import** β plain text, HTML, Outlook `.msg`, `.eml`, with optional local LLM parsing via Ollama
- π **Excel/CSV round-trip import** β export, edit in Excel, re-import; rows with an `id` update existing flights
- π€ **Public REST API + OpenAPI 3.0 / Swagger UI** β Personal Access Tokens with `read` / `write` / `admin` scopes for AI agents and automation
- πΎ **Automated backups** with retention + optional WebDAV sync
- π **Invite-only by default** β toggle public registration anytime from the admin UI; JWT in HttpOnly cookies, 18 rate limiters on sensitive endpoints
- π **German + English UI** with browser-locale auto-detection, i18n-ready
---
## Screenshots
Downloadable PNG certificate with your totals
101 Battlefield-style achievements
---
## Quick start
The only thing you set in a file is a database password. Everything else β
instance name, user cap, API keys, Ollama model, backup schedule, WebDAV β
is captured by the first-run setup wizard in the browser.
### Option A β Stack (bundled Postgres, recommended)
```bash
# 1. Grab the compose file
curl -O https://raw.githubusercontent.com/Abrechen2/TravStats/Main/docker-compose.prod.yml
# 2. Set one variable
echo "DB_PASSWORD=$(openssl rand -base64 32)" > .env
# 3. Start
docker compose -f docker-compose.prod.yml up -d
# 4. Open the setup wizard
open http://localhost:3000/setup
```
That's the complete install. The container seeds the airport database on
first boot (~30 s), auto-generates a JWT secret, and the setup wizard
persists instance settings to the database.
### Option B β External Postgres
If you already run a Postgres server (homelab, managed DB), skip the
bundled `db` service by providing `DATABASE_URL` and removing the `db` and
`depends_on` blocks from the compose file:
```bash
DATABASE_URL=postgresql://user:pass@postgres.lan:5432/travstats
```
### Unraid
Community Apps templates live in a dedicated repo β
[Abrechen2/docker-templates](https://github.com/Abrechen2/docker-templates).
Install `travstats-db` from there first, then `TravStats`, set the
`Database URL` password, open `/setup`. Walk-through with screenshots in
[`docs/unraid/README.md`](docs/unraid/README.md).
> **Optional local LLM parsing:** install the
> [Ollama Community App](https://unraid.net/community/apps?q=ollama)
> (or run Ollama anywhere else on your network), pull `gemma3:12b` (~7.5 GB),
> then point TravStats at it from **Admin β Parser**. Multi-flight
> confirmation mails and unknown airline templates are then handled locally β
> nothing leaves your network.
### Image tags
Both registries (GHCR and Docker Hub) carry the same digests for these
moving tags. Pick the one your platform defaults to.
| Tag | Points to | Use for |
|---|---|---|
| `:latest`, `:stable` | Latest stable release (currently `1.2.1`) | Normal production. Auto-updates to the next promoted release. |
| `:X.Y.Z` (e.g. `:1.2.1`) | Pinned immutable release | Reproducible installs, audit, regulated environments. |
| `:rc-latest` | Newest Release Candidate (currently `1.3.0-rc.7`) | Beta testers β receive every fresh RC via `docker compose pull`. May include breaking schema changes across major bumps; an in-place backup is taken automatically on first start of a new major. |
Specific RC tags (`:1.3.0-rc.1`, `:2.0.0-beta.8`) and dev builds live on
GHCR only β Docker Hub only mirrors the moving tags above plus pinned
final releases.
---
## Configuration
Almost nothing to configure via environment variables. The setup wizard
captures everything instance-level (name, public URL, user cap,
registration mode) and the admin UI handles API keys, Ollama, backup
schedule and WebDAV sync.
| Env variable | When to set it | Default |
|---|---|---|
| `DB_PASSWORD` | Always β shared secret between app and bundled Postgres | **required** |
| `APP_PORT` | Different host port | `3000` |
| `DATABASE_URL` | External Postgres instead of the bundled service | *(derived from `DB_PASSWORD`)* |
| `COOKIE_SECURE` | Reverse proxy doesn't send `X-Forwarded-Proto` | *(auto-detected)* |
| `CORS_ORIGIN` | Frontend lives on a different hostname than the API | *(same-origin only)* |
| `TZ` | Non-UTC container clock (not recommended) | `UTC` |
See [`.env.prod.example`](.env.prod.example) for the annotated list.
### Runtime-configurable from the admin UI
- Instance name, public URL, user cap, registration mode
- **AirLabs / OpenSky / Aviationstack** API keys (encrypted at rest)
- **Ollama** endpoint + model (default `gemma3:12b`)
- Backup schedule and retention
- WebDAV off-site backup sync (Nextcloud, HiDrive, β¦)
- SMTP for invitation and password-reset emails
- Logging level and retention
TravStats works without any API key; manual flight entry and boarding-pass
scanning cover the full feature set.
---
## What's in a release
See [CHANGELOG.md](CHANGELOG.md) for the full history and
[ROADMAP.md](ROADMAP.md) for where things are heading (cruises module,
COβ tracking, trip planner, PWA).
## Security
See [SECURITY.md](SECURITY.md) for the hardening summary, audit history, and
verification commands. TL;DR: JWT in HttpOnly cookies, 18 distinct rate
limiters, Zod validation on every endpoint, Prisma-parameterised queries,
Helmet CSP, invite-only by default.
Report vulnerabilities via
[GitHub Security Advisories](https://github.com/Abrechen2/TravStats/security/advisories/new)
β **please do not open a public issue**.
## API for external tools
TravStats ships an authenticated REST API for AI agents, automation
scripts and integrations. Every flight, trip, airport and stat the
web UI shows is reachable programmatically.
**1. Mint a Personal Access Token**: in the app, go to **Settings β
API Tokens**, give it a label and a scope (`read`, `write`, `admin`),
and copy the `ts_pat_β¦` value. The plaintext is shown exactly once β
only the bcrypt hash is persisted.
**2. Browse the spec**: open `https:///api/v1/docs`
(Swagger UI) or fetch `/api/v1/openapi.json` for the raw OpenAPI 3.0
document. The spec is auto-generated from the same Zod schemas the
backend uses for validation, so it never drifts.
**3. Call it**: every endpoint accepts the token via the standard
`Authorization` header.
```bash
TOKEN="ts_pat_β¦"
# List your flights
curl -H "Authorization: Bearer $TOKEN" \
https://travstats.example.com/api/v1/flights
# Create a flight (write or admin scope required)
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"departure":{"iata":"FRA","lat":50.0379,"lon":8.5622},
"arrival":{"iata":"JFK","lat":40.6413,"lon":-73.7781},
"departureTime":"2026-05-01T08:00:00.000Z",
"arrivalTime":"2026-05-01T17:00:00.000Z",
"flightNumber":"LH400"}' \
https://travstats.example.com/api/v1/flights
```
Pass `?merge=true` on `POST /flights` to enrich an existing matching
flight (boarding-pass re-import, email confirmation upgrade) instead
of creating a duplicate. Read-only tokens are blocked from mutating
endpoints with 403; admin endpoints additionally require an
`admin`-scoped token even if the owning user is an admin.
Token requests get their own per-token rate-limit bucket so an
aggressive script can't lock the user out of the web UI.
## Contributing
Bug reports, feature ideas and pull requests are welcome. The
[Report Bug](https://github.com/Abrechen2/TravStats/issues/new/choose) button
in the app copies an anonymised diagnostic bundle for you.
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.
## Development
```bash
npm run install:all # install backend + frontend
npm run dev # backend :8000 + frontend :3000
npm run typecheck # tsc --noEmit on both
npm run test:frontend # Vitest (backend tests need Postgres)
```
Deep-dive developer reference: [CLAUDE.md](CLAUDE.md).
---
## License
Copyright Β© 2026 Dennis Wittke Β· **AGPL-3.0-or-later**
You may use, modify and redistribute TravStats, but if you run it as a web
service (even modified) you must make the complete source code of your
modifications available under the same licence. See [LICENSE](LICENSE).
## Support the project
TravStats is a solo side project. If it's useful to you,
[a small donation via PayPal](https://www.paypal.com/donate?hosted_button_id=HW9MPYVURCT42)
keeps the lights on for AirLabs quota top-ups. β€οΈ
---
**[β Star on GitHub](https://github.com/Abrechen2/TravStats)** Β·
[Releases](https://github.com/Abrechen2/TravStats/releases) Β·
[Roadmap](ROADMAP.md) Β·
[Issues](https://github.com/Abrechen2/TravStats/issues)
Made with β€οΈ and a bit of AI for flight enthusiasts.