Joachim Hummel 0343dead08 Add Notifications link to User Management page
- Add quick access button to notification settings in user management header
- Styled to match existing UI gradient theme
- Provides easy navigation from user management to notification configuration

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:59:25 +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 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
  • 🎯 Geofencing - Kreisförmige Geofences mit Enter/Exit-Benachrichtigungen:
    • Geofences erstellen, bearbeiten und löschen
    • Radius 50m bis 50km konfigurierbar
    • Visualisierung auf der Karte mit Toggle-Button
    • Event-Historie mit Enter/Exit-Ereignissen
    • E-Mail und MQTT-Benachrichtigungen bei Grenzüberschreitung
  • 💾 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 16.0.7 (App Router)
  • Runtime: React 19.2.1
  • 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)
  • MQTT: MQTT.js v5 + Eclipse Mosquitto
  • E-Mail: Nodemailer + React Email
  • Passwort-Hashing: bcryptjs
  • Datenquelle: MQTT Broker + lokale SQLite-Cache

Dual-Database Architektur

  • database.sqlite - User, Geräte, MQTT-Credentials, Geofences (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 + MQTT + Geofences)
  • 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

Nur Geofence-Tabellen:

npm run db:init:geofence

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
parent_user_id  TEXT                            -- FK zu User.id (Hierarchie)
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'))

MQTT Credentials Tabelle:

id                  INTEGER PRIMARY KEY AUTOINCREMENT
device_id           TEXT NOT NULL UNIQUE            -- FK zu Device.id
mqtt_username       TEXT NOT NULL UNIQUE
mqtt_password_hash  TEXT NOT NULL
enabled             INTEGER DEFAULT 1               -- 0 oder 1
created_at          TEXT DEFAULT (datetime('now'))
updated_at          TEXT DEFAULT (datetime('now'))

MQTT ACL Rules Tabelle:

id              INTEGER PRIMARY KEY AUTOINCREMENT
device_id       TEXT NOT NULL                   -- FK zu Device.id
topic_pattern   TEXT NOT NULL
permission      TEXT NOT NULL                   -- 'read', 'write', 'readwrite'
created_at      TEXT DEFAULT (datetime('now'))

MQTT Sync Status Tabelle:

id                  INTEGER PRIMARY KEY CHECK (id = 1)  -- Singleton
pending_changes     INTEGER DEFAULT 0
last_sync_at        TEXT
last_sync_status    TEXT
created_at          TEXT DEFAULT (datetime('now'))
updated_at          TEXT DEFAULT (datetime('now'))

Geofence Tabelle:

id                  TEXT PRIMARY KEY
name                TEXT NOT NULL
description         TEXT
shape_type          TEXT NOT NULL DEFAULT 'circle'      -- Only 'circle' for MVP
center_latitude     REAL NOT NULL
center_longitude    REAL NOT NULL
radius_meters       INTEGER NOT NULL
owner_id            TEXT NOT NULL                       -- FK zu User.id
device_id           TEXT NOT NULL                       -- FK zu Device.id
is_active           INTEGER DEFAULT 1                   -- 0 oder 1
color               TEXT DEFAULT '#3b82f6'
created_at          TEXT DEFAULT (datetime('now'))
updated_at          TEXT DEFAULT (datetime('now'))

GeofenceEvent Tabelle:

id                      INTEGER PRIMARY KEY AUTOINCREMENT
geofence_id             TEXT NOT NULL                   -- FK zu Geofence.id
device_id               TEXT NOT NULL                   -- FK zu Device.id
location_id             INTEGER NOT NULL
event_type              TEXT NOT NULL                   -- 'enter' oder 'exit'
latitude                REAL NOT NULL
longitude               REAL NOT NULL
distance_from_center    REAL
notification_sent       INTEGER DEFAULT 0               -- 0=not sent, 1=sent, 2=failed
notification_error      TEXT
timestamp               TEXT NOT NULL
created_at              TEXT DEFAULT (datetime('now'))

GeofenceStatus Tabelle:

id                  INTEGER PRIMARY KEY AUTOINCREMENT
device_id           TEXT NOT NULL                       -- FK zu Device.id
geofence_id         TEXT NOT NULL                       -- FK zu Geofence.id
is_inside           INTEGER NOT NULL DEFAULT 0          -- 0 oder 1
last_enter_time     TEXT
last_exit_time      TEXT
last_checked_at     TEXT
created_at          TEXT DEFAULT (datetime('now'))
updated_at          TEXT DEFAULT (datetime('now'))

Indexes:

  • idx_user_username ON User(username)
  • idx_user_parent ON User(parent_user_id)
  • idx_device_owner ON Device(ownerId)
  • idx_device_active ON Device(isActive)
  • idx_mqtt_credentials_device ON mqtt_credentials(device_id)
  • idx_mqtt_credentials_username ON mqtt_credentials(mqtt_username)
  • idx_mqtt_acl_device ON mqtt_acl_rules(device_id)
  • idx_geofence_owner ON Geofence(owner_id)
  • idx_geofence_device ON Geofence(device_id)
  • idx_geofence_active ON Geofence(is_active)
  • idx_geofence_event_geofence ON GeofenceEvent(geofence_id)
  • idx_geofence_event_device ON GeofenceEvent(device_id)
  • idx_geofence_event_timestamp ON GeofenceEvent(timestamp DESC)
  • idx_geofence_event_notification ON GeofenceEvent(notification_sent)
  • idx_geofence_event_composite ON GeofenceEvent(device_id, geofence_id, timestamp DESC)
  • idx_geofence_status_device ON GeofenceStatus(device_id)
  • idx_geofence_status_geofence ON GeofenceStatus(geofence_id)
  • idx_geofence_status_inside ON GeofenceStatus(is_inside)

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 publishes automatically to owntracks/<username>/<device_id>
    • Example: owntracks/device_12_4397af93/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)]
    C -->|Check Geofences| L[🎯 Geofence Monitor]
    L -->|Store Events| K[(💼 SQLite DB<br/>database.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]
    E -->|Render Geofences| M[🔵 Geofence Circles]

    H[👤 Admin User] -->|Login| I[🔐 NextAuth.js]
    I -->|Authenticated| J[📊 Admin Panel]
    J -->|CRUD Operations| K
    J -->|Manage Geofences| K
    K -->|Query Events| J

    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
    style L fill:#9C27B0
    style M fill:#3F51B5

Komponenten-Übersicht

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

    subgraph "Next.js Application"
        C[MQTT Subscriber]
        I[Geofence Monitor]
        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<br/>MQTT, Geofences]
    end

    A -->|MQTT| B
    B -->|Subscribe| C
    C -->|Write Locations| G
    C -->|Trigger| I
    I -->|Check & Store Events| H

    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 I fill:#9C27B0,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{ USER : "parent_user_id"
    USER ||--o{ DEVICE : owns
    USER ||--o{ GEOFENCE : owns
    DEVICE ||--o{ LOCATION : tracks
    DEVICE ||--o| MQTT_CREDENTIALS : has
    DEVICE ||--o{ MQTT_ACL_RULES : has
    DEVICE ||--o{ GEOFENCE : monitors
    GEOFENCE ||--o{ GEOFENCE_EVENT : triggers
    GEOFENCE ||--o{ GEOFENCE_STATUS : tracks
    DEVICE ||--o{ GEOFENCE_EVENT : generates
    DEVICE ||--o{ GEOFENCE_STATUS : has

    USER {
        string id PK
        string username UK
        string email
        string passwordHash
        string role
        string parent_user_id FK
        datetime createdAt
        datetime lastLoginAt
    }

    DEVICE {
        string id PK
        string name
        string color
        string ownerId FK
        boolean isActive
        string description
        datetime createdAt
    }

    MQTT_CREDENTIALS {
        int id PK
        string device_id FK_UK
        string mqtt_username UK
        string mqtt_password_hash
        boolean enabled
        datetime created_at
        datetime updated_at
    }

    MQTT_ACL_RULES {
        int id PK
        string device_id FK
        string topic_pattern
        string permission
        datetime created_at
    }

    MQTT_SYNC_STATUS {
        int id PK
        int pending_changes
        datetime last_sync_at
        string last_sync_status
        datetime created_at
        datetime updated_at
    }

    GEOFENCE {
        string id PK
        string name
        string description
        string shape_type
        float center_latitude
        float center_longitude
        int radius_meters
        string owner_id FK
        string device_id FK
        boolean is_active
        string color
        datetime created_at
        datetime updated_at
    }

    GEOFENCE_EVENT {
        int id PK
        string geofence_id FK
        string device_id FK
        int location_id
        string event_type
        float latitude
        float longitude
        float distance_from_center
        int notification_sent
        string notification_error
        datetime timestamp
        datetime created_at
    }

    GEOFENCE_STATUS {
        int id PK
        string device_id FK
        string geofence_id FK
        boolean is_inside
        datetime last_enter_time
        datetime last_exit_time
        datetime last_checked_at
        datetime created_at
        datetime updated_at
    }

    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
  • 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/
│   │   ├── admin/
│   │   │   ├── emails/
│   │   │   │   ├── preview/         # Email Template Preview
│   │   │   │   └── send-test/       # Test Email versenden
│   │   │   └── settings/
│   │   │       └── smtp/            # SMTP Einstellungen + Test
│   │   ├── auth/
│   │   │   ├── [...nextauth]/       # NextAuth API
│   │   │   ├── forgot-password/     # Passwort vergessen
│   │   │   ├── register/            # User Registrierung
│   │   │   └── reset-password/      # Passwort zurücksetzen
│   │   ├── devices/
│   │   │   ├── [id]/                # Device CRUD (einzelnes Device)
│   │   │   ├── public/              # Öffentliche Device-Liste
│   │   │   └── route.ts             # Device Liste + Create
│   │   ├── export/
│   │   │   └── csv/                 # CSV Export von Locations
│   │   ├── geofences/
│   │   │   ├── [id]/                # Geofence CRUD (einzelner Geofence)
│   │   │   ├── events/              # Geofence Events abfragen
│   │   │   └── route.ts             # Geofence Liste + Create
│   │   ├── locations/
│   │   │   ├── cleanup/             # Alte Locations löschen
│   │   │   ├── ingest/              # Location Import (MQTT)
│   │   │   ├── optimize/            # DB Optimization (VACUUM)
│   │   │   ├── stats/               # DB Statistiken
│   │   │   ├── test/                # Test-Location hinzufügen
│   │   │   └── route.ts             # Location Query API
│   │   ├── mqtt/
│   │   │   ├── acl/
│   │   │   │   ├── [id]/            # ACL Rule CRUD
│   │   │   │   └── route.ts         # ACL Rules Liste + Create
│   │   │   ├── credentials/
│   │   │   │   ├── [device_id]/     # MQTT Credentials CRUD
│   │   │   │   └── route.ts         # Credentials Liste + Create
│   │   │   ├── send-credentials/    # Credentials per Email versenden
│   │   │   └── sync/                # Mosquitto Config Sync
│   │   ├── system/
│   │   │   └── status/              # System Status (Uptime, Memory)
│   │   └── users/
│   │       ├── [id]/                # User CRUD (einzelner User)
│   │       └── route.ts             # User Liste + Create
│   ├── admin/
│   │   ├── devices/                 # Device Management
│   │   ├── emails/                  # Email Template Management
│   │   ├── geofences/
│   │   │   ├── events/              # Geofence Event History
│   │   │   └── page.tsx             # Geofence Management
│   │   ├── mqtt/                    # MQTT Provisioning
│   │   ├── settings/                # SMTP Settings (Super Admin)
│   │   ├── setup/                   # Setup Guide
│   │   ├── users/                   # User Management
│   │   ├── layout.tsx               # Admin Layout + Navigation
│   │   └── page.tsx                 # Dashboard
│   ├── export/                      # CSV Export Seite
│   ├── forgot-password/             # Passwort vergessen Seite
│   ├── login/                       # Login-Seite
│   ├── map/                         # Karten-Seite (alternative Route)
│   ├── register/                    # Registrierungs-Seite
│   ├── reset-password/              # Passwort zurücksetzen Seite
│   ├── unauthorized/                # Unauthorized Access Seite
│   ├── page.tsx                     # Öffentliche Karte (Home)
│   └── layout.tsx                   # Root Layout
├── components/
│   ├── demo/
│   │   └── DemoMap.tsx              # Demo Map Component
│   ├── map/
│   │   ├── GeofenceLayer.tsx        # Geofence Visualization Layer
│   │   └── MapView.tsx              # Leaflet Map Component
│   └── AuthProvider.tsx             # Auth Context Provider
├── lib/
│   ├── auth.ts                      # NextAuth Config
│   ├── crypto-utils.ts              # Encryption/Decryption Utils
│   ├── db.ts                        # SQLite Database Layer (User/Devices)
│   ├── demo-data.ts                 # Demo/Test Data Generator
│   ├── devices.ts                   # Device Helper Functions
│   ├── email-renderer.ts            # React Email Renderer
│   ├── email-service.ts             # Email Service (Nodemailer)
│   ├── geofence-db.ts               # Geofence Database Layer
│   ├── geofence-engine.ts           # Geofence Detection Engine
│   ├── geofence-notifications.ts    # Geofence Notification Handler
│   ├── geo-utils.ts                 # Geolocation Utils (Haversine)
│   ├── mosquitto-sync.ts            # Mosquitto Config File Sync
│   ├── mqtt-db.ts                   # MQTT Database Layer
│   ├── mqtt-subscriber.ts           # MQTT Subscriber Service
│   ├── password-reset-db.ts         # Password Reset Token DB
│   ├── settings-db.ts               # Settings Database Layer
│   ├── startup.ts                   # Application Startup Logic
│   ├── types.ts                     # TypeScript Type Definitions
│   └── types/
│       └── smtp.ts                  # SMTP Type Definitions
├── scripts/
│   ├── add-test-location.js         # Test-Location hinzufügen
│   ├── cleanup-old-locations.js     # Alte Locations löschen
│   ├── init-database.js             # Database.sqlite Setup
│   ├── init-geofence-db.js          # Geofence Tables Setup
│   ├── init-locations-db.js         # Locations.sqlite Setup
│   ├── remove-duplicates.js         # Duplikate bereinigen
│   ├── reset-admin.js               # Admin User Reset
│   ├── show-schema.js               # DB Schema anzeigen
│   ├── sync-mqtt-config.ts          # MQTT Config manuell syncen
│   ├── test-geofence.js             # Geofence Detektion testen
│   ├── test-geofence-email.js       # Geofence Email testen
│   ├── test-geofence-notification.js # Geofence Notification testen
│   ├── test-mqtt-geofence.js        # MQTT Geofence Integration testen
│   ├── test-smtp.js                 # SMTP Connection testen
│   └── test-time-filter.js          # Zeitfilter debuggen
├── data/
│   ├── database.sqlite              # User + Devices + MQTT + Geofences
│   └── locations.sqlite             # Location Tracking
├── emails/                          # React Email Templates
│   └── geofence-alert.tsx           # Geofence Alert Email Template
├── mosquitto/
│   ├── config/                      # Mosquitto Config Files
│   │   ├── mosquitto.conf           # Broker Config
│   │   ├── password.txt             # Password File (bcrypt hashes)
│   │   └── acl.txt                  # ACL Rules File
│   └── logs/                        # Mosquitto Log Files
├── public/                          # Static Assets
├── types/
│   └── (deprecated - moved to lib/types.ts)
├── middleware.ts                    # Route Protection Middleware
├── instrumentation.ts               # Next.js Instrumentation Hook
├── docker-compose.yml               # Docker Compose Setup
├── Dockerfile                       # App Docker Image
└── package.json                     # Dependencies + Scripts

📝 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:init:geofence  # Nur Geofence-Tabellen
npm run db:cleanup       # Cleanup 7 Tage
npm run db:cleanup:7d    # Cleanup 7 Tage
npm run db:cleanup:30d   # Cleanup 30 Tage

# Testing
npm run test:location         # Test-Location hinzufügen
npm run test:geofence         # Geofence Detektion testen
npm run test:geofence:email   # Geofence Email testen
npm run test:geofence:mqtt    # MQTT Geofence testen

# Documentation
npm run docs:check       # Dokumentation validieren
npm run docs:sync        # Dokumentation synchronisieren

# Email Development
npm run email:dev        # Email Template Dev Server

# Linting
npm run lint             # ESLint ausführen

📚 Automatische Dokumentations-Synchronisation

Diese App verwendet automatische Checks, um sicherzustellen, dass die README-Dokumentation immer aktuell bleibt.

🔍 Wie es funktioniert

Pre-Commit Hook (automatisch):

  • Läuft automatisch vor jedem git commit
  • Prüft ob Projektstruktur, API-Routes, oder NPM Scripts aktualisiert werden müssen
  • Warnt dich, wenn Änderungen erkannt werden

Manuell Dokumentation prüfen:

npm run docs:check

Dokumentation automatisch aktualisieren:

npm run docs:sync

⚙️ Workflow-Beispiel

# 1. Du änderst Code oder fügst API-Routes hinzu
git add .
git commit -m "Add new feature"

# 2. Pre-commit Hook warnt (falls README veraltet ist)
⚠️  Documentation may be outdated:
   - New API Route: /api/foo/bar
   - New npm script: test:foo

# 3. Du hast 3 Optionen:

# Option A: Automatisch synchronisieren
npm run docs:sync
git add README.md
git commit -m "Update documentation"

# Option B: Manuell README bearbeiten
# ... edit README.md ...
git add README.md
git commit -m "Update documentation"

# Option C: Hook überspringen (nicht empfohlen)
git commit --no-verify

🎯 Was wird geprüft?

  • NPM Scripts - Alle package.json Scripts dokumentiert?
  • API Routes - Alle app/api/* Routen erwähnt?
  • App Struktur - Alle app/* Verzeichnisse in Projektstruktur?
  • Components - Alle Komponenten dokumentiert?
  • Scripts - Alle scripts/*.js Dateien erwähnt?

💡 Tipps

  • Der Hook ist nicht blockierend - du kannst mit --no-verify überspringen
  • docs:sync aktualisiert nur NPM Scripts (weitere Bereiche können später hinzugefügt werden)
  • Warnungen sind manchmal "false positives" - das ist okay, besser zu vorsichtig!

📄 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 1.6 MiB
Languages
TypeScript 87.1%
JavaScript 12%
Shell 0.6%
Dockerfile 0.2%