From e096ba26006fe007619817253fe2fbfaf43824e4 Mon Sep 17 00:00:00 2001 From: Joachim Hummel Date: Thu, 4 Dec 2025 10:52:23 +0000 Subject: [PATCH] Update README: Add geofencing architecture and update tech stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- README.md | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 137 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0a7856b..b74368f 100644 --- a/README.md +++ b/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 - ⏱️ **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) @@ -51,17 +57,20 @@ Eine moderne Location-Tracking Anwendung basierend auf Next.js 14 mit MQTT/OwnTr ## 🛠 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 - **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 (kritische Daten) +- **database.sqlite** - User, Geräte, MQTT-Credentials, Geofences (kritische Daten) - **locations.sqlite** - Location-Tracking (hohe Schreibrate, isoliert) --- @@ -91,7 +100,7 @@ npm run db:init ``` Dies erstellt: -- `data/database.sqlite` (User + Devices) +- `data/database.sqlite` (User + Devices + MQTT + Geofences) - `data/locations.sqlite` (Location-Tracking) - Standard Admin-User: `admin` / `admin123` - Standard Devices (ID 10, 11) @@ -128,6 +137,11 @@ npm run db:init:app npm run db:init:locations ``` +**Nur Geofence-Tabellen:** +```bash +npm run db:init:geofence +``` + ### Datenbank zurücksetzen **Admin-User neu anlegen:** @@ -207,6 +221,52 @@ created_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:** - `idx_user_username` ON User(username) - `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_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) --- @@ -328,6 +399,8 @@ flowchart TD B -->|Subscribe| C[📡 Next.js MQTT Subscriber] C -->|Store Locations| D[(🗄️ SQLite Cache
locations.sqlite)] + C -->|Check Geofences| L[🎯 Geofence Monitor] + L -->|Store Events| K[(💼 SQLite DB
database.sqlite)] E[🖥️ Browser Client] -->|GET /api/locations
alle 5 Sek| F[📡 Next.js API Route] @@ -335,10 +408,13 @@ flowchart TD 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[(💼 SQLite DB
database.sqlite)] + J -->|CRUD Operations| K + J -->|Manage Geofences| K + K -->|Query Events| J style A fill:#4CAF50 style B fill:#FF9800 @@ -348,6 +424,8 @@ flowchart TD style G fill:#8BC34A style I fill:#E91E63 style K fill:#FFC107 + style L fill:#9C27B0 + style M fill:#3F51B5 ``` ### Komponenten-Übersicht @@ -361,6 +439,7 @@ graph LR subgraph "Next.js Application" C[MQTT Subscriber] + I[Geofence Monitor] D[Frontend
React/Leaflet] E[API Routes] F[Auth Layer
NextAuth.js] @@ -368,12 +447,14 @@ graph LR subgraph "Data Layer" G[locations.sqlite
Tracking Data] - H[database.sqlite
Users & Devices] + H[database.sqlite
Users, Devices
MQTT, Geofences] end A -->|MQTT| B B -->|Subscribe| C - C -->|Write| G + C -->|Write Locations| G + C -->|Trigger| I + I -->|Check & Store Events| H D -->|HTTP| E E -->|Read/Write| G @@ -385,6 +466,7 @@ graph LR 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 @@ -398,9 +480,15 @@ graph LR 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 @@ -450,6 +538,49 @@ erDiagram 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