Skip to content

piyushkumar0707/MediQueue

Repository files navigation

MediQueue — CareQueue + Health-Vault

A production-grade healthcare operations platform built with security and real-time UX at its core.

Node.js React MongoDB Redis Socket.io JWT Tailwind CSS CI

Architecture Overview


What is this?

MediQueue solves two real-world healthcare problems in a single platform:

  • CareQueue — eliminates physical waiting rooms with a real-time digital queue. Patients join remotely, track their position live, and get notified when it's their turn.
  • Health-Vault — gives patients full ownership of their medical records with consent-based sharing, AES-256-GCM encryption at rest, and an immutable audit trail for every access.

Three user roles — Patient, Doctor, Admin — each with their own dashboard, workflows, and permission boundary.


Technical Highlights

Concern Solution
Real-time queue updates Socket.io v4 rooms (user:<id>) — server pushes diffs on every queue state change
Medical record security AES-256-GCM encryption via a dedicated backend service
Authentication Two-token JWT (15 min access + 7 day refresh) with a two-step OTP registration flow; tokens kept in memory only — never written to localStorage
MFA TOTP via speakeasy + backup codes; separate JWT_MFA_SECRET for MFA session tokens
Authorization Stateless protect() + authorize(...roles) middleware; Redis-cached user lookup (60s TTL) to avoid DB hit on every request
Rate limiting Redis-backed createRateLimiter() factory — per-user limits effective across multiple processes
Audit compliance Immutable AuditLog model with SHA-256 integrity hashes; middleware wraps every sensitive admin action
Emergency access Doctors can request override access; all overrides are logged and surfaced to admins for review
File uploads Multer → Cloudinary storage with authenticated backend retrieval flows
AI triage Groq LLaMA 3.1 suggests priority from symptoms — human override always preserved, AI is advisory only
AI summarization On-demand PDF text extraction + LLaMA summary — quota-limited, PII-stripped before Groq sees any text
AI image analysis Groq LLaMA 4 Scout (multimodal) reads consultation note images — confidence scoring flags unclear handwriting
Production hardening Helmet CSP default-src 'none', CORS origin allowlist, 5xx message masking, SIGTERM/SIGINT graceful shutdown, Winston stdout-only in production

Feature Breakdown

Patient

  • Register with email/SMS OTP verification (two-step flow)
  • Book appointments and join a live queue remotely
  • Real-time queue position tracking with estimated wait time
  • Upload, view, and delete medical records (encrypted)
  • Grant / revoke per-doctor consent to individual records
  • View prescriptions from completed consultations
  • In-app notification centre with real-time push

Doctor

  • Live queue dashboard — call next patient, manage consultation flow
  • Access patient records (consent-gated or emergency override with justification)
  • Write and manage prescriptions
  • View shared records and appointment history
  • Receive real-time notifications for queue events

Admin

  • Full user management (create, suspend, promote, delete)
  • Analytics dashboard — appointments, queue throughput, system usage
  • Audit log explorer with filtering (who accessed what, when, why)
  • Emergency access review — approve / reject doctor override requests
  • Real-time system activity monitoring

Architecture

System Architecture Component View

Data Model

Database ER Diagram

Key Design Decisions

  • ES Modules throughout the backend ("type": "module") — native import/export, all relative imports require .js extensions.
  • API versioning — all routes mounted at /api/v1/; health check available at /health, /api/health, and /api/v1/health.
  • Redis-backed caching & rate limitingioredis with lazyConnect: true; user auth cache (60s TTL), analytics cache (5 min TTL), all rate limiters share the same Redis instance.
  • Stateless authprotect() middleware verifies the JWT then calls getCachedUser() (Redis → DB fallback); no full DB hit on hot paths.
  • io shared via app.set('io', io) — controllers emit real-time events without coupling to the Socket layer.
  • Tokens in memory only — access tokens live in a module-level variable inside api.js (Axios interceptor). The Zustand auth-storage persists only non-sensitive user info (name, role, avatar) — never tokens.
  • File proxyGET /api/v1/records/:id/view-file fetches the file from Cloudinary server-side and streams it to the client with Content-Type: application/pdf and Content-Disposition: inline. The raw Cloudinary URL is never given to the browser.

API Surface

Prefix Responsibility
POST /api/v1/auth Register (OTP 2-step), login, MFA, refresh token, forgot/reset password
GET/PUT /api/v1/users Profile management
GET/POST/DELETE /api/v1/appointments Booking lifecycle; available slots
GET/POST /api/v1/queue Join queue, call-next, queue stats
POST /api/v1/queue/triage AI symptom triage — advisory suggestion only, never sets priority automatically
GET/POST/DELETE /api/v1/records Encrypted file upload, list, download, share, PDF export
POST /api/v1/records/:id/summarize On-demand AI summary of a text-based PDF or image record
GET /api/v1/records/:id/view-file Stream file bytes through the backend with correct Content-Type
GET/POST/DELETE /api/v1/consent Grant, revoke, and query consent grants
GET/POST /api/v1/emergency-access Doctor override requests + admin review
GET/POST /api/v1/prescriptions Create and view prescriptions
GET /api/v1/audit Tamper-evident audit log queries (admin)
GET /api/v1/analytics Dashboard metrics (Redis-cached 5 min)
GET/POST/PUT/DELETE /api/v1/admin User and emergency case management
GET/POST /api/v1/notifications Real-time notification inbox

Tech Stack

Backend: Node.js 18 · Express · MongoDB (Mongoose) · Redis (ioredis) · Socket.io v4 · JWT · bcrypt · Multer · Cloudinary · Winston · node-cron · Nodemailer · Twilio

Frontend: React 18 · Vite · Tailwind CSS · Zustand · React Query · Axios · Socket.io-client · React Router v6

Security: AES-256-GCM (medical records) · JWT dual-token + MFA (TOTP) · OTP via SMS + email · RBAC · Helmet (default-src 'none') · Redis rate limiting · Immutable audit log · CORS origin allowlist


Getting Started

Prerequisites

  • Option 1 (Docker): Docker & Docker Compose
  • Option 2 (Manual): Node.js v18+, MongoDB, Redis, npm

Quick Start with Docker (Recommended)

# 1. Clone the repo
git clone https://github.com/piyushkumar0707/MediQueue.git
cd MediQueue

# 2. Configure environment
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
# Edit both .env files with your values (see table below)

# 3. Start all services
docker-compose up --build

# App is now running at:
# - Frontend: http://localhost:80
# - Backend API: http://localhost:5000
# - MongoDB: localhost:27017
# - Redis: localhost:6379

Docker Environment Overrides

Use shell environment variables to override frontend build-time endpoints and backend CORS origin without editing the compose file.

# PowerShell example
$env:VITE_API_URL="http://localhost:5000/api/v1"
$env:VITE_SOCKET_URL="http://localhost:5000"
$env:FRONTEND_URL="http://localhost"
docker-compose up --build
# Bash example
VITE_API_URL=http://localhost:5000/api/v1 \
VITE_SOCKET_URL=http://localhost:5000 \
FRONTEND_URL=http://localhost \
docker-compose up --build

Docker Troubleshooting

  • If compose fails before startup, ensure backend/.env exists:
    • cp backend/.env.example backend/.env
  • Backend validates required variables at boot. Confirm these are set in backend/.env:
    • MONGODB_URI, REDIS_URL, JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, JWT_MFA_SECRET, ENCRYPTION_KEY
  • ENCRYPTION_KEY must be exactly 32 characters in production mode.

Manual Setup (Without Docker)

# 1. Clone
git clone https://github.com/piyushkumar0707/MediQueue.git
cd MediQueue

# 2. Backend
cd backend
cp .env.example .env        # fill in all required vars (see table below)
npm install
npm run dev                 # → http://localhost:5000

# 3. Frontend (new terminal)
cd frontend
cp .env.example .env        # set VITE_API_URL and VITE_SOCKET_URL
npm install
npm run dev                 # → http://localhost:5173

Environment Variables (Backend)

Variable Required Description
MONGODB_URI MongoDB connection string
REDIS_URL Redis connection string (e.g. redis://localhost:6379)
JWT_ACCESS_SECRET Sign access tokens (15 min)
JWT_REFRESH_SECRET Sign refresh tokens (7 days)
JWT_MFA_SECRET Sign MFA session tokens
ENCRYPTION_KEY Exactly 32 characters — AES-256 key for medical records
FRONTEND_URL ✅ (prod) Allowed CORS origin(s) — comma-separated for multi-env
TWILIO_* SMS OTP delivery
EMAIL_* Email OTP delivery
CLOUDINARY_* File storage
GROQ_API_KEY optional Groq LLaMA API key — get free at console.groq.com. If omitted, AI features return 503 and the app starts normally
AI_FEATURE_TRIAGE optional true/false — toggle symptom triage without redeploy (default: true)
AI_FEATURE_SUMMARIZE optional true/false — toggle record summarization without redeploy (default: true)
AI_FEATURE_IMAGE_ANALYSIS optional true/false — toggle image analysis independently (default: true)

⚠️ Never change ENCRYPTION_KEY after records are stored — existing records become permanently unreadable.

Environment Variables (Frontend)

Variable Required Description
VITE_API_URL Backend API base URL (e.g. http://localhost:5000/api/v1) — validated at build time
VITE_SOCKET_URL Socket.io server URL (e.g. http://localhost:5000) — validated at build time

CI/CD Pipeline

GitHub Actions workflow is configured at .github/workflows/ci.yml.

Triggers

  • Push to main
  • Pull request targeting main
  • Manual run (workflow_dispatch)

Jobs

  • Backend Lint and Test

    • Starts MongoDB and Redis service containers
    • Installs backend dependencies
    • Runs npm run lint
    • Runs npm test -- --passWithNoTests
  • Frontend Build

    • Installs frontend dependencies
    • Runs npm run build

Local parity commands (same checks as CI)

npm --prefix backend run lint
npm --prefix backend test -- --passWithNoTests
npm --prefix frontend run build

Branch protection (recommended)

Require both checks before merge into main:

  • Backend Lint and Test
  • Frontend Build

Release & Deployment

Use this as the standard release flow for this repository.

1) Pre-release checklist

  • Confirm required production env vars are set (MONGODB_URI, REDIS_URL, JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, JWT_MFA_SECRET, ENCRYPTION_KEY, FRONTEND_URL, and required provider keys)
  • Ensure branch protection on main requires CI checks
  • Ensure CI workflow is green on the release PR

2) Local verification before merge

# CI parity
npm --prefix backend run lint
npm --prefix backend test -- --passWithNoTests
npm --prefix frontend run build

# Container smoke test
docker-compose up -d --build
docker-compose ps

Health checks:

  • Frontend: http://localhost/health
  • Backend: http://localhost:5000/health

3) Merge and deploy

  • Open PR to main
  • Wait for both CI jobs to pass:
    • Backend Lint and Test
    • Frontend Build
  • Merge PR
  • Deploy using your target environment process (container platform, VM, or orchestration)

4) Post-deploy validation

  • Verify API health endpoint returns healthy status
  • Verify frontend loads and authentication flow works
  • Verify one core end-to-end flow (e.g. registration/login or queue join)
  • Check logs for startup/runtime errors

5) Rollback strategy

  • If health checks fail or critical paths break:
    • rollback to last known good image/commit
    • re-run health checks
    • investigate using CI logs + runtime logs before re-release

Project Structure

├── backend/src/
│   ├── server.js              # Entry: helmet→compression→cors→JSON→morgan → routes → Socket.io
│   ├── config/
│   │   ├── database.js        # Mongoose connection
│   │   ├── redis.js           # ioredis (lazyConnect: true)
│   │   ├── validateEnv.js     # Startup env validation — crashes fast if required vars missing
│   │   └── cloudinary.js      # Cloudinary SDK
│   ├── controllers/           # One controller per domain
│   ├── middleware/
│   │   ├── auth.js            # protect() + authorize(...roles) — Redis-cached user lookup
│   │   ├── auditLogger.js     # Wraps admin routes — writes AuditLog entries
│   │   └── errorHandler.js    # Global — masks 5xx messages in production
│   ├── models/                # 9 Mongoose schemas
│   ├── routes/                # Express routers (all at /api/v1/)
│   ├── services/              # notificationService, emailService, encryption.service
│   └── utils/
│       ├── userCache.js       # getCachedUser(), invalidateUserCache(), getOrSetCache()
│       ├── rateLimiter.js     # createRateLimiter() — Redis-backed factory
│       ├── logger.js          # Winston — stdout always; file transports dev-only
│       ├── jwt.js             # signAccessToken(), signRefreshToken(), verifyAccessToken()
│       └── pagination.js      # parsePagination(query, defaultLimit)
│
└── frontend/src/
    ├── App.jsx                # React Router v6 with ProtectedRoute role guards
    ├── store/
    │   ├── useAuthStore.js    # Zustand — tokens in memory only, non-sensitive fields persisted
    │   └── notificationStore.js
    ├── services/
    │   └── api.js             # Axios instance + auto token-refresh interceptor
    ├── pages/
    │   ├── patient/           # Dashboard, Queue, Appointments, HealthVault, Consent, Prescriptions
    │   ├── doctor/            # Dashboard, QueueManagement, PatientRecords, PrescriptionsList, SharedRecords
    │   └── admin/             # Dashboard, UserManagement, AuditLogs, Analytics, EmergencyReview
    └── components/            # Shared UI: layouts, navigation, NotificationBell

AI Safety Design

MediQueue integrates Groq LLaMA 3.1 for two advisory features. The design follows a strict human-in-the-loop model.

Principles

Principle Implementation
AI is never a hard dependency Every AI call is try/catch wrapped. Queue join and record view work with Groq completely down or rate-limited
Raw medical text never leaves the platform unredacted A PII redaction pass strips names, emails, phone numbers, and IDs before any text is sent to Groq
Final decision always belongs to the user The backend never reads AI output to set queue priority — it only stores the AI suggestion for audit purposes
Every AI action is auditable AuditLog entries written for every summarize call: who, which record, model, latency, success/fail
Features can be toggled independently AI_FEATURE_TRIAGE, AI_FEATURE_SUMMARIZE, AI_FEATURE_IMAGE_ANALYSIS env flags — no redeploy needed
Prompt versioning Every AI call carries a promptVersion field (e.g. triage-v1, summary-v1) for future auditability

Symptom Triage Flow

  1. Patient types symptoms in the Join Queue form
  2. "Suggest priority" button calls POST /api/v1/queue/triage (rate-limited: 5 req/min)
  3. Groq returns { priority, reason, confidence } — pre-fills the priority selector
  4. Patient can override — if they do, aiOverridden: true is stored on the queue entry
  5. Non-dismissable disclaimer always shown: "AI suggests a priority level based on symptoms. This is not a medical diagnosis. A doctor will confirm."

Record Summarization Flow

  1. Patient (or doctor with consent) opens a record and clicks "Summarize with AI"
  2. Backend fetches the file from Cloudinary via server-side signed URL
  3. PDF path: pdf-parse extracts text → PII stripped → sent to llama-3.1-8b-instant
  4. Image path: file base64-encoded server-side → sent to meta-llama/llama-4-scout-17b-16e-instruct (multimodal)
  5. Response: { summary, keyFindings, followUpNeeded, transcriptionConfidence } — displayed in UI, never stored
  6. Per-user quota: max 10 requests/hour (Redis counter, shared across PDF + image)
  7. Non-dismissable disclaimer: "AI-generated summary. Always consult your doctor for medical advice."

transcriptionConfidence field

Value Meaning UI behaviour
high Text printed or clearly legible No additional warning
medium Some words unclear, overall meaning confident Amber note: "Review key findings against your original document"
low Significant portions illegible Red warning: "Handwriting was difficult to read. Key details may be incomplete"

Graceful Degradation

  • GROQ_API_KEY missing → app starts normally, AI endpoints return 503 with a clear message
  • Groq timeout (8 s) → one automatic retry, then fallback response
  • AI_FEATURE_*=false → endpoint returns 503, no Groq call is made

License

This project is proprietary and confidential.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages