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,458 @@
|
|
|
1
|
+
# 11. Billing System
|
|
2
|
+
|
|
3
|
+
> Subscription-based billing with usage tracking, period snapshots, and threshold alerts.
|
|
4
|
+
> Last updated: 2026-04-02
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Billing Model Overview
|
|
9
|
+
|
|
10
|
+
The billing system tracks tenant subscriptions, usage, and charges through five interconnected entities:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Plan (defines quotas)
|
|
14
|
+
|
|
|
15
|
+
v
|
|
16
|
+
Payment (subscription period with start/end dates)
|
|
17
|
+
|
|
|
18
|
+
+---> BillingEvent (append-only ledger of charges/credits)
|
|
19
|
+
|
|
|
20
|
+
+---> BillingPeriodSnapshot (immutable usage record at period end)
|
|
21
|
+
|
|
|
22
|
+
+---> BillingAlert (threshold notifications: 75%, 90%, 100%)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Plans
|
|
26
|
+
|
|
27
|
+
Plans define what a tenant gets. They are system-wide records managed by superadmin/sales.
|
|
28
|
+
|
|
29
|
+
```prisma
|
|
30
|
+
model Plan {
|
|
31
|
+
plan_id String @id @default(uuid())
|
|
32
|
+
plan_name String
|
|
33
|
+
plan_slug String @unique
|
|
34
|
+
plan_price Decimal
|
|
35
|
+
plan_overage_rate Decimal
|
|
36
|
+
plan_included_inbound_minutes Int @default(0)
|
|
37
|
+
plan_included_inbound_calls Int @default(0)
|
|
38
|
+
plan_included_outbound_minutes Int @default(0)
|
|
39
|
+
plan_included_outbound_calls Int @default(0)
|
|
40
|
+
plan_included_wa_chats Int @default(0)
|
|
41
|
+
plan_language_support LanguageSupport @default(single)
|
|
42
|
+
plan_is_active Boolean @default(true)
|
|
43
|
+
plan_sort_order Int @default(0)
|
|
44
|
+
plan_created_at DateTime @default(now())
|
|
45
|
+
plan_updated_at DateTime @updatedAt
|
|
46
|
+
|
|
47
|
+
tenants Tenant[]
|
|
48
|
+
snapshots BillingPeriodSnapshot[]
|
|
49
|
+
|
|
50
|
+
@@map("plans")
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Quotas defined per plan:
|
|
55
|
+
|
|
56
|
+
| Quota | Field | Description |
|
|
57
|
+
|-------|-------|-------------|
|
|
58
|
+
| Inbound minutes | `plan_included_inbound_minutes` | Total inbound call minutes per billing period |
|
|
59
|
+
| Inbound calls | `plan_included_inbound_calls` | Total inbound call count per period |
|
|
60
|
+
| Outbound minutes | `plan_included_outbound_minutes` | Outbound call minutes (campaigns, validation) |
|
|
61
|
+
| Outbound calls | `plan_included_outbound_calls` | Outbound call count |
|
|
62
|
+
| WhatsApp chats | `plan_included_wa_chats` | Billable WhatsApp chat sessions per period |
|
|
63
|
+
| Language support | `plan_language_support` | single, dual, or multi |
|
|
64
|
+
|
|
65
|
+
### Payments
|
|
66
|
+
|
|
67
|
+
A Payment represents a subscription period. Each payment has explicit start/end dates and is linked to the plan in effect at the time.
|
|
68
|
+
|
|
69
|
+
```prisma
|
|
70
|
+
model Payment {
|
|
71
|
+
payment_id String @id @default(uuid())
|
|
72
|
+
payment_tenant_id String
|
|
73
|
+
payment_type PaymentType @default(subscription) // subscription | top_up
|
|
74
|
+
payment_plan_slug String
|
|
75
|
+
payment_plan_name String
|
|
76
|
+
payment_amount Decimal
|
|
77
|
+
payment_extra_minutes Int @default(0)
|
|
78
|
+
payment_period_start DateTime
|
|
79
|
+
payment_period_end DateTime
|
|
80
|
+
payment_method PaymentMethod @default(cash) // cash | bank_transfer | other
|
|
81
|
+
payment_notes String @default("")
|
|
82
|
+
payment_recorded_by_id String
|
|
83
|
+
payment_created_at DateTime @default(now())
|
|
84
|
+
payment_updated_at DateTime @updatedAt
|
|
85
|
+
|
|
86
|
+
tenant Tenant @relation(...)
|
|
87
|
+
recordedBy User @relation("RecordedBy", ...)
|
|
88
|
+
snapshots BillingPeriodSnapshot[]
|
|
89
|
+
alerts BillingAlert[]
|
|
90
|
+
|
|
91
|
+
@@index([payment_tenant_id])
|
|
92
|
+
@@index([payment_tenant_id, payment_period_end(sort: Desc)])
|
|
93
|
+
@@map("payments")
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### BillingEvents (Append-Only Ledger)
|
|
98
|
+
|
|
99
|
+
Every financial event is recorded as an immutable billing event. These are never updated or deleted.
|
|
100
|
+
|
|
101
|
+
```prisma
|
|
102
|
+
model BillingEvent {
|
|
103
|
+
billing_id String @id @default(uuid())
|
|
104
|
+
billing_tenant_id String
|
|
105
|
+
billing_clinic_id String? // FK -> clinics. Populated for call_charge (copied from call's clinic_id). Null for tenant-level events (subscription, top_up).
|
|
106
|
+
billing_type BillingEventType // call_charge | subscription | top_up | refund
|
|
107
|
+
billing_amount Decimal
|
|
108
|
+
billing_currency String @default("TRY")
|
|
109
|
+
billing_call_id String?
|
|
110
|
+
billing_description String @default("")
|
|
111
|
+
billing_created_at DateTime @default(now())
|
|
112
|
+
|
|
113
|
+
tenant Tenant @relation(...)
|
|
114
|
+
clinic Clinic? @relation(...)
|
|
115
|
+
call Call? @relation(...)
|
|
116
|
+
|
|
117
|
+
@@index([billing_tenant_id, billing_created_at(sort: Desc)])
|
|
118
|
+
@@index([billing_clinic_id, billing_created_at(sort: Desc)])
|
|
119
|
+
@@map("billing_events")
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Event types:
|
|
124
|
+
|
|
125
|
+
| Type | When Created | `billing_clinic_id` |
|
|
126
|
+
|------|-------------|---------------------|
|
|
127
|
+
| `call_charge` | After each call completes -- amount = duration * overage_rate | Copied from the call's `call_clinic_id`. Enables per-clinic cost breakdown. |
|
|
128
|
+
| `subscription` | When a new payment/subscription is recorded | `null` (tenant-level event) |
|
|
129
|
+
| `top_up` | When extra minutes are purchased | `null` (tenant-level event) |
|
|
130
|
+
| `refund` | When a charge is refunded | Copied from the original event's clinic, if applicable |
|
|
131
|
+
|
|
132
|
+
### BillingPeriodSnapshots
|
|
133
|
+
|
|
134
|
+
An immutable snapshot of usage at the end of a billing period. Once created, it never changes. This provides a historical record even if the tenant's plan or data changes later.
|
|
135
|
+
|
|
136
|
+
```prisma
|
|
137
|
+
model BillingPeriodSnapshot {
|
|
138
|
+
snapshot_id String @id @default(uuid())
|
|
139
|
+
snapshot_tenant_id String
|
|
140
|
+
snapshot_payment_id String
|
|
141
|
+
snapshot_plan_id String? // FK to Plan at time of snapshot (historical reference)
|
|
142
|
+
snapshot_period_start DateTime
|
|
143
|
+
snapshot_period_end DateTime
|
|
144
|
+
|
|
145
|
+
// Actual usage during period
|
|
146
|
+
snapshot_total_calls Int @default(0)
|
|
147
|
+
snapshot_inbound_calls Int @default(0)
|
|
148
|
+
snapshot_inbound_minutes Decimal @default(0)
|
|
149
|
+
snapshot_outbound_calls Int @default(0)
|
|
150
|
+
snapshot_outbound_minutes Decimal @default(0)
|
|
151
|
+
snapshot_total_charges Decimal @default(0)
|
|
152
|
+
snapshot_wa_billable_chats Int @default(0)
|
|
153
|
+
snapshot_wa_total_sessions Int @default(0)
|
|
154
|
+
snapshot_wa_total_messages Int @default(0)
|
|
155
|
+
snapshot_wa_ai_messages Int @default(0)
|
|
156
|
+
snapshot_wa_el_cost Decimal @default(0)
|
|
157
|
+
snapshot_wa_stt_count Int @default(0)
|
|
158
|
+
snapshot_wa_stt_seconds Decimal @default(0)
|
|
159
|
+
snapshot_created_at DateTime @default(now())
|
|
160
|
+
|
|
161
|
+
tenant Tenant @relation(...)
|
|
162
|
+
payment Payment @relation(...)
|
|
163
|
+
plan Plan? @relation(...)
|
|
164
|
+
|
|
165
|
+
@@index([snapshot_tenant_id, snapshot_period_start(sort: Desc)])
|
|
166
|
+
@@map("billing_period_snapshots")
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The `snapshot_plan_id` field is critical: it captures which plan was active at the time of the snapshot. If the tenant upgrades later, historical snapshots still reference the old plan's limits for accurate reporting.
|
|
171
|
+
|
|
172
|
+
### BillingAlerts
|
|
173
|
+
|
|
174
|
+
Usage threshold alerts. A unique constraint prevents duplicate alerts for the same threshold within the same billing period.
|
|
175
|
+
|
|
176
|
+
```prisma
|
|
177
|
+
model BillingAlert {
|
|
178
|
+
alert_id String @id @default(uuid())
|
|
179
|
+
alert_tenant_id String
|
|
180
|
+
alert_type AlertType // usage_75 | usage_90 | usage_100 | period_expiring
|
|
181
|
+
alert_service AlertService // inbound_minutes | inbound_calls | wa_chats | billing_period
|
|
182
|
+
alert_payment_id String
|
|
183
|
+
alert_created_at DateTime @default(now())
|
|
184
|
+
|
|
185
|
+
tenant Tenant @relation(...)
|
|
186
|
+
payment Payment @relation(...)
|
|
187
|
+
|
|
188
|
+
@@unique([alert_tenant_id, alert_type, alert_service, alert_payment_id])
|
|
189
|
+
@@map("billing_alerts")
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The `@@unique` constraint on `[tenant_id, type, service, payment_id]` ensures a tenant receives at most one alert of each type per service per billing period. Once the "90% inbound minutes" alert fires, it will not fire again until the next period.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Call Charging
|
|
198
|
+
|
|
199
|
+
### Rate
|
|
200
|
+
|
|
201
|
+
Configurable per plan via `plan_overage_rate`. Current default: **1.5 TRY per minute**.
|
|
202
|
+
|
|
203
|
+
### Process
|
|
204
|
+
|
|
205
|
+
After each call completes (via ElevenLabs post-call webhook), the billing service creates a `BillingEvent`:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
async logCallCharge(tenantId: string, clinicId: string, callId: string, durationSeconds: number): Promise<void> {
|
|
209
|
+
const tenant = await this.prisma.tenant.findUnique({ where: { tenant_id: tenantId }, include: { plan: true } });
|
|
210
|
+
const rate = tenant.plan.plan_overage_rate;
|
|
211
|
+
const minutes = Math.ceil(durationSeconds / 60);
|
|
212
|
+
const amount = minutes * rate;
|
|
213
|
+
|
|
214
|
+
await this.prisma.billingEvent.create({
|
|
215
|
+
data: {
|
|
216
|
+
billing_tenant_id: tenantId,
|
|
217
|
+
billing_clinic_id: clinicId, // Copied from the call's clinic for per-clinic cost breakdown
|
|
218
|
+
billing_type: 'call_charge',
|
|
219
|
+
billing_amount: amount,
|
|
220
|
+
billing_call_id: callId,
|
|
221
|
+
billing_description: `Call charge: ${minutes} min @ ${rate} TRY/min`,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Usage Calculation
|
|
230
|
+
|
|
231
|
+
### Active Payment Lookup
|
|
232
|
+
|
|
233
|
+
The active payment is the one whose period has not yet ended. For a given tenant, find the payment where `payment_period_end > now()`, sorted by `payment_period_end DESC`, take the first result.
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
async getActivePayment(tenantId: string): Promise<Payment | null> {
|
|
237
|
+
return this.prisma.payment.findFirst({
|
|
238
|
+
where: {
|
|
239
|
+
payment_tenant_id: tenantId,
|
|
240
|
+
payment_period_end: { gt: new Date() },
|
|
241
|
+
},
|
|
242
|
+
orderBy: { payment_period_end: 'desc' },
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Index used:** `@@index([payment_tenant_id, payment_period_end(sort: Desc)])` -- this is the most critical billing index.
|
|
248
|
+
|
|
249
|
+
### Usage Aggregation
|
|
250
|
+
|
|
251
|
+
Within the active payment's period, aggregate:
|
|
252
|
+
|
|
253
|
+
| Metric | Source |
|
|
254
|
+
|--------|--------|
|
|
255
|
+
| Inbound calls / minutes | `Call` table (where `call_direction = 'inbound'`, `call_start_time` within period) |
|
|
256
|
+
| Outbound calls / minutes | `Call` table (where `call_direction = 'outbound'`, `call_start_time` within period) |
|
|
257
|
+
| WhatsApp sessions | `WhatsAppSession` table (where `created_at` within period, billable = true) |
|
|
258
|
+
| STT usage | `SttUsage` table (where `stt_created_at` within period) |
|
|
259
|
+
| Total charges | `BillingEvent` table (where `billing_created_at` within period, type = `call_charge`) |
|
|
260
|
+
|
|
261
|
+
### Comparison Against Quotas
|
|
262
|
+
|
|
263
|
+
The usage summary compares aggregated values against the plan's included quotas:
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
{
|
|
267
|
+
inbound_minutes: { used: 450, included: 500, percentage: 90 },
|
|
268
|
+
inbound_calls: { used: 120, included: 200, percentage: 60 },
|
|
269
|
+
wa_chats: { used: 80, included: 100, percentage: 80 },
|
|
270
|
+
total_charges: 675.00,
|
|
271
|
+
period: { start: '2026-03-01', end: '2026-03-31' },
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Period Lifecycle
|
|
278
|
+
|
|
279
|
+
### New Payment Recorded
|
|
280
|
+
|
|
281
|
+
When a new payment is recorded:
|
|
282
|
+
|
|
283
|
+
1. Find the previous active payment (if any).
|
|
284
|
+
2. Snapshot the previous period's usage (see below).
|
|
285
|
+
3. The new payment becomes the active payment.
|
|
286
|
+
|
|
287
|
+
### Period Snapshot on Expiry
|
|
288
|
+
|
|
289
|
+
A BullMQ repeatable job runs **daily at 00:05 Istanbul time**. It finds all payments where `payment_period_end < now()` and no snapshot exists yet, then creates an immutable `BillingPeriodSnapshot` for each.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// BullMQ job: billing:snapshot-expired-periods
|
|
293
|
+
// Cron: '5 0 * * *' (00:05 daily, Europe/Istanbul)
|
|
294
|
+
|
|
295
|
+
async snapshotExpiredPeriods(): Promise<void> {
|
|
296
|
+
const expiredPayments = await this.prisma.payment.findMany({
|
|
297
|
+
where: {
|
|
298
|
+
payment_period_end: { lt: new Date() },
|
|
299
|
+
snapshots: { none: {} }, // No snapshot yet
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
for (const payment of expiredPayments) {
|
|
304
|
+
const usage = await this.aggregateUsage(payment.payment_tenant_id, payment.payment_period_start, payment.payment_period_end);
|
|
305
|
+
await this.prisma.billingPeriodSnapshot.create({
|
|
306
|
+
data: {
|
|
307
|
+
snapshot_tenant_id: payment.payment_tenant_id,
|
|
308
|
+
snapshot_payment_id: payment.payment_id,
|
|
309
|
+
snapshot_plan_id: /* tenant's plan at time of snapshot */,
|
|
310
|
+
snapshot_period_start: payment.payment_period_start,
|
|
311
|
+
snapshot_period_end: payment.payment_period_end,
|
|
312
|
+
...usage,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Billing Alert Check
|
|
320
|
+
|
|
321
|
+
A separate BullMQ repeatable job runs **daily at 09:00 Istanbul time**. It checks each tenant's current usage against plan quotas and sends alerts via SMS (NetGSM) when thresholds are crossed.
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// BullMQ job: billing:check-alerts
|
|
325
|
+
// Cron: '0 9 * * *' (09:00 daily, Europe/Istanbul)
|
|
326
|
+
|
|
327
|
+
async checkAndSendBillingAlerts(): Promise<void> {
|
|
328
|
+
const tenants = await this.getTenantsWithActivePayments();
|
|
329
|
+
|
|
330
|
+
for (const { tenant, payment, plan } of tenants) {
|
|
331
|
+
const usage = await this.getUsageSummary(tenant.tenant_id);
|
|
332
|
+
|
|
333
|
+
for (const [service, metric] of [
|
|
334
|
+
['inbound_minutes', usage.inbound_minutes],
|
|
335
|
+
['inbound_calls', usage.inbound_calls],
|
|
336
|
+
['wa_chats', usage.wa_chats],
|
|
337
|
+
]) {
|
|
338
|
+
for (const threshold of [75, 90, 100]) {
|
|
339
|
+
if (metric.percentage >= threshold) {
|
|
340
|
+
// Unique constraint prevents duplicates
|
|
341
|
+
await this.prisma.billingAlert.create({
|
|
342
|
+
data: {
|
|
343
|
+
alert_tenant_id: tenant.tenant_id,
|
|
344
|
+
alert_type: `usage_${threshold}`,
|
|
345
|
+
alert_service: service,
|
|
346
|
+
alert_payment_id: payment.payment_id,
|
|
347
|
+
},
|
|
348
|
+
}).catch(() => {}); // Ignore unique constraint violation (already sent)
|
|
349
|
+
|
|
350
|
+
// Send SMS notification
|
|
351
|
+
await this.netgsmService.sendSms(tenant.manager_phone, alertMessage);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Alert types:**
|
|
360
|
+
|
|
361
|
+
| Type | Trigger |
|
|
362
|
+
|------|---------|
|
|
363
|
+
| `usage_75` | Usage reaches 75% of included quota |
|
|
364
|
+
| `usage_90` | Usage reaches 90% of included quota |
|
|
365
|
+
| `usage_100` | Usage reaches 100% -- overage charges begin |
|
|
366
|
+
| `period_expiring` | Billing period ends within 3 days |
|
|
367
|
+
|
|
368
|
+
**Services tracked:**
|
|
369
|
+
|
|
370
|
+
| Service | Quota Source |
|
|
371
|
+
|---------|-------------|
|
|
372
|
+
| `inbound_minutes` | `plan_included_inbound_minutes` |
|
|
373
|
+
| `inbound_calls` | `plan_included_inbound_calls` |
|
|
374
|
+
| `wa_chats` | `plan_included_wa_chats` |
|
|
375
|
+
| `billing_period` | Payment `period_end` date |
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Data Model Summary
|
|
380
|
+
|
|
381
|
+
### Tables
|
|
382
|
+
|
|
383
|
+
| Table | Purpose | Mutable |
|
|
384
|
+
|-------|---------|---------|
|
|
385
|
+
| `plans` | Define quotas and pricing | Yes (superadmin) |
|
|
386
|
+
| `payments` | Subscription periods with start/end dates | Yes (notes, method) |
|
|
387
|
+
| `billing_events` | Append-only ledger of all financial events | No (append-only) |
|
|
388
|
+
| `billing_period_snapshots` | Immutable usage record at period end | No (immutable) |
|
|
389
|
+
| `billing_alerts` | Usage threshold alert records | No (append-only) |
|
|
390
|
+
|
|
391
|
+
### Enums
|
|
392
|
+
|
|
393
|
+
```prisma
|
|
394
|
+
enum BillingEventType {
|
|
395
|
+
call_charge
|
|
396
|
+
subscription
|
|
397
|
+
top_up
|
|
398
|
+
refund
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
enum PaymentType {
|
|
402
|
+
subscription
|
|
403
|
+
top_up
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
enum PaymentMethod {
|
|
407
|
+
cash
|
|
408
|
+
bank_transfer
|
|
409
|
+
other
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
enum AlertType {
|
|
413
|
+
usage_75
|
|
414
|
+
usage_90
|
|
415
|
+
usage_100
|
|
416
|
+
period_expiring
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
enum AlertService {
|
|
420
|
+
inbound_minutes
|
|
421
|
+
inbound_calls
|
|
422
|
+
wa_chats
|
|
423
|
+
billing_period
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Key Indexes
|
|
428
|
+
|
|
429
|
+
| Index | Purpose |
|
|
430
|
+
|-------|---------|
|
|
431
|
+
| `payments(tenant_id, period_end DESC)` | Active payment lookup -- the single most important billing query |
|
|
432
|
+
| `billing_events(tenant_id, created_at DESC)` | Usage aggregation within a period |
|
|
433
|
+
| `billing_events(clinic_id, created_at DESC)` | Per-clinic cost breakdown |
|
|
434
|
+
| `billing_period_snapshots(tenant_id, period_start DESC)` | Historical period browsing |
|
|
435
|
+
| `billing_alerts(tenant_id, type, service, payment_id) UNIQUE` | Duplicate alert prevention |
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## BullMQ Jobs
|
|
440
|
+
|
|
441
|
+
| Job Name | Schedule | What It Does |
|
|
442
|
+
|----------|----------|-------------|
|
|
443
|
+
| `billing:snapshot-expired-periods` | Daily at 00:05 (Europe/Istanbul) | Snapshot usage for all expired billing periods |
|
|
444
|
+
| `billing:check-alerts` | Daily at 09:00 (Europe/Istanbul) | Check usage thresholds and send SMS alerts |
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Service API
|
|
449
|
+
|
|
450
|
+
The billing service exposes these methods (Mongoose calls migrated to Prisma):
|
|
451
|
+
|
|
452
|
+
| Method | Prisma Equivalent | Description |
|
|
453
|
+
|--------|-------------------|-------------|
|
|
454
|
+
| `logCallCharge()` | `prisma.billingEvent.create()` | Log a call charge as a billing event |
|
|
455
|
+
| `getUsageSummary()` | Prisma aggregation queries | Current period usage vs. plan quotas |
|
|
456
|
+
| `getActivePayment()` | `prisma.payment.findFirst()` with period_end filter | Find current active payment |
|
|
457
|
+
| `snapshotExpiredPeriods()` | BullMQ repeatable job | Create snapshots for ended periods |
|
|
458
|
+
| `checkAndSendBillingAlerts()` | BullMQ repeatable job | Check thresholds, send SMS via NetGSM |
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# 12. Security Architecture
|
|
2
|
+
|
|
3
|
+
> Security is not a separate phase -- it is built into every migration phase from day one.
|
|
4
|
+
> Last updated: 2026-04-02
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Data Encryption
|
|
9
|
+
|
|
10
|
+
### Six AES-256-GCM Encryption Keys
|
|
11
|
+
|
|
12
|
+
Each domain has its own encryption key to limit blast radius. Compromise of one key does not expose data encrypted by another.
|
|
13
|
+
|
|
14
|
+
| Key | Env Variable | What It Protects |
|
|
15
|
+
|-----|-------------|-----------------|
|
|
16
|
+
| Google tokens | `GOOGLE_TOKEN_ENCRYPTION_KEY` | OAuth2 refresh tokens for Google Calendar |
|
|
17
|
+
| Meta tokens | `META_TOKEN_ENCRYPTION_KEY` | Page access tokens, WABA tokens for WhatsApp Business + Instagram |
|
|
18
|
+
| TekTipPay | `TEKTIPPAY_ENCRYPTION_KEY` | Payment API keys and secrets |
|
|
19
|
+
| Message content | `MESSAGE_ENCRYPTION_KEY` | Chat message text (`msg_text`, `msg_quoted_text`), operator request text |
|
|
20
|
+
| Patient PII | `PATIENT_PII_ENCRYPTION_KEY` | TC Kimlik, medical history, exam records, clinical notes, document notes, photo set notes |
|
|
21
|
+
| Patient summaries | `PATIENT_SUMMARY_ENCRYPTION_KEY` | AI-generated patient summaries (`patient_ai_summary`) |
|
|
22
|
+
|
|
23
|
+
All keys are 32-byte hex strings. Generate with: `openssl rand -hex 32`.
|
|
24
|
+
|
|
25
|
+
### EncryptionService (Shared)
|
|
26
|
+
|
|
27
|
+
A single `@Injectable()` service used across the application. Each caller passes the appropriate key for their domain.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
@Injectable()
|
|
31
|
+
export class EncryptionService {
|
|
32
|
+
encrypt(plaintext: string, key: string): string {
|
|
33
|
+
const iv = randomBytes(12);
|
|
34
|
+
const cipher = createCipheriv('aes-256-gcm', Buffer.from(key, 'hex'), iv);
|
|
35
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
36
|
+
const authTag = cipher.getAuthTag();
|
|
37
|
+
return Buffer.concat([iv, authTag, encrypted]).toString('base64');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
decrypt(ciphertext: string, key: string): string {
|
|
41
|
+
const buf = Buffer.from(ciphertext, 'base64');
|
|
42
|
+
const iv = buf.subarray(0, 12);
|
|
43
|
+
const authTag = buf.subarray(12, 28);
|
|
44
|
+
const data = buf.subarray(28);
|
|
45
|
+
const decipher = createDecipheriv('aes-256-gcm', Buffer.from(key, 'hex'), iv);
|
|
46
|
+
decipher.setAuthTag(authTag);
|
|
47
|
+
return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Wire format:** `base64(iv[12] + authTag[16] + ciphertext[...])` -- a single opaque string stored in the database.
|
|
53
|
+
|
|
54
|
+
### Per-Domain Key Derivation
|
|
55
|
+
|
|
56
|
+
All encryption domains (Google, Meta, TekTipPay, messages, patient PII, patient summaries) use the master key directly since there is no per-entity isolation requirement. Tenant isolation is enforced at the query layer, not the encryption layer.
|
|
57
|
+
|
|
58
|
+
### What Is Encrypted at Field Level vs. Disk Level
|
|
59
|
+
|
|
60
|
+
| Data | Encryption Level | Reasoning |
|
|
61
|
+
|------|-----------------|-----------|
|
|
62
|
+
| Google OAuth refresh tokens | **Field-level** (AES-256-GCM) | Calendar access if exposed. Already encrypted in current system. |
|
|
63
|
+
| Meta Business access tokens | **Field-level** (AES-256-GCM) | WhatsApp Business + Instagram takeover if exposed. |
|
|
64
|
+
| TekTipPay credentials | **Field-level** (AES-256-GCM) | Payment system access if exposed. |
|
|
65
|
+
| Chat message text | **Field-level** (AES-256-GCM) | `msg_text`, `msg_quoted_text` encrypted with `MESSAGE_ENCRYPTION_KEY`. Operator request text also encrypted. |
|
|
66
|
+
| Patient PII (TC Kimlik, medical) | **Field-level** (AES-256-GCM) | TC Kimlik numbers, medical history, exam records, clinical notes, document notes, photo set notes encrypted with `PATIENT_PII_ENCRYPTION_KEY`. |
|
|
67
|
+
| Patient AI summaries | **Field-level** (AES-256-GCM) | `patient_ai_summary` encrypted with `PATIENT_SUMMARY_ENCRYPTION_KEY`. |
|
|
68
|
+
| User passwords | **One-way hash** (bcrypt 12 rounds) | Never decrypted. |
|
|
69
|
+
| Tenant API keys | **One-way hash** (bcrypt) | Prefix stored for identification, full key returned once on creation. |
|
|
70
|
+
| Patient phone numbers | **Disk-level** only | Must remain queryable for lookups. Protected by tenant isolation + disk encryption. |
|
|
71
|
+
| Voice recordings | **MinIO SSE** (server-side encryption) | Signed URLs with 1hr TTL control access. |
|
|
72
|
+
| Patient photos | **MinIO SSE** + signed URLs (1hr TTL) | Medical images. Never publicly accessible. |
|
|
73
|
+
| Call transcripts | **Disk-level** only | Must be searchable. Protected by tenant isolation. |
|
|
74
|
+
| CSV exports | **MinIO SSE** + signed URLs (1hr TTL) + auto-delete after 24h | Bulk PII. Time-limited access. |
|
|
75
|
+
|
|
76
|
+
**Design decision:** Chat message content, patient PII, and patient AI summaries are encrypted at the field level with dedicated keys. This means search and AI processing require decryption at the application layer. Phone numbers remain unencrypted for query lookups. Additional protection comes from: tenant-scoped queries (Prisma middleware), RBAC + clinic scoping, PostgreSQL disk encryption, network isolation, and audit logging.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Tenant Data Isolation
|
|
81
|
+
|
|
82
|
+
### Prisma Middleware
|
|
83
|
+
|
|
84
|
+
Every query passes through Prisma middleware that auto-injects tenant scoping:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
prisma.$use(async (params, next) => {
|
|
88
|
+
// Auto-inject WHERE tenantId = currentTenant for all queries
|
|
89
|
+
// Throw if tenantId is missing on mutations
|
|
90
|
+
// superadmin/sales bypass with explicit allTenants: true flag
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Isolation Guarantees
|
|
95
|
+
|
|
96
|
+
| Layer | Mechanism |
|
|
97
|
+
|-------|-----------|
|
|
98
|
+
| **Database queries** | Prisma middleware auto-injects `WHERE tenantId`. No SQL query can return data from another tenant. |
|
|
99
|
+
| **Cross-tenant access** | Requires `superadmin` role + explicit `allTenants: true` flag. |
|
|
100
|
+
| **Clinic scoping** | Layered on top of tenant scoping. `admin/doctor/receptionist` only see assigned clinics. |
|
|
101
|
+
| **MinIO file storage** | Path prefixed with `{tenantId}/`. Signed URLs scoped to tenant path. |
|
|
102
|
+
| **Redis keys** | Prefixed with `tenant:{tenantId}:` where applicable. |
|
|
103
|
+
| **BullMQ jobs** | Jobs carry `tenantId`. Processors verify tenant ownership before acting. |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## API Security (11 Layers)
|
|
108
|
+
|
|
109
|
+
| # | Layer | Implementation |
|
|
110
|
+
|---|-------|---------------|
|
|
111
|
+
| 1 | **Authentication** | Opaque session tokens. Client never sees JWT. Server maps opaque token to JWT stored in Redis. |
|
|
112
|
+
| 2 | **Token revocation** | Delete Redis key = instant revocation. No blacklist needed. |
|
|
113
|
+
| 3 | **Rate limiting** | `@nestjs/throttler` + Redis -- per-route limits (5/min login, 200/min general API, 100/min webhooks). |
|
|
114
|
+
| 4 | **Input validation** | `class-validator` DTOs on every endpoint -- reject malformed requests before they reach business logic. |
|
|
115
|
+
| 5 | **SQL injection** | Prisma parameterized queries -- no raw SQL unless explicitly needed. |
|
|
116
|
+
| 6 | **CORS** | Strict origin whitelist -- only `FRONTEND_URL` allowed. |
|
|
117
|
+
| 7 | **Helmet** | `@fastify/helmet` -- security headers (CSP, HSTS, X-Frame-Options). |
|
|
118
|
+
| 8 | **Body size limits** | Fastify body limit: 10MB default, 50MB for file upload routes. |
|
|
119
|
+
| 9 | **Webhook HMAC** | Signature verification on all inbound webhooks (ElevenLabs, Meta). |
|
|
120
|
+
| 10 | **OAuth state** | HMAC-signed state parameter prevents CSRF on OAuth flows (Google Calendar, Meta). |
|
|
121
|
+
| 11 | **Request logging** | Every request logged with method, URL, status, duration, userId, tenantId, clinicId, requestId. |
|
|
122
|
+
|
|
123
|
+
### Rate Limiting Details
|
|
124
|
+
|
|
125
|
+
| Scope | Limit | Window | Purpose |
|
|
126
|
+
|-------|-------|--------|---------|
|
|
127
|
+
| `/auth/login` | 5 requests | 1 min per IP | Brute force prevention |
|
|
128
|
+
| `/auth/forgot-password` | 3 requests | 15 min per email | Spam prevention |
|
|
129
|
+
| `/auth/register` | 3 requests | 1 hour per IP | Abuse prevention |
|
|
130
|
+
| `/webhooks/*` | 100 requests | 1 min per IP | Webhook flooding |
|
|
131
|
+
| Global API | 200 requests | 1 min per user | General abuse |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Secrets Management
|
|
136
|
+
|
|
137
|
+
### Encryption Keys
|
|
138
|
+
|
|
139
|
+
| Secret | Storage | Rotation Strategy |
|
|
140
|
+
|--------|---------|-------------------|
|
|
141
|
+
| `GOOGLE_TOKEN_ENCRYPTION_KEY` | Env var / Docker secret | Re-encryption migration for all stored refresh tokens |
|
|
142
|
+
| `META_TOKEN_ENCRYPTION_KEY` | Env var / Docker secret | Re-encrypt all stored Meta access tokens |
|
|
143
|
+
| `TEKTIPPAY_ENCRYPTION_KEY` | Env var / Docker secret | Re-encrypt stored TekTipPay credentials |
|
|
144
|
+
| `MESSAGE_ENCRYPTION_KEY` | Env var / Docker secret | Re-encrypt all `msg_text`, `msg_quoted_text`, operator request text |
|
|
145
|
+
| `PATIENT_PII_ENCRYPTION_KEY` | Env var / Docker secret | Re-encrypt all TC Kimlik, medical history, exam records, clinical notes, document notes, photo set notes |
|
|
146
|
+
| `PATIENT_SUMMARY_ENCRYPTION_KEY` | Env var / Docker secret | Re-encrypt all `patient_ai_summary` fields |
|
|
147
|
+
|
|
148
|
+
### Other Secrets
|
|
149
|
+
|
|
150
|
+
| Secret | Storage | Rotation |
|
|
151
|
+
|--------|---------|----------|
|
|
152
|
+
| JWT secret (`JWT_SECRET`) | Env var | Rotate quarterly. JWTs are server-side only (stored in Redis), so rotation affects only new tokens. Old opaque sessions expire naturally. |
|
|
153
|
+
| Database password | Env var / Docker secret | Rotate periodically. Update `DATABASE_URL`. |
|
|
154
|
+
| Redis password | Env var / Docker secret | Rotate periodically. Update `REDIS_URL`. |
|
|
155
|
+
| MinIO credentials (`MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`) | Env var / Docker secret | Rotate periodically. Update MinIO config and app env simultaneously. |
|
|
156
|
+
| External API keys (ElevenLabs, NetGSM, etc.) | Env var | Per provider policy. |
|
|
157
|
+
| Tenant API keys | bcrypt hash in DB | Tenant can regenerate anytime via dashboard. |
|
|
158
|
+
|
|
159
|
+
All encryption keys: 32-byte hex strings generated with `openssl rand -hex 32`.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Network Security
|
|
164
|
+
|
|
165
|
+
### Docker Internal Network
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
+--------------------------------------------------+
|
|
169
|
+
| asistan724-net (internal) |
|
|
170
|
+
| |
|
|
171
|
+
| NestJS <--TLS--> PostgreSQL (port 5432, internal)|
|
|
172
|
+
| NestJS <--TLS--> Redis (port 6379, internal) |
|
|
173
|
+
| NestJS <--TLS--> MinIO (port 9000, internal) |
|
|
174
|
+
| |
|
|
175
|
+
| Only NestJS + React exposed to host network |
|
|
176
|
+
+--------------------------------------------------+
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Exposure Rules
|
|
180
|
+
|
|
181
|
+
| Service | Host Port Binding | Access |
|
|
182
|
+
|---------|------------------|--------|
|
|
183
|
+
| NestJS | `PORT` (3005) | Behind reverse proxy with TLS termination |
|
|
184
|
+
| React | 3020 | Behind same reverse proxy |
|
|
185
|
+
| PostgreSQL | **None** | Internal network only |
|
|
186
|
+
| Redis | **None** | Internal network only |
|
|
187
|
+
| MinIO | **None** | Internal network only |
|
|
188
|
+
|
|
189
|
+
### TLS Requirements
|
|
190
|
+
|
|
191
|
+
- PostgreSQL: TLS for all connections from NestJS.
|
|
192
|
+
- Redis: `requirepass` + TLS.
|
|
193
|
+
- MinIO: SSE-S3 (server-side encryption) + TLS for API calls.
|
|
194
|
+
- External APIs: All outbound HTTPS (ElevenLabs, Google, Meta, NetGSM, TekTipPay).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Security per Migration Phase
|
|
199
|
+
|
|
200
|
+
| Phase | Security Tasks |
|
|
201
|
+
|-------|---------------|
|
|
202
|
+
| **Phase 1** (Config, DB, Redis, MinIO) | `EncryptionService` shared module. PostgreSQL TLS + strong password. Redis `requirepass` + TLS. MinIO SSE-S3 + access policy. Docker network isolation. `@fastify/helmet`. |
|
|
203
|
+
| **Phase 2** (Auth) | Opaque session tokens + Redis (no JWT exposed to client, delete key = revoke). bcrypt for passwords. `@nestjs/throttler` rate limiting with Redis storage. Hashed password reset tokens. CORS whitelist. |
|
|
204
|
+
| **Phase 3** (Tenants, Users, Clinics) | Prisma tenant-scoping middleware. RBAC guards. Clinic access guard. Audit logging on all mutations. |
|
|
205
|
+
| **Phase 4** (Agents) | Tenant-scoped queries. RBAC enforcement. |
|
|
206
|
+
| **Phase 5** (Integrations) | Google token encryption (exists, verify). Meta token encryption (new). ElevenLabs webhook HMAC verification. API key rotation support. |
|
|
207
|
+
| **Phase 6** (Client Tools) | Feature-flag gating on all tools. Tool execution logging. Tenant/clinic context validation in every tool call. |
|
|
208
|
+
| **Phase 7** (Calls, Leads, Billing) | Tenant-scoped and clinic-scoped queries. RBAC on all billing endpoints. `clinic_id` on `billing_events`, `stt_usages`, `tool_execution_logs`. |
|
|
209
|
+
| **Phase 8** (Outbound Campaigns) | Tenant-scoped. RBAC. Call dispatch respects time windows. |
|
|
210
|
+
| **Phase 9** (WhatsApp) | **Message content encryption (AES-256-GCM with `MESSAGE_ENCRYPTION_KEY`).** All media stored in MinIO (not DB). Signed URLs for all media access. WS auth (opaque token on connect). |
|
|
211
|
+
| **Phase 10** (Google Calendar) | Token encryption (verify). OAuth state HMAC signing. |
|
|
212
|
+
| **Phase 11** (Appointment Validation) | Tenant-scoped. RBAC. |
|
|
213
|
+
| **Phase 12** (Inbound Call Schedule) | Tenant-scoped. |
|
|
214
|
+
| **Phase 13** (Webhooks) | HMAC signature verification on all inbound webhooks. Signed outgoing webhooks with per-tenant secret. |
|
|
215
|
+
| **Phase 14** (Admin, Logs, KVKK) | Encrypted data exports (ZIP + password). Crypto-erasure on deletion for encrypted fields. Retention policy enforcement. **Patient PII encryption** (`PATIENT_PII_ENCRYPTION_KEY`). **Patient summary encryption** (`PATIENT_SUMMARY_ENCRYPTION_KEY`). |
|
|
216
|
+
| **Phase 15** (Meta Business API, TekTipPay) | **Meta token encryption.** Webhook signature verification (Meta). **TekTipPay credential encryption.** Payment link tokenization (TekTipPay is a stub -- deferred). |
|