elevenlabs-webhook-nodejs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (969) hide show
  1. package/.claude/settings.local.json +27 -0
  2. package/.dockerignore +6 -0
  3. package/.env.example +43 -0
  4. package/BEFORE-PRODUCTION.md +32 -0
  5. package/Dockerfile +23 -0
  6. package/SYSTEM_OVERVIEW.md +942 -0
  7. package/SYSTEM_STATUS.md +332 -0
  8. package/backup_script/main.py +146 -0
  9. package/baileys/.dockerignore +7 -0
  10. package/baileys/Dockerfile +13 -0
  11. package/baileys/README.md +412 -0
  12. package/baileys/index.js +499 -0
  13. package/baileys/package-lock.json +2532 -0
  14. package/baileys/package.json +25 -0
  15. package/baileys/server.js +96 -0
  16. package/baileys/src/config.js +55 -0
  17. package/baileys/src/middleware/api-key.js +16 -0
  18. package/baileys/src/routes/accounts.js +51 -0
  19. package/baileys/src/routes/chats.js +103 -0
  20. package/baileys/src/routes/webhooks.js +34 -0
  21. package/baileys/src/services/account-store.js +259 -0
  22. package/baileys/src/services/webhook-dispatcher.js +161 -0
  23. package/baileys/src/services/worker-manager.js +597 -0
  24. package/baileys/src/utils/jid.js +79 -0
  25. package/baileys/src/utils/logger.js +16 -0
  26. package/baileys/src/utils/use-mongodb-auth-state.js +122 -0
  27. package/baileys/worker.js +721 -0
  28. package/dist/app.d.ts +1 -0
  29. package/dist/app.js +84 -0
  30. package/dist/app.js.map +1 -0
  31. package/dist/config/agentPrompts.json +19 -0
  32. package/dist/config/database.d.ts +1 -0
  33. package/dist/config/database.js +26 -0
  34. package/dist/config/database.js.map +1 -0
  35. package/dist/config/env.d.ts +41 -0
  36. package/dist/config/env.js +81 -0
  37. package/dist/config/env.js.map +1 -0
  38. package/dist/config/index.d.ts +3 -0
  39. package/dist/config/index.js +73 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/controllers/webhook.controller.d.ts +10 -0
  42. package/dist/controllers/webhook.controller.js +128 -0
  43. package/dist/controllers/webhook.controller.js.map +1 -0
  44. package/dist/errors/index.d.ts +4 -0
  45. package/dist/errors/index.js +13 -0
  46. package/dist/errors/index.js.map +1 -0
  47. package/dist/hooks/webhookValidator.d.ts +2 -0
  48. package/dist/hooks/webhookValidator.js +47 -0
  49. package/dist/hooks/webhookValidator.js.map +1 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +55 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/migrations/001_session-architecture.d.ts +3 -0
  54. package/dist/migrations/001_session-architecture.js +119 -0
  55. package/dist/migrations/001_session-architecture.js.map +1 -0
  56. package/dist/migrations/002_agent-ref.d.ts +3 -0
  57. package/dist/migrations/002_agent-ref.js +55 -0
  58. package/dist/migrations/002_agent-ref.js.map +1 -0
  59. package/dist/migrations/003_shift-schedule.d.ts +3 -0
  60. package/dist/migrations/003_shift-schedule.js +48 -0
  61. package/dist/migrations/003_shift-schedule.js.map +1 -0
  62. package/dist/migrations/004_invert-shift-to-clinic-hours.d.ts +3 -0
  63. package/dist/migrations/004_invert-shift-to-clinic-hours.js +27 -0
  64. package/dist/migrations/004_invert-shift-to-clinic-hours.js.map +1 -0
  65. package/dist/migrations/005_composite-baileys-chat-ids.d.ts +3 -0
  66. package/dist/migrations/005_composite-baileys-chat-ids.js +47 -0
  67. package/dist/migrations/005_composite-baileys-chat-ids.js.map +1 -0
  68. package/dist/migrations/migration.model.d.ts +18 -0
  69. package/dist/migrations/migration.model.js +45 -0
  70. package/dist/migrations/migration.model.js.map +1 -0
  71. package/dist/migrations/migration.routes.d.ts +2 -0
  72. package/dist/migrations/migration.routes.js +37 -0
  73. package/dist/migrations/migration.routes.js.map +1 -0
  74. package/dist/migrations/migration.runner.d.ts +19 -0
  75. package/dist/migrations/migration.runner.js +74 -0
  76. package/dist/migrations/migration.runner.js.map +1 -0
  77. package/dist/models/ConversationClaim.d.ts +13 -0
  78. package/dist/models/ConversationClaim.js +44 -0
  79. package/dist/models/ConversationClaim.js.map +1 -0
  80. package/dist/models/ConversationEvaluation.d.ts +23 -0
  81. package/dist/models/ConversationEvaluation.js +92 -0
  82. package/dist/models/ConversationEvaluation.js.map +1 -0
  83. package/dist/models/Summary.d.ts +37 -0
  84. package/dist/models/Summary.js +78 -0
  85. package/dist/models/Summary.js.map +1 -0
  86. package/dist/models/Transcription.d.ts +34 -0
  87. package/dist/models/Transcription.js +71 -0
  88. package/dist/models/Transcription.js.map +1 -0
  89. package/dist/models/WhatsAppRecipient.d.ts +23 -0
  90. package/dist/models/WhatsAppRecipient.js +53 -0
  91. package/dist/models/WhatsAppRecipient.js.map +1 -0
  92. package/dist/modules/admin/admin.routes.d.ts +2 -0
  93. package/dist/modules/admin/admin.routes.js +1966 -0
  94. package/dist/modules/admin/admin.routes.js.map +1 -0
  95. package/dist/modules/admin/elevenlabs-test-chat.routes.d.ts +2 -0
  96. package/dist/modules/admin/elevenlabs-test-chat.routes.js +158 -0
  97. package/dist/modules/admin/elevenlabs-test-chat.routes.js.map +1 -0
  98. package/dist/modules/admin/whatsapp-test-chat.routes.d.ts +2 -0
  99. package/dist/modules/admin/whatsapp-test-chat.routes.js +204 -0
  100. package/dist/modules/admin/whatsapp-test-chat.routes.js.map +1 -0
  101. package/dist/modules/agents/agent.model.d.ts +38 -0
  102. package/dist/modules/agents/agent.model.js +92 -0
  103. package/dist/modules/agents/agent.model.js.map +1 -0
  104. package/dist/modules/agents/agent.routes.d.ts +2 -0
  105. package/dist/modules/agents/agent.routes.js +61 -0
  106. package/dist/modules/agents/agent.routes.js.map +1 -0
  107. package/dist/modules/appointment-validation/appointment-validation.model.d.ts +76 -0
  108. package/dist/modules/appointment-validation/appointment-validation.model.js +118 -0
  109. package/dist/modules/appointment-validation/appointment-validation.model.js.map +1 -0
  110. package/dist/modules/appointment-validation/appointment-validation.routes.d.ts +2 -0
  111. package/dist/modules/appointment-validation/appointment-validation.routes.js +202 -0
  112. package/dist/modules/appointment-validation/appointment-validation.routes.js.map +1 -0
  113. package/dist/modules/appointment-validation/appointment-validation.service.d.ts +53 -0
  114. package/dist/modules/appointment-validation/appointment-validation.service.js +827 -0
  115. package/dist/modules/appointment-validation/appointment-validation.service.js.map +1 -0
  116. package/dist/modules/auth/auth.model.d.ts +17 -0
  117. package/dist/modules/auth/auth.model.js +64 -0
  118. package/dist/modules/auth/auth.model.js.map +1 -0
  119. package/dist/modules/auth/auth.routes.d.ts +2 -0
  120. package/dist/modules/auth/auth.routes.js +202 -0
  121. package/dist/modules/auth/auth.routes.js.map +1 -0
  122. package/dist/modules/auth/auth.service.d.ts +28 -0
  123. package/dist/modules/auth/auth.service.js +183 -0
  124. package/dist/modules/auth/auth.service.js.map +1 -0
  125. package/dist/modules/auth/refresh-token.model.d.ts +13 -0
  126. package/dist/modules/auth/refresh-token.model.js +52 -0
  127. package/dist/modules/auth/refresh-token.model.js.map +1 -0
  128. package/dist/modules/billing/billing-alert.model.d.ts +16 -0
  129. package/dist/modules/billing/billing-alert.model.js +47 -0
  130. package/dist/modules/billing/billing-alert.model.js.map +1 -0
  131. package/dist/modules/billing/billing-period-snapshot.model.d.ts +35 -0
  132. package/dist/modules/billing/billing-period-snapshot.model.js +68 -0
  133. package/dist/modules/billing/billing-period-snapshot.model.js.map +1 -0
  134. package/dist/modules/billing/billing.model.d.ts +18 -0
  135. package/dist/modules/billing/billing.model.js +62 -0
  136. package/dist/modules/billing/billing.model.js.map +1 -0
  137. package/dist/modules/billing/billing.routes.d.ts +2 -0
  138. package/dist/modules/billing/billing.routes.js +63 -0
  139. package/dist/modules/billing/billing.routes.js.map +1 -0
  140. package/dist/modules/billing/billing.service.d.ts +69 -0
  141. package/dist/modules/billing/billing.service.js +498 -0
  142. package/dist/modules/billing/billing.service.js.map +1 -0
  143. package/dist/modules/billing/payment.model.d.ts +24 -0
  144. package/dist/modules/billing/payment.model.js +57 -0
  145. package/dist/modules/billing/payment.model.js.map +1 -0
  146. package/dist/modules/calls/call.model.d.ts +41 -0
  147. package/dist/modules/calls/call.model.js +97 -0
  148. package/dist/modules/calls/call.model.js.map +1 -0
  149. package/dist/modules/calls/call.routes.d.ts +2 -0
  150. package/dist/modules/calls/call.routes.js +103 -0
  151. package/dist/modules/calls/call.routes.js.map +1 -0
  152. package/dist/modules/campaigns/campaign.model.d.ts +45 -0
  153. package/dist/modules/campaigns/campaign.model.js +98 -0
  154. package/dist/modules/campaigns/campaign.model.js.map +1 -0
  155. package/dist/modules/campaigns/campaign.routes.d.ts +2 -0
  156. package/dist/modules/campaigns/campaign.routes.js +323 -0
  157. package/dist/modules/campaigns/campaign.routes.js.map +1 -0
  158. package/dist/modules/campaigns/campaign.service.d.ts +11 -0
  159. package/dist/modules/campaigns/campaign.service.js +86 -0
  160. package/dist/modules/campaigns/campaign.service.js.map +1 -0
  161. package/dist/modules/google-calendar/google-calendar.routes.d.ts +2 -0
  162. package/dist/modules/google-calendar/google-calendar.routes.js +32 -0
  163. package/dist/modules/google-calendar/google-calendar.routes.js.map +1 -0
  164. package/dist/modules/inbound-call/inbound-call-config.model.d.ts +20 -0
  165. package/dist/modules/inbound-call/inbound-call-config.model.js +68 -0
  166. package/dist/modules/inbound-call/inbound-call-config.model.js.map +1 -0
  167. package/dist/modules/inbound-call/inbound-call.routes.d.ts +2 -0
  168. package/dist/modules/inbound-call/inbound-call.routes.js +243 -0
  169. package/dist/modules/inbound-call/inbound-call.routes.js.map +1 -0
  170. package/dist/modules/leads/lead.model.d.ts +24 -0
  171. package/dist/modules/leads/lead.model.js +54 -0
  172. package/dist/modules/leads/lead.model.js.map +1 -0
  173. package/dist/modules/leads/lead.routes.d.ts +2 -0
  174. package/dist/modules/leads/lead.routes.js +201 -0
  175. package/dist/modules/leads/lead.routes.js.map +1 -0
  176. package/dist/modules/plans/plan.model.d.ts +25 -0
  177. package/dist/modules/plans/plan.model.js +59 -0
  178. package/dist/modules/plans/plan.model.js.map +1 -0
  179. package/dist/modules/surveys/survey.model.d.ts +30 -0
  180. package/dist/modules/surveys/survey.model.js +75 -0
  181. package/dist/modules/surveys/survey.model.js.map +1 -0
  182. package/dist/modules/surveys/survey.routes.d.ts +2 -0
  183. package/dist/modules/surveys/survey.routes.js +153 -0
  184. package/dist/modules/surveys/survey.routes.js.map +1 -0
  185. package/dist/modules/tenants/tenant.model.d.ts +86 -0
  186. package/dist/modules/tenants/tenant.model.js +127 -0
  187. package/dist/modules/tenants/tenant.model.js.map +1 -0
  188. package/dist/modules/tenants/tenant.routes.d.ts +2 -0
  189. package/dist/modules/tenants/tenant.routes.js +65 -0
  190. package/dist/modules/tenants/tenant.routes.js.map +1 -0
  191. package/dist/modules/users/user.routes.d.ts +2 -0
  192. package/dist/modules/users/user.routes.js +106 -0
  193. package/dist/modules/users/user.routes.js.map +1 -0
  194. package/dist/modules/webhooks/elevenlabs-tool.routes.d.ts +20 -0
  195. package/dist/modules/webhooks/elevenlabs-tool.routes.js +85 -0
  196. package/dist/modules/webhooks/elevenlabs-tool.routes.js.map +1 -0
  197. package/dist/modules/webhooks/elevenlabs-tool.service.d.ts +11 -0
  198. package/dist/modules/webhooks/elevenlabs-tool.service.js +360 -0
  199. package/dist/modules/webhooks/elevenlabs-tool.service.js.map +1 -0
  200. package/dist/modules/webhooks/elevenlabs.routes.d.ts +2 -0
  201. package/dist/modules/webhooks/elevenlabs.routes.js +34 -0
  202. package/dist/modules/webhooks/elevenlabs.routes.js.map +1 -0
  203. package/dist/modules/webhooks/elevenlabs.service.d.ts +6 -0
  204. package/dist/modules/webhooks/elevenlabs.service.js +512 -0
  205. package/dist/modules/webhooks/elevenlabs.service.js.map +1 -0
  206. package/dist/modules/webhooks/unipile.routes.d.ts +2 -0
  207. package/dist/modules/webhooks/unipile.routes.js +780 -0
  208. package/dist/modules/webhooks/unipile.routes.js.map +1 -0
  209. package/dist/modules/whatsapp/appointment.model.d.ts +27 -0
  210. package/dist/modules/whatsapp/appointment.model.js +58 -0
  211. package/dist/modules/whatsapp/appointment.model.js.map +1 -0
  212. package/dist/modules/whatsapp/operator-request.model.d.ts +29 -0
  213. package/dist/modules/whatsapp/operator-request.model.js +65 -0
  214. package/dist/modules/whatsapp/operator-request.model.js.map +1 -0
  215. package/dist/modules/whatsapp/stt-usage.model.d.ts +16 -0
  216. package/dist/modules/whatsapp/stt-usage.model.js +53 -0
  217. package/dist/modules/whatsapp/stt-usage.model.js.map +1 -0
  218. package/dist/modules/whatsapp/whatsapp-chat.model.d.ts +23 -0
  219. package/dist/modules/whatsapp/whatsapp-chat.model.js +55 -0
  220. package/dist/modules/whatsapp/whatsapp-chat.model.js.map +1 -0
  221. package/dist/modules/whatsapp/whatsapp-contact-profile.model.d.ts +23 -0
  222. package/dist/modules/whatsapp/whatsapp-contact-profile.model.js +54 -0
  223. package/dist/modules/whatsapp/whatsapp-contact-profile.model.js.map +1 -0
  224. package/dist/modules/whatsapp/whatsapp-message.model.d.ts +30 -0
  225. package/dist/modules/whatsapp/whatsapp-message.model.js +52 -0
  226. package/dist/modules/whatsapp/whatsapp-message.model.js.map +1 -0
  227. package/dist/modules/whatsapp/whatsapp-session.model.d.ts +33 -0
  228. package/dist/modules/whatsapp/whatsapp-session.model.js +65 -0
  229. package/dist/modules/whatsapp/whatsapp-session.model.js.map +1 -0
  230. package/dist/modules/whatsapp/whatsapp.routes.d.ts +2 -0
  231. package/dist/modules/whatsapp/whatsapp.routes.js +1237 -0
  232. package/dist/modules/whatsapp/whatsapp.routes.js.map +1 -0
  233. package/dist/plugins/cors.d.ts +3 -0
  234. package/dist/plugins/cors.js +11 -0
  235. package/dist/plugins/cors.js.map +1 -0
  236. package/dist/plugins/jwt.d.ts +3 -0
  237. package/dist/plugins/jwt.js +22 -0
  238. package/dist/plugins/jwt.js.map +1 -0
  239. package/dist/plugins/rawBody.d.ts +3 -0
  240. package/dist/plugins/rawBody.js +16 -0
  241. package/dist/plugins/rawBody.js.map +1 -0
  242. package/dist/plugins/rbac.d.ts +5 -0
  243. package/dist/plugins/rbac.js +29 -0
  244. package/dist/plugins/rbac.js.map +1 -0
  245. package/dist/routes/admin.routes.d.ts +2 -0
  246. package/dist/routes/admin.routes.js +169 -0
  247. package/dist/routes/admin.routes.js.map +1 -0
  248. package/dist/routes/health.routes.d.ts +2 -0
  249. package/dist/routes/health.routes.js +16 -0
  250. package/dist/routes/health.routes.js.map +1 -0
  251. package/dist/routes/webhook.routes.d.ts +2 -0
  252. package/dist/routes/webhook.routes.js +17 -0
  253. package/dist/routes/webhook.routes.js.map +1 -0
  254. package/dist/services/ai/base.ai.d.ts +19 -0
  255. package/dist/services/ai/base.ai.js +120 -0
  256. package/dist/services/ai/base.ai.js.map +1 -0
  257. package/dist/services/ai/gemini.service.d.ts +11 -0
  258. package/dist/services/ai/gemini.service.js +43 -0
  259. package/dist/services/ai/gemini.service.js.map +1 -0
  260. package/dist/services/ai/index.d.ts +2 -0
  261. package/dist/services/ai/index.js +26 -0
  262. package/dist/services/ai/index.js.map +1 -0
  263. package/dist/services/ai/openai.service.d.ts +11 -0
  264. package/dist/services/ai/openai.service.js +50 -0
  265. package/dist/services/ai/openai.service.js.map +1 -0
  266. package/dist/services/elevenlabs.service.d.ts +52 -0
  267. package/dist/services/elevenlabs.service.js +447 -0
  268. package/dist/services/elevenlabs.service.js.map +1 -0
  269. package/dist/services/google-calendar.service.d.ts +60 -0
  270. package/dist/services/google-calendar.service.js +494 -0
  271. package/dist/services/google-calendar.service.js.map +1 -0
  272. package/dist/services/inbound-call-schedule.service.d.ts +11 -0
  273. package/dist/services/inbound-call-schedule.service.js +162 -0
  274. package/dist/services/inbound-call-schedule.service.js.map +1 -0
  275. package/dist/services/netgsm.service.d.ts +41 -0
  276. package/dist/services/netgsm.service.js +89 -0
  277. package/dist/services/netgsm.service.js.map +1 -0
  278. package/dist/services/unipile.service.d.ts +41 -0
  279. package/dist/services/unipile.service.js +149 -0
  280. package/dist/services/unipile.service.js.map +1 -0
  281. package/dist/services/whatsapp-agent.service.d.ts +139 -0
  282. package/dist/services/whatsapp-agent.service.js +2055 -0
  283. package/dist/services/whatsapp-agent.service.js.map +1 -0
  284. package/dist/services/whatsapp.service.d.ts +26 -0
  285. package/dist/services/whatsapp.service.js +206 -0
  286. package/dist/services/whatsapp.service.js.map +1 -0
  287. package/dist/templates/index.d.ts +39 -0
  288. package/dist/templates/index.js +35 -0
  289. package/dist/templates/index.js.map +1 -0
  290. package/dist/templates/receptionist.d.ts +2 -0
  291. package/dist/templates/receptionist.js +39 -0
  292. package/dist/templates/receptionist.js.map +1 -0
  293. package/dist/templates/survey.d.ts +2 -0
  294. package/dist/templates/survey.js +41 -0
  295. package/dist/templates/survey.js.map +1 -0
  296. package/dist/types/index.d.ts +173 -0
  297. package/dist/types/index.js +3 -0
  298. package/dist/types/index.js.map +1 -0
  299. package/dist/utils/logger.d.ts +3 -0
  300. package/dist/utils/logger.js +105 -0
  301. package/dist/utils/logger.js.map +1 -0
  302. package/docker-compose.nestjs.yml +89 -0
  303. package/docker-compose.yml +78 -0
  304. package/docs/AI_AGENT_ENHANCEMENT_PLAN.md +164 -0
  305. package/docs/API.md +1193 -0
  306. package/docs/API_ENDPOINTS.md +344 -0
  307. package/docs/ARCHITECTURE.md +305 -0
  308. package/docs/AUTH_API.md +252 -0
  309. package/docs/BILLING_SMS_ALERTS.md +94 -0
  310. package/docs/CHAT_ASSIGNMENT_SYSTEM.md +118 -0
  311. package/docs/CLIENT_TOOLS_AND_FEATURES.md +337 -0
  312. package/docs/ELEVENLABS_WEBHOOK_TOOLS.md +644 -0
  313. package/docs/FRONTEND_CHECKLIST.md +227 -0
  314. package/docs/IMPLEMENTATION_STATUS.md +470 -0
  315. package/docs/MIGRATION_GUIDE.md +96 -0
  316. package/docs/MISSINGS_REPORT.md +507 -0
  317. package/docs/NESTJS_MIGRATION_REFERENCE.md +5136 -0
  318. package/docs/PROJECT_DESCRIPTION.md +1038 -0
  319. package/docs/SCALING.md +148 -0
  320. package/docs/SESSION_SUMMARY_2026_03_17.md +135 -0
  321. package/docs/WHATSAPP_AGENT.md +1086 -0
  322. package/docs/architecture/00-SYSTEM-OVERVIEW.md +318 -0
  323. package/docs/architecture/01-DATABASE-SCHEMA.md +2564 -0
  324. package/docs/architecture/02-AUTHENTICATION.md +1040 -0
  325. package/docs/architecture/03-MULTI-CLINIC.md +742 -0
  326. package/docs/architecture/04-WHATSAPP-AGENT.md +608 -0
  327. package/docs/architecture/05-OPERATOR-WORKFLOW.md +444 -0
  328. package/docs/architecture/06-BAILEYS-MICROSERVICE.md +616 -0
  329. package/docs/architecture/07-APPOINTMENTS.md +849 -0
  330. package/docs/architecture/08-VOICE-CALLS.md +470 -0
  331. package/docs/architecture/09-OUTBOUND-CAMPAIGNS.md +542 -0
  332. package/docs/architecture/10-CLIENT-TOOLS.md +665 -0
  333. package/docs/architecture/11-BILLING.md +458 -0
  334. package/docs/architecture/12-SECURITY.md +216 -0
  335. package/docs/architecture/13-LOGGING-AUDIT.md +549 -0
  336. package/docs/architecture/14-META-BUSINESS-API.md +454 -0
  337. package/docs/architecture/15-AI-MODULE.md +479 -0
  338. package/docs/architecture/16-BACKGROUND-JOBS.md +469 -0
  339. package/docs/architecture/17-REALTIME-LIVECHAT.md +447 -0
  340. package/docs/architecture/18-FILE-STORAGE.md +410 -0
  341. package/docs/architecture/19-PATIENTS.md +1034 -0
  342. package/docs/architecture/20-TREATMENTS-AND-PLANS.md +774 -0
  343. package/docs/architecture/21-BEFORE-AFTER-PHOTOS.md +519 -0
  344. package/docs/database.md +456 -0
  345. package/docs/ornek-randevu-onay.csv +3 -0
  346. package/ecosystem.config.js +16 -0
  347. package/elevenlabs-convai-api-reference.md +1171 -0
  348. package/frontend/.dockerignore +2 -0
  349. package/frontend/Dockerfile +24 -0
  350. package/frontend/README.md +75 -0
  351. package/frontend/components.json +25 -0
  352. package/frontend/eslint.config.js +23 -0
  353. package/frontend/index.html +13 -0
  354. package/frontend/nginx.conf +37 -0
  355. package/frontend/package-lock.json +8709 -0
  356. package/frontend/package.json +71 -0
  357. package/frontend/public/favicon.svg +1 -0
  358. package/frontend/public/icons.svg +24 -0
  359. package/frontend/src/App.tsx +125 -0
  360. package/frontend/src/components/error-boundary.tsx +50 -0
  361. package/frontend/src/components/shared/activity-timeline.tsx +66 -0
  362. package/frontend/src/components/shared/appointment-calendar.css +80 -0
  363. package/frontend/src/components/shared/appointment-calendar.tsx +245 -0
  364. package/frontend/src/components/shared/chat-bubble.tsx +72 -0
  365. package/frontend/src/components/shared/combobox.tsx +119 -0
  366. package/frontend/src/components/shared/confirm-dialog.tsx +57 -0
  367. package/frontend/src/components/shared/data-table-pagination.tsx +97 -0
  368. package/frontend/src/components/shared/data-table-toolbar.tsx +39 -0
  369. package/frontend/src/components/shared/empty-state.tsx +27 -0
  370. package/frontend/src/components/shared/file-upload.tsx +140 -0
  371. package/frontend/src/components/shared/index.ts +20 -0
  372. package/frontend/src/components/shared/language-switcher.tsx +29 -0
  373. package/frontend/src/components/shared/notification-dropdown.tsx +115 -0
  374. package/frontend/src/components/shared/page-header.tsx +23 -0
  375. package/frontend/src/components/shared/stat-card.tsx +37 -0
  376. package/frontend/src/components/shared/status-badge.tsx +70 -0
  377. package/frontend/src/components/shared/status-dot.tsx +43 -0
  378. package/frontend/src/components/ui/alert-dialog.tsx +187 -0
  379. package/frontend/src/components/ui/alert.tsx +76 -0
  380. package/frontend/src/components/ui/avatar.tsx +109 -0
  381. package/frontend/src/components/ui/badge.tsx +52 -0
  382. package/frontend/src/components/ui/breadcrumb.tsx +125 -0
  383. package/frontend/src/components/ui/button.tsx +60 -0
  384. package/frontend/src/components/ui/calendar.tsx +219 -0
  385. package/frontend/src/components/ui/card.tsx +103 -0
  386. package/frontend/src/components/ui/chart.tsx +371 -0
  387. package/frontend/src/components/ui/checkbox.tsx +29 -0
  388. package/frontend/src/components/ui/collapsible.tsx +19 -0
  389. package/frontend/src/components/ui/command.tsx +194 -0
  390. package/frontend/src/components/ui/dialog.tsx +158 -0
  391. package/frontend/src/components/ui/dropdown-menu.tsx +268 -0
  392. package/frontend/src/components/ui/input-group.tsx +156 -0
  393. package/frontend/src/components/ui/input.tsx +20 -0
  394. package/frontend/src/components/ui/label.tsx +20 -0
  395. package/frontend/src/components/ui/pagination.tsx +130 -0
  396. package/frontend/src/components/ui/popover.tsx +90 -0
  397. package/frontend/src/components/ui/progress.tsx +83 -0
  398. package/frontend/src/components/ui/radio-group.tsx +36 -0
  399. package/frontend/src/components/ui/scroll-area.tsx +54 -0
  400. package/frontend/src/components/ui/select.tsx +199 -0
  401. package/frontend/src/components/ui/separator.tsx +23 -0
  402. package/frontend/src/components/ui/sheet.tsx +138 -0
  403. package/frontend/src/components/ui/sidebar.tsx +723 -0
  404. package/frontend/src/components/ui/skeleton.tsx +13 -0
  405. package/frontend/src/components/ui/sonner.tsx +47 -0
  406. package/frontend/src/components/ui/switch.tsx +30 -0
  407. package/frontend/src/components/ui/table.tsx +114 -0
  408. package/frontend/src/components/ui/tabs.tsx +82 -0
  409. package/frontend/src/components/ui/textarea.tsx +18 -0
  410. package/frontend/src/components/ui/toggle-group.tsx +89 -0
  411. package/frontend/src/components/ui/toggle.tsx +43 -0
  412. package/frontend/src/components/ui/tooltip.tsx +64 -0
  413. package/frontend/src/hooks/use-mobile.ts +19 -0
  414. package/frontend/src/i18n/index.ts +20 -0
  415. package/frontend/src/i18n/locales/en.json +786 -0
  416. package/frontend/src/i18n/locales/tr.json +786 -0
  417. package/frontend/src/index.css +134 -0
  418. package/frontend/src/layouts/admin-layout.tsx +111 -0
  419. package/frontend/src/layouts/app-header.tsx +130 -0
  420. package/frontend/src/layouts/app-layout.tsx +19 -0
  421. package/frontend/src/layouts/app-sidebar.tsx +212 -0
  422. package/frontend/src/layouts/auth-guard.tsx +31 -0
  423. package/frontend/src/layouts/mobile-sidebar.tsx +120 -0
  424. package/frontend/src/lib/api.ts +68 -0
  425. package/frontend/src/lib/hooks/use-appointments.ts +224 -0
  426. package/frontend/src/lib/hooks/use-availability-blocks.ts +83 -0
  427. package/frontend/src/lib/hooks/use-chats.ts +236 -0
  428. package/frontend/src/lib/hooks/use-clinic-detail.ts +171 -0
  429. package/frontend/src/lib/hooks/use-clinics.ts +37 -0
  430. package/frontend/src/lib/hooks/use-doctor-calendar.ts +80 -0
  431. package/frontend/src/lib/hooks/use-examinations.ts +89 -0
  432. package/frontend/src/lib/hooks/use-medical-history.ts +52 -0
  433. package/frontend/src/lib/hooks/use-patients.ts +296 -0
  434. package/frontend/src/lib/hooks/use-photo-sets.ts +118 -0
  435. package/frontend/src/lib/hooks/use-treatment-plans.ts +152 -0
  436. package/frontend/src/lib/hooks/use-treatments.ts +125 -0
  437. package/frontend/src/lib/hooks/use-users.ts +172 -0
  438. package/frontend/src/lib/utils.ts +6 -0
  439. package/frontend/src/main.tsx +17 -0
  440. package/frontend/src/pages/admin/agents/detail.tsx +774 -0
  441. package/frontend/src/pages/admin/agents/import.tsx +280 -0
  442. package/frontend/src/pages/admin/agents/index.tsx +245 -0
  443. package/frontend/src/pages/admin/ai-playground.tsx +543 -0
  444. package/frontend/src/pages/appointments/create-appointment-dialog.tsx +390 -0
  445. package/frontend/src/pages/appointments/index.tsx +860 -0
  446. package/frontend/src/pages/audit/index.tsx +24 -0
  447. package/frontend/src/pages/auth/login.tsx +194 -0
  448. package/frontend/src/pages/billing/index.tsx +24 -0
  449. package/frontend/src/pages/calendar/index.tsx +704 -0
  450. package/frontend/src/pages/calendar-connections/index.tsx +295 -0
  451. package/frontend/src/pages/calls/index.tsx +24 -0
  452. package/frontend/src/pages/campaigns/index.tsx +24 -0
  453. package/frontend/src/pages/chats/index.tsx +981 -0
  454. package/frontend/src/pages/clinics/index.tsx +224 -0
  455. package/frontend/src/pages/clinics/settings.tsx +412 -0
  456. package/frontend/src/pages/components-showcase.tsx +773 -0
  457. package/frontend/src/pages/connections/index.tsx +328 -0
  458. package/frontend/src/pages/dashboard.tsx +50 -0
  459. package/frontend/src/pages/leads/index.tsx +24 -0
  460. package/frontend/src/pages/my-schedule/index.tsx +496 -0
  461. package/frontend/src/pages/patients/create-patient-dialog.tsx +358 -0
  462. package/frontend/src/pages/patients/detail.tsx +1195 -0
  463. package/frontend/src/pages/patients/edit-patient-dialog.tsx +387 -0
  464. package/frontend/src/pages/patients/examinations-tab.tsx +460 -0
  465. package/frontend/src/pages/patients/index.tsx +381 -0
  466. package/frontend/src/pages/patients/medical-history-dialog.tsx +207 -0
  467. package/frontend/src/pages/patients/photo-sets-tab.tsx +616 -0
  468. package/frontend/src/pages/patients/timeline-tab.tsx +164 -0
  469. package/frontend/src/pages/patients/treatment-plans-tab.tsx +598 -0
  470. package/frontend/src/pages/phone-numbers/detail.tsx +427 -0
  471. package/frontend/src/pages/phone-numbers/index.tsx +455 -0
  472. package/frontend/src/pages/platform/index.tsx +454 -0
  473. package/frontend/src/pages/settings/index.tsx +126 -0
  474. package/frontend/src/pages/treatments/index.tsx +487 -0
  475. package/frontend/src/pages/users/doctor-profile.tsx +672 -0
  476. package/frontend/src/pages/users/edit.tsx +329 -0
  477. package/frontend/src/pages/users/index.tsx +407 -0
  478. package/frontend/src/pages/validation/index.tsx +24 -0
  479. package/frontend/src/stores/auth.store.ts +108 -0
  480. package/frontend/src/stores/ui.store.ts +41 -0
  481. package/frontend/tsconfig.app.json +32 -0
  482. package/frontend/tsconfig.json +13 -0
  483. package/frontend/tsconfig.node.json +26 -0
  484. package/frontend/vite.config.ts +29 -0
  485. package/nestjs/.dockerignore +5 -0
  486. package/nestjs/.env.docker +64 -0
  487. package/nestjs/.prettierrc +4 -0
  488. package/nestjs/Dockerfile +36 -0
  489. package/nestjs/README.md +98 -0
  490. package/nestjs/eslint.config.mjs +35 -0
  491. package/nestjs/nest-cli.json +8 -0
  492. package/nestjs/package-lock.json +13390 -0
  493. package/nestjs/package.json +114 -0
  494. package/nestjs/prisma/migrations/20260409161536_add_message_metadata_fields/migration.sql +1746 -0
  495. package/nestjs/prisma/migrations/20260410140436_add_agent_ai_fields/migration.sql +36 -0
  496. package/nestjs/prisma/migrations/20260410175519_add_agent_clinic_assignments/migration.sql +21 -0
  497. package/nestjs/prisma/migrations/20260412094344_make_agent_tenant_optional/migration.sql +2 -0
  498. package/nestjs/prisma/migrations/20260412110008_add_admin_chat_sessions/migration.sql +47 -0
  499. package/nestjs/prisma/migrations/migration_lock.toml +3 -0
  500. package/nestjs/prisma/schema.prisma +1843 -0
  501. package/nestjs/prisma/seed.ts +375 -0
  502. package/nestjs/prisma.config.ts +14 -0
  503. package/nestjs/src/admin/admin.controller.ts +27 -0
  504. package/nestjs/src/admin/admin.module.ts +16 -0
  505. package/nestjs/src/admin/admin.service.ts +91 -0
  506. package/nestjs/src/admin/ai-chat-session.service.ts +454 -0
  507. package/nestjs/src/admin/ai-test.controller.ts +191 -0
  508. package/nestjs/src/admin/superadmin.controller.ts +106 -0
  509. package/nestjs/src/agents/agents.controller.ts +262 -0
  510. package/nestjs/src/agents/agents.module.ts +13 -0
  511. package/nestjs/src/agents/agents.service.ts +733 -0
  512. package/nestjs/src/agents/dto/create-agent.dto.ts +99 -0
  513. package/nestjs/src/agents/dto/index.ts +2 -0
  514. package/nestjs/src/agents/dto/update-agent.dto.ts +148 -0
  515. package/nestjs/src/app.module.ts +115 -0
  516. package/nestjs/src/appointment-validation/appointment-validation.controller.ts +194 -0
  517. package/nestjs/src/appointment-validation/appointment-validation.module.ts +16 -0
  518. package/nestjs/src/appointment-validation/appointment-validation.service.ts +450 -0
  519. package/nestjs/src/appointment-validation/dto/create-batch.dto.ts +105 -0
  520. package/nestjs/src/appointment-validation/dto/index.ts +1 -0
  521. package/nestjs/src/appointment-validation/processors/validation-dispatch.processor.ts +26 -0
  522. package/nestjs/src/appointment-validation/processors/validation-sync.processor.ts +23 -0
  523. package/nestjs/src/appointments/appointments.controller.ts +268 -0
  524. package/nestjs/src/appointments/appointments.module.ts +13 -0
  525. package/nestjs/src/appointments/appointments.service.ts +773 -0
  526. package/nestjs/src/appointments/dto/create-appointment.dto.ts +72 -0
  527. package/nestjs/src/appointments/dto/index.ts +4 -0
  528. package/nestjs/src/appointments/dto/query-appointments.dto.ts +60 -0
  529. package/nestjs/src/appointments/dto/update-appointment.dto.ts +43 -0
  530. package/nestjs/src/appointments/dto/update-status.dto.ts +18 -0
  531. package/nestjs/src/appointments/processors/reminder.processor.ts +243 -0
  532. package/nestjs/src/audit/audit.controller.ts +84 -0
  533. package/nestjs/src/audit/audit.decorator.ts +20 -0
  534. package/nestjs/src/audit/audit.interceptor.ts +67 -0
  535. package/nestjs/src/audit/audit.interfaces.ts +15 -0
  536. package/nestjs/src/audit/audit.module.ts +12 -0
  537. package/nestjs/src/audit/audit.service.ts +177 -0
  538. package/nestjs/src/auth/auth.controller.ts +116 -0
  539. package/nestjs/src/auth/auth.module.ts +25 -0
  540. package/nestjs/src/auth/auth.service.ts +612 -0
  541. package/nestjs/src/auth/dto/change-password.dto.ts +13 -0
  542. package/nestjs/src/auth/dto/forgot-password.dto.ts +8 -0
  543. package/nestjs/src/auth/dto/index.ts +6 -0
  544. package/nestjs/src/auth/dto/login.dto.ts +13 -0
  545. package/nestjs/src/auth/dto/refresh.dto.ts +8 -0
  546. package/nestjs/src/auth/dto/register.dto.ts +28 -0
  547. package/nestjs/src/auth/dto/reset-password.dto.ts +13 -0
  548. package/nestjs/src/auth/permissions.config.ts +85 -0
  549. package/nestjs/src/availability-blocks/availability-blocks.controller.ts +83 -0
  550. package/nestjs/src/availability-blocks/availability-blocks.module.ts +10 -0
  551. package/nestjs/src/availability-blocks/availability-blocks.service.ts +202 -0
  552. package/nestjs/src/billing/billing.controller.ts +104 -0
  553. package/nestjs/src/billing/billing.module.ts +12 -0
  554. package/nestjs/src/billing/billing.service.ts +398 -0
  555. package/nestjs/src/billing/dto/index.ts +2 -0
  556. package/nestjs/src/billing/dto/query-billing.dto.ts +29 -0
  557. package/nestjs/src/billing/dto/record-payment.dto.ts +60 -0
  558. package/nestjs/src/billing/processors/alerts.processor.ts +216 -0
  559. package/nestjs/src/billing/processors/snapshot.processor.ts +181 -0
  560. package/nestjs/src/calls/calls.controller.ts +92 -0
  561. package/nestjs/src/calls/calls.module.ts +10 -0
  562. package/nestjs/src/calls/calls.service.ts +359 -0
  563. package/nestjs/src/calls/dto/index.ts +1 -0
  564. package/nestjs/src/calls/dto/query-calls.dto.ts +46 -0
  565. package/nestjs/src/clinics/clinics.controller.ts +226 -0
  566. package/nestjs/src/clinics/clinics.module.ts +11 -0
  567. package/nestjs/src/clinics/clinics.service.ts +203 -0
  568. package/nestjs/src/clinics/dto/create-clinic.dto.ts +40 -0
  569. package/nestjs/src/clinics/dto/create-meta-connection.dto.ts +44 -0
  570. package/nestjs/src/clinics/dto/index.ts +4 -0
  571. package/nestjs/src/clinics/dto/update-clinic.dto.ts +133 -0
  572. package/nestjs/src/clinics/dto/update-meta-connection.dto.ts +22 -0
  573. package/nestjs/src/clinics/meta-connections.service.ts +210 -0
  574. package/nestjs/src/common/decorators/accessible-clinics.decorator.ts +9 -0
  575. package/nestjs/src/common/decorators/current-user.decorator.ts +10 -0
  576. package/nestjs/src/common/decorators/features.decorator.ts +7 -0
  577. package/nestjs/src/common/decorators/index.ts +4 -0
  578. package/nestjs/src/common/decorators/permissions.decorator.ts +13 -0
  579. package/nestjs/src/common/gateways/events.gateway.ts +157 -0
  580. package/nestjs/src/common/guards/clinic-access.guard.ts +40 -0
  581. package/nestjs/src/common/guards/features.guard.ts +38 -0
  582. package/nestjs/src/common/guards/index.ts +5 -0
  583. package/nestjs/src/common/guards/jwt-auth.guard.ts +54 -0
  584. package/nestjs/src/common/guards/permissions.guard.ts +50 -0
  585. package/nestjs/src/common/guards/superadmin.guard.ts +35 -0
  586. package/nestjs/src/common/interceptors/request-context.interceptor.ts +32 -0
  587. package/nestjs/src/common/interceptors/superadmin-tenant.interceptor.ts +42 -0
  588. package/nestjs/src/common/interfaces/jwt-payload.interface.ts +11 -0
  589. package/nestjs/src/config/config.module.ts +13 -0
  590. package/nestjs/src/database/database.module.ts +9 -0
  591. package/nestjs/src/database/prisma.service.ts +20 -0
  592. package/nestjs/src/database/tenant-context.ts +27 -0
  593. package/nestjs/src/google-calendar/google-calendar.controller.ts +259 -0
  594. package/nestjs/src/google-calendar/google-calendar.module.ts +10 -0
  595. package/nestjs/src/google-calendar/google-calendar.service.ts +811 -0
  596. package/nestjs/src/integrations/ai/ai.interface.ts +74 -0
  597. package/nestjs/src/integrations/ai/ai.module.ts +11 -0
  598. package/nestjs/src/integrations/ai/ai.service.ts +148 -0
  599. package/nestjs/src/integrations/ai/providers/anthropic.provider.ts +146 -0
  600. package/nestjs/src/integrations/ai/providers/openai.provider.ts +158 -0
  601. package/nestjs/src/integrations/elevenlabs/elevenlabs.module.ts +8 -0
  602. package/nestjs/src/integrations/elevenlabs/elevenlabs.service.ts +226 -0
  603. package/nestjs/src/integrations/encryption/encryption.module.ts +9 -0
  604. package/nestjs/src/integrations/encryption/encryption.service.ts +31 -0
  605. package/nestjs/src/integrations/image/image.module.ts +9 -0
  606. package/nestjs/src/integrations/image/image.service.ts +61 -0
  607. package/nestjs/src/integrations/meta-business/meta-business.module.ts +10 -0
  608. package/nestjs/src/integrations/meta-business/meta-instagram.service.ts +94 -0
  609. package/nestjs/src/integrations/meta-business/meta-webhook.service.ts +52 -0
  610. package/nestjs/src/integrations/meta-business/meta-whatsapp.service.ts +254 -0
  611. package/nestjs/src/integrations/minio/minio.module.ts +9 -0
  612. package/nestjs/src/integrations/minio/minio.service.ts +88 -0
  613. package/nestjs/src/integrations/netgsm/netgsm.module.ts +8 -0
  614. package/nestjs/src/integrations/netgsm/netgsm.service.ts +17 -0
  615. package/nestjs/src/integrations/tektippay/tektippay.module.ts +8 -0
  616. package/nestjs/src/integrations/tektippay/tektippay.service.ts +23 -0
  617. package/nestjs/src/leads/dto/index.ts +2 -0
  618. package/nestjs/src/leads/dto/query-leads.dto.ts +50 -0
  619. package/nestjs/src/leads/dto/update-lead.dto.ts +26 -0
  620. package/nestjs/src/leads/leads.controller.ts +184 -0
  621. package/nestjs/src/leads/leads.module.ts +10 -0
  622. package/nestjs/src/leads/leads.service.ts +375 -0
  623. package/nestjs/src/logging/logging.controller.ts +82 -0
  624. package/nestjs/src/logging/logging.module.ts +11 -0
  625. package/nestjs/src/logging/logging.service.ts +180 -0
  626. package/nestjs/src/main.ts +86 -0
  627. package/nestjs/src/outbound-campaigns/dto/create-campaign.dto.ts +47 -0
  628. package/nestjs/src/outbound-campaigns/dto/index.ts +3 -0
  629. package/nestjs/src/outbound-campaigns/dto/update-campaign.dto.ts +31 -0
  630. package/nestjs/src/outbound-campaigns/dto/upload-entries.dto.ts +35 -0
  631. package/nestjs/src/outbound-campaigns/outbound-campaigns.controller.ts +307 -0
  632. package/nestjs/src/outbound-campaigns/outbound-campaigns.module.ts +13 -0
  633. package/nestjs/src/outbound-campaigns/outbound-campaigns.service.ts +471 -0
  634. package/nestjs/src/outbound-campaigns/processors/campaign-dispatch.processor.ts +366 -0
  635. package/nestjs/src/patients/documents.service.ts +231 -0
  636. package/nestjs/src/patients/dto/create-examination.dto.ts +34 -0
  637. package/nestjs/src/patients/dto/create-note.dto.ts +14 -0
  638. package/nestjs/src/patients/dto/create-patient.dto.ts +86 -0
  639. package/nestjs/src/patients/dto/create-photo-set.dto.ts +32 -0
  640. package/nestjs/src/patients/dto/index.ts +10 -0
  641. package/nestjs/src/patients/dto/update-examination.dto.ts +29 -0
  642. package/nestjs/src/patients/dto/update-medical-history.dto.ts +47 -0
  643. package/nestjs/src/patients/dto/update-patient.dto.ts +87 -0
  644. package/nestjs/src/patients/dto/update-photo-set.dto.ts +28 -0
  645. package/nestjs/src/patients/dto/upload-document.dto.ts +31 -0
  646. package/nestjs/src/patients/dto/upload-photo.dto.ts +27 -0
  647. package/nestjs/src/patients/examinations.service.ts +271 -0
  648. package/nestjs/src/patients/medical-history.service.ts +149 -0
  649. package/nestjs/src/patients/notes.service.ts +172 -0
  650. package/nestjs/src/patients/patients.controller.ts +485 -0
  651. package/nestjs/src/patients/patients.module.ts +22 -0
  652. package/nestjs/src/patients/patients.service.ts +412 -0
  653. package/nestjs/src/patients/photo-sets.service.ts +389 -0
  654. package/nestjs/src/phone-numbers/dto/create-phone-number.dto.ts +57 -0
  655. package/nestjs/src/phone-numbers/dto/index.ts +3 -0
  656. package/nestjs/src/phone-numbers/dto/update-phone-number.dto.ts +38 -0
  657. package/nestjs/src/phone-numbers/dto/update-schedule.dto.ts +39 -0
  658. package/nestjs/src/phone-numbers/phone-numbers.controller.ts +125 -0
  659. package/nestjs/src/phone-numbers/phone-numbers.module.ts +10 -0
  660. package/nestjs/src/phone-numbers/phone-numbers.service.ts +209 -0
  661. package/nestjs/src/plans/dto/create-plan.dto.ts +70 -0
  662. package/nestjs/src/plans/dto/index.ts +2 -0
  663. package/nestjs/src/plans/dto/update-plan.dto.ts +80 -0
  664. package/nestjs/src/plans/plans.controller.ts +50 -0
  665. package/nestjs/src/plans/plans.module.ts +10 -0
  666. package/nestjs/src/plans/plans.service.ts +115 -0
  667. package/nestjs/src/platform/dto/create-tenant.dto.ts +36 -0
  668. package/nestjs/src/platform/dto/index.ts +2 -0
  669. package/nestjs/src/platform/dto/update-tenant-platform.dto.ts +44 -0
  670. package/nestjs/src/platform/platform.controller.ts +79 -0
  671. package/nestjs/src/platform/platform.module.ts +10 -0
  672. package/nestjs/src/platform/platform.service.ts +301 -0
  673. package/nestjs/src/queue/queue.module.ts +56 -0
  674. package/nestjs/src/redis/redis.module.ts +20 -0
  675. package/nestjs/src/tenants/dto/index.ts +1 -0
  676. package/nestjs/src/tenants/dto/update-tenant.dto.ts +15 -0
  677. package/nestjs/src/tenants/tenants.controller.ts +45 -0
  678. package/nestjs/src/tenants/tenants.module.ts +10 -0
  679. package/nestjs/src/tenants/tenants.service.ts +41 -0
  680. package/nestjs/src/tools/adapters/elevenlabs-http.adapter.ts +51 -0
  681. package/nestjs/src/tools/adapters/elevenlabs-ws.adapter.ts +59 -0
  682. package/nestjs/src/tools/handlers/calendar.tools.ts +441 -0
  683. package/nestjs/src/tools/handlers/notification.tools.ts +34 -0
  684. package/nestjs/src/tools/handlers/operator.tools.ts +303 -0
  685. package/nestjs/src/tools/handlers/patient.tools.ts +242 -0
  686. package/nestjs/src/tools/handlers/payment.tools.ts +43 -0
  687. package/nestjs/src/tools/handlers/validation.tools.ts +152 -0
  688. package/nestjs/src/tools/tool-registry.service.ts +221 -0
  689. package/nestjs/src/tools/tool.decorator.ts +16 -0
  690. package/nestjs/src/tools/tool.interfaces.ts +26 -0
  691. package/nestjs/src/tools/tools.module.ts +50 -0
  692. package/nestjs/src/treatments/dto/create-plan-item.dto.ts +27 -0
  693. package/nestjs/src/treatments/dto/create-treatment-plan.dto.ts +69 -0
  694. package/nestjs/src/treatments/dto/create-treatment.dto.ts +59 -0
  695. package/nestjs/src/treatments/dto/index.ts +6 -0
  696. package/nestjs/src/treatments/dto/update-plan-item.dto.ts +23 -0
  697. package/nestjs/src/treatments/dto/update-treatment-plan.dto.ts +22 -0
  698. package/nestjs/src/treatments/dto/update-treatment.dto.ts +70 -0
  699. package/nestjs/src/treatments/treatment-plans.service.ts +362 -0
  700. package/nestjs/src/treatments/treatments.controller.ts +265 -0
  701. package/nestjs/src/treatments/treatments.module.ts +14 -0
  702. package/nestjs/src/treatments/treatments.service.ts +165 -0
  703. package/nestjs/src/users/doctor-profiles.service.ts +202 -0
  704. package/nestjs/src/users/dto/index.ts +4 -0
  705. package/nestjs/src/users/dto/invite-user.dto.ts +52 -0
  706. package/nestjs/src/users/dto/update-clinic-assignments.dto.ts +9 -0
  707. package/nestjs/src/users/dto/update-doctor-profile.dto.ts +49 -0
  708. package/nestjs/src/users/dto/update-user.dto.ts +41 -0
  709. package/nestjs/src/users/users.controller.ts +142 -0
  710. package/nestjs/src/users/users.module.ts +11 -0
  711. package/nestjs/src/users/users.service.ts +250 -0
  712. package/nestjs/src/webhooks/elevenlabs-tool.controller.ts +66 -0
  713. package/nestjs/src/webhooks/elevenlabs-webhook.controller.ts +60 -0
  714. package/nestjs/src/webhooks/meta-webhook.controller.ts +178 -0
  715. package/nestjs/src/webhooks/processors/elevenlabs-webhook.processor.ts +28 -0
  716. package/nestjs/src/webhooks/webhooks.module.ts +17 -0
  717. package/nestjs/src/whatsapp/chat-context.service.ts +281 -0
  718. package/nestjs/src/whatsapp/dto/add-blacklist.dto.ts +13 -0
  719. package/nestjs/src/whatsapp/dto/index.ts +2 -0
  720. package/nestjs/src/whatsapp/dto/send-message.dto.ts +14 -0
  721. package/nestjs/src/whatsapp/listeners/meta-message.listener.ts +579 -0
  722. package/nestjs/src/whatsapp/meta-auth.controller.ts +268 -0
  723. package/nestjs/src/whatsapp/meta-instagram-auth.controller.ts +244 -0
  724. package/nestjs/src/whatsapp/operator.service.ts +692 -0
  725. package/nestjs/src/whatsapp/processors/cost-sweep.processor.ts +130 -0
  726. package/nestjs/src/whatsapp/processors/grace.processor.ts +191 -0
  727. package/nestjs/src/whatsapp/processors/message-buffer.processor.ts +138 -0
  728. package/nestjs/src/whatsapp/processors/message-cleanup.processor.ts +148 -0
  729. package/nestjs/src/whatsapp/processors/meta-token-refresh.processor.ts +114 -0
  730. package/nestjs/src/whatsapp/processors/operator-expiry.processor.ts +105 -0
  731. package/nestjs/src/whatsapp/processors/profile-update.processor.ts +234 -0
  732. package/nestjs/src/whatsapp/processors/session-cleanup.processor.ts +178 -0
  733. package/nestjs/src/whatsapp/processors/session-labels.processor.ts +248 -0
  734. package/nestjs/src/whatsapp/whatsapp-agent.service.ts +2506 -0
  735. package/nestjs/src/whatsapp/whatsapp-recovery.service.ts +117 -0
  736. package/nestjs/src/whatsapp/whatsapp.controller.ts +398 -0
  737. package/nestjs/src/whatsapp/whatsapp.module.ts +51 -0
  738. package/nestjs/src/whatsapp/whatsapp.service.ts +592 -0
  739. package/nestjs/test/app.e2e-spec.ts +25 -0
  740. package/nestjs/test/jest-e2e.json +9 -0
  741. package/nestjs/tsconfig.build.json +4 -0
  742. package/nestjs/tsconfig.json +25 -0
  743. package/nginx.example.conf +18 -0
  744. package/package.json +47 -0
  745. package/scripts/addRecipient.ts +48 -0
  746. package/scripts/listRecipients.ts +31 -0
  747. package/scripts/migrate-agent-ref.ts +86 -0
  748. package/scripts/migrate-sessions.ts +183 -0
  749. package/scripts/promote.ts +27 -0
  750. package/scripts/retrigger.ts +84 -0
  751. package/scripts/seed.ts +435 -0
  752. package/scripts/testSend.ts +63 -0
  753. package/src/app.ts +85 -0
  754. package/src/config/agentPrompts.json +19 -0
  755. package/src/config/database.ts +21 -0
  756. package/src/config/env.ts +94 -0
  757. package/src/config/index.ts +86 -0
  758. package/src/controllers/webhook.controller.ts +150 -0
  759. package/src/errors/index.ts +9 -0
  760. package/src/hooks/webhookValidator.ts +55 -0
  761. package/src/index.ts +68 -0
  762. package/src/migrations/001_session-architecture.ts +138 -0
  763. package/src/migrations/002_agent-ref.ts +65 -0
  764. package/src/migrations/003_shift-schedule.ts +55 -0
  765. package/src/migrations/004_invert-shift-to-clinic-hours.ts +30 -0
  766. package/src/migrations/005_composite-baileys-chat-ids.ts +60 -0
  767. package/src/migrations/migration.model.ts +27 -0
  768. package/src/migrations/migration.routes.ts +40 -0
  769. package/src/migrations/migration.runner.ts +112 -0
  770. package/src/models/ConversationClaim.ts +17 -0
  771. package/src/models/ConversationEvaluation.ts +91 -0
  772. package/src/models/Summary.ts +77 -0
  773. package/src/models/Transcription.ts +68 -0
  774. package/src/models/WhatsAppRecipient.ts +37 -0
  775. package/src/modules/admin/admin.routes.ts +2385 -0
  776. package/src/modules/admin/elevenlabs-test-chat.routes.ts +193 -0
  777. package/src/modules/admin/whatsapp-test-chat.routes.ts +244 -0
  778. package/src/modules/agents/agent.model.ts +93 -0
  779. package/src/modules/agents/agent.routes.ts +65 -0
  780. package/src/modules/appointment-validation/appointment-validation.model.ts +163 -0
  781. package/src/modules/appointment-validation/appointment-validation.routes.ts +275 -0
  782. package/src/modules/appointment-validation/appointment-validation.service.ts +1028 -0
  783. package/src/modules/auth/auth.model.ts +42 -0
  784. package/src/modules/auth/auth.routes.ts +199 -0
  785. package/src/modules/auth/auth.service.ts +210 -0
  786. package/src/modules/auth/refresh-token.model.ts +26 -0
  787. package/src/modules/billing/billing-alert.model.ts +28 -0
  788. package/src/modules/billing/billing-period-snapshot.model.ts +68 -0
  789. package/src/modules/billing/billing.model.ts +42 -0
  790. package/src/modules/billing/billing.routes.ts +67 -0
  791. package/src/modules/billing/billing.service.ts +562 -0
  792. package/src/modules/billing/payment.model.ts +42 -0
  793. package/src/modules/calls/call.model.ts +102 -0
  794. package/src/modules/calls/call.routes.ts +118 -0
  795. package/src/modules/campaigns/campaign.model.ts +111 -0
  796. package/src/modules/campaigns/campaign.routes.ts +402 -0
  797. package/src/modules/campaigns/campaign.service.ts +99 -0
  798. package/src/modules/google-calendar/google-calendar.routes.ts +31 -0
  799. package/src/modules/inbound-call/inbound-call-config.model.ts +49 -0
  800. package/src/modules/inbound-call/inbound-call.routes.ts +289 -0
  801. package/src/modules/leads/lead.model.ts +40 -0
  802. package/src/modules/leads/lead.routes.ts +246 -0
  803. package/src/modules/logs/log.model.ts +27 -0
  804. package/src/modules/logs/log.routes.ts +102 -0
  805. package/src/modules/plans/plan.model.ts +45 -0
  806. package/src/modules/surveys/survey.model.ts +70 -0
  807. package/src/modules/surveys/survey.routes.ts +187 -0
  808. package/src/modules/tenants/tenant.model.ts +181 -0
  809. package/src/modules/tenants/tenant.routes.ts +78 -0
  810. package/src/modules/users/user.routes.ts +126 -0
  811. package/src/modules/webhooks/elevenlabs-tool.routes.ts +94 -0
  812. package/src/modules/webhooks/elevenlabs-tool.service.ts +491 -0
  813. package/src/modules/webhooks/elevenlabs.routes.ts +34 -0
  814. package/src/modules/webhooks/elevenlabs.service.ts +565 -0
  815. package/src/modules/webhooks/unipile.routes.ts +917 -0
  816. package/src/modules/whatsapp/appointment.model.ts +47 -0
  817. package/src/modules/whatsapp/operator-request.model.ts +58 -0
  818. package/src/modules/whatsapp/stt-usage.model.ts +30 -0
  819. package/src/modules/whatsapp/whatsapp-chat.model.ts +39 -0
  820. package/src/modules/whatsapp/whatsapp-contact-profile.model.ts +41 -0
  821. package/src/modules/whatsapp/whatsapp-message.model.ts +41 -0
  822. package/src/modules/whatsapp/whatsapp-session.model.ts +60 -0
  823. package/src/modules/whatsapp/whatsapp.routes.ts +1435 -0
  824. package/src/plugins/cors.ts +7 -0
  825. package/src/plugins/jwt.ts +18 -0
  826. package/src/plugins/rawBody.ts +12 -0
  827. package/src/plugins/rbac.ts +24 -0
  828. package/src/routes/admin.routes.ts +208 -0
  829. package/src/routes/health.routes.ts +12 -0
  830. package/src/routes/webhook.routes.ts +12 -0
  831. package/src/services/ai/base.ai.ts +132 -0
  832. package/src/services/ai/gemini.service.ts +41 -0
  833. package/src/services/ai/index.ts +24 -0
  834. package/src/services/ai/openai.service.ts +48 -0
  835. package/src/services/elevenlabs.service.ts +532 -0
  836. package/src/services/google-calendar.service.ts +656 -0
  837. package/src/services/inbound-call-schedule.service.ts +174 -0
  838. package/src/services/netgsm.service.ts +128 -0
  839. package/src/services/unipile.service.ts +200 -0
  840. package/src/services/whatsapp-agent.service.ts +2479 -0
  841. package/src/services/whatsapp.service.ts +245 -0
  842. package/src/templates/index.ts +71 -0
  843. package/src/templates/receptionist.ts +44 -0
  844. package/src/templates/survey.ts +44 -0
  845. package/src/types/index.ts +218 -0
  846. package/src/utils/logger.ts +83 -0
  847. package/tsconfig.json +19 -0
  848. package/web/.dockerignore +4 -0
  849. package/web/Dockerfile +18 -0
  850. package/web/README.md +73 -0
  851. package/web/components.json +23 -0
  852. package/web/eslint.config.js +23 -0
  853. package/web/index.html +14 -0
  854. package/web/nginx.conf +18 -0
  855. package/web/package-lock.json +10292 -0
  856. package/web/package.json +48 -0
  857. package/web/public/favicon.ico +0 -0
  858. package/web/public/vite.svg +1 -0
  859. package/web/src/App.tsx +191 -0
  860. package/web/src/assets/react.svg +1 -0
  861. package/web/src/components/Layout.tsx +261 -0
  862. package/web/src/components/LeadConversation.tsx +251 -0
  863. package/web/src/components/ProtectedRoute.tsx +20 -0
  864. package/web/src/components/conversation-review/CardAudioPlayer.tsx +200 -0
  865. package/web/src/components/conversation-review/CardEvaluation.tsx +351 -0
  866. package/web/src/components/conversation-review/CardMetadata.tsx +44 -0
  867. package/web/src/components/conversation-review/ConversationCard.tsx +95 -0
  868. package/web/src/components/conversation-review/ReviewContainer.tsx +120 -0
  869. package/web/src/components/conversation-review/ReviewFilters.tsx +88 -0
  870. package/web/src/components/empty-state.tsx +15 -0
  871. package/web/src/components/page-header.tsx +19 -0
  872. package/web/src/components/page-loader.tsx +32 -0
  873. package/web/src/components/pagination.tsx +38 -0
  874. package/web/src/components/stat-card.tsx +27 -0
  875. package/web/src/components/status-badge.tsx +125 -0
  876. package/web/src/components/ui/alert.tsx +66 -0
  877. package/web/src/components/ui/badge.tsx +48 -0
  878. package/web/src/components/ui/button.tsx +64 -0
  879. package/web/src/components/ui/card.tsx +92 -0
  880. package/web/src/components/ui/checkbox.tsx +32 -0
  881. package/web/src/components/ui/dialog.tsx +158 -0
  882. package/web/src/components/ui/dropdown-menu.tsx +255 -0
  883. package/web/src/components/ui/input.tsx +21 -0
  884. package/web/src/components/ui/label.tsx +22 -0
  885. package/web/src/components/ui/progress.tsx +29 -0
  886. package/web/src/components/ui/select.tsx +188 -0
  887. package/web/src/components/ui/separator.tsx +28 -0
  888. package/web/src/components/ui/sheet.tsx +123 -0
  889. package/web/src/components/ui/skeleton.tsx +13 -0
  890. package/web/src/components/ui/sonner.tsx +35 -0
  891. package/web/src/components/ui/table.tsx +116 -0
  892. package/web/src/components/ui/tabs.tsx +89 -0
  893. package/web/src/components/ui/textarea.tsx +18 -0
  894. package/web/src/components/ui/tooltip.tsx +57 -0
  895. package/web/src/components/whatsapp/ChatDetail.tsx +417 -0
  896. package/web/src/components/whatsapp/ChatHeader.tsx +78 -0
  897. package/web/src/components/whatsapp/ChatList.tsx +107 -0
  898. package/web/src/components/whatsapp/ChatListItem.tsx +60 -0
  899. package/web/src/components/whatsapp/MessageBubble.tsx +46 -0
  900. package/web/src/components/whatsapp/MessageInput.tsx +63 -0
  901. package/web/src/components/whatsapp/MessageStream.tsx +135 -0
  902. package/web/src/components/whatsapp/SessionDivider.tsx +65 -0
  903. package/web/src/components/whatsapp/SessionHistory.tsx +119 -0
  904. package/web/src/components/whatsapp/settings/AiSettingsTab.tsx +268 -0
  905. package/web/src/components/whatsapp/settings/CalendarTab.tsx +339 -0
  906. package/web/src/components/whatsapp/settings/ConnectionTab.tsx +236 -0
  907. package/web/src/components/whatsapp/settings/NotificationsTab.tsx +109 -0
  908. package/web/src/components/whatsapp/settings/OperatorTab.tsx +303 -0
  909. package/web/src/contexts/AuthContext.tsx +103 -0
  910. package/web/src/index.css +130 -0
  911. package/web/src/lib/api.ts +92 -0
  912. package/web/src/lib/utils.ts +6 -0
  913. package/web/src/main.tsx +10 -0
  914. package/web/src/pages/AppointmentDetail.tsx +206 -0
  915. package/web/src/pages/AppointmentValidation.tsx +157 -0
  916. package/web/src/pages/AppointmentValidationDetail.tsx +617 -0
  917. package/web/src/pages/AppointmentValidationNew.tsx +1005 -0
  918. package/web/src/pages/Appointments.tsx +283 -0
  919. package/web/src/pages/Billing.tsx +126 -0
  920. package/web/src/pages/CallDetail.tsx +293 -0
  921. package/web/src/pages/CallSettings.tsx +313 -0
  922. package/web/src/pages/Calls.tsx +188 -0
  923. package/web/src/pages/CampaignDetail.tsx +216 -0
  924. package/web/src/pages/CampaignNew.tsx +277 -0
  925. package/web/src/pages/CampaignResults.tsx +185 -0
  926. package/web/src/pages/Campaigns.tsx +171 -0
  927. package/web/src/pages/Dashboard.tsx +336 -0
  928. package/web/src/pages/InboundSchedule.tsx +246 -0
  929. package/web/src/pages/LeadDetail.tsx +183 -0
  930. package/web/src/pages/Leads.tsx +258 -0
  931. package/web/src/pages/Login.tsx +99 -0
  932. package/web/src/pages/Register.tsx +129 -0
  933. package/web/src/pages/Settings.tsx +133 -0
  934. package/web/src/pages/SurveyDetail.tsx +232 -0
  935. package/web/src/pages/SurveyPreview.tsx +179 -0
  936. package/web/src/pages/Surveys.tsx +207 -0
  937. package/web/src/pages/Users.tsx +199 -0
  938. package/web/src/pages/WhatsApp.tsx +147 -0
  939. package/web/src/pages/WhatsAppSettings.tsx +215 -0
  940. package/web/src/pages/admin/AdminBaileysMessageLog.tsx +331 -0
  941. package/web/src/pages/admin/AdminBaileysRawMessages.tsx +318 -0
  942. package/web/src/pages/admin/AdminConversationReview.tsx +116 -0
  943. package/web/src/pages/admin/AdminCredits.tsx +467 -0
  944. package/web/src/pages/admin/AdminElevenLabsAgentDetail.tsx +332 -0
  945. package/web/src/pages/admin/AdminElevenLabsAgents.tsx +164 -0
  946. package/web/src/pages/admin/AdminElevenLabsBatchCallDetail.tsx +214 -0
  947. package/web/src/pages/admin/AdminElevenLabsBatchCalls.tsx +293 -0
  948. package/web/src/pages/admin/AdminElevenLabsConversationDetail.tsx +230 -0
  949. package/web/src/pages/admin/AdminElevenLabsConversations.tsx +228 -0
  950. package/web/src/pages/admin/AdminElevenLabsInboundCalls.tsx +293 -0
  951. package/web/src/pages/admin/AdminElevenLabsPhoneNumbers.tsx +506 -0
  952. package/web/src/pages/admin/AdminElevenLabsTestChat.tsx +258 -0
  953. package/web/src/pages/admin/AdminElevenLabsWebhooks.tsx +289 -0
  954. package/web/src/pages/admin/AdminEvaluationDashboard.tsx +520 -0
  955. package/web/src/pages/admin/AdminLeads.tsx +339 -0
  956. package/web/src/pages/admin/AdminLogs.tsx +283 -0
  957. package/web/src/pages/admin/AdminMigrations.tsx +247 -0
  958. package/web/src/pages/admin/AdminPlans.tsx +313 -0
  959. package/web/src/pages/admin/AdminSystem.tsx +391 -0
  960. package/web/src/pages/admin/AdminTenantBillableItems.tsx +464 -0
  961. package/web/src/pages/admin/AdminTenantDetail.tsx +1317 -0
  962. package/web/src/pages/admin/AdminTenants.tsx +274 -0
  963. package/web/src/pages/admin/AdminWhatsApp.tsx +618 -0
  964. package/web/src/pages/admin/AdminWhatsAppTestChat.tsx +328 -0
  965. package/web/src/types/index.ts +242 -0
  966. package/web/tsconfig.app.json +32 -0
  967. package/web/tsconfig.json +13 -0
  968. package/web/tsconfig.node.json +26 -0
  969. package/web/vite.config.ts +23 -0
@@ -0,0 +1,1040 @@
1
+ # 02 — Authentication & Authorization
2
+
3
+ > Covers opaque token + Redis-backed JWT system, RBAC with 6 roles and 40+ permissions,
4
+ > clinic-scoped access, NestJS guards/decorators, password reset, and rate limiting.
5
+ > Last updated: 2026-04-02
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ 1. [Authentication Overview](#1-authentication-overview)
12
+ 2. [Opaque Token + Redis JWT System](#2-opaque-token--redis-jwt-system)
13
+ 3. [JWT Payload Structure (Server-Side)](#3-jwt-payload-structure-server-side)
14
+ 4. [Login Flow](#4-login-flow)
15
+ 5. [Register Flow](#5-register-flow)
16
+ 6. [Token Refresh Flow](#6-token-refresh-flow)
17
+ 7. [Logout Flow](#7-logout-flow)
18
+ 8. [Password Reset Flow](#8-password-reset-flow)
19
+ 9. [Rate Limiting](#9-rate-limiting)
20
+ 10. [Authorization — RBAC](#10-authorization--rbac)
21
+ 11. [Permissions Catalog](#11-permissions-catalog)
22
+ 12. [Role-to-Permission Mapping](#12-role-to-permission-mapping)
23
+ 13. [Clinic-Scoped Access](#13-clinic-scoped-access)
24
+ 14. [NestJS Guards and Decorators](#14-nestjs-guards-and-decorators)
25
+ 15. [Sequence Diagrams](#15-sequence-diagrams)
26
+
27
+ ---
28
+
29
+ ## 1. Authentication Overview
30
+
31
+ | Aspect | Implementation |
32
+ |--------|---------------|
33
+ | Framework | Passport.js with custom strategy (`@nestjs/passport` + `@nestjs/jwt`) |
34
+ | Password hashing | bcrypt (12 rounds) |
35
+ | Token system | **Opaque tokens** sent to client. Real JWTs stored server-side in Redis. Client never sees the JWT |
36
+ | Access session | Opaque token → Redis lookup → JWT payload. TTL 15 minutes |
37
+ | Refresh session | Opaque token → Redis + PostgreSQL. TTL 30 days |
38
+ | Token revocation | Instant — delete the Redis key. No blacklist needed |
39
+ | Rate limiting | `@nestjs/throttler` with Redis storage |
40
+ | Security headers | `@fastify/helmet` (CSP, HSTS, X-Frame-Options) |
41
+ | CORS | Strict origin whitelist — only `FRONTEND_URL` allowed |
42
+
43
+ ### Environment Variables
44
+
45
+ | Variable | Purpose |
46
+ |----------|---------|
47
+ | `JWT_SECRET` | JWT signing secret (server-side only — JWTs never leave the server) |
48
+ | `JWT_REFRESH_SECRET` | Refresh token signing secret |
49
+ | `JWT_ACCESS_EXPIRY` | `15m` |
50
+ | `JWT_REFRESH_EXPIRY` | `30d` |
51
+
52
+ ---
53
+
54
+ ## 2. Opaque Token + Redis JWT System
55
+
56
+ The client **never receives a JWT**. Instead, it receives an opaque session token (crypto-random string). The real JWT lives in Redis, server-side only.
57
+
58
+ ### Why This Design
59
+
60
+ | Concern | Traditional JWT | Our Approach |
61
+ |---------|----------------|-------------|
62
+ | Token theft | JWT in localStorage/cookie can be decoded, replayed | Opaque string is meaningless without Redis access |
63
+ | Revocation | Requires a blacklist (check every request) | Delete the Redis key — instant, no blacklist |
64
+ | Payload exposure | Client can decode JWT and see permissions, features, etc. | Client sees nothing — opaque token is a random string |
65
+ | Session control | Wait for JWT expiry or maintain blacklist | Full control — delete key = session dead |
66
+
67
+ ### Access Session
68
+ - **Client receives:** Opaque token (32-byte crypto-random hex string)
69
+ - **Redis stores:** `session:{opaqueToken}` → JWT string, TTL = 15 minutes
70
+ - **On each request:** Client sends opaque token in `Authorization: Bearer {opaqueToken}` → Server does `Redis GET session:{opaqueToken}` → gets JWT → verifies signature → extracts payload → proceeds
71
+ - **On revocation:** `Redis DEL session:{opaqueToken}` → next request returns 401
72
+
73
+ ### Refresh Session
74
+ - **Client receives:** Opaque refresh token (32-byte crypto-random hex string)
75
+ - **Redis stores:** `refresh:{opaqueRefreshToken}` → user ID, TTL = 30 days
76
+ - **PostgreSQL stores:** `refresh_tokens` table for persistence (Redis may restart)
77
+ - **On refresh:** Client sends opaque refresh token → Server looks up user ID → re-resolves permissions, features, clinics from DB → generates new JWT → stores in Redis with new opaque token → returns new opaque token pair
78
+ - **Rotation:** Old opaque tokens are deleted on refresh. One refresh token = one use.
79
+
80
+ ### Redis Keys
81
+
82
+ | Key Pattern | TTL | Purpose |
83
+ |-------------|-----|---------|
84
+ | `session:{opaqueToken}` | 15m | Maps opaque access token to JWT string |
85
+ | `refresh:{opaqueRefreshToken}` | 30d | Maps opaque refresh token to user ID |
86
+
87
+ ### Token Generation
88
+
89
+ ```typescript
90
+ import { randomBytes } from 'crypto';
91
+
92
+ function generateOpaqueToken(): string {
93
+ return randomBytes(32).toString('hex'); // 64-char hex string
94
+ }
95
+ ```
96
+
97
+ ### Request Authentication Flow
98
+
99
+ ```
100
+ Client request
101
+ → Authorization: Bearer {opaqueToken}
102
+ → Server: Redis GET session:{opaqueToken}
103
+ → If not found: 401 Unauthorized
104
+ → If found: JWT string
105
+ → Verify JWT signature (jwt.verify with JWT_SECRET)
106
+ → Extract payload (userId, tenantId, role, permissions, features, clinicIds)
107
+ → Proceed to guards (PermissionsGuard, FeaturesGuard, ClinicAccessGuard)
108
+ ```
109
+
110
+ ### Prisma Model (unchanged)
111
+
112
+ ```prisma
113
+ model RefreshToken {
114
+ token_id String @id @default(uuid())
115
+ token_user_id String
116
+ token_value String @unique -- stores the opaque refresh token (not JWT)
117
+ token_expires_at DateTime
118
+ token_created_at DateTime @default(now())
119
+
120
+ user User @relation(fields: [token_user_id], references: [user_id], onDelete: Cascade)
121
+
122
+ @@index([token_user_id])
123
+ @@index([token_expires_at])
124
+ @@map("refresh_tokens")
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## 3. JWT Payload Structure (Server-Side)
131
+
132
+ The JWT is stored in Redis, never sent to the client. It carries everything needed for authorization:
133
+
134
+ ```typescript
135
+ interface JwtPayload {
136
+ sub: string; // User ID (UUID)
137
+ tenantId: string; // Tenant ID (UUID) — null for platform roles
138
+ role: UserRole; // 'superadmin' | 'sales' | 'owner' | 'admin' | 'doctor' | 'receptionist'
139
+ permissions: string[]; // Resolved permission list from role (e.g., ['patients:read', 'chats:send'])
140
+ features: string[]; // Tenant's enabled feature flags (e.g., ['whatsapp_agent', 'google_calendar'])
141
+ clinicIds: string[]; // Assigned clinic IDs — empty for owner (all clinics)
142
+ allClinics: boolean; // true if user sees all clinics (owner/superadmin)
143
+ iat: number; // Issued at
144
+ exp: number; // Expires at
145
+ }
146
+ ```
147
+
148
+ **Design rationale:** The JWT provides a structured, signed payload with all authorization data. Permissions, features, and clinic assignments are embedded so that guards can check access without DB lookups on every request. The only I/O per request is a single Redis GET to resolve the opaque token to the JWT.
149
+
150
+ If a user's role or clinic assignment changes mid-session, the change takes effect after the current access token expires (max 15 minutes) or the user re-authenticates. On refresh, the payload is rebuilt from the database.
151
+
152
+ ---
153
+
154
+ ## 4. Login Flow
155
+
156
+ ### Endpoint
157
+
158
+ | Method | Path | Auth | Description |
159
+ |--------|------|------|-------------|
160
+ | POST | `/auth/login` | None | Login with email + password |
161
+
162
+ ### Flow
163
+
164
+ 1. Client sends `{ email, password }` to `POST /auth/login`.
165
+ 2. Server looks up user by email, verifies bcrypt hash.
166
+ 3. Server checks `user.is_active` and `tenant.is_active`.
167
+ 4. Server resolves permissions from role, fetches tenant features and clinic assignments.
168
+ 5. Server generates JWT (signed, 15m expiry) — **never sent to client**.
169
+ 6. Server generates opaque access token (`randomBytes(32).toString('hex')`).
170
+ 7. Server stores in Redis: `session:{opaqueAccessToken}` = JWT, TTL = 15m.
171
+ 8. Server generates opaque refresh token, stores in Redis (`refresh:{token}`, TTL 30d) + PostgreSQL (`refresh_tokens` table).
172
+ 9. Server returns `{ user, tenant, access_token: opaqueAccessToken, refresh_token: opaqueRefreshToken }`.
173
+
174
+ The client stores the opaque tokens. It never sees or decodes the JWT.
175
+
176
+ ---
177
+
178
+ ## 5. Register Flow
179
+
180
+ | Method | Path | Auth | Description |
181
+ |--------|------|------|-------------|
182
+ | POST | `/auth/register` | None | Register a new tenant + owner user |
183
+
184
+ 1. Client sends `{ firstName, lastName, email, password, tenantName }`.
185
+ 2. Server creates a new Tenant record (thin — just name, features, kvkk).
186
+ 3. Server creates a default Clinic for the tenant (name = tenantName, `is_default = true`, all config defaults: language "tr", AI provider "openai", grace period 180s, etc.).
187
+ 4. Server creates a User with `role = owner`, linked to the new tenant.
188
+ 5. Server generates JWT → stores in Redis with opaque token → stores refresh in Redis + PostgreSQL.
189
+ 6. Server returns `{ user, tenant, access_token: opaqueAccessToken, refresh_token: opaqueRefreshToken }`.
190
+
191
+ **Rate limited:** 3 requests per hour per IP (abuse prevention).
192
+
193
+ **Note:** The clinic gets all operational config with sensible defaults on creation. The tenant holds no config — see [01-DATABASE-SCHEMA clinics](#2-clinics).
194
+
195
+ ---
196
+
197
+ ## 6. Token Refresh Flow
198
+
199
+ | Method | Path | Auth | Description |
200
+ |--------|------|------|-------------|
201
+ | POST | `/auth/refresh` | None (opaque refresh_token in body) | Rotate tokens |
202
+
203
+ 1. Client sends `{ refresh_token: opaqueRefreshToken }`.
204
+ 2. Server does `Redis GET refresh:{opaqueRefreshToken}`. If not found, check PostgreSQL.
205
+ 3. Server checks expiry.
206
+ 4. Server deletes old opaque tokens: `Redis DEL refresh:{old}`, `Redis DEL session:{oldAccess}`, delete from PostgreSQL.
207
+ 5. Server re-resolves the user's current role, permissions, features, and clinic assignments from DB.
208
+ 6. Server generates new JWT → new opaque access token → stores in Redis (`session:{new}`, 15m TTL).
209
+ 7. Server generates new opaque refresh token → stores in Redis (`refresh:{new}`, 30d TTL) + PostgreSQL.
210
+ 8. Server returns `{ access_token: newOpaqueAccessToken, refresh_token: newOpaqueRefreshToken }`.
211
+
212
+ **Key point:** The refresh flow is the moment when role/clinic changes propagate into the JWT payload.
213
+ If an admin changes a user's role at T=0 and the user refreshes at T=5m, the new JWT (stored in Redis) will
214
+ carry the updated permissions.
215
+
216
+ ---
217
+
218
+ ## 7. Logout Flow
219
+
220
+ | Method | Path | Auth | Description |
221
+ |--------|------|------|-------------|
222
+ | POST | `/auth/logout` | Opaque token | Revoke session |
223
+
224
+ 1. Client sends logout request with `Authorization: Bearer {opaqueAccessToken}`.
225
+ 2. Server resolves user from the opaque token (Redis GET).
226
+ 3. Server deletes `session:{opaqueAccessToken}` from Redis.
227
+ 4. Server deletes the user's refresh token from PostgreSQL + Redis.
228
+ 5. Any subsequent request with the old opaque token returns 401 (Redis key gone).
229
+
230
+ **No blacklist needed.** Deleting the Redis key = instant revocation.
231
+
232
+ **On password change:** All Redis session keys for the user are deleted + all refresh tokens deleted from Redis + PostgreSQL. Forces re-authentication on all devices.
233
+
234
+ ---
235
+
236
+ ## 8. Password Reset Flow
237
+
238
+ | Method | Path | Auth | Description |
239
+ |--------|------|------|-------------|
240
+ | POST | `/auth/forgot-password` | None | Request password reset link |
241
+ | POST | `/auth/reset-password` | None (token in body) | Set new password |
242
+
243
+ ### Flow
244
+
245
+ 1. User submits email to `POST /auth/forgot-password`.
246
+ 2. Server generates a random token (32 bytes hex).
247
+ 3. Server stores a **bcrypt hash** of the token in `password_reset_tokens` with a 1-hour TTL.
248
+ 4. Server sends the raw token to the user via email (or SMS via NetGSM as fallback).
249
+ 5. User clicks the link, which opens the frontend with the token in the URL.
250
+ 6. Frontend sends `{ token, new_password }` to `POST /auth/reset-password`.
251
+ 7. Server finds the matching record by hashing the submitted token and comparing.
252
+ 8. Server verifies the token has not expired (1h TTL) and has not been used.
253
+ 9. Server updates the user's password (bcrypt hash).
254
+ 10. Server **revokes all refresh tokens** for the user (forces re-login on all devices).
255
+ 11. Server marks the reset token as used and deletes it.
256
+
257
+ ### Prisma Model
258
+
259
+ ```prisma
260
+ model PasswordResetToken {
261
+ id String @id @default(uuid())
262
+ userId String
263
+ tokenHash String @unique // bcrypt hash of token
264
+ expiresAt DateTime // 1 hour from creation
265
+ usedAt DateTime?
266
+ createdAt DateTime @default(now())
267
+
268
+ user User @relation(fields: [userId], references: [user_id], onDelete: Cascade)
269
+
270
+ @@index([expiresAt])
271
+ @@map("password_reset_tokens")
272
+ }
273
+ ```
274
+
275
+ **Cleanup:** BullMQ repeatable job `auth:cleanup-reset-tokens` deletes expired tokens periodically.
276
+
277
+ **Rate limited:** 3 requests per 15 minutes per email.
278
+
279
+ ---
280
+
281
+ ---
282
+
283
+ ## 10. Rate Limiting
284
+
285
+ Implemented with `@nestjs/throttler` backed by Redis storage.
286
+
287
+ ### Global Configuration
288
+
289
+ ```typescript
290
+ @Module({
291
+ imports: [
292
+ ThrottlerModule.forRoot({
293
+ throttlers: [{ ttl: 60_000, limit: 200 }], // Global: 200 req/min per user
294
+ storage: new ThrottlerStorageRedisService(redis),
295
+ }),
296
+ ],
297
+ })
298
+ ```
299
+
300
+ ### Per-Route Limits
301
+
302
+ | Route | Limit | Window | Keyed By | Purpose |
303
+ |-------|-------|--------|----------|---------|
304
+ | `POST /auth/login` | 5 requests | 1 min | IP | Brute force prevention |
305
+ | `POST /auth/forgot-password` | 3 requests | 15 min | Email | Spam prevention |
306
+ | `POST /auth/register` | 3 requests | 1 hour | IP | Abuse prevention |
307
+ | `/webhooks/*` | 100 requests | 1 min | IP | Webhook flooding |
308
+ | Global API | 200 requests | 1 min | User (JWT sub) | General abuse |
309
+
310
+ ### Redis Key Pattern
311
+
312
+ ```
313
+ throttle:{scope}:{identifier}
314
+ ```
315
+
316
+ TTL equals the window duration. Example: `throttle:auth-login:192.168.1.1` expires after 60s.
317
+
318
+ ### Per-Route Override
319
+
320
+ ```typescript
321
+ @Post('login')
322
+ @Throttle({ default: { ttl: 60_000, limit: 5 } })
323
+ async login(@Body() dto: LoginDto) { ... }
324
+ ```
325
+
326
+ ---
327
+
328
+ ## 11. Authorization -- RBAC
329
+
330
+ Roles are **permission bundles**. Each role maps to a fixed set of atomic permissions. Users have exactly one role. Clinic assignments then scope *where* those permissions apply.
331
+
332
+ ### 11.1 Roles
333
+
334
+ **Platform Roles** (no tenantId -- system-wide access):
335
+
336
+ | Role | Scope | Who | Access |
337
+ |------|-------|-----|--------|
338
+ | `superadmin` | System-wide | Platform operator | All endpoints, all tenants, bypasses all guards |
339
+ | `sales` | System-wide | Platform sales team | Create tenants, assign plans, view tenant list -- no access to tenant internal data |
340
+
341
+ **Tenant Roles** (scoped to a tenant + assigned clinics):
342
+
343
+ | Role | Scope | Who | Access |
344
+ |------|-------|-----|--------|
345
+ | `owner` | All clinics | Clinic chain owner | Full tenant management + all clinics, user management, billing, settings |
346
+ | `admin` | Assigned clinics | Branch administrator | Full access to assigned clinics, invite users for their clinics |
347
+ | `doctor` | Assigned clinics | Doctor/specialist | Patient history, operator requests, appointments, call transcripts |
348
+ | `receptionist` | Assigned clinics | Front desk staff | Live chat, appointments, calls, leads -- the daily operator role |
349
+
350
+ ### 11.2 Platform vs Tenant Roles
351
+
352
+ - `superadmin` and `sales` have `tenantId = null` on the User record. They operate at the platform level.
353
+ - `sales` can only access `/platform/*` endpoints. Attempting to access tenant data (e.g., `/chats`) throws `403 Forbidden`.
354
+ - `owner`, `admin`, `doctor`, `receptionist` always have a `tenantId`. Every query they make is automatically tenant-scoped via Prisma middleware.
355
+ - `owner` automatically has access to **all clinics** within their tenant. No explicit clinic assignments needed.
356
+ - `admin`, `doctor`, `receptionist` must be explicitly assigned to clinics via `UserClinicAssignment`.
357
+
358
+ ---
359
+
360
+ ## 12. Permissions Catalog
361
+
362
+ Format: `resource:action`. There are 37 atomic permissions across 16 resource categories.
363
+
364
+ ### Platform Permissions (superadmin + sales)
365
+
366
+ | Permission | Description |
367
+ |------------|-------------|
368
+ | `platform:tenants.list` | List all tenants across the platform |
369
+ | `platform:tenants.create` | Create new tenant + owner user |
370
+ | `platform:tenants.read` | View any tenant detail (name, plan, status) |
371
+ | `platform:tenants.update` | Update tenant plan, features, active status |
372
+ | `platform:plans.read` | View available plans |
373
+ | `platform:plans.manage` | Create/update plans |
374
+
375
+ ### Tenant Management
376
+
377
+ | Permission | Description |
378
+ |------------|-------------|
379
+ | `tenant:read` | View tenant info and settings |
380
+ | `tenant:update` | Update tenant settings |
381
+ | `tenant:features` | Toggle feature flags |
382
+
383
+ ### Clinics
384
+
385
+ | Permission | Description |
386
+ |------------|-------------|
387
+ | `clinics:read` | View clinic list and settings |
388
+ | `clinics:create` | Create new clinic |
389
+ | `clinics:update` | Update clinic settings |
390
+ | `clinics:delete` | Delete clinic |
391
+
392
+ ### Users
393
+
394
+ | Permission | Description |
395
+ |------------|-------------|
396
+ | `users:read` | View user list |
397
+ | `users:invite` | Invite new users |
398
+ | `users:update` | Update user roles/clinics |
399
+ | `users:delete` | Remove users |
400
+
401
+ ### Phone Numbers
402
+
403
+ | Permission | Description |
404
+ |------------|-------------|
405
+ | `phone_numbers:read` | View phone numbers |
406
+ | `phone_numbers:manage` | Add/edit/remove phone numbers |
407
+
408
+ ### Agents
409
+
410
+ | Permission | Description |
411
+ |------------|-------------|
412
+ | `agents:read` | View agents |
413
+ | `agents:manage` | Configure agents |
414
+
415
+ ### WhatsApp / Live Chat
416
+
417
+ | Permission | Description |
418
+ |------------|-------------|
419
+ | `chats:read` | View chat list and messages |
420
+ | `chats:takeover` | Take over session from AI |
421
+ | `chats:send` | Send messages in live chat |
422
+ | `chats:resolve` | Manually resolve sessions |
423
+ | `chats:config` | Configure WhatsApp settings (connect, disconnect, blacklist) |
424
+
425
+ ### Calls
426
+
427
+ | Permission | Description |
428
+ |------------|-------------|
429
+ | `calls:read` | View call records and transcripts |
430
+
431
+ ### Leads
432
+
433
+ | Permission | Description |
434
+ |------------|-------------|
435
+ | `leads:read` | View leads |
436
+ | `leads:update` | Update lead status |
437
+ | `leads:export` | Export leads as CSV |
438
+
439
+ ### Appointments
440
+
441
+ | Permission | Description |
442
+ |------------|-------------|
443
+ | `appointments:read` | View appointments |
444
+ | `appointments:manage` | Create/cancel/reschedule appointments manually |
445
+
446
+ ### Outbound Campaigns
447
+
448
+ | Permission | Description |
449
+ |------------|-------------|
450
+ | `campaigns:read` | View campaigns and results |
451
+ | `campaigns:manage` | Create/start/stop campaigns |
452
+ | `campaigns:export` | Export campaign results |
453
+
454
+ ### Appointment Validation
455
+
456
+ | Permission | Description |
457
+ |------------|-------------|
458
+ | `validation:read` | View validation batches |
459
+ | `validation:manage` | Create/submit/cancel batches |
460
+ | `validation:export` | Export validation results |
461
+
462
+ ### Billing
463
+
464
+ | Permission | Description |
465
+ |------------|-------------|
466
+ | `billing:read` | View usage, invoices, billing history |
467
+ | `billing:manage` | Record payments, manage plans |
468
+
469
+ ### Analytics
470
+
471
+ | Permission | Description |
472
+ |------------|-------------|
473
+ | `analytics:read` | View dashboards and reports |
474
+
475
+ ### Patients
476
+
477
+ | Permission | Description |
478
+ |------------|-------------|
479
+ | `patients:read` | View patient list, detail, medical history |
480
+ | `patients:create` | Create new patients manually |
481
+ | `patients:update` | Edit patient info, medical history, documents, notes |
482
+ | `patients:delete` | Soft-delete patients |
483
+ | `patients:export` | Export patient data (CSV, KVKK) |
484
+
485
+ ### Treatments
486
+
487
+ | Permission | Description |
488
+ |------------|-------------|
489
+ | `treatments:read` | View treatment catalog |
490
+ | `treatments:manage` | Create/edit/delete treatments |
491
+ | `treatment_plans:manage` | Create/edit treatment plans and items |
492
+
493
+ ### Calendar
494
+
495
+ | Permission | Description |
496
+ |------------|-------------|
497
+ | `calendar:read` | View calendar settings |
498
+ | `calendar:manage` | Connect/disconnect Google Calendar, configure |
499
+
500
+ ### KVKK / Compliance
501
+
502
+ | Permission | Description |
503
+ |------------|-------------|
504
+ | `kvkk:read` | View deletion requests, retention policy |
505
+ | `kvkk:manage` | Request data export/deletion |
506
+
507
+ ### Audit
508
+
509
+ | Permission | Description |
510
+ |------------|-------------|
511
+ | `audit:read` | View audit trail |
512
+
513
+ ### Payments (Patient)
514
+
515
+ | Permission | Description |
516
+ |------------|-------------|
517
+ | `payments:read` | View patient payments |
518
+ | `payments:manage` | Create payment links, refund |
519
+
520
+ ---
521
+
522
+ ## 13. Role-to-Permission Mapping
523
+
524
+ The complete mapping from each role to its granted permissions.
525
+
526
+ ### superadmin
527
+
528
+ Bypasses all permission checks entirely. Has implicit access to everything.
529
+
530
+ ### sales
531
+
532
+ ```
533
+ platform:tenants.list
534
+ platform:tenants.create
535
+ platform:tenants.read
536
+ platform:tenants.update
537
+ platform:plans.read
538
+ ```
539
+
540
+ Sales can only onboard tenants and manage plans. Zero access to tenant internal data (chats, calls, leads, etc.).
541
+
542
+ ### owner
543
+
544
+ Full access within their tenant. All permissions below:
545
+
546
+ ```
547
+ tenant:read, tenant:update, tenant:features
548
+ clinics:read, clinics:create, clinics:update, clinics:delete
549
+ users:read, users:invite, users:update, users:delete
550
+ phone_numbers:read, phone_numbers:manage
551
+ agents:read, agents:manage
552
+ chats:read, chats:takeover, chats:send, chats:resolve, chats:config
553
+ calls:read
554
+ leads:read, leads:update, leads:export
555
+ appointments:read, appointments:manage
556
+ campaigns:read, campaigns:manage, campaigns:export
557
+ validation:read, validation:manage, validation:export
558
+ billing:read, billing:manage
559
+ analytics:read
560
+ contacts:read, contacts:update, contacts:export
561
+ calendar:read, calendar:manage
562
+ kvkk:read, kvkk:manage
563
+ audit:read
564
+ payments:read, payments:manage
565
+ ```
566
+
567
+ ### admin
568
+
569
+ Same as owner minus: `clinics:create`, `clinics:delete`, `tenant:update`, `tenant:features`, `users:update`, `users:delete`, `billing:manage`, `kvkk:manage`. Only sees assigned clinics.
570
+
571
+ ```
572
+ tenant:read
573
+ clinics:read, clinics:update
574
+ users:read, users:invite
575
+ phone_numbers:read, phone_numbers:manage
576
+ agents:read, agents:manage
577
+ chats:read, chats:takeover, chats:send, chats:resolve, chats:config
578
+ calls:read
579
+ leads:read, leads:update, leads:export
580
+ appointments:read, appointments:manage
581
+ campaigns:read, campaigns:manage, campaigns:export
582
+ validation:read, validation:manage, validation:export
583
+ billing:read
584
+ analytics:read
585
+ contacts:read, contacts:update, contacts:export
586
+ calendar:read, calendar:manage
587
+ kvkk:read
588
+ audit:read
589
+ payments:read, payments:manage
590
+ ```
591
+
592
+ ### doctor
593
+
594
+ Read-heavy role focused on clinical data:
595
+
596
+ ```
597
+ tenant:read
598
+ clinics:read
599
+ chats:read
600
+ calls:read
601
+ leads:read
602
+ appointments:read, appointments:manage
603
+ contacts:read, contacts:update
604
+ analytics:read
605
+ ```
606
+
607
+ ### receptionist
608
+
609
+ Day-to-day operations role:
610
+
611
+ ```
612
+ tenant:read
613
+ clinics:read
614
+ chats:read, chats:takeover, chats:send, chats:resolve
615
+ calls:read
616
+ leads:read, leads:update
617
+ appointments:read, appointments:manage
618
+ contacts:read, contacts:update
619
+ payments:read, payments:manage
620
+ ```
621
+
622
+ ### Complete Matrix
623
+
624
+ | Permission | superadmin | sales | owner | admin | doctor | receptionist |
625
+ |------------|:---:|:---:|:---:|:---:|:---:|:---:|
626
+ | `platform:tenants.list` | * | Y | - | - | - | - |
627
+ | `platform:tenants.create` | * | Y | - | - | - | - |
628
+ | `platform:tenants.read` | * | Y | - | - | - | - |
629
+ | `platform:tenants.update` | * | Y | - | - | - | - |
630
+ | `platform:plans.read` | * | Y | - | - | - | - |
631
+ | `platform:plans.manage` | * | - | - | - | - | - |
632
+ | `tenant:read` | * | - | Y | Y | Y | Y |
633
+ | `tenant:update` | * | - | Y | - | - | - |
634
+ | `tenant:features` | * | - | Y | - | - | - |
635
+ | `clinics:read` | * | - | Y | Y | Y | Y |
636
+ | `clinics:create` | * | - | Y | - | - | - |
637
+ | `clinics:update` | * | - | Y | Y | - | - |
638
+ | `clinics:delete` | * | - | Y | - | - | - |
639
+ | `users:read` | * | - | Y | Y | - | - |
640
+ | `users:invite` | * | - | Y | Y | - | - |
641
+ | `users:update` | * | - | Y | - | - | - |
642
+ | `users:delete` | * | - | Y | - | - | - |
643
+ | `phone_numbers:read` | * | - | Y | Y | - | - |
644
+ | `phone_numbers:manage` | * | - | Y | Y | - | - |
645
+ | `agents:read` | * | - | Y | Y | - | - |
646
+ | `agents:manage` | * | - | Y | Y | - | - |
647
+ | `chats:read` | * | - | Y | Y | Y | Y |
648
+ | `chats:takeover` | * | - | Y | Y | - | Y |
649
+ | `chats:send` | * | - | Y | Y | - | Y |
650
+ | `chats:resolve` | * | - | Y | Y | - | Y |
651
+ | `chats:config` | * | - | Y | Y | - | - |
652
+ | `calls:read` | * | - | Y | Y | Y | Y |
653
+ | `leads:read` | * | - | Y | Y | Y | Y |
654
+ | `leads:update` | * | - | Y | Y | - | Y |
655
+ | `leads:export` | * | - | Y | Y | - | - |
656
+ | `appointments:read` | * | - | Y | Y | Y | Y |
657
+ | `appointments:manage` | * | - | Y | Y | Y | Y |
658
+ | `campaigns:read` | * | - | Y | Y | - | - |
659
+ | `campaigns:manage` | * | - | Y | Y | - | - |
660
+ | `campaigns:export` | * | - | Y | Y | - | - |
661
+ | `validation:read` | * | - | Y | Y | - | - |
662
+ | `validation:manage` | * | - | Y | Y | - | - |
663
+ | `validation:export` | * | - | Y | Y | - | - |
664
+ | `billing:read` | * | - | Y | Y | - | - |
665
+ | `billing:manage` | * | - | Y | - | - | - |
666
+ | `analytics:read` | * | - | Y | Y | Y | - |
667
+ | `patients:read` | * | - | Y | Y | Y | Y |
668
+ | `patients:create` | * | - | Y | Y | - | Y |
669
+ | `patients:update` | * | - | Y | Y | Y | Y |
670
+ | `patients:delete` | * | - | Y | - | - | - |
671
+ | `patients:export` | * | - | Y | Y | - | - |
672
+ | `treatments:read` | * | - | Y | Y | Y | Y |
673
+ | `treatments:manage` | * | - | Y | Y | - | - |
674
+ | `treatment_plans:manage` | * | - | Y | Y | Y | - |
675
+ | `calendar:read` | * | - | Y | Y | - | - |
676
+ | `calendar:manage` | * | - | Y | Y | - | - |
677
+ | `kvkk:read` | * | - | Y | Y | - | - |
678
+ | `kvkk:manage` | * | - | Y | - | - | - |
679
+ | `audit:read` | * | - | Y | Y | - | - |
680
+ | `payments:read` | * | - | Y | Y | - | Y |
681
+ | `payments:manage` | * | - | Y | Y | - | Y |
682
+
683
+ `*` = superadmin bypasses all checks. `Y` = explicitly granted. `-` = not granted.
684
+
685
+ ---
686
+
687
+ ## 14. Clinic-Scoped Access
688
+
689
+ Beyond permissions, data visibility is further scoped by **clinic assignment**. A user might have `chats:read` permission but can only see chats belonging to their assigned clinics.
690
+
691
+ ### Rules
692
+
693
+ | Role | Clinic Access |
694
+ |------|---------------|
695
+ | `superadmin` | All tenants, all clinics (bypasses everything) |
696
+ | `owner` | All clinics within their tenant (automatic, no assignments needed) |
697
+ | `admin` | Only explicitly assigned clinics |
698
+ | `doctor` | Only explicitly assigned clinics |
699
+ | `receptionist` | Only explicitly assigned clinics |
700
+ | `sales` | No clinic access (platform role -- only `/platform/*` endpoints) |
701
+
702
+ ### UserClinicAssignment Table
703
+
704
+ ```prisma
705
+ model UserClinicAssignment {
706
+ assign_id String @id @default(uuid())
707
+ assign_user_id String
708
+ assign_clinic_id String
709
+ assign_created_at DateTime @default(now())
710
+
711
+ user User @relation(fields: [assign_user_id], references: [user_id], onDelete: Cascade)
712
+ clinic Clinic @relation(fields: [assign_clinic_id], references: [clinic_id], onDelete: Cascade)
713
+
714
+ @@unique([assign_user_id, assign_clinic_id])
715
+ @@index([assign_user_id])
716
+ @@index([assign_clinic_id])
717
+ @@map("user_clinic_assignments")
718
+ }
719
+ ```
720
+
721
+ ### ClinicAccessGuard Logic
722
+
723
+ ```typescript
724
+ async function clinicAccessGuard(request) {
725
+ const user = request.user; // From JWT payload
726
+
727
+ // Superadmin and owner bypass clinic scoping
728
+ if (['superadmin', 'owner'].includes(user.role)) {
729
+ return; // Full access to all clinics
730
+ }
731
+
732
+ // Sales role has no tenant/clinic access
733
+ if (user.role === 'sales') {
734
+ throw new ForbiddenException('Sales role cannot access tenant data');
735
+ }
736
+
737
+ // For admin, doctor, receptionist: resolve assigned clinics
738
+ const assignedClinicIds = await getAssignedClinicIds(user.id);
739
+
740
+ // If route targets a specific clinic, verify access
741
+ if (request.params.clinicId) {
742
+ if (!assignedClinicIds.includes(request.params.clinicId)) {
743
+ throw new ForbiddenException('No access to this clinic');
744
+ }
745
+ }
746
+
747
+ // Inject for downstream use (services filter queries by these IDs)
748
+ request.accessibleClinicIds = assignedClinicIds;
749
+ }
750
+ ```
751
+
752
+ ### How Services Use Clinic Scoping
753
+
754
+ Services receive `clinicIds` from the guard and add it to every database query:
755
+
756
+ ```typescript
757
+ async findAll(tenantId: string, clinicIds: string[]) {
758
+ return this.prisma.whatsappChat.findMany({
759
+ where: {
760
+ chat_tenant_id: tenantId,
761
+ chat_clinic_id: { in: clinicIds }, // Scoped by accessible clinics
762
+ },
763
+ });
764
+ }
765
+ ```
766
+
767
+ For `owner` and `superadmin`, `clinicIds` is empty/undefined, and the clinic filter is omitted (all clinics returned).
768
+
769
+ ---
770
+
771
+ ## 15. NestJS Guards and Decorators
772
+
773
+ ### Guards (applied via `@UseGuards`)
774
+
775
+ | Guard | Purpose | Order |
776
+ |-------|---------|-------|
777
+ | `JwtAuthGuard` | Validates JWT signature, checks Redis blacklist, extracts user payload | 1st (always) |
778
+ | `PermissionsGuard` | Checks that the user's role grants the required permission(s) | 2nd |
779
+ | `FeaturesGuard` | Checks that the tenant has the required feature flag(s) enabled | 3rd |
780
+ | `ClinicAccessGuard` | Verifies clinic assignment, injects `accessibleClinicIds` | 4th |
781
+
782
+ ### Decorators
783
+
784
+ | Decorator | Type | Usage |
785
+ |-----------|------|-------|
786
+ | `@Permissions('chats:read', 'chats:send')` | Method | Requires **any** of the listed permissions |
787
+ | `@RequireAllPermissions('users:invite', 'clinics:read')` | Method | Requires **all** of the listed permissions |
788
+ | `@Features('whatsapp_agent')` | Method/Controller | Requires the tenant feature flag to be enabled |
789
+ | `@CurrentUser()` | Parameter | Extracts `JwtPayload` from the request |
790
+ | `@AccessibleClinics()` | Parameter | Extracts `clinicIds` injected by `ClinicAccessGuard` |
791
+
792
+ ### Guard Composition Examples
793
+
794
+ ```typescript
795
+ // Basic: just authentication
796
+ @UseGuards(JwtAuthGuard)
797
+
798
+ // Authentication + permission check
799
+ @UseGuards(JwtAuthGuard, PermissionsGuard)
800
+
801
+ // Authentication + permission + feature flag
802
+ @UseGuards(JwtAuthGuard, PermissionsGuard, FeaturesGuard)
803
+
804
+ // Authentication + clinic scoping
805
+ @UseGuards(JwtAuthGuard, ClinicAccessGuard)
806
+
807
+ // Full stack: auth + permission + feature + clinic
808
+ @UseGuards(JwtAuthGuard, PermissionsGuard, FeaturesGuard, ClinicAccessGuard)
809
+ ```
810
+
811
+ ### Complete Controller Example
812
+
813
+ ```typescript
814
+ @Controller('chats')
815
+ @UseGuards(JwtAuthGuard, PermissionsGuard, ClinicAccessGuard)
816
+ export class ChatsController {
817
+
818
+ @Get()
819
+ @Permissions('chats:read')
820
+ async listChats(
821
+ @CurrentUser() user: JwtPayload,
822
+ @AccessibleClinics() clinicIds: string[],
823
+ ) {
824
+ // clinicIds is auto-populated:
825
+ // owner/superadmin -> all clinics (empty array = no filter)
826
+ // receptionist -> only assigned clinics
827
+ return this.chatsService.findAll(user.tenantId, clinicIds);
828
+ }
829
+
830
+ @Post(':chatId/messages')
831
+ @Permissions('chats:send')
832
+ async sendMessage(
833
+ @CurrentUser() user: JwtPayload,
834
+ @Param('chatId') chatId: string,
835
+ @Body() body: SendMessageDto,
836
+ ) {
837
+ // ClinicAccessGuard already verified user has access to this chat's clinic
838
+ return this.chatsService.sendHumanMessage(chatId, user.id, body.text);
839
+ }
840
+ }
841
+ ```
842
+
843
+ ### Feature-Gated Example
844
+
845
+ ```typescript
846
+ @Controller('whatsapp')
847
+ @UseGuards(JwtAuthGuard, PermissionsGuard, FeaturesGuard)
848
+ @Features('whatsapp_agent') // Entire controller requires whatsapp_agent feature
849
+ export class WhatsAppController {
850
+ // All routes here are gated by the feature flag
851
+ }
852
+ ```
853
+
854
+ ---
855
+
856
+ ## 15. Sequence Diagrams
857
+
858
+ ### Login (Standard -- No 2FA)
859
+
860
+ ```
861
+ Client Server Redis PostgreSQL
862
+ | | | |
863
+ | POST /auth/login | | |
864
+ | {email, password} | | |
865
+ |------------------------------>| | |
866
+ | | Find user by email | |
867
+ | |-------------------------------------------->|
868
+ | |<--------------------------------------------|
869
+ | | | |
870
+ | | Verify bcrypt(password) | |
871
+ | | Check user.is_active | |
872
+ | | Check tenant.is_active | |
873
+ | | | |
874
+ | | Resolve permissions, features, clinics |
875
+ | |-------------------------------------------->|
876
+ | |<--------------------------------------------|
877
+ | | | |
878
+ | | Sign access JWT (15m) | |
879
+ | | Generate refresh token | |
880
+ | | | |
881
+ | | Store refresh token | |
882
+ | |----->SET auth:refresh:{tok} | |
883
+ | |<-----------------------------| |
884
+ | | Store refresh token in DB | |
885
+ | |-------------------------------------------->|
886
+ | |<--------------------------------------------|
887
+ | | | |
888
+ | {user, tenant, | | |
889
+ | access_token, | | |
890
+ | refresh_token} | | |
891
+ |<------------------------------| | |
892
+ ```
893
+
894
+ ### Login with 2FA
895
+
896
+ ```
897
+ Client Server Redis PostgreSQL
898
+ | | | |
899
+ | POST /auth/login | | |
900
+ | {email, password} | | |
901
+ |------------------------------>| | |
902
+ | | Find user, verify password | |
903
+ | |-------------------------------------------->|
904
+ | |<--------------------------------------------|
905
+ | | | |
906
+ | | user.twoFactorEnabled = true| |
907
+ | | Sign temp JWT (5m, sub only)| |
908
+ | | | |
909
+ | {requires_2fa: true, | | |
910
+ | temp_token: "..."} | | |
911
+ |<------------------------------| | |
912
+ | | | |
913
+ | POST /auth/2fa/authenticate | | |
914
+ | {temp_token, totp_code} | | |
915
+ |------------------------------>| | |
916
+ | | Verify temp_token (5m TTL) | |
917
+ | | Decrypt TOTP secret | |
918
+ | | Verify TOTP code (otplib) | |
919
+ | | | |
920
+ | | (same as standard login | |
921
+ | | from here: resolve perms, | |
922
+ | | sign tokens, store refresh)| |
923
+ | |----->SET auth:refresh:{tok} | |
924
+ | |-------------------------------------------->|
925
+ | | | |
926
+ | {user, tenant, | | |
927
+ | access_token, | | |
928
+ | refresh_token} | | |
929
+ |<------------------------------| | |
930
+ ```
931
+
932
+ ### Token Refresh
933
+
934
+ ```
935
+ Client Server Redis PostgreSQL
936
+ | | | |
937
+ | POST /auth/refresh | | |
938
+ | {refresh_token} | | |
939
+ |------------------------------>| | |
940
+ | | Check Redis set | |
941
+ | |----->GET auth:refresh:{tok} | |
942
+ | |<-----------------------------| |
943
+ | | | |
944
+ | | Verify in PostgreSQL | |
945
+ | |-------------------------------------------->|
946
+ | |<--------------------------------------------|
947
+ | | | |
948
+ | | Check expiry | |
949
+ | | | |
950
+ | | Delete old refresh token | |
951
+ | |----->DEL auth:refresh:{tok} | |
952
+ | |-------------------------------------------->|
953
+ | | | |
954
+ | | Re-resolve user role, | |
955
+ | | permissions, features, | |
956
+ | | clinic assignments from DB | |
957
+ | |-------------------------------------------->|
958
+ | |<--------------------------------------------|
959
+ | | | |
960
+ | | Sign new access JWT (15m) | |
961
+ | | Generate new refresh token | |
962
+ | | | |
963
+ | | Store new refresh token | |
964
+ | |----->SET auth:refresh:{new} | |
965
+ | |-------------------------------------------->|
966
+ | | | |
967
+ | {access_token, | | |
968
+ | refresh_token} | | |
969
+ |<------------------------------| | |
970
+ ```
971
+
972
+ ### Password Reset
973
+
974
+ ```
975
+ Client Server Email/SMS PostgreSQL
976
+ | | | |
977
+ | POST /auth/forgot-password | | |
978
+ | {email} | | |
979
+ |------------------------------>| | |
980
+ | | Find user by email | |
981
+ | |-------------------------------------------->|
982
+ | |<--------------------------------------------|
983
+ | | | |
984
+ | | Generate 32-byte token | |
985
+ | | bcrypt(token) -> hash | |
986
+ | | Store hash, 1h TTL | |
987
+ | |-------------------------------------------->|
988
+ | | | |
989
+ | | Send raw token | |
990
+ | |------------------------->| |
991
+ | | | |
992
+ | {message: "Reset link sent"} | | |
993
+ |<------------------------------| | |
994
+ | | | |
995
+ | ... user clicks link ... | | |
996
+ | | | |
997
+ | POST /auth/reset-password | | |
998
+ | {token, new_password} | | |
999
+ |------------------------------>| | |
1000
+ | | bcrypt(token), find match |
1001
+ | |-------------------------------------------->|
1002
+ | |<--------------------------------------------|
1003
+ | | | |
1004
+ | | Check expiry (1h) | |
1005
+ | | Check not used | |
1006
+ | | | |
1007
+ | | Update password hash | |
1008
+ | | Revoke ALL refresh tokens |
1009
+ | | Mark reset token used | |
1010
+ | |-------------------------------------------->|
1011
+ | | | |
1012
+ | {message: "Password reset"} | | |
1013
+ |<------------------------------| | |
1014
+ ```
1015
+
1016
+ ---
1017
+
1018
+ ## Appendix: File Locations (NestJS Target)
1019
+
1020
+ ```
1021
+ src/
1022
+ auth/
1023
+ auth.module.ts
1024
+ auth.controller.ts # /auth/* endpoints
1025
+ auth.service.ts # JWT + bcrypt + Redis blacklist + 2FA
1026
+ strategies/
1027
+ jwt.strategy.ts # Passport JWT strategy
1028
+
1029
+ common/
1030
+ guards/
1031
+ jwt-auth.guard.ts # @UseGuards(JwtAuthGuard)
1032
+ permissions.guard.ts # @UseGuards(PermissionsGuard)
1033
+ features.guard.ts # @UseGuards(FeaturesGuard)
1034
+ clinic-access.guard.ts # @UseGuards(ClinicAccessGuard)
1035
+ decorators/
1036
+ permissions.decorator.ts # @Permissions()
1037
+ features.decorator.ts # @Features()
1038
+ current-user.decorator.ts # @CurrentUser()
1039
+ accessible-clinics.decorator.ts # @AccessibleClinics()
1040
+ ```