Skip to content

Repository files navigation

MeduzaAI

Backend API for MeduzaAI — a multi-tenant hospital platform that connects hospitals, departments, doctors, and patients through QR check-in, AI-assisted intake interviews, queue notifications, and real-time staff tools.

Swagger title in the app: meduza.ai REST-API. Production CORS allows meduzaai.uz.


Overview

MeduzaAI helps clinics digitize the front desk:

  1. A doctor shares a public check-in link / QR code.
  2. The patient opens the link, starts a session, and talks to an AI intake assistant (OpenAI).
  3. Structured facts, risk level, and a summary are stored on a PatientResponse session.
  4. The patient joins a queue; doctors see updates over WebSocket; Telegram can notify the patient about queue progress.
  5. Hospital admins manage org data, analytics, news, and department chat.

Domain model

Hospitals
  └── Departments
        └── Doctors
              └── Patients / PatientResponse (intake sessions)
                    ├── IntakeMessages
                    ├── IntakeFacts
                    └── Answers

Staff accounts live in Users with roles super_admin, admin, and doctor. Admins may be hospital-scoped or platform-wide (Admins.is_super).


Tech stack

Layer Technology
Runtime Node.js, TypeScript
Framework NestJS 11
Database PostgreSQL via Prisma 7 (@prisma/adapter-pg)
Auth JWT + httpOnly cookies (access_token, refresh_token), bcrypt
AI intake OpenAI (openai SDK, default model gpt-4o-mini)
Messaging Telegram bot (Grammy)
Realtime Socket.IO (@nestjs/websockets)
Media Cloudinary (doctor avatars, news images)
Docs Swagger UI at /api/docs
Validation class-validator / class-transformer global ValidationPipe

Features

  • Multi-tenant hospitals — hospitals, departments, doctors, patients, scoped by role
  • Role-based accessSUPER_ADMIN, HOSPITAL_ADMIN, DOCTOR
  • Public QR check-in — HMAC checkin_token, session start, optional drafts
  • AI intake chat — layered prompts (global + department + doctor), fact extraction, risk levels, doctor dashboard summary
  • Queue + Telegram — deep-link subscribe, queue progress / turn notifications
  • Realtime — doctor patient-list updates; department chat over Socket.IO
  • Questions & answers — template vs doctor-scoped intake questions
  • Notifications, categories, priorities
  • Audit logs — login and sensitive actions
  • Analytics — dashboard, peak hours, doctor-scoped stats (APP_TIMEZONE, default Asia/Tashkent)
  • News — specialty/department articles with image upload
  • Department chat — REST + WebSocket rooms for staff

Project structure

meduzaai/
├── prisma/
│   ├── schema.prisma              # Data model
│   └── clear-checkin-sessions.ts  # Utility: wipe check-in sessions
├── providers/
│   ├── cloudinary/                # Image upload service
│   └── prisma/                    # PrismaModule / PrismaService
├── scripts/
│   └── add-super-admin-once.mjs   # Bootstrap a super admin user
└── src/
    ├── main.ts                    # Bootstrap, CORS, Swagger, global prefix
    ├── app.module.ts
    ├── public-checkin.controller.ts
    ├── common/                    # Auth guards, check-in helpers, realtime
    └── modules/                   # Feature modules (auth, intake-ai, …)

Requirements

  • Node.js 20+ (recommended)
  • PostgreSQL database
  • OpenAI API key (for AI intake)
  • Optional: Telegram bot token, Cloudinary credentials

Getting started

1. Install dependencies

npm install

postinstall runs prisma generate.

2. Environment variables

Create a .env in the project root (not committed). There is no .env.example in the repo; use this template:

# Server
PORT=4000
DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/meduzaai

# JWT / cookies
ACCESS_TOKEN_KEY=change-me-access
ACCESS_TOKEN_KEY_TIME=15m
REFRESH_TOKEN_KEY=change-me-refresh
REFRESH_TOKEN_TIME=7d
REFRESH_TIME_MS=604800000

# Public check-in (frontend base for QR / redirect)
CHECKIN_PUBLIC_BASE_URL=http://localhost:3000
# FRONTEND_BASE_URL=http://localhost:3000   # fallback if CHECKIN_PUBLIC_BASE_URL unset
CHECKIN_TOKEN_SECRET=change-me-checkin       # falls back to ACCESS_TOKEN_KEY

# OpenAI
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini

# Telegram (optional)
TELEGRAM_BOT_TOKEN=
TELEGRAM_BOT_USERNAME=meduzaal_bot
# TELEGRAM_POLLING=0   # set 0/false for webhook-only (production)

# Cloudinary (optional; for avatars & news images)
CLOUDINARY_URL=
# or:
# CLOUDINARY_CLOUD_NAME=
# CLOUDINARY_API_KEY=
# CLOUDINARY_API_SECRET=

# Analytics timezone
APP_TIMEZONE=Asia/Tashkent

3. Database

Apply the schema to Postgres. Migrations are configured in prisma.config.ts (prisma/migrations), but migration history may not be present in the repo — sync the schema as appropriate for your environment, for example:

npx prisma db push
# or, when migrations exist:
npx prisma migrate deploy

Note: npm run db:deploy currently has a typo (primsa instead of prisma). Prefer npx prisma migrate deploy.

4. Create a super admin

node scripts/add-super-admin-once.mjs [phone] [password]

Defaults (if args omitted): phone +998979105060, password amin123. Requires DATABASE_URL.

5. Run the API

# development (watch)
npm run start:dev
# or
npm run dev

# production build + start
npm run build
npm run start:prod

Server listens on 0.0.0.0 at PORT (default 4000).

  • API base: http://localhost:4000/api
  • Swagger: http://localhost:4000/api/docs

API conventions

Setting Value
Global prefix /api
Exception (no /api) GET /h/:hospitalId/:departmentSlug/d/:doctorId → 302 to frontend check-in
Auth Bearer access token and/or httpOnly cookies
CORS https://meduzaai.uz, http://meduzaai.uz (+ requests with no Origin); credentials enabled
Proxy trust proxy = 1 (nginx X-Forwarded-For for audit/IP)
Body limit 10mb

Main route prefixes

Prefix Purpose
/api/auth Login, refresh, me, logout, change password
/api/admins Admin users
/api/hospitals Hospitals
/api/departments Departments + AI system prompts
/api/doctors Doctors + /me/* doctor self-service
/api/patients (/api/potients) Patients
/api/questions Intake question templates / doctor questions
/api/answers Answers
/api/checkin Public patient check-in
/api/ai-intake AI intake conversation
/api/patient-response Staff-facing intake sessions
/api/analytics Dashboard analytics
/api/hospital-admin/analytics Peak-hours analytics
/api/notifications Notifications
/api/audit-logs Audit trail
/api/news Medical news articles
/api/department-chat Department staff chat
/api/telegram Telegram webhook
/api/categories, /api/priorities Notification metadata
/api/users User accounts

Interactive docs: /api/docs.


Roles

API role Who Typical access
SUPER_ADMIN Platform admin (is_super) All hospitals, departments, news write, global chat rooms, unscoped analytics
HOSPITAL_ADMIN Hospital-scoped admin Own hospital data, doctors, analytics, audit, chat rooms
DOCTOR Clinician Own patients, intake dashboard, AI prompt, avatar, doctor questions, dept chat me/*

Guards: JwtAuthGuard + RolesGuard with @Roles(...). Public surfaces include login, check-in, AI intake (tokenized), and the Telegram webhook.


Patient check-in & AI intake

sequenceDiagram
  participant P as Patient
  participant FE as Frontend
  participant API as MeduzaAI API
  participant AI as OpenAI
  participant D as Doctor (Socket.IO)
  participant TG as Telegram

  P->>FE: Open /h/{hospital}/{dept}/d/{doctor}
  FE->>API: GET /api/checkin/doctor/:doctorId
  API-->>FE: profile + checkin_token
  FE->>API: POST /api/checkin/session/start
  FE->>API: POST /api/ai-intake/start
  loop Conversation
    FE->>API: POST /api/ai-intake/message
    API->>AI: chat + structured JSON
    API-->>FE: assistant message + facts
  end
  FE->>API: POST /api/checkin/submissions
  API->>D: doctor:patients_changed
  API->>TG: queue notify (if linked)
Loading
  1. Link / QR — Built as {CHECKIN_PUBLIC_BASE_URL}/h/{hospitalId}/{departmentSlug}/d/{doctorId}. Hitting the same path on the API redirects to the frontend.
  2. Doctor profileGET /api/checkin/doctor/:doctorId returns a short-lived HMAC checkin_token (~30 minutes).
  3. SessionPOST /api/checkin/session/start upserts Patients + PatientResponse.
  4. AI chatPOST /api/ai-intake/start and /message drive the interview; prompts combine global rules, department ai_system_prompt, and optional doctor ai_intake_prompt.
  5. Submit / queuePOST /api/checkin/submissions finalizes answers, assigns queue workflow, emits realtime updates, and can notify Telegram.
  6. Doctor viewGET /api/ai-intake/:sessionId/dashboard and Socket.IO namespace /doctor-updates.

Session statuses (IntakeSessionStatus): collecting, enough_data, urgent, completed, cancelled. Risk levels: low | medium | high | urgent.


Realtime (Socket.IO)

/doctor-updates

  • Subscribe with handshake doctorId / doctor_id or event doctor:subscribe.
  • Events: doctor:patients_changed (patient_created, patient_status_updated, patient_updated, queue_updated).

/department-chat

  • Auth via JWT (auth.token or query.token, secret ACCESS_TOKEN_KEY).
  • Join room with join { departmentId } → room department:{id}.
  • Events: department_chat:message.

Telegram

  • Bot token: TELEGRAM_BOT_TOKEN.
  • Username default: meduzaal_bot.
  • Development: polling unless TELEGRAM_POLLING is 0 / false.
  • Production: POST /api/telegram/webhook.
  • Patients link via POST /api/checkin/telegram-linkt.me/{bot}?start={token}, then share contact to subscribe to queue updates.

npm scripts

Script Description
npm run start:dev / dev Nest watch mode
npm run build prisma generate + Nest build
npm start / start:prod Run compiled dist/src/main.js
npm run lint ESLint
npm run format Prettier
npm test / test:e2e / test:cov Jest
npm run db:clear-checkin-sessions Delete answers + patient responses
node scripts/add-super-admin-once.mjs Create/upgrade super admin

Scripts db:wipe-intake-full and seed:cardiology-intake are referenced in package.json but their Prisma script files are not currently in the repository.


Auth details

  • Login: POST /api/auth/login with phone + password.
  • Issues JWT access + refresh; sets httpOnly cookies access_token and refresh_token (sameSite: 'lax').
  • Also returns accessToken in the JSON body for Bearer clients.
  • GET /api/auth/me, POST /api/auth/refresh, POST /api/auth/logout, POST /api/auth/change-password.

Deployment notes

  • Bind address is already 0.0.0.0 — suitable behind nginx.
  • Enable trust proxy is already set for correct client IPs.
  • Point CORS / frontend base URLs at your real domains.
  • Prefer Telegram webhook mode in production (TELEGRAM_POLLING=0) and configure the webhook to /api/telegram/webhook.
  • Keep DATABASE_URL, JWT secrets, CHECKIN_TOKEN_SECRET, and OPENAI_API_KEY out of version control.

License

Private / UNLICENSED (see package.json).

About

AI-powered hospital management platform that streamlines patient intake, reduces waiting time, and helps doctors focus on diagnosis through intelligent pre-consultation.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages