Joachim Hummel 632c92f3ee Add MQTT tables and parent_user_id column to database schema
Added MQTT credentials, ACL rules, and sync status tables for broker integration. Added parent_user_id column to User table for hierarchical user management.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 13:37:01 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 20:33:15 +00:00
2025-11-24 16:30:37 +00:00
2025-11-24 16:30:37 +00:00

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

Ö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:
    • 🧹 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: MQTT Broker + 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

  1. Repository klonen
git clone https://github.com/yourusername/location-tracker-app.git
cd location-tracker-app
  1. Dependencies installieren
npm install
  1. 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)
  1. Development Server starten
npm run dev
  1. Im Browser öffnen

🗄️ 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_username ON User(username)
  • idx_device_owner ON Device(ownerId)
  • idx_device_active ON 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_timestamp ON Location(timestamp DESC)
  • idx_location_username ON Location(username)
  • idx_location_user_id ON Location(user_id)
  • idx_location_composite ON Location(user_id, username, timestamp DESC)
  • idx_location_unique UNIQUE 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

  1. Admin-Panel öffnen: /admin/devices
  2. "Add Device" klicken
  3. Device ID (muss mit OwnTracks tid übereinstimmen)
  4. Name und Farbe festlegen
  5. 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 konfigurieren (siehe MQTT Setup)

Die App empfängt die Daten direkt vom MQTT Broker.

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:

  1. Im Header unter "Time:" gewünschten Zeitraum auswählen
  2. 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:

  1. Auf den "📅 Custom" Button klicken
  2. 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)
  3. Die Route wird automatisch für den gewählten Zeitraum angezeigt
  4. 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[📡 Next.js MQTT Subscriber]

    C -->|Store Locations| D[(🗄️ SQLite Cache<br/>locations.sqlite)]

    E[🖥️ Browser Client] -->|GET /api/locations<br/>alle 5 Sek| F[📡 Next.js API Route]

    D -->|Query Filtered Data| F
    F -->|JSON Response| E

    E -->|Render| G[🗺️ React Leaflet Map]

    H[👤 Admin User] -->|Login| I[🔐 NextAuth.js]
    I -->|Authenticated| J[📊 Admin Panel]
    J -->|CRUD Operations| K[(💼 SQLite DB<br/>database.sqlite)]

    style A fill:#4CAF50
    style B fill:#FF9800
    style C fill:#2196F3
    style D fill:#FFC107
    style F fill:#00BCD4
    style G fill:#8BC34A
    style I fill:#E91E63
    style K fill:#FFC107

Komponenten-Übersicht

graph LR
    subgraph "External Services"
        A[OwnTracks App]
        B[MQTT Broker]
    end

    subgraph "Next.js Application"
        C[MQTT Subscriber]
        D[Frontend<br/>React/Leaflet]
        E[API Routes]
        F[Auth Layer<br/>NextAuth.js]
    end

    subgraph "Data Layer"
        G[locations.sqlite<br/>Tracking Data]
        H[database.sqlite<br/>Users & Devices]
    end

    A -->|MQTT| B
    B -->|Subscribe| C
    C -->|Write| G

    D -->|HTTP| E
    E -->|Read/Write| G
    E -->|Read/Write| H

    D -->|Auth| F
    F -->|Validate| H

    style A fill:#4CAF50,color:#fff
    style B fill:#FF9800,color:#fff
    style C fill:#2196F3,color:#fff
    style D fill:#00BCD4,color:#000
    style E fill:#00BCD4,color:#000
    style F fill:#E91E63,color:#fff
    style G fill:#FFC107,color:#000
    style H 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-Refresh Mechanismus

Die App verwendet einen Echtzeit-Ansatz:

  1. MQTT Subscriber empfängt OwnTracks Messages direkt vom Broker
  2. Locations werden sofort in SQLite gespeichert
  3. Frontend polling (alle 5 Sek.) → /api/locations
  4. API liest gefilterte Daten aus lokalem SQLite Cache
  5. Duplikate werden durch UNIQUE Index verhindert

Vorteile:

  • Echtzeitdaten ohne Verzögerung
  • Schnelle Antwortzeiten (direkter SQLite Zugriff)
  • Längere Zeiträume abrufbar (24h+)
  • Keine externen Dependencies (kein n8n nötig)
  • Duplikate werden automatisch verhindert (UNIQUE Index)

Datenvalidierung & Normalisierung

Die App behandelt spezielle Fälle bei speed/battery korrekt:

speed = 0 Behandlung:

  • MQTT sendet speed: 0 (gültig - Gerät steht still)
  • Wird mit typeof === 'number' Check explizit als 0 gespeichert
  • Wird NICHT zu null konvertiert (wichtig für Telemetrie)
  • Popup zeigt "Speed: 0.0 km/h" an

Debug-Logging:

  • Server-Logs zeigen MQTT Message-Verarbeitung
  • Browser Console zeigt Daten-Flow (MapView, Popup)
  • Hilfreich für Troubleshooting

📡 API-Endpunkte

Öffentlich

GET /api/locations

  • Location-Daten abrufen (aus SQLite Cache)
  • 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)

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/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 freigeben
  • ANALYZE - Query-Statistiken aktualisieren

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:

  1. Script ausführen um zu sehen, welche Locations in der DB sind
  2. Überprüfen ob die Zeitfilterung korrekt funktioniert
  3. 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

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_SECRET mit starkem Wert setzen
  • NEXTAUTH_URL auf 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 startTime und endTime API-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.js fü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

  1. MQTT Broker erreichbar? mosquitto_sub -h localhost -p 1883 -t '#'
  2. Locations in Datenbank? /admin → Database Statistics prüfen
  3. MQTT Subscriber läuft? Server-Logs prüfen

"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

Apache License 2.0

  • TypeScript - Copyright (c) Microsoft Corporation

ISC License

BSD-2-Clause License

  • Leaflet - Copyright (c) Vladimir Agafonkin

Hippocratic License 2.1

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
  • MQTT.js - MQTT Client für Node.js
  • OwnTracks - Location Tracking Apps

📞 Support

Bei Fragen oder Problemen:

  1. Logs prüfen (npm run dev Output)
  2. Browser Console öffnen (F12)
  3. Datenbank-Status in /admin prüfen
  4. Issues im Repository erstellen
Description
No description provided
Readme 816 KiB
Languages
TypeScript 89.6%
JavaScript 10%
Dockerfile 0.3%