23 KiB
Location Tracker - Next.js Anwendung
Eine moderne Location-Tracking Anwendung basierend auf Next.js 14 mit MQTT/OwnTracks Integration, SQLite-Datenbank, Admin-Panel und Authentifizierung.
📋 Inhaltsverzeichnis
- Features
- Tech Stack
- Installation
- Datenbank-Setup
- Verwendung
- Architektur
- API-Endpunkte
- Device Management
- Wartung
- Deployment
✨ Features
Öffentliche Features
- 🗺️ Interaktive Karte - Echtzeit-Standortverfolgung mit Leaflet.js
- 🎨 Mehrere Kartenansichten - Standard, Satellit, Dark Mode
- 🔍 Device-Filterung - Filtern nach Gerät und Zeitraum
- ⏱️ Flexible Zeitfilter:
- Quick Filters: 1h, 3h, 6h, 12h, 24h, All
- Custom Range: Benutzerdefinierter Zeitraum mit DateTime-Picker (z.B. 16.11.2025 16:00 - 17.11.2025 06:00)
- Kompakte UI - Custom Range nur sichtbar wenn aktiviert
- 🔄 Auto-Refresh - Automatische Aktualisierung alle 5 Sekunden mit Pause/Resume Button
- 🎯 Auto-Center - Karte zentriert automatisch auf neueste Position
- ⏸️ Pause/Resume - Toggle-Button zum Stoppen/Starten des Auto-Refresh
- 📱 Responsive Design - Optimiert für Desktop und Mobile
- 📊 Polylines - Bewegungspfade mit farbcodierter Darstellung
- 🎨 Marker-Sortierung - Neueste Position immer im Vordergrund (z-index optimiert)
- 📍 Zoom-basierte Icon-Skalierung - Marker passen sich automatisch an Zoom-Level an
Admin-Panel (Login erforderlich)
- 🔐 Authentifizierung - NextAuth.js v5 mit bcrypt-Hashing
- 📊 Dashboard - Übersicht über Geräte, Statistiken und Datenbankstatus
- ⏱️ System Status - Live-Uptime, Memory Usage, Runtime Info
- 📱 Device Management - Geräte hinzufügen, bearbeiten, löschen
- 💾 Datenbank-Wartung:
- 🔄 Manueller Sync von n8n
- 🧹 Cleanup alter Daten (7, 15, 30, 90 Tage)
- ⚡ Datenbank-Optimierung (VACUUM)
- 📈 Detaillierte Statistiken
- 🟢 Online/Offline Status - Echtzeit-Status (< 10 Min = Online)
- 🔋 Telemetrie-Daten - Batterie, Geschwindigkeit, letzte Position (speed=0 wird korrekt behandelt)
🛠 Tech Stack
- Framework: Next.js 14 (App Router)
- Sprache: TypeScript 5.9
- Styling: Tailwind CSS v4
- Karten: Leaflet 1.9.4 + React-Leaflet 5.0
- Authentifizierung: NextAuth.js v5 (beta)
- Datenbank: SQLite (better-sqlite3)
- Passwort-Hashing: bcryptjs
- Datenquelle: n8n Webhook API + lokale SQLite-Cache
Dual-Database Architektur
- database.sqlite - User, Geräte (kritische Daten)
- locations.sqlite - Location-Tracking (hohe Schreibrate, isoliert)
📦 Installation
Voraussetzungen
- Node.js 18+
- npm oder yarn
Schritte
- Repository klonen
git clone https://github.com/yourusername/location-tracker-app.git
cd location-tracker-app
- Dependencies installieren
npm install
- Datenbank initialisieren
npm run db:init
Dies erstellt:
data/database.sqlite(User + Devices)data/locations.sqlite(Location-Tracking)- Standard Admin-User:
admin/admin123 - Standard Devices (ID 10, 11)
- Development Server starten
npm run dev
- Im Browser öffnen
- Karte: http://localhost:3000
- Login: http://localhost:3000/login
- Admin: http://localhost:3000/admin
- Devices: http://localhost:3000/admin/devices
🗄️ Datenbank-Setup
Initialisierung
Beide Datenbanken erstellen:
npm run db:init
Nur database.sqlite (User/Devices):
npm run db:init:app
Nur locations.sqlite (Tracking):
npm run db:init:locations
Datenbank zurücksetzen
Admin-User neu anlegen:
node scripts/reset-admin.js
Alte Locations löschen:
npm run db:cleanup # Älter als 7 Tage
npm run db:cleanup:7d # Älter als 7 Tage
npm run db:cleanup:30d # Älter als 30 Tage
Duplikate entfernen (falls vorhanden):
node scripts/remove-duplicates.js
Schema
database.sqlite (User & Devices)
User Tabelle:
id TEXT PRIMARY KEY
username TEXT UNIQUE NOT NULL
email TEXT
passwordHash TEXT NOT NULL
role TEXT NOT NULL DEFAULT 'VIEWER' -- ADMIN oder VIEWER
createdAt TEXT DEFAULT (datetime('now'))
updatedAt TEXT DEFAULT (datetime('now'))
lastLoginAt TEXT
Device Tabelle:
id TEXT PRIMARY KEY
name TEXT NOT NULL
color TEXT NOT NULL
ownerId TEXT -- FK zu User.id
isActive INTEGER DEFAULT 1 -- 0 oder 1
description TEXT
icon TEXT
createdAt TEXT DEFAULT (datetime('now'))
updatedAt TEXT DEFAULT (datetime('now'))
Indexes:
idx_user_usernameON User(username)idx_device_ownerON Device(ownerId)idx_device_activeON Device(isActive)
locations.sqlite (Tracking Data)
Location Tabelle:
id INTEGER PRIMARY KEY AUTOINCREMENT
latitude REAL NOT NULL -- -90 bis +90
longitude REAL NOT NULL -- -180 bis +180
timestamp TEXT NOT NULL -- ISO 8601 format
user_id INTEGER DEFAULT 0
first_name TEXT
last_name TEXT
username TEXT -- Device Tracker ID
marker_label TEXT
display_time TEXT -- Formatierte Zeit für UI
chat_id INTEGER DEFAULT 0
battery INTEGER -- Batteriestand in %
speed REAL -- Geschwindigkeit in km/h
created_at TEXT DEFAULT (datetime('now'))
Indexes:
idx_location_timestampON Location(timestamp DESC)idx_location_usernameON Location(username)idx_location_user_idON Location(user_id)idx_location_compositeON Location(user_id, username, timestamp DESC)idx_location_uniqueUNIQUE ON Location(timestamp, username, latitude, longitude)
Constraints:
- Latitude: -90 bis +90
- Longitude: -180 bis +180
- UNIQUE: Kombination aus timestamp, username, latitude, longitude verhindert Duplikate
WAL Mode: Beide Datenbanken nutzen Write-Ahead Logging für bessere Concurrency
🚀 Verwendung
Login
Standard-Zugangsdaten:
Benutzername: admin
Passwort: admin123
⚠️ Wichtig: Für Production neuen User anlegen und Passwort ändern!
Geräte hinzufügen
- Admin-Panel öffnen:
/admin/devices - "Add Device" klicken
- Device ID (muss mit OwnTracks
tidübereinstimmen) - Name und Farbe festlegen
- Speichern
Wichtig: Die Device ID muss mit der OwnTracks Tracker-ID übereinstimmen!
OwnTracks konfigurieren
In der OwnTracks App:
- Tracker ID (tid): z.B.
12 - Topic:
owntracks/user/12 - MQTT Broker wie gewohnt
Die n8n-Workflow holt die Daten, und die App synct automatisch alle 5 Sekunden.
Zeitfilter verwenden
Die App bietet zwei Modi für die Zeitfilterung:
Quick Filters (Schnellauswahl)
Vordefinierte Zeiträume für schnellen Zugriff:
- 1 Hour - Locations der letzten Stunde
- 3 Hours - Locations der letzten 3 Stunden
- 6 Hours - Locations der letzten 6 Stunden
- 12 Hours - Locations der letzten 12 Stunden
- 24 Hours - Locations der letzten 24 Stunden
- All - Alle verfügbaren Locations
Verwendung:
- Im Header unter "Time:" gewünschten Zeitraum auswählen
- Die Karte aktualisiert sich automatisch
Custom Range (Benutzerdefiniert)
Für spezifische Zeiträume, z.B. "Route von gestern Abend 18:00 bis heute Morgen 08:00":
Verwendung:
- Auf den "📅 Custom" Button klicken
- Custom Range Felder erscheinen:
- From: Start-Datum und -Zeit wählen (z.B. 16.11.2025 16:00)
- To: End-Datum und -Zeit wählen (z.B. 17.11.2025 06:00)
- Die Route wird automatisch für den gewählten Zeitraum angezeigt
- Zum Zurückschalten: "📅 Quick" Button klicken
Hinweis: Custom Range Controls sind nur sichtbar wenn aktiviert - spart Platz im Header!
🏗 Architektur
Datenfluss
flowchart TD
A[📱 OwnTracks App] -->|MQTT Publish| B[🔌 MQTT Broker]
B -->|Subscribe| C[⚙️ n8n MQTT Trigger]
C -->|Store| D[💾 NocoDB]
D -->|Webhook API| E[🌐 n8n Webhook<br/>/webhook/location]
F[🖥️ Browser Client] -->|GET /api/locations<br/>alle 5 Sek| G[📡 Next.js API Route]
G -->|1. Fetch Fresh Data| E
E -->|JSON Response| G
G -->|2. Sync New Locations| H[(🗄️ SQLite Cache<br/>locations.sqlite)]
H -->|3. Query Filtered Data| G
G -->|JSON Response| F
F -->|Render| I[🗺️ React Leaflet Map]
J[👤 Admin User] -->|Login| K[🔐 NextAuth.js]
K -->|Authenticated| L[📊 Admin Panel]
L -->|CRUD Operations| M[(💼 SQLite DB<br/>database.sqlite)]
style A fill:#4CAF50
style B fill:#FF9800
style C fill:#2196F3
style D fill:#9C27B0
style E fill:#F44336
style G fill:#00BCD4
style H fill:#FFC107
style I fill:#8BC34A
style K fill:#E91E63
style M fill:#FFC107
Komponenten-Übersicht
graph LR
subgraph "External Services"
A[OwnTracks App]
B[MQTT Broker]
C[n8n Automation]
D[NocoDB]
end
subgraph "Next.js Application"
E[Frontend<br/>React/Leaflet]
F[API Routes]
G[Auth Layer<br/>NextAuth.js]
end
subgraph "Data Layer"
H[locations.sqlite<br/>Tracking Data]
I[database.sqlite<br/>Users & Devices]
end
A -->|MQTT| B
B -->|Subscribe| C
C -->|Store| D
C -->|Webhook| F
E -->|HTTP| F
F -->|Read/Write| H
F -->|Read/Write| I
E -->|Auth| G
G -->|Validate| I
style A fill:#4CAF50,color:#fff
style B fill:#FF9800,color:#fff
style C fill:#2196F3,color:#fff
style D fill:#9C27B0,color:#fff
style E fill:#00BCD4,color:#000
style F fill:#00BCD4,color:#000
style G fill:#E91E63,color:#fff
style H fill:#FFC107,color:#000
style I fill:#FFC107,color:#000
Datenbank-Architektur
erDiagram
USER ||--o{ DEVICE : owns
DEVICE ||--o{ LOCATION : tracks
USER {
string id PK
string username UK
string email
string passwordHash
string role
datetime createdAt
datetime lastLoginAt
}
DEVICE {
string id PK
string name
string color
string ownerId FK
boolean isActive
string description
datetime createdAt
}
LOCATION {
int id PK
float latitude
float longitude
datetime timestamp
string username FK
int user_id
string display_time
int battery
float speed
int chat_id
}
Auto-Sync Mechanismus
Die App verwendet einen Hybrid-Ansatz:
- Frontend polling (alle 5 Sek.) →
/api/locations - API prüft ob neue Daten in n8n verfügbar
- Nur neue Locations werden in SQLite gespeichert
- Duplikate werden durch UNIQUE Index verhindert
- Antwort kommt aus lokalem SQLite Cache
Vorteile:
- Schnelle Antwortzeiten (SQLite statt n8n)
- Längere Zeiträume abrufbar (24h+)
- Funktioniert auch wenn n8n nicht erreichbar ist (Fallback auf n8n-Daten)
- Duplikate werden automatisch verhindert (UNIQUE Index)
Datenvalidierung & Normalisierung
Die App behandelt spezielle Fälle bei speed/battery korrekt:
speed = 0 Behandlung:
- n8n sendet
speed: 0(gültig - Gerät steht still) - Wird mit
typeof === 'number'Check explizit als0gespeichert - Wird NICHT zu
nullkonvertiert (wichtig für Telemetrie) - Popup zeigt "Speed: 0.0 km/h" an
n8n Fallback:
- Wenn DB leer ist, gibt API direkt n8n-Daten zurück
- Alle Filter (username, timeRangeHours) funktionieren auch mit Fallback
- Ermöglicht sofortigen Betrieb ohne DB-Initialisierung
Debug-Logging:
- Server-Logs zeigen n8n Sync-Aktivität
- Browser Console zeigt Daten-Flow (MapView, Popup)
- Hilfreich für Troubleshooting
📡 API-Endpunkte
Öffentlich
GET /api/locations
- Location-Daten abrufen (mit Auto-Sync)
- Query-Parameter:
username- Device-Filter (z.B. "10", "11")- Zeitfilter (wähle eine Methode):
timeRangeHours- Quick Filter (1, 3, 6, 12, 24)startTime&endTime- Custom Range (ISO 8601 Format)
limit- Max. Anzahl (Standard: 1000)sync=false- Nur Cache ohne n8n Sync
Beispiele:
# Quick Filter: Letzte 3 Stunden
GET /api/locations?timeRangeHours=3
# Custom Range: Spezifischer Zeitraum
GET /api/locations?startTime=2025-11-16T16:00:00.000Z&endTime=2025-11-17T06:00:00.000Z
# Kombiniert: Device + Custom Range
GET /api/locations?username=10&startTime=2025-11-16T16:00:00.000Z&endTime=2025-11-17T06:00:00.000Z
GET /api/devices/public
- Öffentliche Device-Liste (nur ID, Name, Color)
Geschützt (Login erforderlich)
GET /api/devices
- Alle Geräte mit Latest Location und Telemetrie
POST /api/devices
- Neues Gerät erstellen
- Body:
{ id, name, color, description? }
PATCH /api/devices/:id
- Gerät aktualisieren
- Body:
{ name?, color?, description? }
DELETE /api/devices/:id
- Gerät löschen (soft delete)
POST /api/locations/sync
- Manueller Sync von n8n
- Gibt Anzahl neu eingefügter Locations zurück
POST /api/locations/cleanup
- Alte Locations löschen
- Body:
{ retentionHours }
POST /api/locations/optimize
- VACUUM + ANALYZE ausführen
- Gibt freigegebenen Speicher zurück
GET /api/locations/stats
- Detaillierte DB-Statistiken
- Größe, Zeitraum, Locations pro Device
GET /api/system/status
- System-Status abrufen
- Returns: Uptime, Memory Usage, Node.js Version, Platform
- Auto-Refresh: Alle 10 Sekunden im Admin-Panel
📱 Device Management
Device-Karte zeigt:
- 🟢/⚫ Online/Offline Status
- Online = letzte Location < 10 Minuten
- Offline = letzte Location > 10 Minuten
- 🕒 Last Seen - Zeitstempel letzter Location
- 🔋 Batterie - Prozent (Rot bei < 20%)
- 🚗 Geschwindigkeit - km/h (umgerechnet von m/s)
- 📍 Koordinaten - Lat/Lon mit 5 Dezimalen
Auto-Refresh
- Devices-Seite aktualisiert sich alle 10 Sekunden
- Online/Offline Status wird automatisch aktualisiert
🧹 Wartung
Datenbank aufräumen
Via Admin-Panel:
/admin→ Database Maintenance → Cleanup Buttons
Via CLI:
npm run db:cleanup # 7 Tage
npm run db:cleanup:30d # 30 Tage
Datenbank optimieren
Via Admin-Panel:
/admin→ Database Maintenance → Optimize Button
Via CLI:
# Manuell
node scripts/optimize-db.js
Was macht Optimize:
VACUUM- Speicherplatz freigebenANALYZE- Query-Statistiken aktualisieren
Sync von n8n
Via Admin-Panel:
/admin→ Database Maintenance → Sync Now
Automatisch:
- Passiert alle 5 Sekunden beim Abruf der Karte
Logs prüfen
# Development Server Logs
npm run dev
# Production Logs (PM2)
pm2 logs location-tracker-app
Zeitfilter debuggen
Bei Problemen mit der Zeitfilterung (z.B. alte Locations werden nicht ausgefiltert):
node scripts/test-time-filter.js
Das Script zeigt:
- Aktuelle Zeit (UTC und lokal)
- Letzte Locations in der Datenbank
- Welche Locations mit 1-Stunden-Filter angezeigt werden sollten
- Vergleich zwischen alter (SQLite datetime) und neuer (JavaScript) Methode
- Anzahl der gefilterten Locations
Verwendung:
- Script ausführen um zu sehen, welche Locations in der DB sind
- Überprüfen ob die Zeitfilterung korrekt funktioniert
- Bei Problemen: App neu starten nach Code-Updates
🚀 Deployment
Environment Variables
Erstelle .env.local:
# NextAuth
AUTH_SECRET=<generiere-mit-openssl-rand-base64-32>
NEXTAUTH_URL=https://your-domain.com
# Optional: n8n API URL (Standard in Code definiert)
N8N_API_URL=https://n8n.example.com/webhook/location
Secret generieren:
openssl rand -base64 32
Production Build
# Build
npm run build
# Start
npm run start
Mit PM2 (empfohlen)
# PM2 installieren
npm install -g pm2
# App starten
pm2 start npm --name "location-tracker-app" -- start
# Auto-Start bei Server-Neustart
pm2 startup
pm2 save
Nginx Reverse Proxy
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
🔒 Sicherheit
Production Checklist
AUTH_SECRETmit starkem Wert setzenNEXTAUTH_URLauf Production-Domain setzen- Admin-Passwort ändern (nicht
admin123) - Ggf. weitere User anlegen mit VIEWER Rolle
- HTTPS aktivieren (Let's Encrypt)
- Firewall-Regeln prüfen
- Regelmäßige Backups einrichten
User-Rollen
- ADMIN - Voller Zugriff auf alle Admin-Funktionen
- VIEWER - Nur lesender Zugriff (geplant, noch nicht implementiert)
📂 Projektstruktur
location-tracker-app/
├── app/
│ ├── api/
│ │ ├── auth/[...nextauth]/ # NextAuth API
│ │ ├── devices/ # Device CRUD
│ │ ├── locations/ # Location API + Sync/Cleanup/Stats
│ │ └── system/status/ # System Status (Uptime, Memory)
│ ├── admin/
│ │ ├── devices/ # Device Management
│ │ └── page.tsx # Dashboard
│ ├── login/ # Login-Seite
│ ├── page.tsx # Öffentliche Karte
│ └── layout.tsx # Root Layout
├── components/
│ └── map/
│ └── MapView.tsx # Leaflet Map Component
├── lib/
│ ├── auth.ts # NextAuth Config
│ └── db.ts # SQLite Database Layer
├── scripts/
│ ├── init-database.js # Database.sqlite Setup
│ ├── init-locations-db.js # Locations.sqlite Setup
│ ├── reset-admin.js # Admin User Reset
│ ├── remove-duplicates.js # Duplikate bereinigen
│ └── cleanup-old-locations.js # Alte Daten löschen
├── data/
│ ├── database.sqlite # User + Devices
│ └── locations.sqlite # Location Tracking
├── types/
│ └── location.ts # TypeScript Interfaces
└── middleware.ts # Route Protection
📝 Changelog
Version 1.1.0 - November 2025
🆕 Neue Features
- Custom Date Range Filter
- Benutzerdefinierte Zeiträume mit DateTime-Picker (z.B. 16.11.2025 16:00 - 17.11.2025 06:00)
- Toggle-Button zwischen Quick Filters und Custom Range
- Kompakte UI - Custom Range nur sichtbar wenn aktiviert
- Backend-Support mit
startTimeundendTimeAPI-Parametern
🐛 Bug Fixes
- Zeitfilter-Bug behoben
- Alte Locations (z.B. 6+ Stunden alt) werden jetzt korrekt ausgefiltert
- JavaScript-basierte Zeitberechnung statt SQLite
datetime('now') - Verhindert Zeitversatz-Probleme
🛠️ Verbesserungen
- Zoom-basierte Icon-Skalierung für bessere Sichtbarkeit
- Optimierte Zeitfilter-Logik in Datenbank-Queries
- Debug-Script
test-time-filter.jsfür Troubleshooting
📚 Dokumentation
- README aktualisiert mit Custom Range Anleitung
- API-Endpunkte Dokumentation erweitert
- Wartungs-Abschnitt mit Debug-Script Information
🐛 Troubleshooting
"Invalid username or password"
Lösung:
node scripts/reset-admin.js
Datenbank-Dateien fehlen
Lösung:
npm run db:init
Duplikate in locations.sqlite
Lösung:
# Erst Duplikate entfernen
node scripts/remove-duplicates.js
# Dann UNIQUE Index hinzufügen
node scripts/init-locations-db.js
Map zeigt keine Daten
- n8n Webhook erreichbar?
curl https://n8n.example.com/webhook/location - Locations in Datenbank?
/admin→ Database Statistics prüfen - Auto-Sync aktiv? Browser Console öffnen
"ENOENT: no such file or directory, open 'data/database.sqlite'"
Lösung:
mkdir -p data
npm run db:init
📝 NPM Scripts
# Development
npm run dev # Dev Server starten
# Production
npm run build # Production Build
npm run start # Production Server
# Database
npm run db:init # Beide DBs initialisieren
npm run db:init:app # Nur database.sqlite
npm run db:init:locations # Nur locations.sqlite
npm run db:cleanup # Cleanup 7 Tage
npm run db:cleanup:7d # Cleanup 7 Tage
npm run db:cleanup:30d # Cleanup 30 Tage
# Linting
npm run lint # ESLint ausführen
📄 Lizenz
MIT License - Open Source
📜 Open Source Lizenzen
Diese Anwendung verwendet folgende Open-Source-Software:
MIT License
- Next.js - Copyright (c) Vercel, Inc.
- React - Copyright (c) Meta Platforms, Inc.
- React-DOM - Copyright (c) Meta Platforms, Inc.
- Tailwind CSS - Copyright (c) Tailwind Labs, Inc.
- better-sqlite3 - Copyright (c) Joshua Wise
- bcryptjs - Copyright (c) Daniel Wirtz
Apache License 2.0
- TypeScript - Copyright (c) Microsoft Corporation
ISC License
- NextAuth.js - Copyright (c) NextAuth.js Contributors
BSD-2-Clause License
- Leaflet - Copyright (c) Vladimir Agafonkin
Hippocratic License 2.1
- React-Leaflet - Copyright (c) Paul Le Cam
Vollständige Lizenztexte:
Alle vollständigen Lizenztexte der verwendeten Bibliotheken finden Sie in den jeweiligen GitHub-Repositories oder in der node_modules Directory nach Installation.
🙏 Credits
- Next.js 14 - React Framework
- Leaflet.js - Karten-Bibliothek
- NextAuth.js - Authentifizierung
- better-sqlite3 - SQLite für Node.js
- Tailwind CSS - Utility-First CSS
- n8n - Workflow Automation (Backend)
- OwnTracks - Location Tracking Apps
📞 Support
Bei Fragen oder Problemen:
- Logs prüfen (
npm run devOutput) - Browser Console öffnen (F12)
- Datenbank-Status in
/adminprüfen - Issues im Repository erstellen