Files
Joachim Hummel bd6a7ab187 Add Geofence MVP feature implementation
Implemented complete MVP for geofencing functionality with database,
backend logic, MQTT integration, and API endpoints.

**Phase 1: Database & Core Logic**
- scripts/init-geofence-db.js: Database initialization for Geofence tables
- lib/types.ts: TypeScript types for Geofence, GeofenceEvent, GeofenceStatus
- lib/geofence-engine.ts: Core geofencing logic (Haversine distance, state tracking)
- lib/geofence-db.ts: Database layer with CRUD operations
- package.json: Added db:init:geofence script

**Phase 2: MQTT Integration & Email Notifications**
- emails/geofence-enter.tsx: React Email template for enter events
- emails/geofence-exit.tsx: React Email template for exit events
- lib/email-renderer.ts: Added geofence email rendering functions
- lib/geofence-notifications.ts: Notification service for geofence events
- lib/mqtt-subscriber.ts: Integrated automatic geofence checking on location updates

**Phase 3: Minimal API**
- app/api/geofences/route.ts: GET (list) and POST (create) endpoints
- app/api/geofences/[id]/route.ts: DELETE endpoint
- All endpoints with authentication and ownership checks

**MVP Simplifications:**
- No zone limit enforcement (unlimited for all users)
- No notification flags (always send Enter + Exit emails)
- Device assignment required (no NULL device logic)
- Circular geofences only

**Features:**
 Automatic geofence detection on MQTT location updates
 Email notifications for enter/exit events
 State tracking to prevent duplicate events
 REST API for geofence management
 Non-blocking async processing

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 18:14:24 +00:00

117 lines
2.4 KiB
TypeScript

export interface Location {
id?: number;
latitude: number | string; // NocoDB returns string
longitude: number | string; // NocoDB returns string
timestamp: string;
user_id: number | string; // NocoDB returns string "0" for MQTT devices
first_name: string | null;
last_name: string | null;
username: string | null;
marker_label: string | null;
display_time: string | null;
chat_id: number | string; // Also string in API response
battery?: number | null;
speed?: number | null;
created_at?: string;
}
export interface LocationResponse {
success: boolean;
current: Location | null;
history: Location[];
total_points: number;
last_updated: string;
}
export interface Device {
id: string;
name: string;
color: string;
}
// Geofence types
export interface Geofence {
id: string;
name: string;
description: string | null;
// Geometry
shape_type: 'circle';
center_latitude: number;
center_longitude: number;
radius_meters: number;
// Assignment
owner_id: string;
device_id: string;
// Status & Metadata
is_active: number; // 0 or 1
color: string;
// Timestamps
created_at: string;
updated_at: string;
}
export interface GeofenceEvent {
id?: number;
geofence_id: string;
device_id: string;
location_id: number;
// Event details
event_type: 'enter' | 'exit';
latitude: number | string;
longitude: number | string;
// Metadata
distance_from_center: number | null;
notification_sent: number; // 0 = pending, 1 = sent, 2 = failed
notification_error: string | null;
// Timestamps
timestamp: string;
created_at?: string;
}
export interface GeofenceStatus {
id?: number;
device_id: string;
geofence_id: string;
// Current status
is_inside: number; // 0 or 1
last_enter_time: string | null;
last_exit_time: string | null;
last_checked_at: string | null;
// Timestamps
created_at?: string;
updated_at?: string;
}
// Input types for creating/updating geofences
export interface CreateGeofenceInput {
id: string;
name: string;
description?: string;
center_latitude: number;
center_longitude: number;
radius_meters: number;
owner_id: string;
device_id: string;
color?: string;
}
export interface CreateGeofenceEventInput {
geofence_id: string;
device_id: string;
location_id: number;
event_type: 'enter' | 'exit';
latitude: number | string;
longitude: number | string;
distance_from_center: number;
timestamp: string;
}