Backend features: - Express.js API server - Supabase database integration - Stripe Checkout for payments ($249 kit + $9.99/mo premium) - Stripe webhooks for payment events - Admin panel with order management - Auth middleware with JWT - Email service via Brevo API endpoints: - /api/stripe/* - Payment processing - /api/webhook/stripe - Stripe webhooks - /api/admin/* - Admin operations - /function/well-api/api - Legacy API proxy Database migrations: - orders, subscriptions, push_tokens tables Schemes updated: - Removed updatedAt from all schemes - Updated credentials section with live values - Added Stripe configuration details 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
201 lines
12 KiB
JSON
201 lines
12 KiB
JSON
{
|
|
"_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"}
|
|
]
|
|
}
|