Skip to content

XDMINT/VeloHub

Repository files navigation

VeloHub

Mehrtägige Rennrad- und Radreisen strukturiert planen – mit Basecamps, Sterntouren, Unterkünften, Transfers und automatisch erzeugter Reiseroute.

Idee und Alleinstellungsmerkmal

VeloHub ist keine klassische Trainings-, Tracking- oder reine Routenplanungs-App. Die zentrale Idee der Anwendung ist es, komplette mehrtägige Radreisen strukturiert planbar zu machen – insbesondere Reisen, bei denen man an mehreren Standorten übernachtet, von dort aus Sterntouren fährt und zwischendurch Transfers organisiert.

Viele bestehende Tools konzentrieren sich entweder auf:

  • die Planung einer einzelnen Route
  • das Aufzeichnen und Auswerten sportlicher Aktivitäten

VeloHub setzt an einer anderen Stelle an: Die App betrachtet nicht nur die einzelne Ausfahrt, sondern die gesamte Reise als zusammenhängendes Planungskonstrukt.

Wodurch grenzt sich VeloHub ab?

  • Basecamp-orientierte Planung statt rein routenorientierter Planung
    Touren werden nicht isoliert betrachtet, sondern einem konkreten Aufenthaltsort zugeordnet.

  • Unterstützung von Sterntouren
    Mehrere Ausfahrten können rund um denselben Standort geplant und logisch gruppiert werden.

  • Reiseplanung statt nur Streckenplanung
    Unterkünfte, Transfers, Ruhetage und Tagesrouten sind Teil desselben Modells.

  • Ganzheitlicher Überblick über die Reise
    Statistiken, Itinerary und Kartenansicht helfen dabei, nicht nur einzelne Touren, sondern die gesamte Struktur der Reise zu erfassen.

  • Integration von GPX-Daten in einen größeren Planungskontext
    Importierte Routen stehen nicht für sich allein, sondern werden in die Gesamtplanung eingebettet.

Kurz gesagt: VeloHub verbindet Routenplanung, Reiseorganisation und visuelle Strukturierung in einer Anwendung.


Live-System

Das Frontend ist öffentlich deployt. Das Backend wird containerisiert ausgeliefert und über einen GitHub-Workflow via Docker Hub auf einen VPS ausgerollt.


Funktionsumfang

  • Reisen anlegen und verwalten
  • Basecamps mit Aufenthaltszeitraum definieren
  • Unterkünfte je Basecamp hinterlegen
  • Radrouten je Basecamp planen
  • GPX-Dateien importieren
  • Höhenprofile abrufen und visualisieren
  • Transfers zwischen Standorten verwalten
  • Transferdaten durch das Backend neu berechnen lassen
  • Ruhetage pflegen und sortieren
  • Ein Itinerary für die gesamte Reise erzeugen
  • Aggregierte Trip- und Basecamp-Statistiken anzeigen
  • Orte über MapTiler bzw. Nominatim suchen
  • Reisedaten auf einer interaktiven Karte darstellen

Tech-Stack

Frontend

  • React 19
  • TypeScript
  • Vite
  • Tailwind CSS 4
  • TanStack React Query für Server-State
  • Zustand für lokalen UI-/App-State
  • Axios für die Kommunikation mit dem Backend
  • MapLibre GL / react-map-gl für Karten
  • Recharts für Diagramme
  • Radix UI für UI-Primitives
  • dnd-kit für Reordering per Drag & Drop

Das Frontend bezieht seine API-Basis über VITE_API_URL oder VITE_API_BASE_URL und fällt andernfalls auf /api zurück.

Backend

  • Kotlin
  • Spring Boot
  • Spring Web
  • Spring Data JPA
  • Spring Validation
  • springdoc OpenAPI / Swagger UI
  • Hibernate Spatial
  • JTS / PostGIS-Unterstützung
  • Jackson XML für GPX-nahe Verarbeitung

Das Backend ist in klassische Schichten unterteilt, unter anderem controller, service, repository, dto, entity, mapper, config und exception.

Datenbank

  • PostgreSQL 16
  • PostGIS 3.4

Für lokale Setups wird eine PostGIS-fähige PostgreSQL-Instanz über Docker Compose bereitgestellt.


Architekturüberblick

VeloHub folgt einer getrennten Frontend-Backend-Architektur:

[ Browser / Nutzer ]
        ↓
[ React + Vite Frontend ]
        ↓ HTTP / JSON
[ Spring Boot Backend ]
        ↓
[ PostgreSQL + PostGIS ]

Ergänzend dazu werden externe Dienste eingebunden:

Frontend
 ├─ Karten / Geocoding
 │   ├─ MapTiler (bevorzugt bei vorhandenem API-Key)
 │   └─ Nominatim als Fallback
 │
Backend
 └─ Routing-Dienst über VELOHUB_ROUTING_API_KEY

Die Aufgabentrennung ist dabei klar:

  • Das Frontend übernimmt Interaktion, Visualisierung, Kartenansicht und State-Handling.
  • Das Backend übernimmt Persistenz, Validierung, Aggregation, Ableitungen und fachliche Logik.
  • Die Datenbank speichert strukturierte Reisedaten sowie räumliche Informationen.

Projektstruktur

VeloHub/
├── .github/workflows/
│   └── deploy-backend.yml
├── client/
│   ├── public/
│   ├── src/
│   │   ├── api/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── lib/
│   │   ├── stores/
│   │   ├── types/
│   │   ├── App.tsx
│   │   └── main.tsx
│   ├── Dockerfile
│   └── nginx.conf
├── server/
│   ├── src/main/kotlin/privat/velohub/
│   │   ├── config/
│   │   ├── controller/
│   │   ├── dto/
│   │   ├── entity/
│   │   ├── exception/
│   │   ├── mapper/
│   │   ├── repository/
│   │   ├── service/
│   │   ├── util/
│   │   └── VeloHubApplication.kt
│   ├── Dockerfile
│   ├── build.gradle.kts
│   └── gradlew
├── API_DOCUMENTATION.md
└── docker-compose.yml

Frontend-Architektur

Das Frontend ist als moderne SPA aufgebaut und trennt klar zwischen:

  • UI-Komponenten
  • API-Modulen
  • Server-State-Hooks
  • lokalem App- und UI-State

API-Schicht

Die gesamte Kommunikation mit dem Backend ist in client/src/api gekapselt. Dort gibt es separate Module für die fachlichen Bereiche, unter anderem:

  • trips.ts
  • basecamps.ts
  • accommodations.ts
  • cycling-routes.ts
  • transfers.ts
  • itinerary.ts
  • geocoding.ts

Dadurch bleibt die eigentliche UI von HTTP-Details weitgehend entkoppelt.

Datenabruf und Caching

Für den Server-State wird TanStack React Query verwendet. Das ist besonders passend für VeloHub, weil viele Screens auf persistierten Backend-Ressourcen basieren, die:

  • zwischengespeichert werden sollen
  • nach Mutationen gezielt aktualisiert werden müssen
  • auch bei temporären Verbindungsproblemen stabil bleiben sollen

Lokaler State

UI-naher und nicht persistenter Zustand wird über Zustand organisiert, zum Beispiel in:

  • app-store.ts
  • map-store.ts
  • ui-store.ts

So bleibt die Trennung zwischen fachlichen Backend-Daten und lokalem UI-State sauber.

Karten und Geocoding

Die Kartenansicht basiert auf MapLibre. Wenn ein VITE_MAPTILER_KEY vorhanden ist, werden MapTiler-Dienste verwendet. Andernfalls greift das Frontend bei Geocoding-Anfragen auf Nominatim zurück.


Backend-Architektur

Das Backend folgt einer klassischen Spring-Boot-Struktur mit fachlich getrennten Controllern und einer üblichen Schichtung aus API, Service, Persistenz und Mapping.

Zentrale Pakete

  • controller/ – REST-Endpunkte
  • service/ – fachliche Logik
  • repository/ – Datenzugriff
  • entity/ – Datenbankmodelle
  • dto/ – Request- und Response-Modelle
  • mapper/ – Umwandlung zwischen Entity und DTO
  • exception/ – Fehlerbehandlung
  • config/ – Konfiguration, unter anderem für OpenAPI, CORS und Routing

Fachliche Controller

Das Backend stellt dedizierte Controller für folgende Bereiche bereit:

  • TripController
  • BaseCampController
  • AccommodationController
  • CyclingRouteController
  • TransferController
  • ItineraryController

Diese Struktur spiegelt direkt das Domänenmodell der Anwendung wider.

Räumliche Daten und GPX-Verarbeitung

Da VeloHub mit geografischen Informationen arbeitet, kommen im Backend unter anderem folgende Technologien zum Einsatz:

  • Hibernate Spatial
  • JTS Core
  • PostGIS
  • XML-Verarbeitung für GPX-nahe Daten

Das ist insbesondere relevant für:

  • räumliche Datenpunkte
  • Streckenverläufe
  • GPX-Import
  • routenbezogene Berechnungen

API-Struktur und typische Aufruflogik

Ein zentraler Aspekt von VeloHub ist, dass die API-Struktur die fachliche Hierarchie der Reise abbildet.

1. Trips

/trips und /trips/{id} bilden die oberste Ebene.

Darüber werden typischerweise folgende Aktionen ausgeführt:

  • alle Reisen laden
  • einzelne Reise laden
  • Reise anlegen
  • Reise aktualisieren
  • Reise löschen
  • Gesamtstatistiken über /trips/{id}/stats abrufen

Wann wird diese API aufgerufen?

  • beim Laden einer Übersicht aller Reisen
  • beim Öffnen einer konkreten Reise
  • nach dem Anlegen, Umbenennen oder Löschen einer Reise
  • wenn aggregierte Kennzahlen für die gesamte Reise dargestellt werden sollen

2. Basecamps

Basecamps sind unter einer Reise verschachtelt und liegen unter /trips/{tripId}/basecamps.

Zusätzlich existieren Endpunkte für:

  • Statistiken
  • Ruhetage
  • Reordering von Ruhetagen
  • Reordering der Basecamps

Wann wird diese API aufgerufen?

  • wenn ein neuer Aufenthaltsort angelegt wird
  • wenn Reihenfolge oder Zeitraum der Basecamps angepasst werden
  • wenn Ruhetage hinzugefügt oder sortiert werden
  • wenn basecampbezogene Kennzahlen benötigt werden

3. Unterkünfte

Unterkünfte sind an ein Basecamp gebunden und werden unter
/trips/{tripId}/basecamps/{baseCampId}/accommodation verwaltet.

Wann wird diese API aufgerufen?

  • beim Anlegen oder Bearbeiten einer Unterkunft
  • wenn Detaildaten zum Aufenthalt gepflegt werden
  • wenn pro Basecamp eine konkrete Übernachtung hinterlegt werden soll

4. Radrouten

Radrouten sind ebenfalls einem Basecamp zugeordnet und liegen unter
/trips/{tripId}/basecamps/{baseCampId}/routes.

Dazu gehören unter anderem:

  • CRUD-Operationen
  • GPX-Upload
  • Höhenprofil-Abruf
  • Reordering der Routen

Wann wird diese API aufgerufen?

  • beim Anlegen einer Tour
  • beim Import einer GPX-Datei
  • beim Anzeigen eines Höhenprofils
  • wenn mehrere Touren innerhalb eines Basecamps sortiert werden

5. Transfers

Transfers liegen unter /trips/{tripId}/transfers.

Zusätzlich gibt es eine Aktion zur Neuberechnung.

Wann wird diese API aufgerufen?

  • wenn ein Wechsel zwischen zwei Aufenthaltsorten geplant wird
  • wenn Transferdaten gepflegt oder angepasst werden
  • wenn sich die Reiseplanung verändert und Transferdaten neu berechnet werden sollen

6. Itinerary

Das Itinerary wird über /trips/{tripId}/itinerary abgerufen.

Wann wird diese API aufgerufen?

  • wenn aus den vorhandenen Planungsdaten eine chronologische Gesamtansicht erzeugt werden soll
  • wenn die Reise als durchgehender Ablauf statt als Einzelobjekte betrachtet werden soll

Einbindung externer APIs

VeloHub verwendet externe APIs gezielt als Ergänzung zur eigenen Fachlogik.

Im Frontend

MapTiler

Wenn VITE_MAPTILER_KEY gesetzt ist, verwendet das Frontend MapTiler für:

  • Kartenstile
  • Geocoding

Das ist insbesondere relevant bei:

  • der Kartenanzeige
  • der Suche nach Orten beim Anlegen oder Bearbeiten von Reisedaten

Nominatim als Fallback

Falls kein MapTiler-Key vorhanden ist, greift das Frontend für Geocoding auf Nominatim zurück.

Das erleichtert lokale Entwicklung und einfache Deployments ohne zwingende Drittanbieter-Konfiguration.

Im Backend

Das Backend nutzt einen Routing-bezogenen API-Key über VELOHUB_ROUTING_API_KEY.

Dadurch lassen sich routing- oder transferbezogene externe Dienste in die Backend-Logik einbinden, während Persistenz, Datenmodell und Orchestrierung in der Anwendung selbst verbleiben.


API-Dokumentation

Für die detaillierte Beschreibung der Backend-Endpunkte ist bereits eine eigene Dokumentation im Repository vorhanden:

Laut vorhandener Dokumentation stehen lokal außerdem zur Verfügung:

  • Base URL: http://localhost:8080
  • Swagger UI: http://localhost:8080/swagger-ui.html
  • OpenAPI JSON: http://localhost:8080/api-docs

Diese README dient vor allem als Produkt-, Architektur- und Betriebsübersicht, während API_DOCUMENTATION.md die ausführlichere API-Referenz bildet.


Domänenmodell im Überblick

Trip
├── BaseCamp[]
│   ├── Accommodation[]
│   ├── CyclingRoute[]
│   └── RestDay[]
├── Transfer[]
└── Itinerary / Statistics

Dieses Modell bildet die praktische Struktur einer mehrtägigen Radreise ab.


Lokale Entwicklung

Voraussetzungen

  • Node.js (vorzugsweise aktuelle LTS-Version)
  • Java 21 für das Backend
  • Docker & Docker Compose

Lokales Starten mit Docker Compose

Die docker-compose.yml unterstützt verschiedene Betriebsmodi.

Backend und Datenbank starten

docker compose up

Dadurch werden standardmäßig gestartet:

  • db auf Port 5432
  • app auf Port 8080

Nur die Datenbank starten

docker compose up db

Das ist nützlich, wenn das Backend lokal per Gradle gestartet werden soll.

Gesamten Stack über Docker starten

docker compose --profile full up --build

Damit wird zusätzlich das Frontend auf Port 3000 bereitgestellt.


Frontend und Backend getrennt lokal starten

Backend

cd server
./gradlew bootRun

Frontend

cd client
npm install
npm run dev

Umgebungsvariablen

Frontend

Beispielhafte Frontend-Konfiguration:

VITE_API_URL=http://localhost:8080
VITE_MAPTILER_KEY=your_maptiler_key

Das Frontend prüft VITE_API_URL, dann VITE_API_BASE_URL und fällt andernfalls auf /api zurück.

Backend / Docker Compose

Im Compose-Setup werden unter anderem folgende Variablen verwendet:

SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/velohub
SPRING_DATASOURCE_USERNAME=velohub
SPRING_DATASOURCE_PASSWORD=velohub
VELOHUB_ROUTING_API_KEY=...
VELOHUB_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
JAVA_TOOL_OPTIONS=-Xms256m -Xmx512m -XX:+UseContainerSupport

Für produktive Deployments sollten Secrets und API-Keys ausschließlich über die jeweilige Laufzeitumgebung bereitgestellt werden.


Docker-Setup

Frontend-Container

Das Frontend-Dockerfile verwendet einen Multi-Stage-Build:

  1. Build mit node:22-alpine
  2. Auslieferung über nginx:1.27-alpine

Der Build unterstützt unter anderem VITE_MAPTILER_KEY als Build-Argument.

Backend-Container

Auch das Backend wird per Multi-Stage-Build erzeugt:

  1. Build mit eclipse-temurin:21-jdk-alpine
  2. Laufzeit mit eclipse-temurin:21-jre-alpine

Dabei wird:

  • der Gradle Wrapper verwendet
  • mit einem nicht-root Benutzer gearbeitet
  • die Anwendung als app.jar auf Port 8080 ausgeführt

Deployment

Öffentliches Deployment-Setup

Das Deployment ist in zwei klar getrennte Bereiche aufgeteilt:

  • Frontend: Netlify
  • Backend: GitHub Actions → Docker Hub → VPS

Diese Trennung erlaubt es, das statische Frontend unabhängig vom Backend auszuliefern und das Backend weiterhin vollständig containerisiert zu betreiben.

Backend-Deployment über GitHub Actions, Docker Hub und VPS

Der Workflow .github/workflows/deploy-backend.yml bildet die Backend-Auslieferung ab.

Der Ablauf ist im Wesentlichen:

  1. Trigger bei Änderungen im server/-Bereich
  2. Checkout des Repositories
  3. Setup von Java
  4. Build des Backends mit Gradle
  5. Login bei Docker Hub
  6. Build und Push des Backend-Images
  7. SSH-Verbindung zum VPS
  8. Aktualisierung des laufenden Containers per Docker Compose

Auf dem VPS wird dabei sinngemäß Folgendes ausgeführt:

cd ~/app
docker compose pull app
docker compose up -d app

Damit ist GitHub Actions die Automatisierungsschicht, Docker Hub die Registry und der VPS die eigentliche Laufzeitumgebung des Backends.


CI/CD

Aktuell ist im Repository ein Workflow für das Backend-Deployment vorhanden.

Die technische Basis dafür umfasst bereits:

  • automatisierten Build
  • automatisierten Image-Build
  • Push zur Registry
  • automatisiertes Update auf dem VPS

Aktueller technischer Fokus

Die derzeitige Projektstruktur konzentriert sich insbesondere auf:

  • eine getrennte Frontend-Backend-Architektur
  • eine domänenorientierte REST-Struktur
  • Server-State-Handling über React Query
  • dedizierte API-Module im Frontend
  • räumliche Datenverarbeitung über PostGIS
  • GPX-Import
  • OpenAPI- und Swagger-Unterstützung
  • containerisierte lokale und öffentliche Betriebsumgebungen
  • automatisierte Backend-Auslieferung

Roadmap

Die bestehende Architektur lässt sich sinnvoll in folgende Richtungen weiterentwickeln.

Produkt-Roadmap

  • Benutzerkonten und Authentifizierung
  • Teilen von Reisen mit Mitreisenden oder Trainingspartnern
  • Kollaboratives Bearbeiten
  • Exportfunktionen, z. B. PDF oder GPX-Bundles
  • Wetterintegration pro Basecamp oder Tourtag
  • Erweiterte Transferarten wie Bahn, Fähre oder ÖPNV

Technische Roadmap

  • Versionierte Image-Tags zusätzlich zu latest
  • Staging-Umgebung
  • Automatisierte Tests in CI/CD
  • Health-Checks nach Deployments
  • Erweiterte Observability und Monitoring

Einstieg

Repository klonen

git clone https://github.com/XDMINT/VeloHub.git
cd VeloHub

Schnellstart mit Docker Compose

docker compose up

Alternativ lokal getrennt starten

# Backend
cd server
./gradlew bootRun

# Frontend
cd ../client
npm install
npm run dev

Lizenz

Im Repository ist aktuell keine explizite Lizenzdatei im Root-Verzeichnis ersichtlich. Falls das Projekt als Open-Source-Projekt veröffentlicht werden soll, sollte eine passende Lizenz ergänzt werden.


Schlussbemerkung

VeloHub ist darauf ausgelegt, Radreisen nicht nur als Sammlung einzelner Strecken zu betrachten, sondern als strukturierten Gesamtablauf. Diese Ausrichtung zeigt sich im Datenmodell, in der REST-API, in der Frontend-Architektur und im Deployment-Setup.

Dadurch entsteht eine Anwendung, die Routenplanung, Reiseorganisation und visuelle Strukturierung in einem gemeinsamen Modell zusammenführt.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors