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.
- package/.claude/settings.local.json +27 -0
- package/.dockerignore +6 -0
- package/.env.example +43 -0
- package/BEFORE-PRODUCTION.md +32 -0
- package/Dockerfile +23 -0
- package/SYSTEM_OVERVIEW.md +942 -0
- package/SYSTEM_STATUS.md +332 -0
- package/backup_script/main.py +146 -0
- package/baileys/.dockerignore +7 -0
- package/baileys/Dockerfile +13 -0
- package/baileys/README.md +412 -0
- package/baileys/index.js +499 -0
- package/baileys/package-lock.json +2532 -0
- package/baileys/package.json +25 -0
- package/baileys/server.js +96 -0
- package/baileys/src/config.js +55 -0
- package/baileys/src/middleware/api-key.js +16 -0
- package/baileys/src/routes/accounts.js +51 -0
- package/baileys/src/routes/chats.js +103 -0
- package/baileys/src/routes/webhooks.js +34 -0
- package/baileys/src/services/account-store.js +259 -0
- package/baileys/src/services/webhook-dispatcher.js +161 -0
- package/baileys/src/services/worker-manager.js +597 -0
- package/baileys/src/utils/jid.js +79 -0
- package/baileys/src/utils/logger.js +16 -0
- package/baileys/src/utils/use-mongodb-auth-state.js +122 -0
- package/baileys/worker.js +721 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +84 -0
- package/dist/app.js.map +1 -0
- package/dist/config/agentPrompts.json +19 -0
- package/dist/config/database.d.ts +1 -0
- package/dist/config/database.js +26 -0
- package/dist/config/database.js.map +1 -0
- package/dist/config/env.d.ts +41 -0
- package/dist/config/env.js +81 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +73 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controllers/webhook.controller.d.ts +10 -0
- package/dist/controllers/webhook.controller.js +128 -0
- package/dist/controllers/webhook.controller.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/hooks/webhookValidator.d.ts +2 -0
- package/dist/hooks/webhookValidator.js +47 -0
- package/dist/hooks/webhookValidator.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/001_session-architecture.d.ts +3 -0
- package/dist/migrations/001_session-architecture.js +119 -0
- package/dist/migrations/001_session-architecture.js.map +1 -0
- package/dist/migrations/002_agent-ref.d.ts +3 -0
- package/dist/migrations/002_agent-ref.js +55 -0
- package/dist/migrations/002_agent-ref.js.map +1 -0
- package/dist/migrations/003_shift-schedule.d.ts +3 -0
- package/dist/migrations/003_shift-schedule.js +48 -0
- package/dist/migrations/003_shift-schedule.js.map +1 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.d.ts +3 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js +27 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js.map +1 -0
- package/dist/migrations/005_composite-baileys-chat-ids.d.ts +3 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js +47 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js.map +1 -0
- package/dist/migrations/migration.model.d.ts +18 -0
- package/dist/migrations/migration.model.js +45 -0
- package/dist/migrations/migration.model.js.map +1 -0
- package/dist/migrations/migration.routes.d.ts +2 -0
- package/dist/migrations/migration.routes.js +37 -0
- package/dist/migrations/migration.routes.js.map +1 -0
- package/dist/migrations/migration.runner.d.ts +19 -0
- package/dist/migrations/migration.runner.js +74 -0
- package/dist/migrations/migration.runner.js.map +1 -0
- package/dist/models/ConversationClaim.d.ts +13 -0
- package/dist/models/ConversationClaim.js +44 -0
- package/dist/models/ConversationClaim.js.map +1 -0
- package/dist/models/ConversationEvaluation.d.ts +23 -0
- package/dist/models/ConversationEvaluation.js +92 -0
- package/dist/models/ConversationEvaluation.js.map +1 -0
- package/dist/models/Summary.d.ts +37 -0
- package/dist/models/Summary.js +78 -0
- package/dist/models/Summary.js.map +1 -0
- package/dist/models/Transcription.d.ts +34 -0
- package/dist/models/Transcription.js +71 -0
- package/dist/models/Transcription.js.map +1 -0
- package/dist/models/WhatsAppRecipient.d.ts +23 -0
- package/dist/models/WhatsAppRecipient.js +53 -0
- package/dist/models/WhatsAppRecipient.js.map +1 -0
- package/dist/modules/admin/admin.routes.d.ts +2 -0
- package/dist/modules/admin/admin.routes.js +1966 -0
- package/dist/modules/admin/admin.routes.js.map +1 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js +158 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js.map +1 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js +204 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js.map +1 -0
- package/dist/modules/agents/agent.model.d.ts +38 -0
- package/dist/modules/agents/agent.model.js +92 -0
- package/dist/modules/agents/agent.model.js.map +1 -0
- package/dist/modules/agents/agent.routes.d.ts +2 -0
- package/dist/modules/agents/agent.routes.js +61 -0
- package/dist/modules/agents/agent.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.model.d.ts +76 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js +118 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.d.ts +2 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js +202 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.service.d.ts +53 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js +827 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js.map +1 -0
- package/dist/modules/auth/auth.model.d.ts +17 -0
- package/dist/modules/auth/auth.model.js +64 -0
- package/dist/modules/auth/auth.model.js.map +1 -0
- package/dist/modules/auth/auth.routes.d.ts +2 -0
- package/dist/modules/auth/auth.routes.js +202 -0
- package/dist/modules/auth/auth.routes.js.map +1 -0
- package/dist/modules/auth/auth.service.d.ts +28 -0
- package/dist/modules/auth/auth.service.js +183 -0
- package/dist/modules/auth/auth.service.js.map +1 -0
- package/dist/modules/auth/refresh-token.model.d.ts +13 -0
- package/dist/modules/auth/refresh-token.model.js +52 -0
- package/dist/modules/auth/refresh-token.model.js.map +1 -0
- package/dist/modules/billing/billing-alert.model.d.ts +16 -0
- package/dist/modules/billing/billing-alert.model.js +47 -0
- package/dist/modules/billing/billing-alert.model.js.map +1 -0
- package/dist/modules/billing/billing-period-snapshot.model.d.ts +35 -0
- package/dist/modules/billing/billing-period-snapshot.model.js +68 -0
- package/dist/modules/billing/billing-period-snapshot.model.js.map +1 -0
- package/dist/modules/billing/billing.model.d.ts +18 -0
- package/dist/modules/billing/billing.model.js +62 -0
- package/dist/modules/billing/billing.model.js.map +1 -0
- package/dist/modules/billing/billing.routes.d.ts +2 -0
- package/dist/modules/billing/billing.routes.js +63 -0
- package/dist/modules/billing/billing.routes.js.map +1 -0
- package/dist/modules/billing/billing.service.d.ts +69 -0
- package/dist/modules/billing/billing.service.js +498 -0
- package/dist/modules/billing/billing.service.js.map +1 -0
- package/dist/modules/billing/payment.model.d.ts +24 -0
- package/dist/modules/billing/payment.model.js +57 -0
- package/dist/modules/billing/payment.model.js.map +1 -0
- package/dist/modules/calls/call.model.d.ts +41 -0
- package/dist/modules/calls/call.model.js +97 -0
- package/dist/modules/calls/call.model.js.map +1 -0
- package/dist/modules/calls/call.routes.d.ts +2 -0
- package/dist/modules/calls/call.routes.js +103 -0
- package/dist/modules/calls/call.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.model.d.ts +45 -0
- package/dist/modules/campaigns/campaign.model.js +98 -0
- package/dist/modules/campaigns/campaign.model.js.map +1 -0
- package/dist/modules/campaigns/campaign.routes.d.ts +2 -0
- package/dist/modules/campaigns/campaign.routes.js +323 -0
- package/dist/modules/campaigns/campaign.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.service.d.ts +11 -0
- package/dist/modules/campaigns/campaign.service.js +86 -0
- package/dist/modules/campaigns/campaign.service.js.map +1 -0
- package/dist/modules/google-calendar/google-calendar.routes.d.ts +2 -0
- package/dist/modules/google-calendar/google-calendar.routes.js +32 -0
- package/dist/modules/google-calendar/google-calendar.routes.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call-config.model.d.ts +20 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js +68 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call.routes.d.ts +2 -0
- package/dist/modules/inbound-call/inbound-call.routes.js +243 -0
- package/dist/modules/inbound-call/inbound-call.routes.js.map +1 -0
- package/dist/modules/leads/lead.model.d.ts +24 -0
- package/dist/modules/leads/lead.model.js +54 -0
- package/dist/modules/leads/lead.model.js.map +1 -0
- package/dist/modules/leads/lead.routes.d.ts +2 -0
- package/dist/modules/leads/lead.routes.js +201 -0
- package/dist/modules/leads/lead.routes.js.map +1 -0
- package/dist/modules/plans/plan.model.d.ts +25 -0
- package/dist/modules/plans/plan.model.js +59 -0
- package/dist/modules/plans/plan.model.js.map +1 -0
- package/dist/modules/surveys/survey.model.d.ts +30 -0
- package/dist/modules/surveys/survey.model.js +75 -0
- package/dist/modules/surveys/survey.model.js.map +1 -0
- package/dist/modules/surveys/survey.routes.d.ts +2 -0
- package/dist/modules/surveys/survey.routes.js +153 -0
- package/dist/modules/surveys/survey.routes.js.map +1 -0
- package/dist/modules/tenants/tenant.model.d.ts +86 -0
- package/dist/modules/tenants/tenant.model.js +127 -0
- package/dist/modules/tenants/tenant.model.js.map +1 -0
- package/dist/modules/tenants/tenant.routes.d.ts +2 -0
- package/dist/modules/tenants/tenant.routes.js +65 -0
- package/dist/modules/tenants/tenant.routes.js.map +1 -0
- package/dist/modules/users/user.routes.d.ts +2 -0
- package/dist/modules/users/user.routes.js +106 -0
- package/dist/modules/users/user.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.d.ts +20 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js +85 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.d.ts +11 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js +360 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.routes.d.ts +2 -0
- package/dist/modules/webhooks/elevenlabs.routes.js +34 -0
- package/dist/modules/webhooks/elevenlabs.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.service.d.ts +6 -0
- package/dist/modules/webhooks/elevenlabs.service.js +512 -0
- package/dist/modules/webhooks/elevenlabs.service.js.map +1 -0
- package/dist/modules/webhooks/unipile.routes.d.ts +2 -0
- package/dist/modules/webhooks/unipile.routes.js +780 -0
- package/dist/modules/webhooks/unipile.routes.js.map +1 -0
- package/dist/modules/whatsapp/appointment.model.d.ts +27 -0
- package/dist/modules/whatsapp/appointment.model.js +58 -0
- package/dist/modules/whatsapp/appointment.model.js.map +1 -0
- package/dist/modules/whatsapp/operator-request.model.d.ts +29 -0
- package/dist/modules/whatsapp/operator-request.model.js +65 -0
- package/dist/modules/whatsapp/operator-request.model.js.map +1 -0
- package/dist/modules/whatsapp/stt-usage.model.d.ts +16 -0
- package/dist/modules/whatsapp/stt-usage.model.js +53 -0
- package/dist/modules/whatsapp/stt-usage.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js +55 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js +54 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-message.model.d.ts +30 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js +52 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-session.model.d.ts +33 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js +65 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp.routes.d.ts +2 -0
- package/dist/modules/whatsapp/whatsapp.routes.js +1237 -0
- package/dist/modules/whatsapp/whatsapp.routes.js.map +1 -0
- package/dist/plugins/cors.d.ts +3 -0
- package/dist/plugins/cors.js +11 -0
- package/dist/plugins/cors.js.map +1 -0
- package/dist/plugins/jwt.d.ts +3 -0
- package/dist/plugins/jwt.js +22 -0
- package/dist/plugins/jwt.js.map +1 -0
- package/dist/plugins/rawBody.d.ts +3 -0
- package/dist/plugins/rawBody.js +16 -0
- package/dist/plugins/rawBody.js.map +1 -0
- package/dist/plugins/rbac.d.ts +5 -0
- package/dist/plugins/rbac.js +29 -0
- package/dist/plugins/rbac.js.map +1 -0
- package/dist/routes/admin.routes.d.ts +2 -0
- package/dist/routes/admin.routes.js +169 -0
- package/dist/routes/admin.routes.js.map +1 -0
- package/dist/routes/health.routes.d.ts +2 -0
- package/dist/routes/health.routes.js +16 -0
- package/dist/routes/health.routes.js.map +1 -0
- package/dist/routes/webhook.routes.d.ts +2 -0
- package/dist/routes/webhook.routes.js +17 -0
- package/dist/routes/webhook.routes.js.map +1 -0
- package/dist/services/ai/base.ai.d.ts +19 -0
- package/dist/services/ai/base.ai.js +120 -0
- package/dist/services/ai/base.ai.js.map +1 -0
- package/dist/services/ai/gemini.service.d.ts +11 -0
- package/dist/services/ai/gemini.service.js +43 -0
- package/dist/services/ai/gemini.service.js.map +1 -0
- package/dist/services/ai/index.d.ts +2 -0
- package/dist/services/ai/index.js +26 -0
- package/dist/services/ai/index.js.map +1 -0
- package/dist/services/ai/openai.service.d.ts +11 -0
- package/dist/services/ai/openai.service.js +50 -0
- package/dist/services/ai/openai.service.js.map +1 -0
- package/dist/services/elevenlabs.service.d.ts +52 -0
- package/dist/services/elevenlabs.service.js +447 -0
- package/dist/services/elevenlabs.service.js.map +1 -0
- package/dist/services/google-calendar.service.d.ts +60 -0
- package/dist/services/google-calendar.service.js +494 -0
- package/dist/services/google-calendar.service.js.map +1 -0
- package/dist/services/inbound-call-schedule.service.d.ts +11 -0
- package/dist/services/inbound-call-schedule.service.js +162 -0
- package/dist/services/inbound-call-schedule.service.js.map +1 -0
- package/dist/services/netgsm.service.d.ts +41 -0
- package/dist/services/netgsm.service.js +89 -0
- package/dist/services/netgsm.service.js.map +1 -0
- package/dist/services/unipile.service.d.ts +41 -0
- package/dist/services/unipile.service.js +149 -0
- package/dist/services/unipile.service.js.map +1 -0
- package/dist/services/whatsapp-agent.service.d.ts +139 -0
- package/dist/services/whatsapp-agent.service.js +2055 -0
- package/dist/services/whatsapp-agent.service.js.map +1 -0
- package/dist/services/whatsapp.service.d.ts +26 -0
- package/dist/services/whatsapp.service.js +206 -0
- package/dist/services/whatsapp.service.js.map +1 -0
- package/dist/templates/index.d.ts +39 -0
- package/dist/templates/index.js +35 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/receptionist.d.ts +2 -0
- package/dist/templates/receptionist.js +39 -0
- package/dist/templates/receptionist.js.map +1 -0
- package/dist/templates/survey.d.ts +2 -0
- package/dist/templates/survey.js +41 -0
- package/dist/templates/survey.js.map +1 -0
- package/dist/types/index.d.ts +173 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker-compose.nestjs.yml +89 -0
- package/docker-compose.yml +78 -0
- package/docs/AI_AGENT_ENHANCEMENT_PLAN.md +164 -0
- package/docs/API.md +1193 -0
- package/docs/API_ENDPOINTS.md +344 -0
- package/docs/ARCHITECTURE.md +305 -0
- package/docs/AUTH_API.md +252 -0
- package/docs/BILLING_SMS_ALERTS.md +94 -0
- package/docs/CHAT_ASSIGNMENT_SYSTEM.md +118 -0
- package/docs/CLIENT_TOOLS_AND_FEATURES.md +337 -0
- package/docs/ELEVENLABS_WEBHOOK_TOOLS.md +644 -0
- package/docs/FRONTEND_CHECKLIST.md +227 -0
- package/docs/IMPLEMENTATION_STATUS.md +470 -0
- package/docs/MIGRATION_GUIDE.md +96 -0
- package/docs/MISSINGS_REPORT.md +507 -0
- package/docs/NESTJS_MIGRATION_REFERENCE.md +5136 -0
- package/docs/PROJECT_DESCRIPTION.md +1038 -0
- package/docs/SCALING.md +148 -0
- package/docs/SESSION_SUMMARY_2026_03_17.md +135 -0
- package/docs/WHATSAPP_AGENT.md +1086 -0
- package/docs/architecture/00-SYSTEM-OVERVIEW.md +318 -0
- package/docs/architecture/01-DATABASE-SCHEMA.md +2564 -0
- package/docs/architecture/02-AUTHENTICATION.md +1040 -0
- package/docs/architecture/03-MULTI-CLINIC.md +742 -0
- package/docs/architecture/04-WHATSAPP-AGENT.md +608 -0
- package/docs/architecture/05-OPERATOR-WORKFLOW.md +444 -0
- package/docs/architecture/06-BAILEYS-MICROSERVICE.md +616 -0
- package/docs/architecture/07-APPOINTMENTS.md +849 -0
- package/docs/architecture/08-VOICE-CALLS.md +470 -0
- package/docs/architecture/09-OUTBOUND-CAMPAIGNS.md +542 -0
- package/docs/architecture/10-CLIENT-TOOLS.md +665 -0
- package/docs/architecture/11-BILLING.md +458 -0
- package/docs/architecture/12-SECURITY.md +216 -0
- package/docs/architecture/13-LOGGING-AUDIT.md +549 -0
- package/docs/architecture/14-META-BUSINESS-API.md +454 -0
- package/docs/architecture/15-AI-MODULE.md +479 -0
- package/docs/architecture/16-BACKGROUND-JOBS.md +469 -0
- package/docs/architecture/17-REALTIME-LIVECHAT.md +447 -0
- package/docs/architecture/18-FILE-STORAGE.md +410 -0
- package/docs/architecture/19-PATIENTS.md +1034 -0
- package/docs/architecture/20-TREATMENTS-AND-PLANS.md +774 -0
- package/docs/architecture/21-BEFORE-AFTER-PHOTOS.md +519 -0
- package/docs/database.md +456 -0
- package/docs/ornek-randevu-onay.csv +3 -0
- package/ecosystem.config.js +16 -0
- package/elevenlabs-convai-api-reference.md +1171 -0
- package/frontend/.dockerignore +2 -0
- package/frontend/Dockerfile +24 -0
- package/frontend/README.md +75 -0
- package/frontend/components.json +25 -0
- package/frontend/eslint.config.js +23 -0
- package/frontend/index.html +13 -0
- package/frontend/nginx.conf +37 -0
- package/frontend/package-lock.json +8709 -0
- package/frontend/package.json +71 -0
- package/frontend/public/favicon.svg +1 -0
- package/frontend/public/icons.svg +24 -0
- package/frontend/src/App.tsx +125 -0
- package/frontend/src/components/error-boundary.tsx +50 -0
- package/frontend/src/components/shared/activity-timeline.tsx +66 -0
- package/frontend/src/components/shared/appointment-calendar.css +80 -0
- package/frontend/src/components/shared/appointment-calendar.tsx +245 -0
- package/frontend/src/components/shared/chat-bubble.tsx +72 -0
- package/frontend/src/components/shared/combobox.tsx +119 -0
- package/frontend/src/components/shared/confirm-dialog.tsx +57 -0
- package/frontend/src/components/shared/data-table-pagination.tsx +97 -0
- package/frontend/src/components/shared/data-table-toolbar.tsx +39 -0
- package/frontend/src/components/shared/empty-state.tsx +27 -0
- package/frontend/src/components/shared/file-upload.tsx +140 -0
- package/frontend/src/components/shared/index.ts +20 -0
- package/frontend/src/components/shared/language-switcher.tsx +29 -0
- package/frontend/src/components/shared/notification-dropdown.tsx +115 -0
- package/frontend/src/components/shared/page-header.tsx +23 -0
- package/frontend/src/components/shared/stat-card.tsx +37 -0
- package/frontend/src/components/shared/status-badge.tsx +70 -0
- package/frontend/src/components/shared/status-dot.tsx +43 -0
- package/frontend/src/components/ui/alert-dialog.tsx +187 -0
- package/frontend/src/components/ui/alert.tsx +76 -0
- package/frontend/src/components/ui/avatar.tsx +109 -0
- package/frontend/src/components/ui/badge.tsx +52 -0
- package/frontend/src/components/ui/breadcrumb.tsx +125 -0
- package/frontend/src/components/ui/button.tsx +60 -0
- package/frontend/src/components/ui/calendar.tsx +219 -0
- package/frontend/src/components/ui/card.tsx +103 -0
- package/frontend/src/components/ui/chart.tsx +371 -0
- package/frontend/src/components/ui/checkbox.tsx +29 -0
- package/frontend/src/components/ui/collapsible.tsx +19 -0
- package/frontend/src/components/ui/command.tsx +194 -0
- package/frontend/src/components/ui/dialog.tsx +158 -0
- package/frontend/src/components/ui/dropdown-menu.tsx +268 -0
- package/frontend/src/components/ui/input-group.tsx +156 -0
- package/frontend/src/components/ui/input.tsx +20 -0
- package/frontend/src/components/ui/label.tsx +20 -0
- package/frontend/src/components/ui/pagination.tsx +130 -0
- package/frontend/src/components/ui/popover.tsx +90 -0
- package/frontend/src/components/ui/progress.tsx +83 -0
- package/frontend/src/components/ui/radio-group.tsx +36 -0
- package/frontend/src/components/ui/scroll-area.tsx +54 -0
- package/frontend/src/components/ui/select.tsx +199 -0
- package/frontend/src/components/ui/separator.tsx +23 -0
- package/frontend/src/components/ui/sheet.tsx +138 -0
- package/frontend/src/components/ui/sidebar.tsx +723 -0
- package/frontend/src/components/ui/skeleton.tsx +13 -0
- package/frontend/src/components/ui/sonner.tsx +47 -0
- package/frontend/src/components/ui/switch.tsx +30 -0
- package/frontend/src/components/ui/table.tsx +114 -0
- package/frontend/src/components/ui/tabs.tsx +82 -0
- package/frontend/src/components/ui/textarea.tsx +18 -0
- package/frontend/src/components/ui/toggle-group.tsx +89 -0
- package/frontend/src/components/ui/toggle.tsx +43 -0
- package/frontend/src/components/ui/tooltip.tsx +64 -0
- package/frontend/src/hooks/use-mobile.ts +19 -0
- package/frontend/src/i18n/index.ts +20 -0
- package/frontend/src/i18n/locales/en.json +786 -0
- package/frontend/src/i18n/locales/tr.json +786 -0
- package/frontend/src/index.css +134 -0
- package/frontend/src/layouts/admin-layout.tsx +111 -0
- package/frontend/src/layouts/app-header.tsx +130 -0
- package/frontend/src/layouts/app-layout.tsx +19 -0
- package/frontend/src/layouts/app-sidebar.tsx +212 -0
- package/frontend/src/layouts/auth-guard.tsx +31 -0
- package/frontend/src/layouts/mobile-sidebar.tsx +120 -0
- package/frontend/src/lib/api.ts +68 -0
- package/frontend/src/lib/hooks/use-appointments.ts +224 -0
- package/frontend/src/lib/hooks/use-availability-blocks.ts +83 -0
- package/frontend/src/lib/hooks/use-chats.ts +236 -0
- package/frontend/src/lib/hooks/use-clinic-detail.ts +171 -0
- package/frontend/src/lib/hooks/use-clinics.ts +37 -0
- package/frontend/src/lib/hooks/use-doctor-calendar.ts +80 -0
- package/frontend/src/lib/hooks/use-examinations.ts +89 -0
- package/frontend/src/lib/hooks/use-medical-history.ts +52 -0
- package/frontend/src/lib/hooks/use-patients.ts +296 -0
- package/frontend/src/lib/hooks/use-photo-sets.ts +118 -0
- package/frontend/src/lib/hooks/use-treatment-plans.ts +152 -0
- package/frontend/src/lib/hooks/use-treatments.ts +125 -0
- package/frontend/src/lib/hooks/use-users.ts +172 -0
- package/frontend/src/lib/utils.ts +6 -0
- package/frontend/src/main.tsx +17 -0
- package/frontend/src/pages/admin/agents/detail.tsx +774 -0
- package/frontend/src/pages/admin/agents/import.tsx +280 -0
- package/frontend/src/pages/admin/agents/index.tsx +245 -0
- package/frontend/src/pages/admin/ai-playground.tsx +543 -0
- package/frontend/src/pages/appointments/create-appointment-dialog.tsx +390 -0
- package/frontend/src/pages/appointments/index.tsx +860 -0
- package/frontend/src/pages/audit/index.tsx +24 -0
- package/frontend/src/pages/auth/login.tsx +194 -0
- package/frontend/src/pages/billing/index.tsx +24 -0
- package/frontend/src/pages/calendar/index.tsx +704 -0
- package/frontend/src/pages/calendar-connections/index.tsx +295 -0
- package/frontend/src/pages/calls/index.tsx +24 -0
- package/frontend/src/pages/campaigns/index.tsx +24 -0
- package/frontend/src/pages/chats/index.tsx +981 -0
- package/frontend/src/pages/clinics/index.tsx +224 -0
- package/frontend/src/pages/clinics/settings.tsx +412 -0
- package/frontend/src/pages/components-showcase.tsx +773 -0
- package/frontend/src/pages/connections/index.tsx +328 -0
- package/frontend/src/pages/dashboard.tsx +50 -0
- package/frontend/src/pages/leads/index.tsx +24 -0
- package/frontend/src/pages/my-schedule/index.tsx +496 -0
- package/frontend/src/pages/patients/create-patient-dialog.tsx +358 -0
- package/frontend/src/pages/patients/detail.tsx +1195 -0
- package/frontend/src/pages/patients/edit-patient-dialog.tsx +387 -0
- package/frontend/src/pages/patients/examinations-tab.tsx +460 -0
- package/frontend/src/pages/patients/index.tsx +381 -0
- package/frontend/src/pages/patients/medical-history-dialog.tsx +207 -0
- package/frontend/src/pages/patients/photo-sets-tab.tsx +616 -0
- package/frontend/src/pages/patients/timeline-tab.tsx +164 -0
- package/frontend/src/pages/patients/treatment-plans-tab.tsx +598 -0
- package/frontend/src/pages/phone-numbers/detail.tsx +427 -0
- package/frontend/src/pages/phone-numbers/index.tsx +455 -0
- package/frontend/src/pages/platform/index.tsx +454 -0
- package/frontend/src/pages/settings/index.tsx +126 -0
- package/frontend/src/pages/treatments/index.tsx +487 -0
- package/frontend/src/pages/users/doctor-profile.tsx +672 -0
- package/frontend/src/pages/users/edit.tsx +329 -0
- package/frontend/src/pages/users/index.tsx +407 -0
- package/frontend/src/pages/validation/index.tsx +24 -0
- package/frontend/src/stores/auth.store.ts +108 -0
- package/frontend/src/stores/ui.store.ts +41 -0
- package/frontend/tsconfig.app.json +32 -0
- package/frontend/tsconfig.json +13 -0
- package/frontend/tsconfig.node.json +26 -0
- package/frontend/vite.config.ts +29 -0
- package/nestjs/.dockerignore +5 -0
- package/nestjs/.env.docker +64 -0
- package/nestjs/.prettierrc +4 -0
- package/nestjs/Dockerfile +36 -0
- package/nestjs/README.md +98 -0
- package/nestjs/eslint.config.mjs +35 -0
- package/nestjs/nest-cli.json +8 -0
- package/nestjs/package-lock.json +13390 -0
- package/nestjs/package.json +114 -0
- package/nestjs/prisma/migrations/20260409161536_add_message_metadata_fields/migration.sql +1746 -0
- package/nestjs/prisma/migrations/20260410140436_add_agent_ai_fields/migration.sql +36 -0
- package/nestjs/prisma/migrations/20260410175519_add_agent_clinic_assignments/migration.sql +21 -0
- package/nestjs/prisma/migrations/20260412094344_make_agent_tenant_optional/migration.sql +2 -0
- package/nestjs/prisma/migrations/20260412110008_add_admin_chat_sessions/migration.sql +47 -0
- package/nestjs/prisma/migrations/migration_lock.toml +3 -0
- package/nestjs/prisma/schema.prisma +1843 -0
- package/nestjs/prisma/seed.ts +375 -0
- package/nestjs/prisma.config.ts +14 -0
- package/nestjs/src/admin/admin.controller.ts +27 -0
- package/nestjs/src/admin/admin.module.ts +16 -0
- package/nestjs/src/admin/admin.service.ts +91 -0
- package/nestjs/src/admin/ai-chat-session.service.ts +454 -0
- package/nestjs/src/admin/ai-test.controller.ts +191 -0
- package/nestjs/src/admin/superadmin.controller.ts +106 -0
- package/nestjs/src/agents/agents.controller.ts +262 -0
- package/nestjs/src/agents/agents.module.ts +13 -0
- package/nestjs/src/agents/agents.service.ts +733 -0
- package/nestjs/src/agents/dto/create-agent.dto.ts +99 -0
- package/nestjs/src/agents/dto/index.ts +2 -0
- package/nestjs/src/agents/dto/update-agent.dto.ts +148 -0
- package/nestjs/src/app.module.ts +115 -0
- package/nestjs/src/appointment-validation/appointment-validation.controller.ts +194 -0
- package/nestjs/src/appointment-validation/appointment-validation.module.ts +16 -0
- package/nestjs/src/appointment-validation/appointment-validation.service.ts +450 -0
- package/nestjs/src/appointment-validation/dto/create-batch.dto.ts +105 -0
- package/nestjs/src/appointment-validation/dto/index.ts +1 -0
- package/nestjs/src/appointment-validation/processors/validation-dispatch.processor.ts +26 -0
- package/nestjs/src/appointment-validation/processors/validation-sync.processor.ts +23 -0
- package/nestjs/src/appointments/appointments.controller.ts +268 -0
- package/nestjs/src/appointments/appointments.module.ts +13 -0
- package/nestjs/src/appointments/appointments.service.ts +773 -0
- package/nestjs/src/appointments/dto/create-appointment.dto.ts +72 -0
- package/nestjs/src/appointments/dto/index.ts +4 -0
- package/nestjs/src/appointments/dto/query-appointments.dto.ts +60 -0
- package/nestjs/src/appointments/dto/update-appointment.dto.ts +43 -0
- package/nestjs/src/appointments/dto/update-status.dto.ts +18 -0
- package/nestjs/src/appointments/processors/reminder.processor.ts +243 -0
- package/nestjs/src/audit/audit.controller.ts +84 -0
- package/nestjs/src/audit/audit.decorator.ts +20 -0
- package/nestjs/src/audit/audit.interceptor.ts +67 -0
- package/nestjs/src/audit/audit.interfaces.ts +15 -0
- package/nestjs/src/audit/audit.module.ts +12 -0
- package/nestjs/src/audit/audit.service.ts +177 -0
- package/nestjs/src/auth/auth.controller.ts +116 -0
- package/nestjs/src/auth/auth.module.ts +25 -0
- package/nestjs/src/auth/auth.service.ts +612 -0
- package/nestjs/src/auth/dto/change-password.dto.ts +13 -0
- package/nestjs/src/auth/dto/forgot-password.dto.ts +8 -0
- package/nestjs/src/auth/dto/index.ts +6 -0
- package/nestjs/src/auth/dto/login.dto.ts +13 -0
- package/nestjs/src/auth/dto/refresh.dto.ts +8 -0
- package/nestjs/src/auth/dto/register.dto.ts +28 -0
- package/nestjs/src/auth/dto/reset-password.dto.ts +13 -0
- package/nestjs/src/auth/permissions.config.ts +85 -0
- package/nestjs/src/availability-blocks/availability-blocks.controller.ts +83 -0
- package/nestjs/src/availability-blocks/availability-blocks.module.ts +10 -0
- package/nestjs/src/availability-blocks/availability-blocks.service.ts +202 -0
- package/nestjs/src/billing/billing.controller.ts +104 -0
- package/nestjs/src/billing/billing.module.ts +12 -0
- package/nestjs/src/billing/billing.service.ts +398 -0
- package/nestjs/src/billing/dto/index.ts +2 -0
- package/nestjs/src/billing/dto/query-billing.dto.ts +29 -0
- package/nestjs/src/billing/dto/record-payment.dto.ts +60 -0
- package/nestjs/src/billing/processors/alerts.processor.ts +216 -0
- package/nestjs/src/billing/processors/snapshot.processor.ts +181 -0
- package/nestjs/src/calls/calls.controller.ts +92 -0
- package/nestjs/src/calls/calls.module.ts +10 -0
- package/nestjs/src/calls/calls.service.ts +359 -0
- package/nestjs/src/calls/dto/index.ts +1 -0
- package/nestjs/src/calls/dto/query-calls.dto.ts +46 -0
- package/nestjs/src/clinics/clinics.controller.ts +226 -0
- package/nestjs/src/clinics/clinics.module.ts +11 -0
- package/nestjs/src/clinics/clinics.service.ts +203 -0
- package/nestjs/src/clinics/dto/create-clinic.dto.ts +40 -0
- package/nestjs/src/clinics/dto/create-meta-connection.dto.ts +44 -0
- package/nestjs/src/clinics/dto/index.ts +4 -0
- package/nestjs/src/clinics/dto/update-clinic.dto.ts +133 -0
- package/nestjs/src/clinics/dto/update-meta-connection.dto.ts +22 -0
- package/nestjs/src/clinics/meta-connections.service.ts +210 -0
- package/nestjs/src/common/decorators/accessible-clinics.decorator.ts +9 -0
- package/nestjs/src/common/decorators/current-user.decorator.ts +10 -0
- package/nestjs/src/common/decorators/features.decorator.ts +7 -0
- package/nestjs/src/common/decorators/index.ts +4 -0
- package/nestjs/src/common/decorators/permissions.decorator.ts +13 -0
- package/nestjs/src/common/gateways/events.gateway.ts +157 -0
- package/nestjs/src/common/guards/clinic-access.guard.ts +40 -0
- package/nestjs/src/common/guards/features.guard.ts +38 -0
- package/nestjs/src/common/guards/index.ts +5 -0
- package/nestjs/src/common/guards/jwt-auth.guard.ts +54 -0
- package/nestjs/src/common/guards/permissions.guard.ts +50 -0
- package/nestjs/src/common/guards/superadmin.guard.ts +35 -0
- package/nestjs/src/common/interceptors/request-context.interceptor.ts +32 -0
- package/nestjs/src/common/interceptors/superadmin-tenant.interceptor.ts +42 -0
- package/nestjs/src/common/interfaces/jwt-payload.interface.ts +11 -0
- package/nestjs/src/config/config.module.ts +13 -0
- package/nestjs/src/database/database.module.ts +9 -0
- package/nestjs/src/database/prisma.service.ts +20 -0
- package/nestjs/src/database/tenant-context.ts +27 -0
- package/nestjs/src/google-calendar/google-calendar.controller.ts +259 -0
- package/nestjs/src/google-calendar/google-calendar.module.ts +10 -0
- package/nestjs/src/google-calendar/google-calendar.service.ts +811 -0
- package/nestjs/src/integrations/ai/ai.interface.ts +74 -0
- package/nestjs/src/integrations/ai/ai.module.ts +11 -0
- package/nestjs/src/integrations/ai/ai.service.ts +148 -0
- package/nestjs/src/integrations/ai/providers/anthropic.provider.ts +146 -0
- package/nestjs/src/integrations/ai/providers/openai.provider.ts +158 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.module.ts +8 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.service.ts +226 -0
- package/nestjs/src/integrations/encryption/encryption.module.ts +9 -0
- package/nestjs/src/integrations/encryption/encryption.service.ts +31 -0
- package/nestjs/src/integrations/image/image.module.ts +9 -0
- package/nestjs/src/integrations/image/image.service.ts +61 -0
- package/nestjs/src/integrations/meta-business/meta-business.module.ts +10 -0
- package/nestjs/src/integrations/meta-business/meta-instagram.service.ts +94 -0
- package/nestjs/src/integrations/meta-business/meta-webhook.service.ts +52 -0
- package/nestjs/src/integrations/meta-business/meta-whatsapp.service.ts +254 -0
- package/nestjs/src/integrations/minio/minio.module.ts +9 -0
- package/nestjs/src/integrations/minio/minio.service.ts +88 -0
- package/nestjs/src/integrations/netgsm/netgsm.module.ts +8 -0
- package/nestjs/src/integrations/netgsm/netgsm.service.ts +17 -0
- package/nestjs/src/integrations/tektippay/tektippay.module.ts +8 -0
- package/nestjs/src/integrations/tektippay/tektippay.service.ts +23 -0
- package/nestjs/src/leads/dto/index.ts +2 -0
- package/nestjs/src/leads/dto/query-leads.dto.ts +50 -0
- package/nestjs/src/leads/dto/update-lead.dto.ts +26 -0
- package/nestjs/src/leads/leads.controller.ts +184 -0
- package/nestjs/src/leads/leads.module.ts +10 -0
- package/nestjs/src/leads/leads.service.ts +375 -0
- package/nestjs/src/logging/logging.controller.ts +82 -0
- package/nestjs/src/logging/logging.module.ts +11 -0
- package/nestjs/src/logging/logging.service.ts +180 -0
- package/nestjs/src/main.ts +86 -0
- package/nestjs/src/outbound-campaigns/dto/create-campaign.dto.ts +47 -0
- package/nestjs/src/outbound-campaigns/dto/index.ts +3 -0
- package/nestjs/src/outbound-campaigns/dto/update-campaign.dto.ts +31 -0
- package/nestjs/src/outbound-campaigns/dto/upload-entries.dto.ts +35 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.controller.ts +307 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.module.ts +13 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.service.ts +471 -0
- package/nestjs/src/outbound-campaigns/processors/campaign-dispatch.processor.ts +366 -0
- package/nestjs/src/patients/documents.service.ts +231 -0
- package/nestjs/src/patients/dto/create-examination.dto.ts +34 -0
- package/nestjs/src/patients/dto/create-note.dto.ts +14 -0
- package/nestjs/src/patients/dto/create-patient.dto.ts +86 -0
- package/nestjs/src/patients/dto/create-photo-set.dto.ts +32 -0
- package/nestjs/src/patients/dto/index.ts +10 -0
- package/nestjs/src/patients/dto/update-examination.dto.ts +29 -0
- package/nestjs/src/patients/dto/update-medical-history.dto.ts +47 -0
- package/nestjs/src/patients/dto/update-patient.dto.ts +87 -0
- package/nestjs/src/patients/dto/update-photo-set.dto.ts +28 -0
- package/nestjs/src/patients/dto/upload-document.dto.ts +31 -0
- package/nestjs/src/patients/dto/upload-photo.dto.ts +27 -0
- package/nestjs/src/patients/examinations.service.ts +271 -0
- package/nestjs/src/patients/medical-history.service.ts +149 -0
- package/nestjs/src/patients/notes.service.ts +172 -0
- package/nestjs/src/patients/patients.controller.ts +485 -0
- package/nestjs/src/patients/patients.module.ts +22 -0
- package/nestjs/src/patients/patients.service.ts +412 -0
- package/nestjs/src/patients/photo-sets.service.ts +389 -0
- package/nestjs/src/phone-numbers/dto/create-phone-number.dto.ts +57 -0
- package/nestjs/src/phone-numbers/dto/index.ts +3 -0
- package/nestjs/src/phone-numbers/dto/update-phone-number.dto.ts +38 -0
- package/nestjs/src/phone-numbers/dto/update-schedule.dto.ts +39 -0
- package/nestjs/src/phone-numbers/phone-numbers.controller.ts +125 -0
- package/nestjs/src/phone-numbers/phone-numbers.module.ts +10 -0
- package/nestjs/src/phone-numbers/phone-numbers.service.ts +209 -0
- package/nestjs/src/plans/dto/create-plan.dto.ts +70 -0
- package/nestjs/src/plans/dto/index.ts +2 -0
- package/nestjs/src/plans/dto/update-plan.dto.ts +80 -0
- package/nestjs/src/plans/plans.controller.ts +50 -0
- package/nestjs/src/plans/plans.module.ts +10 -0
- package/nestjs/src/plans/plans.service.ts +115 -0
- package/nestjs/src/platform/dto/create-tenant.dto.ts +36 -0
- package/nestjs/src/platform/dto/index.ts +2 -0
- package/nestjs/src/platform/dto/update-tenant-platform.dto.ts +44 -0
- package/nestjs/src/platform/platform.controller.ts +79 -0
- package/nestjs/src/platform/platform.module.ts +10 -0
- package/nestjs/src/platform/platform.service.ts +301 -0
- package/nestjs/src/queue/queue.module.ts +56 -0
- package/nestjs/src/redis/redis.module.ts +20 -0
- package/nestjs/src/tenants/dto/index.ts +1 -0
- package/nestjs/src/tenants/dto/update-tenant.dto.ts +15 -0
- package/nestjs/src/tenants/tenants.controller.ts +45 -0
- package/nestjs/src/tenants/tenants.module.ts +10 -0
- package/nestjs/src/tenants/tenants.service.ts +41 -0
- package/nestjs/src/tools/adapters/elevenlabs-http.adapter.ts +51 -0
- package/nestjs/src/tools/adapters/elevenlabs-ws.adapter.ts +59 -0
- package/nestjs/src/tools/handlers/calendar.tools.ts +441 -0
- package/nestjs/src/tools/handlers/notification.tools.ts +34 -0
- package/nestjs/src/tools/handlers/operator.tools.ts +303 -0
- package/nestjs/src/tools/handlers/patient.tools.ts +242 -0
- package/nestjs/src/tools/handlers/payment.tools.ts +43 -0
- package/nestjs/src/tools/handlers/validation.tools.ts +152 -0
- package/nestjs/src/tools/tool-registry.service.ts +221 -0
- package/nestjs/src/tools/tool.decorator.ts +16 -0
- package/nestjs/src/tools/tool.interfaces.ts +26 -0
- package/nestjs/src/tools/tools.module.ts +50 -0
- package/nestjs/src/treatments/dto/create-plan-item.dto.ts +27 -0
- package/nestjs/src/treatments/dto/create-treatment-plan.dto.ts +69 -0
- package/nestjs/src/treatments/dto/create-treatment.dto.ts +59 -0
- package/nestjs/src/treatments/dto/index.ts +6 -0
- package/nestjs/src/treatments/dto/update-plan-item.dto.ts +23 -0
- package/nestjs/src/treatments/dto/update-treatment-plan.dto.ts +22 -0
- package/nestjs/src/treatments/dto/update-treatment.dto.ts +70 -0
- package/nestjs/src/treatments/treatment-plans.service.ts +362 -0
- package/nestjs/src/treatments/treatments.controller.ts +265 -0
- package/nestjs/src/treatments/treatments.module.ts +14 -0
- package/nestjs/src/treatments/treatments.service.ts +165 -0
- package/nestjs/src/users/doctor-profiles.service.ts +202 -0
- package/nestjs/src/users/dto/index.ts +4 -0
- package/nestjs/src/users/dto/invite-user.dto.ts +52 -0
- package/nestjs/src/users/dto/update-clinic-assignments.dto.ts +9 -0
- package/nestjs/src/users/dto/update-doctor-profile.dto.ts +49 -0
- package/nestjs/src/users/dto/update-user.dto.ts +41 -0
- package/nestjs/src/users/users.controller.ts +142 -0
- package/nestjs/src/users/users.module.ts +11 -0
- package/nestjs/src/users/users.service.ts +250 -0
- package/nestjs/src/webhooks/elevenlabs-tool.controller.ts +66 -0
- package/nestjs/src/webhooks/elevenlabs-webhook.controller.ts +60 -0
- package/nestjs/src/webhooks/meta-webhook.controller.ts +178 -0
- package/nestjs/src/webhooks/processors/elevenlabs-webhook.processor.ts +28 -0
- package/nestjs/src/webhooks/webhooks.module.ts +17 -0
- package/nestjs/src/whatsapp/chat-context.service.ts +281 -0
- package/nestjs/src/whatsapp/dto/add-blacklist.dto.ts +13 -0
- package/nestjs/src/whatsapp/dto/index.ts +2 -0
- package/nestjs/src/whatsapp/dto/send-message.dto.ts +14 -0
- package/nestjs/src/whatsapp/listeners/meta-message.listener.ts +579 -0
- package/nestjs/src/whatsapp/meta-auth.controller.ts +268 -0
- package/nestjs/src/whatsapp/meta-instagram-auth.controller.ts +244 -0
- package/nestjs/src/whatsapp/operator.service.ts +692 -0
- package/nestjs/src/whatsapp/processors/cost-sweep.processor.ts +130 -0
- package/nestjs/src/whatsapp/processors/grace.processor.ts +191 -0
- package/nestjs/src/whatsapp/processors/message-buffer.processor.ts +138 -0
- package/nestjs/src/whatsapp/processors/message-cleanup.processor.ts +148 -0
- package/nestjs/src/whatsapp/processors/meta-token-refresh.processor.ts +114 -0
- package/nestjs/src/whatsapp/processors/operator-expiry.processor.ts +105 -0
- package/nestjs/src/whatsapp/processors/profile-update.processor.ts +234 -0
- package/nestjs/src/whatsapp/processors/session-cleanup.processor.ts +178 -0
- package/nestjs/src/whatsapp/processors/session-labels.processor.ts +248 -0
- package/nestjs/src/whatsapp/whatsapp-agent.service.ts +2506 -0
- package/nestjs/src/whatsapp/whatsapp-recovery.service.ts +117 -0
- package/nestjs/src/whatsapp/whatsapp.controller.ts +398 -0
- package/nestjs/src/whatsapp/whatsapp.module.ts +51 -0
- package/nestjs/src/whatsapp/whatsapp.service.ts +592 -0
- package/nestjs/test/app.e2e-spec.ts +25 -0
- package/nestjs/test/jest-e2e.json +9 -0
- package/nestjs/tsconfig.build.json +4 -0
- package/nestjs/tsconfig.json +25 -0
- package/nginx.example.conf +18 -0
- package/package.json +47 -0
- package/scripts/addRecipient.ts +48 -0
- package/scripts/listRecipients.ts +31 -0
- package/scripts/migrate-agent-ref.ts +86 -0
- package/scripts/migrate-sessions.ts +183 -0
- package/scripts/promote.ts +27 -0
- package/scripts/retrigger.ts +84 -0
- package/scripts/seed.ts +435 -0
- package/scripts/testSend.ts +63 -0
- package/src/app.ts +85 -0
- package/src/config/agentPrompts.json +19 -0
- package/src/config/database.ts +21 -0
- package/src/config/env.ts +94 -0
- package/src/config/index.ts +86 -0
- package/src/controllers/webhook.controller.ts +150 -0
- package/src/errors/index.ts +9 -0
- package/src/hooks/webhookValidator.ts +55 -0
- package/src/index.ts +68 -0
- package/src/migrations/001_session-architecture.ts +138 -0
- package/src/migrations/002_agent-ref.ts +65 -0
- package/src/migrations/003_shift-schedule.ts +55 -0
- package/src/migrations/004_invert-shift-to-clinic-hours.ts +30 -0
- package/src/migrations/005_composite-baileys-chat-ids.ts +60 -0
- package/src/migrations/migration.model.ts +27 -0
- package/src/migrations/migration.routes.ts +40 -0
- package/src/migrations/migration.runner.ts +112 -0
- package/src/models/ConversationClaim.ts +17 -0
- package/src/models/ConversationEvaluation.ts +91 -0
- package/src/models/Summary.ts +77 -0
- package/src/models/Transcription.ts +68 -0
- package/src/models/WhatsAppRecipient.ts +37 -0
- package/src/modules/admin/admin.routes.ts +2385 -0
- package/src/modules/admin/elevenlabs-test-chat.routes.ts +193 -0
- package/src/modules/admin/whatsapp-test-chat.routes.ts +244 -0
- package/src/modules/agents/agent.model.ts +93 -0
- package/src/modules/agents/agent.routes.ts +65 -0
- package/src/modules/appointment-validation/appointment-validation.model.ts +163 -0
- package/src/modules/appointment-validation/appointment-validation.routes.ts +275 -0
- package/src/modules/appointment-validation/appointment-validation.service.ts +1028 -0
- package/src/modules/auth/auth.model.ts +42 -0
- package/src/modules/auth/auth.routes.ts +199 -0
- package/src/modules/auth/auth.service.ts +210 -0
- package/src/modules/auth/refresh-token.model.ts +26 -0
- package/src/modules/billing/billing-alert.model.ts +28 -0
- package/src/modules/billing/billing-period-snapshot.model.ts +68 -0
- package/src/modules/billing/billing.model.ts +42 -0
- package/src/modules/billing/billing.routes.ts +67 -0
- package/src/modules/billing/billing.service.ts +562 -0
- package/src/modules/billing/payment.model.ts +42 -0
- package/src/modules/calls/call.model.ts +102 -0
- package/src/modules/calls/call.routes.ts +118 -0
- package/src/modules/campaigns/campaign.model.ts +111 -0
- package/src/modules/campaigns/campaign.routes.ts +402 -0
- package/src/modules/campaigns/campaign.service.ts +99 -0
- package/src/modules/google-calendar/google-calendar.routes.ts +31 -0
- package/src/modules/inbound-call/inbound-call-config.model.ts +49 -0
- package/src/modules/inbound-call/inbound-call.routes.ts +289 -0
- package/src/modules/leads/lead.model.ts +40 -0
- package/src/modules/leads/lead.routes.ts +246 -0
- package/src/modules/logs/log.model.ts +27 -0
- package/src/modules/logs/log.routes.ts +102 -0
- package/src/modules/plans/plan.model.ts +45 -0
- package/src/modules/surveys/survey.model.ts +70 -0
- package/src/modules/surveys/survey.routes.ts +187 -0
- package/src/modules/tenants/tenant.model.ts +181 -0
- package/src/modules/tenants/tenant.routes.ts +78 -0
- package/src/modules/users/user.routes.ts +126 -0
- package/src/modules/webhooks/elevenlabs-tool.routes.ts +94 -0
- package/src/modules/webhooks/elevenlabs-tool.service.ts +491 -0
- package/src/modules/webhooks/elevenlabs.routes.ts +34 -0
- package/src/modules/webhooks/elevenlabs.service.ts +565 -0
- package/src/modules/webhooks/unipile.routes.ts +917 -0
- package/src/modules/whatsapp/appointment.model.ts +47 -0
- package/src/modules/whatsapp/operator-request.model.ts +58 -0
- package/src/modules/whatsapp/stt-usage.model.ts +30 -0
- package/src/modules/whatsapp/whatsapp-chat.model.ts +39 -0
- package/src/modules/whatsapp/whatsapp-contact-profile.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-message.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-session.model.ts +60 -0
- package/src/modules/whatsapp/whatsapp.routes.ts +1435 -0
- package/src/plugins/cors.ts +7 -0
- package/src/plugins/jwt.ts +18 -0
- package/src/plugins/rawBody.ts +12 -0
- package/src/plugins/rbac.ts +24 -0
- package/src/routes/admin.routes.ts +208 -0
- package/src/routes/health.routes.ts +12 -0
- package/src/routes/webhook.routes.ts +12 -0
- package/src/services/ai/base.ai.ts +132 -0
- package/src/services/ai/gemini.service.ts +41 -0
- package/src/services/ai/index.ts +24 -0
- package/src/services/ai/openai.service.ts +48 -0
- package/src/services/elevenlabs.service.ts +532 -0
- package/src/services/google-calendar.service.ts +656 -0
- package/src/services/inbound-call-schedule.service.ts +174 -0
- package/src/services/netgsm.service.ts +128 -0
- package/src/services/unipile.service.ts +200 -0
- package/src/services/whatsapp-agent.service.ts +2479 -0
- package/src/services/whatsapp.service.ts +245 -0
- package/src/templates/index.ts +71 -0
- package/src/templates/receptionist.ts +44 -0
- package/src/templates/survey.ts +44 -0
- package/src/types/index.ts +218 -0
- package/src/utils/logger.ts +83 -0
- package/tsconfig.json +19 -0
- package/web/.dockerignore +4 -0
- package/web/Dockerfile +18 -0
- package/web/README.md +73 -0
- package/web/components.json +23 -0
- package/web/eslint.config.js +23 -0
- package/web/index.html +14 -0
- package/web/nginx.conf +18 -0
- package/web/package-lock.json +10292 -0
- package/web/package.json +48 -0
- package/web/public/favicon.ico +0 -0
- package/web/public/vite.svg +1 -0
- package/web/src/App.tsx +191 -0
- package/web/src/assets/react.svg +1 -0
- package/web/src/components/Layout.tsx +261 -0
- package/web/src/components/LeadConversation.tsx +251 -0
- package/web/src/components/ProtectedRoute.tsx +20 -0
- package/web/src/components/conversation-review/CardAudioPlayer.tsx +200 -0
- package/web/src/components/conversation-review/CardEvaluation.tsx +351 -0
- package/web/src/components/conversation-review/CardMetadata.tsx +44 -0
- package/web/src/components/conversation-review/ConversationCard.tsx +95 -0
- package/web/src/components/conversation-review/ReviewContainer.tsx +120 -0
- package/web/src/components/conversation-review/ReviewFilters.tsx +88 -0
- package/web/src/components/empty-state.tsx +15 -0
- package/web/src/components/page-header.tsx +19 -0
- package/web/src/components/page-loader.tsx +32 -0
- package/web/src/components/pagination.tsx +38 -0
- package/web/src/components/stat-card.tsx +27 -0
- package/web/src/components/status-badge.tsx +125 -0
- package/web/src/components/ui/alert.tsx +66 -0
- package/web/src/components/ui/badge.tsx +48 -0
- package/web/src/components/ui/button.tsx +64 -0
- package/web/src/components/ui/card.tsx +92 -0
- package/web/src/components/ui/checkbox.tsx +32 -0
- package/web/src/components/ui/dialog.tsx +158 -0
- package/web/src/components/ui/dropdown-menu.tsx +255 -0
- package/web/src/components/ui/input.tsx +21 -0
- package/web/src/components/ui/label.tsx +22 -0
- package/web/src/components/ui/progress.tsx +29 -0
- package/web/src/components/ui/select.tsx +188 -0
- package/web/src/components/ui/separator.tsx +28 -0
- package/web/src/components/ui/sheet.tsx +123 -0
- package/web/src/components/ui/skeleton.tsx +13 -0
- package/web/src/components/ui/sonner.tsx +35 -0
- package/web/src/components/ui/table.tsx +116 -0
- package/web/src/components/ui/tabs.tsx +89 -0
- package/web/src/components/ui/textarea.tsx +18 -0
- package/web/src/components/ui/tooltip.tsx +57 -0
- package/web/src/components/whatsapp/ChatDetail.tsx +417 -0
- package/web/src/components/whatsapp/ChatHeader.tsx +78 -0
- package/web/src/components/whatsapp/ChatList.tsx +107 -0
- package/web/src/components/whatsapp/ChatListItem.tsx +60 -0
- package/web/src/components/whatsapp/MessageBubble.tsx +46 -0
- package/web/src/components/whatsapp/MessageInput.tsx +63 -0
- package/web/src/components/whatsapp/MessageStream.tsx +135 -0
- package/web/src/components/whatsapp/SessionDivider.tsx +65 -0
- package/web/src/components/whatsapp/SessionHistory.tsx +119 -0
- package/web/src/components/whatsapp/settings/AiSettingsTab.tsx +268 -0
- package/web/src/components/whatsapp/settings/CalendarTab.tsx +339 -0
- package/web/src/components/whatsapp/settings/ConnectionTab.tsx +236 -0
- package/web/src/components/whatsapp/settings/NotificationsTab.tsx +109 -0
- package/web/src/components/whatsapp/settings/OperatorTab.tsx +303 -0
- package/web/src/contexts/AuthContext.tsx +103 -0
- package/web/src/index.css +130 -0
- package/web/src/lib/api.ts +92 -0
- package/web/src/lib/utils.ts +6 -0
- package/web/src/main.tsx +10 -0
- package/web/src/pages/AppointmentDetail.tsx +206 -0
- package/web/src/pages/AppointmentValidation.tsx +157 -0
- package/web/src/pages/AppointmentValidationDetail.tsx +617 -0
- package/web/src/pages/AppointmentValidationNew.tsx +1005 -0
- package/web/src/pages/Appointments.tsx +283 -0
- package/web/src/pages/Billing.tsx +126 -0
- package/web/src/pages/CallDetail.tsx +293 -0
- package/web/src/pages/CallSettings.tsx +313 -0
- package/web/src/pages/Calls.tsx +188 -0
- package/web/src/pages/CampaignDetail.tsx +216 -0
- package/web/src/pages/CampaignNew.tsx +277 -0
- package/web/src/pages/CampaignResults.tsx +185 -0
- package/web/src/pages/Campaigns.tsx +171 -0
- package/web/src/pages/Dashboard.tsx +336 -0
- package/web/src/pages/InboundSchedule.tsx +246 -0
- package/web/src/pages/LeadDetail.tsx +183 -0
- package/web/src/pages/Leads.tsx +258 -0
- package/web/src/pages/Login.tsx +99 -0
- package/web/src/pages/Register.tsx +129 -0
- package/web/src/pages/Settings.tsx +133 -0
- package/web/src/pages/SurveyDetail.tsx +232 -0
- package/web/src/pages/SurveyPreview.tsx +179 -0
- package/web/src/pages/Surveys.tsx +207 -0
- package/web/src/pages/Users.tsx +199 -0
- package/web/src/pages/WhatsApp.tsx +147 -0
- package/web/src/pages/WhatsAppSettings.tsx +215 -0
- package/web/src/pages/admin/AdminBaileysMessageLog.tsx +331 -0
- package/web/src/pages/admin/AdminBaileysRawMessages.tsx +318 -0
- package/web/src/pages/admin/AdminConversationReview.tsx +116 -0
- package/web/src/pages/admin/AdminCredits.tsx +467 -0
- package/web/src/pages/admin/AdminElevenLabsAgentDetail.tsx +332 -0
- package/web/src/pages/admin/AdminElevenLabsAgents.tsx +164 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCallDetail.tsx +214 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsConversationDetail.tsx +230 -0
- package/web/src/pages/admin/AdminElevenLabsConversations.tsx +228 -0
- package/web/src/pages/admin/AdminElevenLabsInboundCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsPhoneNumbers.tsx +506 -0
- package/web/src/pages/admin/AdminElevenLabsTestChat.tsx +258 -0
- package/web/src/pages/admin/AdminElevenLabsWebhooks.tsx +289 -0
- package/web/src/pages/admin/AdminEvaluationDashboard.tsx +520 -0
- package/web/src/pages/admin/AdminLeads.tsx +339 -0
- package/web/src/pages/admin/AdminLogs.tsx +283 -0
- package/web/src/pages/admin/AdminMigrations.tsx +247 -0
- package/web/src/pages/admin/AdminPlans.tsx +313 -0
- package/web/src/pages/admin/AdminSystem.tsx +391 -0
- package/web/src/pages/admin/AdminTenantBillableItems.tsx +464 -0
- package/web/src/pages/admin/AdminTenantDetail.tsx +1317 -0
- package/web/src/pages/admin/AdminTenants.tsx +274 -0
- package/web/src/pages/admin/AdminWhatsApp.tsx +618 -0
- package/web/src/pages/admin/AdminWhatsAppTestChat.tsx +328 -0
- package/web/src/types/index.ts +242 -0
- package/web/tsconfig.app.json +32 -0
- package/web/tsconfig.json +13 -0
- package/web/tsconfig.node.json +26 -0
- 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
|
+
```
|