# Sehat Sahoolat v2 — Project Memory

> **Last updated:** 2026-05-25 — Claude (this branch: `cms-module-import`)
>
> Read this file first when picking the project back up after a break.

---

## What this is

A telehealth platform that lets patients in Pakistan book video consultations with doctors worldwide. Stack:

- **Backend** — NestJS 11 + TypeORM + Postgres 16 (pgvector) + Redis + Bull queues
- **Web** — Three Next.js 15 portals: patient (3001), doctor (3002), admin (3003), plus a marketing site (3004)
- **Mobile** — Flutter app (iOS + Android)
- **Whisper sidecar** — FastAPI + faster-whisper for consult transcription
- **Video** — Agora RTC for live consultations
- **AI** — Anthropic Claude for triage, consult notes, Rx drafting, patient EMR voice agent

Target scale: 1000 patients, 300 doctors, 10 admins. Hosted in DigitalOcean Bangalore (GDPR/UK ICO adequacy region).

**Repo:** `https://github.com/mujtabatariq18/sehat-sahoolat` (private)
**Clean local copy:** `~/Downloads/sehat-sahoolat/` (NOT `~/Desktop/` — iCloud corrupts node_modules there)

---

## Running the stack locally

```bash
# 1. Backend (port 3000)
cd ~/Downloads/sehat-sahoolat/backend
node dist/src/main.js       # or `npm run start:prod`

# 2. Three portals in separate terminals
cd ~/Downloads/sehat-sahoolat/web/apps/patient-portal && npx next dev -p 3001
cd ~/Downloads/sehat-sahoolat/web/apps/admin-portal   && npx next dev -p 3003
cd ~/Downloads/sehat-sahoolat/web/apps/doctor-portal  && npx next dev -p 3002

# 3. Whisper (optional — only needed for transcription tests)
cd ~/Downloads/sehat-sahoolat/services/whisper && uvicorn app:app --port 8001
```

URLs once running:
- Patient: http://localhost:3001
- Doctor:  http://localhost:3002
- Admin:   http://localhost:3003
- API:     http://localhost:3000/api/v2

### Test accounts (already in the dev DB)

| Role | Email | Password | Notes |
|---|---|---|---|
| Admin | `admin@sehat.local` | `AdminPass1!` | **Has MFA enabled** — use `set-agora-creds.ts` script for credential changes |
| Patient (sub holder) | `patient1@sehat.local` | `PatientPass1!` | Has Starter subscription, several appointments |
| Patient (no sub) | `zara5.test+1779559099@example.com` | `SafePass123` | Used for free-tier flows |
| Doctor (dermatology) | `derma@sehat.local` | `DoctorPass1!` | Assigned to b37e3b14… appointment |
| Doctor (cardiology) | `cardio@sehat.local` | `DoctorPass1!` | |

---

## Module status, A–Z

Legend: 🟢 = production-ready · 🟡 = working but rough edges · 🔧 = in progress · ⚠️ = known issue

### Backend modules (`backend/src/modules/`, 43 total)

| Module | Status | What's working | Known gaps |
|---|---|---|---|
| `admin` | 🟢 | Full admin panel CRUD, Patient 360 with subscription block, doctor verification | `admin.service.spec.ts` has 12 pre-existing test failures (missing repo mocks — unrelated to features) |
| `admin-v1-parity` | 🟢 | Plan-change request approval, package swap with family-member reconciliation | |
| `ai` | 🟢 | Anthropic Claude integration, triage, EMR assistant, consultation note + Rx generation | |
| `analytics` | 🟢 | Overview + doctor utilisation endpoints, admin dashboard charts | |
| `appointments` | 🟢 | Booking, slot validation, reschedule, cancel, status journey, admin override | Admin reschedule wired in the queue module, not here |
| `audit` | 🟢 | Append-only logs for every state change; visible in Patient 360 + audit-logs page | |
| `auth` | 🟢 | JWT (12h) + refresh rotation, device cap per package, MFA TOTP, change-password | |
| `cms` | 🔧 | Imported from local backup onto `cms-module-import` branch — public + admin services for global/pages/sections/testimonials/faqs/features. Not merged to main yet | Review + PR-merge into main |
| `complaints` | 🟢 | Patient files → admin triages → message thread | |
| `copilot` | 🟢 | Doctor copilot for in-consult suggestions | |
| `doctor-coach` | 🟢 | AI coach for doctor's learning paths | |
| `doctor-views` | 🟢 | Doctor-facing read models | |
| `doctors` | 🟢 | Verification workflow, schedules, specialties, live status, fee | |
| `drug-safety` | 🟢 | Allergy + interaction checks in Rx flow | |
| `email` + `email-templates` | 🟢 | Resend SMTP + admin-editable templates | |
| `emr` | 🟢 | Demographics, lifestyle, allergies, conditions, medications, immunizations, family-history, procedures, hospitalisations, lab results (with trending), reports (multi-file timeline) | |
| `emr-assistant` | 🟢 | Patient builds EMR by chatting with AI; document upload + structured extraction | |
| `enquiries` | 🟢 | Pre-signup contact forms | |
| `feedback` (`users-feedback`) | 🟢 | Patient feedback after appointments | |
| `file-attachments` | 🟢 | Generic upload + serve; backs EMR reports, profile photos, custom field files | |
| `follow-up` | 🟢 | Doctor schedules + reminds | |
| `form-fields` | 🟢 | Admin-defined custom fields per context (patient/doctor signup+profile). Bug fixed this session: DTO whitelist rejected `recordId` query param | |
| `health` | 🟢 | Liveness probe | |
| `health-coach` | 🟢 | AI health coach for patients (sehat-coach in patient portal) | Recently merged from PR #1 |
| `integrations` | 🟢 | Encrypted credentials for Agora + Stripe; admin UI under /settings/integrations | |
| `knowledge-base` | 🟢 | Admin-managed articles with pgvector embeddings | |
| `learning` | 🟢 | Doctor CME modules | |
| `medicines` | 🟢 | Admin-managed drug catalog + bulk CSV import | |
| `notifications` | 🟢 | In-app + email + FCM (mobile push) + Web Push (VAPID); per-user prefs | |
| `onboarding` | 🟢 | First-login walkthrough | |
| `packages` | 🟢 | **3-tier model**: PAYG ($30/appt), Bundle (3/6/9/12 pre-paid w/ tier discount), Subscription ($10/mo + $10 reg, waived 1st mo, 12-mo min, 4 members + 2 chargeable extras). Tier-aware credit logic in `hasConsultationCredits` + `consumeConsultationCredit`. New `subscription_family_members` table + service for shared plans. `swapPackage` reconciles family members LIFO when downgrading | |
| `patients` | 🟢 | Patient + FamilyAccount + FamilyMember (clinical dependents). `family.service.create` now enforces `package.maxFamilyMembers` cap | |
| `payments` | 🟢 | Stripe + mock checkout. Now includes `registrationFeeMinor` + `firstMonthFeeWaived` logic for first-cycle subscription buyers. Two-line Stripe receipt | |
| `prescriptions` | 🟢 | Doctor authors Rx, drug-safety checks, PDF generation, refills | |
| `queue` | 🟢 | Admin allocation queue with AI doctor suggestions, scoring engine, manual override, reschedule modal (slot picker), no-doctor-available fallback, AI pick dedup | |
| `realtime` | 🟢 | Socket.IO gateway for live updates (queue, notifications, call signalling) | |
| `refills` | 🟢 | Patient requests, doctor approves | |
| `reminders` | 🟢 | Cron-driven appointment + medication reminders | |
| `reports` | 🟢 | Admin revenue/earnings/ops/AI/funnel reports | |
| `scribe` | 🟢 | Audio → Whisper transcript → AI structured consult note | |
| `services` | 🟢 | Service catalog (consultations, procedures, etc.) | |
| `users` | 🟢 | Base User entity + role management | |
| `video` | 🟢 | **Agora real-time video calls — verified working end-to-end this session.** Tokens generated correctly for patient + doctor on the same channel | Mock mode available when credentials unset |

### Frontend portals (`web/apps/`)

| Portal | Status | Notes |
|---|---|---|
| `patient-portal` | 🟢 | Login + MFA, dashboard, EMR (7 sections), book appointment, /packages (3-tier render), /profile/family (subscription members), prescriptions, notifications, AI EMR assistant. **Bug fixed this session:** hydration mismatch on 7 EMR pages — replaced `Math.random()`-based datalist IDs with row indices |
| `admin-portal` | 🟢 | All 25 sidebar routes return 200. Patient 360 has the new Subscription panel. Queue page has reschedule + alternatives dedup + no-doctor fallback. Form-designer works. CMS pages copied over (on cms-module-import branch). **Bug fixed this session:** 25+ macOS Finder `route 2.ts` duplicates removed (were shadowing real proxy routes) |
| `doctor-portal` | 🟢 | All routes work. **Bug fixed this session:** `engine.io-client` peer dep was missing → 500 on every page; installed + cleared `.next` cache |
| `public-site` | 🟢 | Marketing site at 3004 |

### Mobile (`mobile/`, Flutter)

🟡 — Basic flows work (login, dashboard, appointments). Active development is web-first.

---

## What we just shipped (last 7 commits)

```
97f316f  (HEAD) import: CMS module + admin pages + iCloud damage script
b0b711c  Merge PR #1 from sehat-coach-agent
93cd277  feat(sehat-coach): patient + doctor + Flutter AI health agent
9865b82  chore(scripts): add set-agora-creds.ts
81b63e9  feat: 3-tier packages, admin reschedule, 360 subscription panel + hydration fixes
e1b028c  docs: add knowledge base
2fbe84d  chore(db): legacy v1 database snapshot
e5de957  feat: session/MFA hardening + production deploy stack
```

---

## Live external credentials currently configured

| Service | Where | State |
|---|---|---|
| **Agora RTC** | `agora_config` table (encrypted at rest with AES-GCM) | ✅ Live — appId `8ad8a183…`, cert encrypted, `enabled=true`, mock_mode off. Tested: generates real `007e…`-prefixed tokens for patient + doctor on the same channel |
| **OpenAI** | `backend/.env` → `OPENAI_API_KEY` | set in dev |
| **Anthropic** | `backend/.env` → `ANTHROPIC_API_KEY` | set in dev |
| **Stripe** | `stripe_config` table | mock mode by default — flip via admin /settings/integrations |
| **FCM (mobile push)** | service account JSON | project `sehat-a4c23` |
| **Web Push** | VAPID keys in env | working |
| **SMTP** | `notification_config_smtp` table | Resend by default |

To rotate Agora later:
```bash
cd ~/Downloads/sehat-sahoolat/backend
node ./dist/scripts/set-agora-creds.js <newAppId> <newCert>
```

---

## Known issues / footguns

| Issue | Workaround |
|---|---|
| **iCloud corrupts `node_modules`** on Desktop/Documents folders | This is why the project lives at `~/Downloads/sehat-sahoolat/` not `~/Desktop/`. Disable iCloud Drive's Desktop & Documents sync in System Settings to prevent recurrence anywhere on the Mac |
| `admin.service.spec.ts` has 12 failing tests | Pre-existing — missing `ConsultationNote`, `EmailTemplates`, etc. repo mocks. Doesn't affect runtime. Out of scope until someone restores the spec |
| Next.js dev cache occasionally goes stale after big edits | `rm -rf web/apps/<portal>/.next && restart` |
| Backend startup logs warn `Agora credentials missing` | Only on a fresh DB. Run `set-agora-creds.ts` to fix (already run in current DB) |
| Old Desktop install (`~/Desktop/sehat_sahoolat/v2/`) is damaged beyond repair | Safe to `rm -rf` once you've confirmed Downloads works. All code is on GitHub |

---

## Deploy

Production target: DigitalOcean Bangalore (s-4vcpu-8gb droplet + managed Postgres + Spaces). Full runbook at `deploy/README.md`. Scripts at `deploy/scripts/`:

| Script | Purpose |
|---|---|
| `provision.sh` | One-shot droplet bootstrap (UFW, Docker, swap, sehat user, JWT secrets, acme.sh) |
| `deploy.sh` | git pull → docker build → migrate → rolling restart → health probe |
| `backup.sh` | pg_dump → AES-256 GPG → DO Spaces with GFS retention (7d/5w/12m) |
| `restore.sh` | List Spaces backups + restore into any DB by key |

---

## Database

- 48 TypeORM migrations on disk under `backend/src/migrations/`
- Run: `cd backend && npm run db:migrate`
- pgvector extension required for knowledge-base embeddings + AI features
- Legacy v1 Laravel SQL dump at `database/legacy/v1-snapshot.sql` (confirmed PII-free)

---

## Useful commands

```bash
# Health check the local copy for iCloud damage
./check-icloud-damage.sh

# Run all backend tests
cd backend && npm test

# Build everything
cd backend && npx tsc -p tsconfig.build.json
cd web && npm run build

# Reset all user sessions (when device cap blocks you)
psql -h $DB_HOST -U doadmin -d sehat_sahoolat \
  -c "UPDATE refresh_tokens SET revoked_at=NOW() WHERE revoked_at IS NULL"
```

---

## Open follow-ups

1. **Merge `cms-module-import` branch** into main via GitHub PR review
2. Provision the DO droplet using `deploy/scripts/provision.sh`
3. Fix the pre-existing admin.service.spec.ts test mocks (12 failures)
4. Decide whether to scrub the legacy SQL snapshot off git history once v1→v2 backfill runs (~8 MB always in clones)
