GPX_Routes_Web Svelte Themes

Gpx_routes_web

GPX cycling routes gallery with admin panel — SvelteKit, MariaDB, Leaflet, Docker

GPX Routes

A public gallery of downloadable GPX cycling routes with an admin panel for uploading and managing routes.

Features

  • Public route gallery with ride type badges and stats
  • Route detail pages with interactive Leaflet map, elevation stats, and GPX download
  • Admin panel for uploading GPX files with automatic metadata extraction (distance, elevation, bounding box, GeoJSON preview)
  • Ride types: Gravel, Mountain Bike, Road, Enduro, Cross Country
  • Server-side GPX parsing and GeoJSON simplification (no re-parsing on page load)

Tech Stack

  • Framework: SvelteKit 2 (Svelte 5, TypeScript)
  • Database: MariaDB via Prisma ORM
  • Auth: Better Auth (email/password)
  • Maps: Leaflet with OpenStreetMap tiles
  • GPX Parsing: @we-gold/gpxjs
  • UI: TailwindCSS v4 + shadcn-svelte

Prerequisites

  • Docker and Docker Compose installed

1. Clone the repository

git clone https://github.com/gverbist/GPX_Routes_Web.git
cd GPX_Routes_Web

2. Configure environment

cp .env.example .env

Edit .env and set the required values:

# REQUIRED: Generate a secret for session signing
# Run: openssl rand -base64 32
BETTER_AUTH_SECRET="your-generated-secret-here"

# REQUIRED: The public URL where the app will be accessible
BETTER_AUTH_URL="https://routes.example.com"

# REQUIRED: Cloudflare Tunnel token (see "Cloudflare Tunnel setup" below)
TUNNEL_TOKEN="your-tunnel-token-here"

# Admin credentials (used to seed the first admin user)
ADMIN_EMAIL="[email protected]"
ADMIN_PASSWORD="changeme123"
ADMIN_NAME="Admin"

# Optional: change the MariaDB root password (only used internally)
MARIADB_ROOT_PASSWORD="root_password"

3. Start the application

docker compose up -d

This will:

  1. Start a MariaDB 11 container with a health check
  2. Build the app container from the Dockerfile
  3. Run Prisma migrations automatically on startup
  4. Seed the admin user
  5. Start the SvelteKit app on port 3000
  6. Start the Cloudflare Tunnel to expose the app on your configured domain

The app will be available at the domain you configured in the Cloudflare Tunnel.

4. Verify it's running

docker compose ps
docker compose logs app

Updating

Pull the latest changes and rebuild:

git pull
docker compose up -d --build

Migrations run automatically on container startup, so schema changes are applied on deploy.

Data persistence

Two Docker volumes are used:

Volume Contents
mariadb_data Database files
gpx_storage Uploaded GPX files (/app/storage/gpx/)

These persist across container restarts and rebuilds.

Cloudflare Tunnel setup

The app uses a Cloudflare Tunnel to securely expose the app to the internet without opening ports or configuring a reverse proxy. Cloudflare handles TLS automatically.

To get your tunnel token:

  1. Log in to the Cloudflare Zero Trust Dashboard
  2. Go to Networks > Tunnels
  3. Click Create a tunnel and choose Cloudflared
  4. Name your tunnel (e.g. gpx-routes)
  5. Copy the tunnel token and paste it into your .env file as TUNNEL_TOKEN
  6. In the Public Hostname tab, add a route:
    • Domain: your domain (e.g. routes.example.com)
    • Service type: HTTP
    • URL: app:3000 (the Docker service name and port)

Make sure BETTER_AUTH_URL in .env matches the public URL (e.g. https://routes.example.com).

The cloudflared container connects outbound to Cloudflare's network — no inbound ports need to be open on your server.

Stopping

docker compose down        # stop containers, data preserved in volumes
docker compose down -v     # stop and DELETE all data (removes volumes)

Common Issues

"Cross-site POST form submissions are forbidden"

This means the BETTER_AUTH_URL in your .env file doesn't match the public URL where the app is accessed. SvelteKit's CSRF protection compares the request Origin header against the configured origin.

Fix: Double-check your .env file and ensure BETTER_AUTH_URL matches your public domain exactly:

# Must match the URL users visit in their browser
BETTER_AUTH_URL="https://routes.example.com"

Then restart the app container:

docker compose up -d app

Map tiles not loading (401 error)

If you see a 401 error referencing docs.stadiamaps.com/authentication, the app may still be configured to use Stadia Maps tiles which require domain authentication. The current setup uses OpenStreetMap tiles which work without auth. If you've customised the tile provider, ensure your domain is registered with the tile service.

Cloudflare Tunnel UDP buffer warning

You may see: failed to sufficiently increase receive buffer size. This is a non-fatal warning from the QUIC library — the tunnel still works. To silence it, increase the host's UDP buffer size:

sudo sysctl -w net.core.rmem_max=7500000
sudo sysctl -w net.core.wmem_max=7500000

To persist across reboots, add to /etc/sysctl.conf:

net.core.rmem_max=7500000
net.core.wmem_max=7500000

Local Development

Prerequisites

  • Node.js 22+
  • Docker (for MariaDB)

Setup

# Install dependencies
npm install

# Start MariaDB
docker compose up -d mariadb

# Configure environment
cp .env.example .env
# Edit .env: set BETTER_AUTH_SECRET (openssl rand -base64 32)

# Run database migrations
npx prisma migrate dev

# Seed admin user
npx prisma db seed

# Start dev server
npm run dev

The dev server runs at http://localhost:5173.

Useful commands

npm run build              # Production build
npm run preview            # Preview production build locally
npx prisma generate        # Regenerate Prisma client after schema changes
npx prisma studio          # Visual database explorer

Project Structure

src/
  routes/
    +page.svelte                  # Public gallery
    routes/[id]/
      +page.svelte                # Route detail (map + stats + download)
      download/+server.ts         # GPX file download endpoint
    admin/
      login/                      # Admin login
      (app)/
        routes/                   # Admin route list
        routes/new/               # Upload GPX + metadata
        routes/[id]/edit/         # Edit route
  lib/
    components/
      RouteMap.svelte             # Leaflet map (SSR-safe)
      ui/                         # shadcn-svelte components
    server/
      auth.ts                     # Better Auth config
      db.ts                       # Prisma client
      gpx-parser.ts               # GPX parsing + metadata extraction
    schemas/
      route.ts                    # Zod validation + ride type labels
prisma/
  schema.prisma                   # Database schema
  seed.ts                         # Admin user seeder
  migrations/                     # Database migrations
storage/gpx/                      # Uploaded GPX files (gitignored)

Top categories

Loading Svelte Themes