Changed hardcoded MQTT_BROKER_URL to read from .env file with fallback to mqtt://mosquitto:1883. This allows using external MQTT brokers like mqtt://tracking.unixweb.de:1883 by simply updating the .env file. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
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:
- 🧹 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
- 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
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'))
Indexes:
idx_user_usernameON User(username)idx_user_parentON User(parent_user_id)idx_device_ownerON Device(ownerId)idx_device_activeON Device(isActive)idx_mqtt_credentials_deviceON mqtt_credentials(device_id)idx_mqtt_credentials_usernameON mqtt_credentials(mqtt_username)idx_mqtt_acl_deviceON mqtt_acl_rules(device_id)
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 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:
- 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[📡 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{ USER : "parent_user_id"
USER ||--o{ DEVICE : owns
DEVICE ||--o{ LOCATION : tracks
DEVICE ||--o| MQTT_CREDENTIALS : has
DEVICE ||--o{ MQTT_ACL_RULES : 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
}
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:
- MQTT Subscriber empfängt OwnTracks Messages direkt vom Broker
- Locations werden sofort in SQLite gespeichert
- Frontend polling (alle 5 Sek.) →
/api/locations - API liest gefilterte Daten aus lokalem SQLite Cache
- 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 als0gespeichert - Wird NICHT zu
nullkonvertiert (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 freigebenANALYZE- 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:
- 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
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
- MQTT Broker erreichbar?
mosquitto_sub -h localhost -p 1883 -t '#' - Locations in Datenbank?
/admin→ Database Statistics prüfen - 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
- 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
- MQTT.js - MQTT Client für Node.js
- 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