CRITICAL FIX: The OwnTracks app publishes to owntracks/<username>/<device_id>, not owntracks/owntrack/<device_id>. This was causing data delivery failures and privacy violations. Changes: - Fix ACL topic pattern: owntracks/<username>/# (was: owntracks/owntrack/<device_id>) - Backend now uses MQTT_ADMIN_USERNAME for global subscription - Update UI forms and placeholders with correct pattern - Update email template with correct topic format - Enable Mosquitto ACL file for user isolation - Add migration script for existing ACL rules - Update documentation (README, GEMINI.md) Privacy & Security: - Each user isolated at MQTT broker level via ACL - Backend subscribes with admin credentials to owntracks/+/+ - Web UI filters data by parent_user_id for additional security - GDPR compliant multi-layer defense in depth Files changed: - lib/mqtt-db.ts - Updated createDefaultRule() to use username - app/api/mqtt/credentials/route.ts - Pass username to ACL creation - app/admin/mqtt/page.tsx - UI forms and state management - emails/mqtt-credentials.tsx - Email template topic pattern - lib/mqtt-subscriber.ts - Use admin credentials from env - mosquitto/config/mosquitto.conf - Enable ACL enforcement - README.md, GEMINI.md - Documentation updates - scripts/fix-acl-topic-patterns.js - Migration script - MQTT_TOPIC_FIX.md - Detailed implementation guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
910 lines
24 KiB
Markdown
910 lines
24 KiB
Markdown
# 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](#-features)
|
||
- [Tech Stack](#-tech-stack)
|
||
- [Installation](#-installation)
|
||
- [Datenbank-Setup](#-datenbank-setup)
|
||
- [Verwendung](#-verwendung)
|
||
- [Architektur](#-architektur)
|
||
- [API-Endpunkte](#-api-endpunkte)
|
||
- [Device Management](#-device-management)
|
||
- [Wartung](#-wartung)
|
||
- [Deployment](#-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
|
||
|
||
1. **Repository klonen**
|
||
```bash
|
||
git clone https://github.com/yourusername/location-tracker-app.git
|
||
cd location-tracker-app
|
||
```
|
||
|
||
2. **Dependencies installieren**
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
3. **Datenbank initialisieren**
|
||
```bash
|
||
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)
|
||
|
||
4. **Development Server starten**
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
5. **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:**
|
||
```bash
|
||
npm run db:init
|
||
```
|
||
|
||
**Nur database.sqlite (User/Devices):**
|
||
```bash
|
||
npm run db:init:app
|
||
```
|
||
|
||
**Nur locations.sqlite (Tracking):**
|
||
```bash
|
||
npm run db:init:locations
|
||
```
|
||
|
||
### Datenbank zurücksetzen
|
||
|
||
**Admin-User neu anlegen:**
|
||
```bash
|
||
node scripts/reset-admin.js
|
||
```
|
||
|
||
**Alte Locations löschen:**
|
||
```bash
|
||
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):**
|
||
```bash
|
||
node scripts/remove-duplicates.js
|
||
```
|
||
|
||
### Schema
|
||
|
||
#### **database.sqlite** (User & Devices)
|
||
|
||
**User Tabelle:**
|
||
```sql
|
||
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:**
|
||
```sql
|
||
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:**
|
||
```sql
|
||
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:**
|
||
```sql
|
||
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:**
|
||
```sql
|
||
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_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)
|
||
|
||
---
|
||
|
||
#### **locations.sqlite** (Tracking Data)
|
||
|
||
**Location Tabelle:**
|
||
```sql
|
||
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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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**:
|
||
|
||
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:**
|
||
```bash
|
||
# 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:**
|
||
```bash
|
||
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:**
|
||
```bash
|
||
# Manuell
|
||
node scripts/optimize-db.js
|
||
```
|
||
|
||
**Was macht Optimize:**
|
||
- `VACUUM` - Speicherplatz freigeben
|
||
- `ANALYZE` - Query-Statistiken aktualisieren
|
||
|
||
### Logs prüfen
|
||
|
||
```bash
|
||
# 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):
|
||
|
||
```bash
|
||
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`:
|
||
|
||
```env
|
||
# NextAuth
|
||
AUTH_SECRET=<generiere-mit-openssl-rand-base64-32>
|
||
NEXTAUTH_URL=https://your-domain.com
|
||
```
|
||
|
||
**Secret generieren:**
|
||
```bash
|
||
openssl rand -base64 32
|
||
```
|
||
|
||
### Production Build
|
||
|
||
```bash
|
||
# Build
|
||
npm run build
|
||
|
||
# Start
|
||
npm run start
|
||
```
|
||
|
||
### Mit PM2 (empfohlen)
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```nginx
|
||
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:**
|
||
```bash
|
||
node scripts/reset-admin.js
|
||
```
|
||
|
||
### Datenbank-Dateien fehlen
|
||
|
||
**Lösung:**
|
||
```bash
|
||
npm run db:init
|
||
```
|
||
|
||
### Duplikate in locations.sqlite
|
||
|
||
**Lösung:**
|
||
```bash
|
||
# 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:**
|
||
```bash
|
||
mkdir -p data
|
||
npm run db:init
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 NPM Scripts
|
||
|
||
```bash
|
||
# 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](https://github.com/vercel/next.js) - Copyright (c) Vercel, Inc.
|
||
- [React](https://github.com/facebook/react) - Copyright (c) Meta Platforms, Inc.
|
||
- [React-DOM](https://github.com/facebook/react) - Copyright (c) Meta Platforms, Inc.
|
||
- [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss) - Copyright (c) Tailwind Labs, Inc.
|
||
- [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) - Copyright (c) Joshua Wise
|
||
- [bcryptjs](https://github.com/dcodeIO/bcrypt.js) - Copyright (c) Daniel Wirtz
|
||
|
||
### Apache License 2.0
|
||
- [TypeScript](https://github.com/microsoft/TypeScript) - Copyright (c) Microsoft Corporation
|
||
|
||
### ISC License
|
||
- [NextAuth.js](https://github.com/nextauthjs/next-auth) - Copyright (c) NextAuth.js Contributors
|
||
|
||
### BSD-2-Clause License
|
||
- [Leaflet](https://github.com/Leaflet/Leaflet) - Copyright (c) Vladimir Agafonkin
|
||
|
||
### Hippocratic License 2.1
|
||
- [React-Leaflet](https://github.com/PaulLeCam/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:
|
||
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
|