Smart Construction Site Safety & Environment Monitoring Platform
A production-grade full-stack IoT platform for real-time monitoring of construction sites — tracking air quality, noise, temperature, smoke, device health, incidents, and worker safety from a single dashboard.
- Direct ESP32 device connectivity via MQTT (HiveMQ Cloud over TLS)
- Auto-device registration and heartbeat monitoring
- Sub-5-second sensor data ingestion and dashboard updates via Socket.IO
- Manual control commands (sprinkler, buzzer) published back to devices
- Threshold-based auto-alert generation with severity levels (info/warning/critical)
- Predictive risk analysis using AI (Gemini 2.0 Flash, GPT-4o-mini, Groq Llama 3.3)
- Root cause analysis and safety optimization suggestions
- Incident lifecycle management (create, acknowledge, resolve, audit log)
- Google Places Autocomplete for site registration
- Interactive map with satellite/roadmap toggle and click-to-set location
- Real-time weather per site (OpenWeather + OpenUV API)
- Reverse geocoding for precise location capture
- Multi-format report generation (Daily Safety, Weekly Pollution, Monthly Executive, Incident Analysis)
- Export to PDF, CSV, Excel with real data aggregation
- Historical timeline with unified alert + action logs
- Custom date range filtering and category-based search
- Dual authentication: Google OAuth 2.0 + local email/password
- Role-based access control (superadmin, admin, manager, supervisor, viewer)
- httpOnly JWT cookies (not accessible via JavaScript)
- All API keys server-side only; never exposed to browser
- MQTT over TLS with certificate validation
- Rate limiting and Helmet security headers
- Real-time metric cards with live charts (line, bar, pie)
- Zone heatmap for sensor health visualization
- Dark/light theme toggle
- Mobile-responsive design with Tailwind CSS v4
- Notification bell with unread badge and type filters
- Microservices-ready with clear separation of concerns
- MongoDB Atlas for horizontal scaling
- Socket.IO for real-time multi-client updates
- Zustand for lightweight state management
- Vercel + Render deployment ready
- Dust & AQI trend forecasting
- Predictive risk alerts
- Executive summaries with actionable recommendations
- Automatic provider fallback (Gemini → OpenAI → Groq)
| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite + Tailwind CSS v4 + React Router v7 |
| State | Zustand + Axios |
| Charts | Recharts |
| Backend | Node.js + Express |
| Realtime | Socket.IO + MQTT (HiveMQ Cloud over TLS) |
| Database | MongoDB Atlas + Mongoose |
| Auth | Google OAuth 2.0 + Local (JWT + httpOnly cookies) |
| AI | Gemini 2.0 Flash / GPT-4o-mini / Groq Llama 3.3 |
| Maps | Google Maps (Places, Static Maps, Geocoding) |
| Weather | OpenWeather API + OpenUV API |
| Deploy | Vercel (frontend) + Render/Railway (backend) |
structivo/
├── client/ # React + Vite frontend
│ ├── src/
│ │ ├── api/ # Axios service layer
│ │ ├── components/ # Reusable UI components + layout
│ │ ├── hooks/ # Socket.IO, Places Autocomplete
│ │ ├── lib/ # Axios instance, Socket.IO client
│ │ ├── pages/ # All page components
│ │ └── store/ # Zustand stores
│ └── .env.example
│
├── server/ # Node.js + Express backend
│ ├── src/
│ │ ├── config/ # DB, Socket.IO, MQTT, Passport
│ │ ├── controllers/ # HTTP request handlers
│ │ ├── middleware/ # Auth, error, validation
│ │ ├── models/ # Mongoose schemas
│ │ ├── routes/ # Express routers
│ │ ├── services/ # Business logic, AI, incident engine
│ │ └── utils/ # Token, response helpers
│ └── .env.example
│
├── .gitignore
└── README.md
- Live metric cards — AQI, Noise, Smoke/CO, Temperature (OpenWeather), Humidity (OpenWeather), UV Index (OpenUV), Device count
- Real-time charts — AQI trend (line), Noise levels (bar), Hazard distribution (pie)
- Zone heatmap — sensor health grouped by site zone
- Live incident feed — active alerts with severity colours
- Manual controls — Start/Stop Sprinkler, Activate/Stop Buzzer (publishes to MQTT)
- Add sites with Google Places Autocomplete + interactive map
- Click map to set location (reverse geocoding)
- Satellite / roadmap toggle
- Live weather per site (OpenWeather + OpenUV)
- Static map thumbnail on each site card
- Auto-registration when ESP32 connects via MQTT
- Manual device registration
- Online/offline status with heartbeat watcher
- Rename, delete, filter by status
- Auto-created from MQTT payloads
- Historical readings with TTL (90 days auto-delete)
- Per-sensor charts
- Threshold-based auto-alerts from incident engine
- Severity levels: info / warning / critical
- Acknowledge & resolve workflow
- Real-time via Socket.IO
- Types: AQI High, Fire Detected, Excessive Noise, Device Offline, Power Outage
- Navbar bell with unread count badge
- Mark read / mark all read / delete
- Full notifications page with type filters
- Types: Daily Safety, Weekly Pollution, Monthly Executive, Incident Analysis
- Export to PDF, CSV, Excel (server-generated)
- Aggregates real data: alerts, readings, workers, sensors
- Predictive Risk Alerts
- Dust & AQI Trend Forecast
- Root Cause Analysis
- Safety Optimisation Suggestions
- Executive Summary
- Provider fallback chain: Gemini 2.0 Flash → GPT-4o-mini → Groq Llama 3.3
- Unified timeline: alerts + control actions
- Filters: Today / Week / Month / Custom range
- Category filters: AQI, Smoke, Device Offline, Sprinkler, Buzzer, Automation
- Searchable, paginated table
- Activate/Stop Emergency Buzzer
- Start/Stop Sprinkler
- Override Automation
- Confirmation modals with reason input
- Action log persisted to MongoDB
- Commands published to device via MQTT
- Threshold overrides (override ESP32 hardware defaults)
- Notification preferences (in-app, email, quiet hours)
- Theme toggle (light / dark)
- Account security (change password for local auth)
- Google OAuth 2.0 (httpOnly JWT cookie)
- Local email + password
- Role-based access: superadmin / admin / manager / supervisor / viewer
- Auto session refresh on window focus
- Force logout on token expiry
ESP32 → HiveMQ Cloud (TLS) → Node.js Backend → MongoDB + Socket.IO → Frontend
Topic conventions:
structivo/<siteId>/<deviceId>/readings ← sensor data from ESP32
structivo/<siteId>/<deviceId>/status ← device heartbeat
structivo/<siteId>/<deviceId>/commands ← control commands to device
ESP32 payload format:
{
"temperature": 32.5,
"humidity": 65,
"aqi": 120,
"noise": 78,
"smoke": 0.3,
"co": 12,
"uv": 4.2
}- Node.js v20+
- MongoDB Atlas account
- HiveMQ Cloud account (free tier)
cd server
cp .env.example .env
# Fill in all environment variables (see table below)
npm install
npm run devcd client
cp .env.example .env.local
npm install
npm run devFrontend runs at http://localhost:5173
Backend runs at http://localhost:5000
| Variable | Required | Description |
|---|---|---|
PORT |
No | Server port (default: 5000) |
NODE_ENV |
No | development or production |
CLIENT_URL |
Yes | Frontend URL for CORS |
MONGODB_URI |
Yes | MongoDB Atlas connection string |
JWT_SECRET |
Yes | Secret for signing JWTs (min 32 chars) |
JWT_EXPIRES_IN |
No | Token expiry (default: 7d) |
COOKIE_SECRET |
Yes | Secret for signed cookies |
GOOGLE_CLIENT_ID |
Yes | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Yes | Google OAuth client secret |
GOOGLE_CALLBACK_URL |
Yes | OAuth callback (e.g. http://localhost:5000/api/auth/google/callback) |
MQTT_URL |
Yes | HiveMQ broker URL (e.g. mqtts://xxx.hivemq.cloud:8883) |
MQTT_USERNAME |
Yes | HiveMQ username |
MQTT_PASSWORD |
Yes | HiveMQ password |
DEVICE_OFFLINE_MINUTES |
No | Minutes before device marked offline (default: 5) |
OPENWEATHER_API_KEY |
Yes | openweathermap.org |
OPENUV_API_KEY |
Yes | openuv.io |
GOOGLE_MAPS_API_KEY |
Yes | Google Maps (Places, Static Maps, Geocoding APIs enabled) |
GEMINI_API_KEY |
Optional | aistudio.google.com — Gemini 2.0 Flash |
OPENAI_API_KEY |
Optional | platform.openai.com — GPT-4o-mini |
GROK_API_KEY |
Optional | console.groq.com — Llama 3.3 70B (free tier) |
At least one AI key is needed for AI Insights. Priority: Gemini → OpenAI → Groq.
| Variable | Description |
|---|---|
VITE_API_URL |
Backend API URL (e.g. http://localhost:5000/api) |
VITE_SOCKET_URL |
Socket.IO server URL (e.g. http://localhost:5000) |
VITE_GOOGLE_MAPS_API_KEY |
Google Maps key (for Places Autocomplete in browser) |
vercel --cwd clientSet environment variables in Vercel dashboard. Add vercel.json is already included.
render.yaml is included. Set all environment variables in Render dashboard.
Never commit
.envfiles. All.envfiles are in.gitignore.
- JWT stored in
httpOnlycookies — not accessible via JavaScript - All API keys server-side only — never exposed to browser
- Google Maps Static Maps proxied through backend — key never in HTML
- Weather/UV APIs proxied through backend
- Rate limiting on all API routes (stricter on
/api/auth) - Helmet security headers
- CORS restricted to
CLIENT_URL - MQTT over TLS (
mqtts://) with certificate validation
| Model | Purpose |
|---|---|
User |
Auth, roles, preferences |
UserSettings |
Per-user threshold overrides, notification prefs |
Site |
Construction site with coordinates |
Device |
IoT hardware (ESP32 etc.), auto-registered from MQTT |
Sensor |
Measurement channel on a device |
SensorReading |
Time-series data, TTL 90 days |
Threshold |
Reusable alert thresholds |
Alert |
Real-time triggered events |
Incident |
Safety events with full lifecycle |
Action |
Corrective tasks assigned to users |
ActionLog |
Immutable audit log of control commands |
Notification |
In-app notifications, TTL 30 days |
Report |
Generated safety reports |
AiInsight |
AI-generated analysis, TTL 180 days |
Private — Structivo © 2026. All rights reserved.