{ "_meta": { "name": "Database Schema", }, "elements": [ { "id": "header", "type": "card", "title": "WellNuo Database Schema", "borderColor": "purple", "tags": ["overview"], "description": "**Supabase PostgreSQL**\n\nURL: bfzizknbxbsfrffqityf.supabase.co\n\n**Existing Tables (migrated):**\n- person_details (8 rows)\n- deployments (45 rows)\n- devices (455 rows)\n- deployment_details (23 rows)\n\n**New Tables (to create):**\n- push_tokens\n- notification_settings\n- password_resets\n- chat_history\n\n**Updated:** Dec 18, 2025", "x": 140, "y": 80 }, { "id": "table-person-details", "type": "card", "title": "person_details", "subtitle": "Users & Authentication", "borderColor": "blue", "tags": ["existing", "auth"], "description": "**Primary user table (auth)**\n\n```\nuser_id INTEGER PK (auto)\nuser_name TEXT (login)\nkey TEXT (password - PLAIN!)\nemail TEXT\nrole_ids TEXT (\"1,2\")\naccess_to_deployments TEXT (\"21,38,29\")\nfirst_name TEXT\nlast_name TEXT\naddress_street TEXT\naddress_city TEXT\naddress_zip TEXT\naddress_state TEXT\naddress_country TEXT\nphone_number TEXT\npicture TEXT (URL)\ntime_edit REAL\nuser_edit INTEGER\n```\n\n**Rows:** 8\n**Index:** email, user_name", "x": 520, "y": 80 }, { "id": "warning-passwords", "type": "card", "title": "⚠️ SECURITY WARNING", "borderColor": "red", "tags": ["critical"], "description": "**Passwords stored in PLAIN TEXT!**\n\nField `key` contains unencrypted passwords.\n\nExample: `key: \"anandk_8\"`\n\n**ACTION REQUIRED:**\n1. Add `password_hash` column\n2. Migrate existing passwords with bcrypt\n3. Remove `key` column\n4. Update auth.js to use bcrypt.compare()\n\n**Priority:** CRITICAL before production", "x": 520, "y": 340 }, { "id": "table-deployments", "type": "card", "title": "deployments", "subtitle": "Monitored Locations", "borderColor": "green", "tags": ["existing", "core"], "description": "**Deployment = monitored home/location**\n\n```\ndeployment_id INTEGER PK (auto)\ntime_edit REAL (unix timestamp)\nuser_edit INTEGER (who edited)\ntime_zone_s TEXT (\"America/Los_Angeles\")\npersons INTEGER (people count)\ngender INTEGER (0-2)\nrace INTEGER\nborn INTEGER (year)\npets INTEGER\ncontext TEXT (notes about person)\nalarm_details TEXT (JSON with alarm config)\n```\n\n**Rows:** 45\n**Note:** `alarm_details` contains complex JSON", "x": 900, "y": 80 }, { "id": "table-deployment-details", "type": "card", "title": "deployment_details", "subtitle": "Extended Deployment Info", "borderColor": "green", "tags": ["existing", "core"], "description": "**Additional deployment data**\n\n```\ndeployment_id INTEGER PK (FK)\nbeneficiary_id INTEGER (FK person)\ncaretaker_id INTEGER (FK person)\nowner_id INTEGER\ninstaller_id INTEGER\ndevices TEXT (JSON array of MACs)\naddress_street TEXT\naddress_city TEXT\naddress_zip TEXT\naddress_state TEXT\naddress_country TEXT\nwifis TEXT\nlat REAL\nlng REAL\ngps_age INTEGER\nnote TEXT\nfloor_plan TEXT (layout JSON)\noverlapps TEXT\n```\n\n**Rows:** 23", "x": 900, "y": 340 }, { "id": "table-devices", "type": "card", "title": "devices", "subtitle": "IoT Sensors", "borderColor": "teal", "tags": ["existing", "iot"], "description": "**Physical sensor devices**\n\n```\ndevice_id INTEGER PK\ndevice_mac TEXT NOT NULL\nwell_id INTEGER\ndescription TEXT\nlocation INTEGER (room type)\nclose_to TEXT\nradar_threshold TEXT\nfw_version TEXT\nhw_version TEXT\nble_scan_period INTEGER\nble_scan_duration INTEGER\ntemperature_calib TEXT\nhumidity_calib TEXT\nreporting_period_s INTEGER\nreboot_time REAL\nled_schema TEXT\nalert_details TEXT\nother TEXT\ngroup_id INTEGER\n```\n\n**Rows:** 455\n**Unique:** (well_id, device_mac)", "x": 1280, "y": 80 }, { "id": "table-disclaimers", "type": "card", "title": "disclaimers", "subtitle": "User Agreements", "borderColor": "gray", "tags": ["existing"], "description": "**Privacy policy agreements**\n\n```\nid INTEGER PK (auto)\nfirst_name TEXT\nlast_name TEXT\nuser_name TEXT\nemail TEXT\ndevices TEXT\ndate TIMESTAMPTZ\npolicy_version TEXT\n```", "x": 1280, "y": 340 }, { "id": "new-tables-header", "type": "card", "title": "NEW TABLES (to create)", "borderColor": "orange", "tags": ["new"], "description": "Tables needed for mobile app MVP.\n\nRequired for:\n- Push notifications\n- Password reset (Brevo)\n- Notification settings\n- Chat history persistence", "x": 140, "y": 500 }, { "id": "table-push-tokens", "type": "card", "title": "push_tokens", "subtitle": "NEW - Push Notification Tokens", "borderColor": "orange", "tags": ["new", "push"], "description": "**Store device push tokens**\n\n```sql\nCREATE TABLE push_tokens (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n user_id INTEGER REFERENCES person_details(user_id),\n token TEXT NOT NULL,\n platform TEXT CHECK (platform IN ('ios', 'android')),\n device_id TEXT,\n created_at TIMESTAMPTZ DEFAULT NOW(),\n updated_at TIMESTAMPTZ DEFAULT NOW(),\n UNIQUE(user_id, device_id)\n);\n```\n\n**Used by:** register_push_token endpoint", "x": 520, "y": 500 }, { "id": "table-notification-settings", "type": "card", "title": "notification_settings", "subtitle": "NEW - User Notification Preferences", "borderColor": "orange", "tags": ["new", "push"], "description": "**Per-user notification settings**\n\n```sql\nCREATE TABLE notification_settings (\n user_id INTEGER PRIMARY KEY REFERENCES person_details(user_id),\n alerts_enabled BOOLEAN DEFAULT true,\n daily_report BOOLEAN DEFAULT true,\n quiet_hours_enabled BOOLEAN DEFAULT false,\n quiet_hours_start TIME DEFAULT '22:00',\n quiet_hours_end TIME DEFAULT '07:00',\n email_alerts BOOLEAN DEFAULT true,\n sms_alerts BOOLEAN DEFAULT false,\n updated_at TIMESTAMPTZ DEFAULT NOW()\n);\n```", "x": 520, "y": 700 }, { "id": "table-password-resets", "type": "card", "title": "password_resets", "subtitle": "NEW - Password Reset Tokens", "borderColor": "orange", "tags": ["new", "auth"], "description": "**Forgot password flow (via Brevo)**\n\n```sql\nCREATE TABLE password_resets (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n user_id INTEGER REFERENCES person_details(user_id),\n token TEXT NOT NULL UNIQUE,\n expires_at TIMESTAMPTZ NOT NULL,\n used_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ DEFAULT NOW()\n);\n\nCREATE INDEX idx_password_resets_token \n ON password_resets(token);\nCREATE INDEX idx_password_resets_expires \n ON password_resets(expires_at);\n```\n\n**Flow:** User requests reset → token created → email via Brevo → user clicks link → token validated → password updated", "x": 900, "y": 500 }, { "id": "table-chat-history", "type": "card", "title": "chat_history", "subtitle": "NEW - AI Chat Messages", "borderColor": "orange", "tags": ["new", "chat"], "description": "**Persist AI chat conversations**\n\n```sql\nCREATE TABLE chat_history (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n user_id INTEGER REFERENCES person_details(user_id),\n deployment_id INTEGER REFERENCES deployments(deployment_id),\n role TEXT CHECK (role IN ('user', 'assistant')),\n content TEXT NOT NULL,\n created_at TIMESTAMPTZ DEFAULT NOW()\n);\n\nCREATE INDEX idx_chat_user_deployment \n ON chat_history(user_id, deployment_id);\n```\n\n**Note:** Optional for MVP, can add later", "x": 900, "y": 700 }, { "id": "relationships", "type": "card", "title": "RELATIONSHIPS", "borderColor": "purple", "tags": ["overview"], "description": "**Key Relationships:**\n\n```\nperson_details.access_to_deployments\n → deployments.deployment_id (comma-separated!)\n\ndeployment_details.deployment_id\n → deployments.deployment_id\n\ndeployment_details.beneficiary_id\n → person_details.user_id\n\ndeployment_details.caretaker_id\n → person_details.user_id\n\ndevices.well_id\n → deployments.deployment_id (via mapping)\n```\n\n**Note:** `access_to_deployments` is TEXT not FK!\nNeed to parse: `\"21,38,29\".split(',')`", "x": 1280, "y": 500 }, { "id": "roles", "type": "card", "title": "ROLE SYSTEM", "borderColor": "cyan", "tags": ["auth"], "description": "**role_ids field values:**\n\n```\n1 = Beneficiary (elderly person)\n2 = Caretaker (family member)\n3 = Admin\n4 = Installer\n```\n\n**Examples:**\n- `\"1\"` = only beneficiary\n- `\"1,2\"` = beneficiary + caretaker\n- `\"2\"` = only caretaker\n\n**In mobile app:**\n- Most users will be role 2 (caretakers)\n- They monitor role 1 (beneficiaries)", "x": 1280, "y": 700 }, { "id": "migration-todo", "type": "card", "title": "MIGRATION TODO", "borderColor": "red", "tags": ["critical"], "description": "**Before production:**\n\n1. ⬜ Add `password_hash` to person_details\n2. ⬜ Migrate passwords: bcrypt(key) → password_hash\n3. ⬜ Create push_tokens table\n4. ⬜ Create notification_settings table\n5. ⬜ Create password_resets table\n6. ⬜ (Optional) Create chat_history table\n7. ⬜ Add RLS policies for new tables\n8. ⬜ Remove `key` column after migration\n\n**SQL file:** /backend/migrations/001_add_auth_tables.sql", "x": 140, "y": 700 } ], "connections": [ {"from": "header", "to": "table-person-details"}, {"from": "header", "to": "new-tables-header"}, {"from": "table-person-details", "to": "warning-passwords"}, {"from": "table-person-details", "to": "table-deployments", "label": "access_to_deployments"}, {"from": "table-deployments", "to": "table-deployment-details"}, {"from": "table-deployments", "to": "table-devices", "label": "well_id"}, {"from": "table-deployment-details", "to": "table-person-details", "label": "beneficiary_id"}, {"from": "new-tables-header", "to": "table-push-tokens"}, {"from": "new-tables-header", "to": "table-notification-settings"}, {"from": "new-tables-header", "to": "table-password-resets"}, {"from": "new-tables-header", "to": "table-chat-history"}, {"from": "new-tables-header", "to": "migration-todo"}, {"from": "table-push-tokens", "to": "table-person-details", "label": "FK user_id"}, {"from": "table-notification-settings", "to": "table-person-details", "label": "FK user_id"}, {"from": "table-password-resets", "to": "table-person-details", "label": "FK user_id"}, {"from": "table-chat-history", "to": "table-person-details", "label": "FK user_id"}, {"from": "table-chat-history", "to": "table-deployments", "label": "FK deployment_id"}, {"from": "table-devices", "to": "table-disclaimers"}, {"from": "table-disclaimers", "to": "relationships"}, {"from": "relationships", "to": "roles"} ], "tagsDictionary": [ {"name": "overview", "color": "purple"}, {"name": "existing", "color": "green"}, {"name": "new", "color": "orange"}, {"name": "auth", "color": "blue"}, {"name": "core", "color": "green"}, {"name": "iot", "color": "teal"}, {"name": "push", "color": "pink"}, {"name": "chat", "color": "cyan"}, {"name": "critical", "color": "red"} ] }