Update README: Add geofencing architecture and update tech stack
Documentation updates: - Add geofencing feature description with capabilities - Update database architecture ER diagram with Geofence, GeofenceEvent, and GeofenceStatus tables - Add complete schema documentation for all geofence tables and indexes - Update data flow diagram to show geofence monitoring pipeline - Update component overview with Geofence Monitor - Update Tech Stack versions: Next.js 16.0.7, React 19.2.1 - Add MQTT.js, Eclipse Mosquitto, Nodemailer, React Email to tech stack - Update dual-database architecture description - Add npm run db:init:geofence command documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
143
README.md
143
README.md
@@ -40,6 +40,12 @@ Eine moderne Location-Tracking Anwendung basierend auf Next.js 14 mit MQTT/OwnTr
|
|||||||
- 📊 **Dashboard** - Übersicht über Geräte, Statistiken und Datenbankstatus
|
- 📊 **Dashboard** - Übersicht über Geräte, Statistiken und Datenbankstatus
|
||||||
- ⏱️ **System Status** - Live-Uptime, Memory Usage, Runtime Info
|
- ⏱️ **System Status** - Live-Uptime, Memory Usage, Runtime Info
|
||||||
- 📱 **Device Management** - Geräte hinzufügen, bearbeiten, löschen
|
- 📱 **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**:
|
- 💾 **Datenbank-Wartung**:
|
||||||
- 🧹 Cleanup alter Daten (7, 15, 30, 90 Tage)
|
- 🧹 Cleanup alter Daten (7, 15, 30, 90 Tage)
|
||||||
- ⚡ Datenbank-Optimierung (VACUUM)
|
- ⚡ Datenbank-Optimierung (VACUUM)
|
||||||
@@ -51,17 +57,20 @@ Eine moderne Location-Tracking Anwendung basierend auf Next.js 14 mit MQTT/OwnTr
|
|||||||
|
|
||||||
## 🛠 Tech Stack
|
## 🛠 Tech Stack
|
||||||
|
|
||||||
- **Framework:** Next.js 14 (App Router)
|
- **Framework:** Next.js 16.0.7 (App Router)
|
||||||
|
- **Runtime:** React 19.2.1
|
||||||
- **Sprache:** TypeScript 5.9
|
- **Sprache:** TypeScript 5.9
|
||||||
- **Styling:** Tailwind CSS v4
|
- **Styling:** Tailwind CSS v4
|
||||||
- **Karten:** Leaflet 1.9.4 + React-Leaflet 5.0
|
- **Karten:** Leaflet 1.9.4 + React-Leaflet 5.0
|
||||||
- **Authentifizierung:** NextAuth.js v5 (beta)
|
- **Authentifizierung:** NextAuth.js v5 (beta)
|
||||||
- **Datenbank:** SQLite (better-sqlite3)
|
- **Datenbank:** SQLite (better-sqlite3)
|
||||||
|
- **MQTT:** MQTT.js v5 + Eclipse Mosquitto
|
||||||
|
- **E-Mail:** Nodemailer + React Email
|
||||||
- **Passwort-Hashing:** bcryptjs
|
- **Passwort-Hashing:** bcryptjs
|
||||||
- **Datenquelle:** MQTT Broker + lokale SQLite-Cache
|
- **Datenquelle:** MQTT Broker + lokale SQLite-Cache
|
||||||
|
|
||||||
### Dual-Database Architektur
|
### Dual-Database Architektur
|
||||||
- **database.sqlite** - User, Geräte (kritische Daten)
|
- **database.sqlite** - User, Geräte, MQTT-Credentials, Geofences (kritische Daten)
|
||||||
- **locations.sqlite** - Location-Tracking (hohe Schreibrate, isoliert)
|
- **locations.sqlite** - Location-Tracking (hohe Schreibrate, isoliert)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -91,7 +100,7 @@ npm run db:init
|
|||||||
```
|
```
|
||||||
|
|
||||||
Dies erstellt:
|
Dies erstellt:
|
||||||
- `data/database.sqlite` (User + Devices)
|
- `data/database.sqlite` (User + Devices + MQTT + Geofences)
|
||||||
- `data/locations.sqlite` (Location-Tracking)
|
- `data/locations.sqlite` (Location-Tracking)
|
||||||
- Standard Admin-User: `admin` / `admin123`
|
- Standard Admin-User: `admin` / `admin123`
|
||||||
- Standard Devices (ID 10, 11)
|
- Standard Devices (ID 10, 11)
|
||||||
@@ -128,6 +137,11 @@ npm run db:init:app
|
|||||||
npm run db:init:locations
|
npm run db:init:locations
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Nur Geofence-Tabellen:**
|
||||||
|
```bash
|
||||||
|
npm run db:init:geofence
|
||||||
|
```
|
||||||
|
|
||||||
### Datenbank zurücksetzen
|
### Datenbank zurücksetzen
|
||||||
|
|
||||||
**Admin-User neu anlegen:**
|
**Admin-User neu anlegen:**
|
||||||
@@ -207,6 +221,52 @@ created_at TEXT DEFAULT (datetime('now'))
|
|||||||
updated_at TEXT DEFAULT (datetime('now'))
|
updated_at TEXT DEFAULT (datetime('now'))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Geofence Tabelle:**
|
||||||
|
```sql
|
||||||
|
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:**
|
||||||
|
```sql
|
||||||
|
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:**
|
||||||
|
```sql
|
||||||
|
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:**
|
**Indexes:**
|
||||||
- `idx_user_username` ON User(username)
|
- `idx_user_username` ON User(username)
|
||||||
- `idx_user_parent` ON User(parent_user_id)
|
- `idx_user_parent` ON User(parent_user_id)
|
||||||
@@ -215,6 +275,17 @@ updated_at TEXT DEFAULT (datetime('now'))
|
|||||||
- `idx_mqtt_credentials_device` ON mqtt_credentials(device_id)
|
- `idx_mqtt_credentials_device` ON mqtt_credentials(device_id)
|
||||||
- `idx_mqtt_credentials_username` ON mqtt_credentials(mqtt_username)
|
- `idx_mqtt_credentials_username` ON mqtt_credentials(mqtt_username)
|
||||||
- `idx_mqtt_acl_device` ON mqtt_acl_rules(device_id)
|
- `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)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -328,6 +399,8 @@ flowchart TD
|
|||||||
B -->|Subscribe| C[📡 Next.js MQTT Subscriber]
|
B -->|Subscribe| C[📡 Next.js MQTT Subscriber]
|
||||||
|
|
||||||
C -->|Store Locations| D[(🗄️ SQLite Cache<br/>locations.sqlite)]
|
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]
|
E[🖥️ Browser Client] -->|GET /api/locations<br/>alle 5 Sek| F[📡 Next.js API Route]
|
||||||
|
|
||||||
@@ -335,10 +408,13 @@ flowchart TD
|
|||||||
F -->|JSON Response| E
|
F -->|JSON Response| E
|
||||||
|
|
||||||
E -->|Render| G[🗺️ React Leaflet Map]
|
E -->|Render| G[🗺️ React Leaflet Map]
|
||||||
|
E -->|Render Geofences| M[🔵 Geofence Circles]
|
||||||
|
|
||||||
H[👤 Admin User] -->|Login| I[🔐 NextAuth.js]
|
H[👤 Admin User] -->|Login| I[🔐 NextAuth.js]
|
||||||
I -->|Authenticated| J[📊 Admin Panel]
|
I -->|Authenticated| J[📊 Admin Panel]
|
||||||
J -->|CRUD Operations| K[(💼 SQLite DB<br/>database.sqlite)]
|
J -->|CRUD Operations| K
|
||||||
|
J -->|Manage Geofences| K
|
||||||
|
K -->|Query Events| J
|
||||||
|
|
||||||
style A fill:#4CAF50
|
style A fill:#4CAF50
|
||||||
style B fill:#FF9800
|
style B fill:#FF9800
|
||||||
@@ -348,6 +424,8 @@ flowchart TD
|
|||||||
style G fill:#8BC34A
|
style G fill:#8BC34A
|
||||||
style I fill:#E91E63
|
style I fill:#E91E63
|
||||||
style K fill:#FFC107
|
style K fill:#FFC107
|
||||||
|
style L fill:#9C27B0
|
||||||
|
style M fill:#3F51B5
|
||||||
```
|
```
|
||||||
|
|
||||||
### Komponenten-Übersicht
|
### Komponenten-Übersicht
|
||||||
@@ -361,6 +439,7 @@ graph LR
|
|||||||
|
|
||||||
subgraph "Next.js Application"
|
subgraph "Next.js Application"
|
||||||
C[MQTT Subscriber]
|
C[MQTT Subscriber]
|
||||||
|
I[Geofence Monitor]
|
||||||
D[Frontend<br/>React/Leaflet]
|
D[Frontend<br/>React/Leaflet]
|
||||||
E[API Routes]
|
E[API Routes]
|
||||||
F[Auth Layer<br/>NextAuth.js]
|
F[Auth Layer<br/>NextAuth.js]
|
||||||
@@ -368,12 +447,14 @@ graph LR
|
|||||||
|
|
||||||
subgraph "Data Layer"
|
subgraph "Data Layer"
|
||||||
G[locations.sqlite<br/>Tracking Data]
|
G[locations.sqlite<br/>Tracking Data]
|
||||||
H[database.sqlite<br/>Users & Devices]
|
H[database.sqlite<br/>Users, Devices<br/>MQTT, Geofences]
|
||||||
end
|
end
|
||||||
|
|
||||||
A -->|MQTT| B
|
A -->|MQTT| B
|
||||||
B -->|Subscribe| C
|
B -->|Subscribe| C
|
||||||
C -->|Write| G
|
C -->|Write Locations| G
|
||||||
|
C -->|Trigger| I
|
||||||
|
I -->|Check & Store Events| H
|
||||||
|
|
||||||
D -->|HTTP| E
|
D -->|HTTP| E
|
||||||
E -->|Read/Write| G
|
E -->|Read/Write| G
|
||||||
@@ -385,6 +466,7 @@ graph LR
|
|||||||
style A fill:#4CAF50,color:#fff
|
style A fill:#4CAF50,color:#fff
|
||||||
style B fill:#FF9800,color:#fff
|
style B fill:#FF9800,color:#fff
|
||||||
style C fill:#2196F3,color:#fff
|
style C fill:#2196F3,color:#fff
|
||||||
|
style I fill:#9C27B0,color:#fff
|
||||||
style D fill:#00BCD4,color:#000
|
style D fill:#00BCD4,color:#000
|
||||||
style E fill:#00BCD4,color:#000
|
style E fill:#00BCD4,color:#000
|
||||||
style F fill:#E91E63,color:#fff
|
style F fill:#E91E63,color:#fff
|
||||||
@@ -398,9 +480,15 @@ graph LR
|
|||||||
erDiagram
|
erDiagram
|
||||||
USER ||--o{ USER : "parent_user_id"
|
USER ||--o{ USER : "parent_user_id"
|
||||||
USER ||--o{ DEVICE : owns
|
USER ||--o{ DEVICE : owns
|
||||||
|
USER ||--o{ GEOFENCE : owns
|
||||||
DEVICE ||--o{ LOCATION : tracks
|
DEVICE ||--o{ LOCATION : tracks
|
||||||
DEVICE ||--o| MQTT_CREDENTIALS : has
|
DEVICE ||--o| MQTT_CREDENTIALS : has
|
||||||
DEVICE ||--o{ MQTT_ACL_RULES : 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 {
|
USER {
|
||||||
string id PK
|
string id PK
|
||||||
@@ -450,6 +538,49 @@ erDiagram
|
|||||||
datetime updated_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 {
|
LOCATION {
|
||||||
int id PK
|
int id PK
|
||||||
float latitude
|
float latitude
|
||||||
|
|||||||
Reference in New Issue
Block a user