├── .env-backend-acme
├── .env-backend-cloudflare
├── .env-backend-local
├── .env-cloudflared
├── .env-frontend-acme
├── .env-frontend-cloudflare
├── .env-frontend-local
├── .externalToolBuilders
└── build ticketz backend.launch
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
└── workflows
│ └── build-docker.yml
├── .gitignore
├── LICENSE.md
├── README.md
├── README.pt.md
├── Ticketz PRO.md
├── backend
├── .DS_Store
├── .dockerignore
├── .editorconfig
├── .env.dev
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .sequelizerc
├── Dockerfile
├── certification
│ ├── cert.pem
│ └── key.pem
├── certs
│ ├── cert.pem
│ ├── coloque_seus_certificado_aqui
│ ├── homologacaoActom.p12
│ ├── key.pem
│ ├── producao-448607-Admin.p12
│ └── producaoActom.p12
├── jest.config.js
├── package-lock.json
├── package.json
├── prettier.config.js
├── private
│ └── index.html
├── scripts
│ ├── fix-sequence.sql
│ └── load-retrieved.sh
├── src
│ ├── @types
│ │ ├── express.d.ts
│ │ └── qrcode-terminal.d.ts
│ ├── app.ts
│ ├── bootstrap.ts
│ ├── config
│ │ ├── auth.ts
│ │ ├── database.ts
│ │ ├── privateFiles.ts
│ │ ├── redis.ts
│ │ └── upload.ts
│ ├── controllers
│ │ ├── AnnouncementController.ts
│ │ ├── CampaignController.ts
│ │ ├── CampaignSettingController.ts
│ │ ├── ChatController.ts
│ │ ├── CompanyController.ts
│ │ ├── ContactController.ts
│ │ ├── ContactListController.ts
│ │ ├── ContactListItemController.ts
│ │ ├── DashboardController.ts
│ │ ├── HelpController.ts
│ │ ├── ImportPhoneContactsController.ts
│ │ ├── InvoicesController.ts
│ │ ├── MessageController.ts
│ │ ├── PlanController.ts
│ │ ├── PrivacyController.ts
│ │ ├── PwaController.ts
│ │ ├── QueueController.ts
│ │ ├── QueueOptionController.ts
│ │ ├── QuickMessageController.ts
│ │ ├── ScheduleController.ts
│ │ ├── SessionController.ts
│ │ ├── SettingController.ts
│ │ ├── SubscriptionController.ts
│ │ ├── TagController.ts
│ │ ├── TicketController.ts
│ │ ├── TicketNoteController.ts
│ │ ├── TicketTagController.ts
│ │ ├── TicketzOSSController.ts
│ │ ├── UserController.ts
│ │ ├── VersionController.ts
│ │ ├── WhatsAppController.ts
│ │ └── WhatsAppSessionController.ts
│ ├── database
│ │ ├── index.ts
│ │ ├── markSeedsAsExecuted.ts
│ │ ├── migrations
│ │ │ ├── 20200717133431-add-uuid-ossp.ts
│ │ │ ├── 20200717133438-create-users.ts
│ │ │ ├── 20200717144403-create-contacts.ts
│ │ │ ├── 20200717145643-create-tickets.ts
│ │ │ ├── 20200717151645-create-messages.ts
│ │ │ ├── 20200717170223-create-whatsapps.ts
│ │ │ ├── 20200723200315-create-contacts-custom-fields.ts
│ │ │ ├── 20200723202116-add-email-field-to-contacts.ts
│ │ │ ├── 20200730153237-remove-user-association-from-messages.ts
│ │ │ ├── 20200730153545-add-fromMe-to-messages.ts
│ │ │ ├── 20200813114236-change-ticket-lastMessage-column-type.ts
│ │ │ ├── 20200901235509-add-profile-column-to-users.ts
│ │ │ ├── 20200903215941-create-settings.ts
│ │ │ ├── 20200904220257-add-name-to-whatsapp.ts
│ │ │ ├── 20200906122228-add-name-default-field-to-whatsapp.ts
│ │ │ ├── 20200906155658-add-whatsapp-field-to-tickets.ts
│ │ │ ├── 20200919124112-update-default-column-name-on-whatsappp.ts
│ │ │ ├── 20200927220708-add-isDeleted-column-to-messages.ts
│ │ │ ├── 20200929145451-add-user-tokenVersion-column.ts
│ │ │ ├── 20200930162323-add-isGroup-column-to-tickets.ts
│ │ │ ├── 20200930194808-add-isGroup-column-to-contacts.ts
│ │ │ ├── 20201004150008-add-contactId-column-to-messages.ts
│ │ │ ├── 20201004155719-add-vcardContactId-column-to-messages.ts
│ │ │ ├── 20201004955719-remove-vcardContactId-column-to-messages.ts
│ │ │ ├── 20201026215410-add-retries-to-whatsapps.ts
│ │ │ ├── 20201028124427-add-quoted-msg-to-messages.ts
│ │ │ ├── 20210108001431-add-unreadMessages-to-tickets.ts
│ │ │ ├── 20210108164404-create-queues.ts
│ │ │ ├── 20210108164504-add-queueId-to-tickets.ts
│ │ │ ├── 20210108174594-associate-whatsapp-queue.ts
│ │ │ ├── 20210108204708-associate-users-queue.ts
│ │ │ ├── 20210109192513-add-greetingMessage-to-whatsapp.ts
│ │ │ ├── 20210109192514-create-companies-table.ts
│ │ │ ├── 20210109192515-add-column-companyId-to-Settings-table.ts
│ │ │ ├── 20210109192516-add-column-companyId-to-Users-table.ts
│ │ │ ├── 20210109192517-add-column-companyId-to-Contacts-table.ts
│ │ │ ├── 20210109192518-add-column-companyId-to-Messages-table.ts
│ │ │ ├── 20210109192519-add-column-companyId-to-Queues-table.ts
│ │ │ ├── 20210109192520-add-column-companyId-to-Whatsapps-table.ts
│ │ │ ├── 20210109192521-add-column-companyId-to-Tickets-table.ts
│ │ │ ├── 20210109192522-create-plans-table.ts
│ │ │ ├── 20210109192523-add-column-planId-to-Companies.ts
│ │ │ ├── 20210109192523-add-column-status-and-schedules-to-Companies.ts
│ │ │ ├── 20210109192523-create-ticket-notes.ts
│ │ │ ├── 20210109192524-create-quick-messages.ts
│ │ │ ├── 20210109192525-add-column-complationMessage-to-whatsapp.ts
│ │ │ ├── 20210109192526-add-column-outOfHoursMessage-to-whatsapp .ts
│ │ │ ├── 20210109192527-add-column-super-to-Users-table.ts
│ │ │ ├── 20210109192528-change-column-message-to-quick-messages-table.ts
│ │ │ ├── 20210109192529-create-helps.ts
│ │ │ ├── 20210109192530-add-unique-constraint-to-Contacts-table.ts
│ │ │ ├── 20210109192531-create-TicketTracking-table.ts
│ │ │ ├── 20210109192532-add-column-online-to-Users-table.ts
│ │ │ ├── 20210109192533-create-UserRatings-table.ts
│ │ │ ├── 20210109192534-add-rated-to-TicketTraking.ts
│ │ │ ├── 20210109192535-add-column-ratingMessage-to-whatsapp.ts
│ │ │ ├── 20210109192536-add-unique-constraint-to-Tickets-table.ts
│ │ │ ├── 20210818102606-add-uuid-to-tickets.ts
│ │ │ ├── 20210818102607-remove-unique-indexes-to-Contacts-table.ts
│ │ │ ├── 20210818102607-remove-unique-indexes-to-Queues-table.ts
│ │ │ ├── 20210818102608-add-unique-indexes-to-Queues-table.ts
│ │ │ ├── 20210818102609-add-token-to-Whatsapps.ts
│ │ │ ├── 20211205164404-create-queue-options.ts
│ │ │ ├── 20211212125704-add-chatbot-to-tickets.ts
│ │ │ ├── 20211227010200-create-schedules.ts
│ │ │ ├── 20220115114088-add-column-userId-to-QuickMessages-table.ts
│ │ │ ├── 20220117130000-create-tags.ts
│ │ │ ├── 20220117134400-associate-tickets-tags.ts
│ │ │ ├── 20220122160900-add-status-to-schedules.ts
│ │ │ ├── 20220220014719-add-farewellMessage-to-whatsapp.ts
│ │ │ ├── 20220221014717-add-provider-whatsapp.ts
│ │ │ ├── 20220221014718-add-remoteJid-messages.ts
│ │ │ ├── 20220221014719-add-jsonMessage-messages.ts
│ │ │ ├── 20220221014720-add-participant-messages.ts
│ │ │ ├── 20220221014721-create-baileys.ts
│ │ │ ├── 20220315110000-create-ContactLists-table.ts
│ │ │ ├── 20220315110001-create-ContactListItems-table.ts
│ │ │ ├── 20220315110002-create-Campaigns-table.ts
│ │ │ ├── 20220315110004-create-CampaignSettings-table.ts
│ │ │ ├── 20220315110005-remove-constraint-to-Settings.ts
│ │ │ ├── 20220321130000-create-CampaignShipping.ts
│ │ │ ├── 20220404000000-add-column-queueId-to-Messages-table.ts
│ │ │ ├── 20220406000000-add-column-dueDate-to-Companies.ts
│ │ │ ├── 20220406000001-add-column-recurrence-to-Companies.ts
│ │ │ ├── 20220411000000-add-column-startTime-and-endTime-to-Queues.ts
│ │ │ ├── 20220411000001-remove-column-startTime-and-endTime-to-Queues.ts
│ │ │ ├── 20220411000002-add-column-schedules-and-outOfHoursMessage-to-Queues.ts
│ │ │ ├── 20220411000003-create-table-Announcements.ts
│ │ │ ├── 20220425000000-create-table-Chats.ts
│ │ │ ├── 20220425000001-create-table-ChatUsers.ts
│ │ │ ├── 20220425000002-create-table-ChatMessages.ts
│ │ │ ├── 20220512000001-create-Indexes.ts
│ │ │ ├── 20220512000002-create-subscriptions.ts
│ │ │ ├── 20220512000003-create-invoices.ts
│ │ │ ├── 20220814000000-add-value-to-plans.ts
│ │ │ ├── 20222016014719-add-channel-session.ts
│ │ │ ├── 20222016014719-add-channel-to-contacts.ts
│ │ │ ├── 20222016014719-add-channel-to-message.ts
│ │ │ ├── 20222016014719-add-channel-to-ticket.ts
│ │ │ ├── 20222016014719-add-channel-token.ts
│ │ │ ├── 20222016014719-add-channel-tokenUser.ts
│ │ │ ├── 20222016014719-add-facebookPageUserId-whatsapp.ts
│ │ │ ├── 20222016014719-add-facebookUserId-whatsapp.ts
│ │ │ ├── 20230528202116-add-transferMessage-field-to-Whatsapp.ts
│ │ │ ├── 20230723301001-add-kanban-to-Tags.ts
│ │ │ ├── 20230813114236-change-ticket-lastMessage-column-type.ts
│ │ │ ├── 20230904212800-alter-value-to-plans.ts
│ │ │ ├── 20230915212800-add-public-to-plans.ts
│ │ │ ├── 20230918122800-add-media-to-Queues.ts
│ │ │ ├── 20230918142800-add-media-to-QueueOptions.ts
│ │ │ ├── 20231117174628-create-oldMessages.ts
│ │ │ ├── 20231219153800-add-isEdited-column-to-messages.ts
│ │ │ ├── 20231230100900-remove-unique-constraint-to-ticket-table.ts
│ │ │ ├── 20232016014719-add-column-mediaType-to-ChatMessages.ts
│ │ │ ├── 20240417150500-add-unique-constrait-to-Contacts-table.ts
│ │ │ ├── 20240418173900-changes-messages-and-oldmessages-tables.ts
│ │ │ ├── 20240504105300-add-paymentfields-to-invoices.ts
│ │ │ ├── 20240507112700-add-queue-options-fields.ts
│ │ │ ├── 20240522165800-add-disablebot-to-contact.ts
│ │ │ ├── 20240524011900-add-presence-to-contact.ts
│ │ │ ├── 20240620080700-create-baileysKeys.ts
│ │ │ ├── 20240814111700-create-user-socket-sessions.ts
│ │ │ ├── 20240820082900-add-expired-to-TicketTraking.ts
│ │ │ ├── 20240820082900-set-expired-to-TicketTraking.ts
│ │ │ ├── 20240827171200-add-thumbnailurl-to-messages.ts
│ │ │ ├── 20240914200100-whatsapps-change-unique-name.ts
│ │ │ ├── 20241013110200-whatsapps-channel-default.ts
│ │ │ ├── 20241110080800-change-QueueOptions-forwardQueueId-column.ts
│ │ │ ├── 20241122134400-create-contactTags.ts
│ │ │ ├── 20241128175500-create-insensitive-index-for-users-email.ts
│ │ │ ├── 20241128175800-add-save-messages-to-schedules.ts
│ │ │ ├── 20241201102700-add-unaccent-extension.ts
│ │ │ ├── 20250114172400-add-whatsappqueues-constraint.ts
│ │ │ ├── 20250114173900-add-userqueues-constraint.ts
│ │ │ ├── 20250116195300-add-whatsappqueues-constraint.ts
│ │ │ ├── 20250226163500-fix-queueoptions-constraints.ts
│ │ │ ├── 20250321122300-add-indexes-to-messages.ts
│ │ │ ├── 20250325080200-add-index-id-fromme-to-messages.ts
│ │ │ ├── 20250326110400-add-indexes-to-baileyskeys.ts
│ │ │ ├── 20250327164700-add-index-quotedMsgId-to-messages.ts
│ │ │ ├── 20250410205700-add-index-ticketId-to-ticketTraking.ts
│ │ │ ├── 20250415163900-add-index-timestamps-to-tickets.ts
│ │ │ ├── 20250415164000-add-index-timestamps-to-tickettraking.ts
│ │ │ ├── 20250415164200-add-index-timestamps-to-messages.ts
│ │ │ ├── 20250506155200-fix-groq-typo-on-settings.ts
│ │ │ ├── 20250513082600-create-counters.ts
│ │ │ ├── 20250516095500-add-chatbotendat-to-tickettraking.ts
│ │ │ ├── 20250516100600-add-timingfields-to-tickettraking.ts
│ │ │ ├── 20250520150600-change-duedate-to-dateonly.ts
│ │ │ ├── 20250522093400-fix-finidsedAt-on-TicketTraking.ts
│ │ │ └── 20250524084100-create-outofticketmessages.ts
│ │ └── seeds
│ │ │ ├── 20200904070005-create-default-company.ts
│ │ │ ├── 20200904070006-create-default-user.ts
│ │ │ └── 20200904070007-create-default-settings.ts
│ ├── errors
│ │ └── AppError.ts
│ ├── gitinfo.ts
│ ├── helpers
│ │ ├── CheckCompanyCompliant.ts
│ │ ├── CheckContactOpenTickets.ts
│ │ ├── CheckContactSomeTicket.ts
│ │ ├── CheckSettings.ts
│ │ ├── CreateTokens.ts
│ │ ├── Debounce.ts
│ │ ├── GetDefaultWhatsApp.ts
│ │ ├── GetPublicPath.ts
│ │ ├── GetTicketWbot.ts
│ │ ├── GetWbotMessage.ts
│ │ ├── GetWhatsappWbot.ts
│ │ ├── MakeRandomId.ts
│ │ ├── Mustache.ts
│ │ ├── SendMessage.ts
│ │ ├── SendRefreshToken.ts
│ │ ├── SerializeUser.ts
│ │ ├── SerializeWbotMsgId.ts
│ │ ├── SetTicketMessagesAsRead.ts
│ │ ├── UpdateDeletedUserOpenTicketsStatus.ts
│ │ ├── authState.ts
│ │ ├── bufferToReadStreamTmp.ts
│ │ ├── colorGenerator.ts
│ │ ├── mediaConversion.ts
│ │ ├── parseToMilliseconds.ts
│ │ ├── randomValue.ts
│ │ ├── replaceExtension.ts
│ │ ├── saveMediaFile.ts
│ │ ├── simpleObjectCache.ts
│ │ ├── transcriber.ts
│ │ └── useMultiFileAuthState.ts
│ ├── libs
│ │ ├── cache.ts
│ │ ├── counter.ts
│ │ ├── socket.ts
│ │ └── wbot.ts
│ ├── middleware
│ │ ├── apiTokenAuth.ts
│ │ ├── envTokenAuth.ts
│ │ ├── isAdmin.ts
│ │ ├── isAuth.ts
│ │ ├── isCompliant.ts
│ │ ├── isSuper.ts
│ │ └── tokenAuth.ts
│ ├── models
│ │ ├── Announcement.ts
│ │ ├── Baileys.ts
│ │ ├── BaileysKeys.ts
│ │ ├── Campaign.ts
│ │ ├── CampaignSetting.ts
│ │ ├── CampaignShipping.ts
│ │ ├── Chat.ts
│ │ ├── ChatMessage.ts
│ │ ├── ChatUser.ts
│ │ ├── Company.ts
│ │ ├── Contact.ts
│ │ ├── ContactCustomField.ts
│ │ ├── ContactList.ts
│ │ ├── ContactListItem.ts
│ │ ├── ContactTag.ts
│ │ ├── Counter.ts
│ │ ├── Help.ts
│ │ ├── Invoices.ts
│ │ ├── Message.ts
│ │ ├── OldMessage.ts
│ │ ├── OutOfTicketMessages.ts
│ │ ├── Plan.ts
│ │ ├── Queue.ts
│ │ ├── QueueOption.ts
│ │ ├── QuickMessage.ts
│ │ ├── Schedule.ts
│ │ ├── Setting.ts
│ │ ├── Subscriptions.ts
│ │ ├── Tag.ts
│ │ ├── Ticket.ts
│ │ ├── TicketNote.ts
│ │ ├── TicketTag.ts
│ │ ├── TicketTraking.ts
│ │ ├── User.ts
│ │ ├── UserQueue.ts
│ │ ├── UserRating.ts
│ │ ├── UserSocketSession.ts
│ │ ├── Whatsapp.ts
│ │ └── WhatsappQueue.ts
│ ├── queues.ts
│ ├── queues
│ │ └── campaign.ts
│ ├── routes
│ │ ├── announcementRoutes.ts
│ │ ├── authRoutes.ts
│ │ ├── campaignRoutes.ts
│ │ ├── campaignSettingRoutes.ts
│ │ ├── chatRoutes.ts
│ │ ├── companyRoutes.ts
│ │ ├── contactListItemRoutes.ts
│ │ ├── contactListRoutes.ts
│ │ ├── contactRoutes.ts
│ │ ├── dashboardRoutes.ts
│ │ ├── helpRoutes.ts
│ │ ├── index.ts
│ │ ├── invoicesRoutes.ts
│ │ ├── messageRoutes.ts
│ │ ├── planRoutes.ts
│ │ ├── pwaRoutes.ts
│ │ ├── queueOptionRoutes.ts
│ │ ├── queueRoutes.ts
│ │ ├── quickMessageRoutes.ts
│ │ ├── scheduleRoutes.ts
│ │ ├── settingRoutes.ts
│ │ ├── subScriptionRoutes.ts
│ │ ├── tagRoutes.ts
│ │ ├── ticketNoteRoutes.ts
│ │ ├── ticketRoutes.ts
│ │ ├── ticketTagRoutes.ts
│ │ ├── ticketzOSSRoutes.ts
│ │ ├── userRoutes.ts
│ │ ├── versionRoutes.ts
│ │ ├── whatsappRoutes.ts
│ │ └── whatsappSessionRoutes.ts
│ ├── server.ts
│ ├── services
│ │ ├── AnnouncementService
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── AuthServices
│ │ │ ├── FindUserFromToken.ts
│ │ │ └── RefreshTokenService.ts
│ │ ├── BaileysServices
│ │ │ ├── CreateOrUpdateBaileysService.ts
│ │ │ ├── DeleteBaileysService.ts
│ │ │ └── ShowBaileysService.ts
│ │ ├── CampaignService
│ │ │ ├── CancelService.ts
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── RestartService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── CampaignSettingServices
│ │ │ ├── CreateService.ts
│ │ │ └── ListService.ts
│ │ ├── ChatService
│ │ │ ├── CreateMessageService.ts
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindMessages.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowFromUuidService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── CompanyService
│ │ │ ├── CreateCompanyService.ts
│ │ │ ├── DeleteCompanyService.ts
│ │ │ ├── FindAllCompaniesService.ts
│ │ │ ├── ListCompaniesService.ts
│ │ │ ├── ShowCompanyService.ts
│ │ │ ├── UpdateCompanyService.ts
│ │ │ ├── UpdateSchedulesService.ts
│ │ │ └── VerifyCurrentSchedule.ts
│ │ ├── ContactListItemService
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── ContactListService
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ImportContacts.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── ContactServices
│ │ │ ├── CreateContactService.ts
│ │ │ ├── CreateOrUpdateContactService.ts
│ │ │ ├── DeleteContactService.ts
│ │ │ ├── GetContactService.ts
│ │ │ ├── ListContactsService.ts
│ │ │ ├── ShowContactService.ts
│ │ │ ├── SimpleListService.ts
│ │ │ └── UpdateContactService.ts
│ │ ├── CounterServices
│ │ │ ├── IncrementCounter.ts
│ │ │ └── ListCounterSerie.ts
│ │ ├── HelpServices
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── InvoicesService
│ │ │ ├── FindAllInvoiceService.ts
│ │ │ ├── ListInvoicesServices.ts
│ │ │ ├── ShowInvoiceService.ts
│ │ │ └── UpdateInvoiceService.ts
│ │ ├── MessageServices
│ │ │ ├── CreateMessageService.ts
│ │ │ ├── ForwardMessageService.ts
│ │ │ ├── GetMessagesService.ts
│ │ │ └── ListMessagesService.ts
│ │ ├── PaymentGatewayServices
│ │ │ ├── EfiServices.ts
│ │ │ ├── OwenServices.ts
│ │ │ └── PaymentGatewayServices.ts
│ │ ├── PlanService
│ │ │ ├── CreatePlanService.ts
│ │ │ ├── DeletePlanService.ts
│ │ │ ├── FindAllPlanService.ts
│ │ │ ├── ListPlansService.ts
│ │ │ ├── ShowPlanService.ts
│ │ │ └── UpdatePlanService.ts
│ │ ├── PrivacyService
│ │ │ ├── ShowPrivacyService.ts
│ │ │ └── UpdatePrivacyWhatsapp.ts
│ │ ├── QueueOptionService
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── QueueService
│ │ │ ├── CreateQueueService.ts
│ │ │ ├── DeleteQueueService.ts
│ │ │ ├── ListQueuesService.ts
│ │ │ ├── ShowQueueService.ts
│ │ │ └── UpdateQueueService.ts
│ │ ├── QuickMessageService
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── FindAllService.ts
│ │ │ ├── FindService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── ReportService
│ │ │ └── DashboardService.ts
│ │ ├── ScheduleServices
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ └── UpdateService.ts
│ │ ├── SettingServices
│ │ │ ├── GetPublicSettingService.ts
│ │ │ ├── GetSettingService.ts
│ │ │ ├── GetSuperSettingService.ts
│ │ │ ├── ListSettingsService.ts
│ │ │ └── UpdateSettingService.ts
│ │ ├── TagServices
│ │ │ ├── CreateService.ts
│ │ │ ├── DeleteService.ts
│ │ │ ├── KanbanListService.ts
│ │ │ ├── ListService.ts
│ │ │ ├── ShowService.ts
│ │ │ ├── SimpleListService.ts
│ │ │ ├── SyncTagsService.ts
│ │ │ └── UpdateService.ts
│ │ ├── TicketNoteService
│ │ │ ├── CreateTicketNoteService.ts
│ │ │ ├── DeleteTicketNoteService.ts
│ │ │ ├── FindAllTicketNotesService.ts
│ │ │ ├── FindNotesByContactIdAndTicketId.ts
│ │ │ ├── ListTicketNotesService.ts
│ │ │ ├── ShowTicketNoteService.ts
│ │ │ └── UpdateTicketNoteService.ts
│ │ ├── TicketServices
│ │ │ ├── CreateTicketService.ts
│ │ │ ├── DeleteTicketService.ts
│ │ │ ├── FindOrCreateATicketTrakingService.ts
│ │ │ ├── FindOrCreateTicketService.ts
│ │ │ ├── FindOrCreateTicketServiceMeta.ts
│ │ │ ├── ListTicketsService.ts
│ │ │ ├── ListTicketsServiceKanban.ts
│ │ │ ├── ShowTicketFromUUIDService.ts
│ │ │ ├── ShowTicketService.ts
│ │ │ └── UpdateTicketService.ts
│ │ ├── TicketTagServices
│ │ │ └── TicketTagServices.ts
│ │ ├── UserServices
│ │ │ ├── AuthUserService.ts
│ │ │ ├── CreateUserService.ts
│ │ │ ├── DeleteUserService.ts
│ │ │ ├── ListUsersService.ts
│ │ │ ├── ShowUserService.ts
│ │ │ ├── SimpleListService.ts
│ │ │ └── UpdateUserService.ts
│ │ ├── WbotServices
│ │ │ ├── CheckIsValidContact.ts
│ │ │ ├── CheckNumber.ts
│ │ │ ├── DeleteWhatsAppMessage.ts
│ │ │ ├── EditWhatsAppMessage.ts
│ │ │ ├── GetProfilePicUrl.ts
│ │ │ ├── ImportContactsService.ts
│ │ │ ├── SendWhatsAppMedia.ts
│ │ │ ├── SendWhatsAppMessage.ts
│ │ │ ├── StartAllWhatsAppsSessions.ts
│ │ │ ├── StartWhatsAppSession.ts
│ │ │ ├── wbotMessageListener.ts
│ │ │ └── wbotMonitor.ts
│ │ ├── WhatsappService
│ │ │ ├── AssociateWhatsappQueue.ts
│ │ │ ├── CreateWhatsAppService.ts
│ │ │ ├── DeleteWhatsAppService.ts
│ │ │ ├── ListWhatsAppsService.ts
│ │ │ ├── ShowWhatsAppService.ts
│ │ │ └── UpdateWhatsAppService.ts
│ │ └── services.zip
│ ├── userMonitor.ts
│ ├── utils
│ │ └── logger.ts
│ └── waversion.json
└── tsconfig.json
├── confs
├── nginx-ticketz.conf
└── pgadmin4-servers.json
├── docker-compose-acme.yaml
├── docker-compose-cloudflare.yaml
├── docker-compose-dev.yaml
├── docker-compose-local.yaml
├── docs
├── Local Development.en.md
└── Local Development.pt.md
└── frontend
├── .dockerignore
├── .gitignore
├── Dockerfile
├── certification
├── cert.pem
└── key.pem
├── nginx
├── conf.d
│ ├── default.conf
│ └── mimetypes.conf
├── include.d
│ ├── allcache.conf
│ ├── nocache.conf
│ └── spa.conf
└── sites.d
│ └── frontend.conf
├── package-lock.json
├── package.json
├── public
├── android-chrome-192x192.png
├── apple-touch-icon.png
├── config-dev-example.json
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── favicon.png
├── gitinfo.json
├── index.html
├── manifest.json
├── mercadopago.png
├── mstile-150x150.png
├── ticketzpix.png
└── vector
│ ├── favicon.svg
│ ├── logo-dark.svg
│ └── logo.svg
└── src
├── App.js
├── assets
├── chat_notify.mp3
├── planilha.xlsx
├── sacmais.png
├── sound.mp3
├── sound.ogg
├── wa-background-dark.png
├── wa-background-light.png
└── wa-background.png
├── components
├── AboutModal
│ └── index.js
├── AnnouncementModal
│ └── index.js
├── AnnouncementsPopover
│ └── index.js
├── BackdropLoading
│ └── index.js
├── Backendlogs
│ └── index.js
├── ButtonWithSpinner
│ └── index.js
├── CampaignModal
│ └── index.js
├── Can
│ └── index.js
├── CheckoutPage
│ ├── CheckoutPage.js
│ ├── CheckoutSuccess
│ │ ├── CheckoutSuccess.js
│ │ ├── index.js
│ │ └── style.js
│ ├── FormModel
│ │ ├── checkoutFormModel.js
│ │ ├── formInitialValues.js
│ │ └── validationSchema.js
│ ├── Forms
│ │ ├── AddressForm.js
│ │ └── PaymentForm.js
│ ├── ReviewOrder
│ │ ├── PaymentDetails.js
│ │ ├── ProductDetails.js
│ │ ├── ReviewOrder.js
│ │ ├── ShippingDetails.js
│ │ ├── index.js
│ │ └── styles.js
│ ├── index.js
│ └── styles.js
├── ColorPicker
│ └── index.js
├── CompaniesManager
│ └── index.js
├── ConfirmationModal
│ └── index.js
├── ContactDrawer
│ └── index.js
├── ContactDrawerSkeleton
│ └── index.js
├── ContactForm
│ └── index.js
├── ContactListDialog
│ └── index.js
├── ContactListItemModal
│ └── index.js
├── ContactListTable
│ └── index.js
├── ContactModal
│ └── index.js
├── ContactNotes
│ └── index.js
├── ContactNotesDialog
│ └── index.js
├── ContactNotesDialogListItem
│ └── index.js
├── CurrencyInput
│ └── index.js
├── DarkMode
│ └── index.js
├── Dashboard
│ ├── CardCounter.js
│ └── TableAttendantsStatus.js
├── Dialog
│ └── index.js
├── FormFields
│ ├── CheckboxField.js
│ ├── DatePickerField.js
│ ├── InputField.js
│ ├── SelectField.js
│ └── index.js
├── GoogleAnalytics
│ └── index.js
├── HelpsManager
│ └── index.js
├── LocationPreview
│ └── index.js
├── MainContainer
│ └── index.js
├── MainHeader
│ └── index.js
├── MainHeaderButtonsWrapper
│ └── index.js
├── MessageForwardModal
│ └── index.js
├── MessageHistoryModal
│ └── index.js
├── MessageInputCustom
│ ├── ProgressBarCustom.js
│ ├── RecordingTimer.js
│ └── index.js
├── MessageOptionsMenu
│ ├── index.js
│ └── style.js
├── MessagesList
│ └── index.js
├── ModalImageCors
│ └── index.js
├── ModalUsers
│ └── index.js
├── NewTicketModal
│ └── index.js
├── NotificationsPopOver
│ └── index.js
├── NotificationsVolume
│ └── index.js
├── OnlyForSuperUser
│ └── index.js
├── PaymentGateways
│ ├── Efi
│ │ └── EfiSettings.js
│ └── Owen
│ │ ├── OwenAd.js
│ │ └── OwenSettings.js
├── PlansManager
│ └── index.js
├── PrivacyModal
│ └── index.js
├── QrcodeModal
│ └── index.js
├── QueueModal
│ └── index.js
├── QueueOptions
│ └── index.js
├── QueueSelect
│ └── index.js
├── QueueSelectCustom
│ └── index.js
├── QuickMessageDialog
│ └── index.js
├── QuickMessagesTable
│ └── index.js
├── ScheduleModal
│ └── index.js
├── SchedulesForm
│ └── index.js
├── SelectCountry
│ └── index.js
├── Settings
│ ├── Options.js
│ ├── PaymentGateway.js
│ └── Whitelabel.js
├── SubscriptionModal
│ └── index.js
├── SubscriptionStepper
│ └── index.js
├── TabPanel
│ └── index.js
├── TableRowSkeleton
│ └── index.js
├── TagModal
│ └── index.js
├── TagsContainer
│ └── index.js
├── TagsFilter
│ └── index.js
├── TagsLine
│ └── index.js
├── Ticket
│ └── index.js
├── TicketActionButtons
│ └── index.js
├── TicketActionButtonsCustom
│ └── index.js
├── TicketAdvancedLayout
│ └── index.js
├── TicketHeader
│ └── index.js
├── TicketHeaderSkeleton
│ └── index.js
├── TicketInfo
│ └── index.js
├── TicketListItem
│ └── index.js
├── TicketListItemCustom
│ └── index.js
├── TicketMessagesDialog
│ └── index.js
├── TicketOptionsMenu
│ └── index.js
├── TicketsList
│ └── index.js
├── TicketsListCustom
│ └── index.js
├── TicketsListSkeleton
│ └── index.js
├── TicketsManager
│ └── index.js
├── TicketsManagerTabs
│ └── index.js
├── TicketsQueueSelect
│ └── index.js
├── TicketzRegistry
│ └── index.js
├── Title
│ └── index.js
├── TransferTicketModal
│ └── index.js
├── TransferTicketModalCustom
│ └── index.js
├── UserModal
│ └── index.js
├── UsersFilter
│ └── index.js
└── WhatsAppModal
│ └── index.js
├── context
├── Auth
│ └── AuthContext.js
├── EditingMessage
│ └── EditingMessageContext.js
├── ReplyingMessage
│ └── ReplyingMessageContext.js
├── Socket
│ └── SocketContext.js
├── Tickets
│ └── TicketsContext.js
└── WhatsApp
│ └── WhatsAppsContext.js
├── errors
└── toastError.js
├── helpers
├── colorGenerator.js
├── copyToClipboard.js
├── exportCsv.js
├── generateSecureToken.js
├── getInitials.js
├── i18nToast.js
├── isMobile.js
└── loadJSON.js
├── hooks
├── useAuth.js
│ └── index.js
├── useCompanies
│ └── index.js
├── useContactListItems
│ └── index.js
├── useContactLists
│ └── index.js
├── useDate
│ └── index.js
├── useHelps
│ └── index.js
├── useInvoices
│ └── index.js
├── useLocalStorage
│ └── index.js
├── usePlans
│ └── index.js
├── useQueues
│ └── index.js
├── useQuickMessages
│ └── index.js
├── useSettings
│ └── index.js
├── useTicketNotes
│ └── index.js
├── useTickets
│ └── index.js
└── useWhatsApps
│ └── index.js
├── index.js
├── layout
├── MainListItems.js
├── index.js
└── themeContext.js
├── pages
├── Annoucements
│ └── index.js
├── CampaignReport
│ └── index.js
├── Campaigns
│ └── index.js
├── CampaignsConfig
│ └── index.js
├── Chat
│ ├── ChatList.js
│ ├── ChatMessages.js
│ ├── ChatPopover.js
│ └── index.js
├── Companies
│ └── index.js
├── Connections
│ └── index.js
├── ContactListItems
│ └── index.js
├── ContactLists
│ └── index.js
├── Contacts
│ └── index.js
├── Dashboard
│ ├── CustomTooltip.js
│ ├── SmallPie.js
│ ├── TicketCountersChart.js
│ ├── Title.js
│ └── index.js
├── Financeiro
│ └── index.js
├── Helps
│ └── index.js
├── Login
│ ├── index.js
│ └── style.css
├── MessagesAPI
│ └── index.js
├── Queues
│ └── index.js
├── QuickMessages
│ ├── ToDoList
│ │ └── index.js
│ └── index.js
├── Schedules
│ └── index.js
├── Settings
│ └── index.js
├── SettingsCustom
│ └── index.js
├── Signup
│ └── index.js
├── Subscription
│ └── index.js
├── Tags
│ └── index.js
├── TicketResponsiveContainer
│ └── index.js
├── Tickets
│ └── index.js
├── TicketsAdvanced
│ └── index.js
├── TicketsCustom
│ └── index.js
├── ToDoList
│ └── index.js
└── Users
│ └── index.js
├── routes
├── Route.js
└── index.js
├── rules.js
├── services
├── api.js
├── config.js
└── socket.js
└── translate
├── i18n.js
└── languages
├── de.js
├── en.js
├── es.js
├── fr.js
├── id.js
├── index.js
├── it.js
├── pt.js
└── pt_PT.js
/.env-backend-acme:
--------------------------------------------------------------------------------
1 | FRONTEND_HOST=ticketz.exemplo.com.br
2 | EMAIL_ADDRESS=ticketz@exemplo.com.br
3 | TZ=America/Sao_Paulo
4 |
5 | # Normalmente não é necessário alterar estes valores
6 |
7 | BACKEND_PATH=/backend
8 |
9 | RECAPTCHA_SECRET_KEY=
10 |
11 | BACKEND_URL=https://${FRONTEND_HOST}${BACKEND_PATH}
12 | FRONTEND_URL=https://${FRONTEND_HOST}
13 |
14 | DB_DIALECT=postgres
15 | DB_HOST=postgres
16 | DB_PORT=5432
17 | DB_USER=ticketz
18 | DB_NAME=ticketz
19 | DB_TIMEZONE=-03:00
20 |
21 |
22 | REDIS_URI=redis://redis:6379
23 | REDIS_OPT_LIMITER_MAX=1
24 | REDIS_OPT_LIMITER_DURATION=3000
25 |
26 | USER_LIMIT=10000
27 | CONNECTIONS_LIMIT=100000
28 | CLOSED_SEND_BY_ME=true
29 |
30 | FACEBOOK_APP_ID=
31 | FACEBOOK_APP_SECRET=
32 |
33 | VERIFY_TOKEN=ticketz
34 |
35 | SOCKET_ADMIN=true
36 |
--------------------------------------------------------------------------------
/.env-backend-cloudflare:
--------------------------------------------------------------------------------
1 | BACKEND_URL=https://apiticketz.example.com
2 | FRONTEND_URL=https://ticketz.example.com
3 | TZ=America/Sao_Paulo
4 |
5 | ## normalmente não precisa editar daqui em diante
6 |
7 | DB_DIALECT=postgres
8 | DB_HOST=postgres
9 | DB_PORT=5432
10 | DB_USER=ticketz
11 | DB_NAME=ticketz
12 | DB_TIMEZONE=-03:00
13 |
14 | REDIS_URI=redis://redis:6379
15 | REDIS_OPT_LIMITER_MAX=1
16 | REDIS_OPT_LIMITER_DURATION=3000
17 |
18 | USER_LIMIT=10000
19 | CONNECTIONS_LIMIT=100000
20 | CLOSED_SEND_BY_ME=true
21 |
22 | VERIFY_TOKEN=ticketz
23 | SOCKET_ADMIN=true
24 |
--------------------------------------------------------------------------------
/.env-backend-local:
--------------------------------------------------------------------------------
1 | FRONTEND_HOST=localhost
2 | FRONTEND_PORT=3000
3 | BACKEND_PATH=/backend
4 |
5 | EMAIL_ADDRESS=admin@ticketz.host
6 | TZ=America/Sao_Paulo
7 |
8 | BACKEND_URL=http://${FRONTEND_HOST}:${FRONTEND_PORT}${BACKEND_PATH}
9 | FRONTEND_URL=http://${FRONTEND_HOST}:${FRONTEND_PORT}
10 |
11 | DB_DIALECT=postgres
12 | DB_HOST=postgres
13 | DB_PORT=5432
14 | DB_USER=ticketz
15 | DB_NAME=ticketz
16 | DB_TIMEZONE=-03:00
17 |
18 |
19 | REDIS_URI=redis://redis:6379
20 | REDIS_OPT_LIMITER_MAX=1
21 | REDIS_OPT_LIMITER_DURATION=3000
22 |
23 | USER_LIMIT=10000
24 | CONNECTIONS_LIMIT=100000
25 | CLOSED_SEND_BY_ME=true
26 |
27 | FACEBOOK_APP_ID=
28 | FACEBOOK_APP_SECRET=
29 |
30 | VERIFY_TOKEN=ticketz
31 | SOCKET_ADMIN=true
32 |
--------------------------------------------------------------------------------
/.env-cloudflared:
--------------------------------------------------------------------------------
1 | TUNNEL_TOKEN=TunnelTokenAsProvidedByCloudflare
2 |
--------------------------------------------------------------------------------
/.env-frontend-acme:
--------------------------------------------------------------------------------
1 | FRONTEND_HOST=ticketz.exemplo.com.br
2 | EMAIL_ADDRESS=ticketz@exemplo.com.br
3 |
4 | ### Normalmente não é necessário alterar estes valores
5 |
6 | BACKEND_HOST=${FRONTEND_HOST}
7 | BACKEND_PATH=/backend
8 |
9 | RECAPTCHA_SITE_KEY=
10 |
11 | VIRTUAL_HOST=${FRONTEND_HOST}
12 | VIRTUAL_PORT=80
13 | LETSENCRYPT_HOST=${FRONTEND_HOST}
14 | LETSENCRYPT_EMAIL=${EMAIL_ADDRESS}
15 |
--------------------------------------------------------------------------------
/.env-frontend-cloudflare:
--------------------------------------------------------------------------------
1 | BACKEND_HOST=apiticketz.example.com
2 |
--------------------------------------------------------------------------------
/.env-frontend-local:
--------------------------------------------------------------------------------
1 | FRONTEND_HOST=localhost
2 | FRONTEND_PORT=3000
3 | BACKEND_PATH=/backend
4 |
5 | BACKEND_HOST=${FRONTEND_HOST}
6 | BACKEND_PROTOCOL=http
7 | BACKEND_PORT=${FRONTEND_PORT}
8 |
9 | EMAIL_ADDRESS=admin@ticketz.host
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .docker
2 | *~
3 | /.project
4 |
--------------------------------------------------------------------------------
/backend/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/backend/.DS_Store
--------------------------------------------------------------------------------
/backend/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | dist
4 |
--------------------------------------------------------------------------------
/backend/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/backend/.env.dev:
--------------------------------------------------------------------------------
1 | NODE_ENV=
2 | BACKEND_URL=http://localhost:8080
3 | FRONTEND_URL=http://localhost:3000
4 | #PROXY_PORT=8080
5 | PORT=8080
6 |
7 | DB_DIALECT=postgres
8 | DB_HOST=127.0.0.1
9 | DB_PORT=5432
10 | DB_USER=ticketz
11 | #DB_PASS=
12 | DB_NAME=ticketz
13 | #DB_DEBUG=true
14 |
15 | REDIS_URI=redis://127.0.0.1:6379
16 | REDIS_OPT_LIMITER_MAX=1
17 | REDIS_OPT_LIMITER_DURATION=3000
18 |
19 | USER_LIMIT=10000
20 | CONNECTIONS_LIMIT=100000
21 | CLOSED_SEND_BY_ME=true
22 |
23 | LOG_LEVEL=debug
24 |
--------------------------------------------------------------------------------
/backend/.eslintignore:
--------------------------------------------------------------------------------
1 | /*.js
2 | node_modules
3 | dist
4 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | /public/*
4 | /.well-known
5 | /.env
6 | /.externalToolBuilders/
7 | /.project
8 | /.settings/
9 | /private/*
10 | !/private/index.html
11 |
--------------------------------------------------------------------------------
/backend/.sequelizerc:
--------------------------------------------------------------------------------
1 | const { resolve } = require("path");
2 |
3 | module.exports = {
4 | "config": resolve(__dirname, "dist", "config", "database.js"),
5 | "modules-path": resolve(__dirname, "dist", "models"),
6 | "migrations-path": resolve(__dirname, "dist", "database", "migrations"),
7 | "seeders-path": resolve(__dirname, "dist", "database", "seeds"),
8 | };
9 |
--------------------------------------------------------------------------------
/backend/certs/coloque_seus_certificado_aqui:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/backend/certs/homologacaoActom.p12:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/backend/certs/homologacaoActom.p12
--------------------------------------------------------------------------------
/backend/certs/producao-448607-Admin.p12:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/backend/certs/producao-448607-Admin.p12
--------------------------------------------------------------------------------
/backend/certs/producaoActom.p12:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/backend/certs/producaoActom.p12
--------------------------------------------------------------------------------
/backend/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: false,
3 | trailingComma: "none",
4 | arrowParens: "avoid"
5 | };
6 |
--------------------------------------------------------------------------------
/backend/private/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Private Folder
4 |
5 |
6 | Private Folder
7 |
8 |
9 |
--------------------------------------------------------------------------------
/backend/src/@types/express.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace Express {
2 | export interface Request {
3 | user: { id: string; profile: string; isSuper: boolean; companyId: number };
4 | companyId: number | undefined;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/backend/src/@types/qrcode-terminal.d.ts:
--------------------------------------------------------------------------------
1 | declare module "qrcode-terminal";
2 |
--------------------------------------------------------------------------------
/backend/src/bootstrap.ts:
--------------------------------------------------------------------------------
1 | import dotenv from "dotenv";
2 |
3 | dotenv.config({
4 | path: process.env.NODE_ENV === "test" ? ".env.test" : ".env"
5 | });
6 |
--------------------------------------------------------------------------------
/backend/src/config/database.ts:
--------------------------------------------------------------------------------
1 | import "../bootstrap";
2 |
3 | module.exports = {
4 | define: {
5 | charset: "utf8mb4",
6 | collate: "utf8mb4_bin"
7 | },
8 | pool: {
9 | max: process.env.DB_MAX_CONNECTIONS || 60,
10 | min: process.env.DB_MIN_CONNECTIONS || 5,
11 | acquire: process.env.DB_ACQUIRE || 30000,
12 | idle: process.env.DB_IDLE || 10000
13 | },
14 | dialect: process.env.DB_DIALECT || "postgres",
15 | timezone: process.env.DB_TIMEZONE || "-03:00",
16 | host: process.env.DB_HOST,
17 | port: process.env.DB_PORT || 5432,
18 | database: process.env.DB_NAME,
19 | username: process.env.DB_USER,
20 | password: process.env.DB_PASS,
21 | logging: process.env.DB_DEBUG && console.log,
22 | seederStorage: "sequelize"
23 | };
24 |
--------------------------------------------------------------------------------
/backend/src/config/privateFiles.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import multer from "multer";
3 |
4 | const privateFolder = __dirname.endsWith("/dist")
5 | ? path.resolve(__dirname, "..", "private")
6 | : path.resolve(__dirname, "..", "..", "private");
7 |
8 | export default {
9 | directory: privateFolder,
10 |
11 | storage: multer.diskStorage({
12 | destination: privateFolder,
13 | filename(req, file, cb) {
14 | const fileName = new Date().getTime() + path.extname(file.originalname);
15 |
16 | return cb(null, fileName);
17 | }
18 | })
19 | };
20 |
--------------------------------------------------------------------------------
/backend/src/config/redis.ts:
--------------------------------------------------------------------------------
1 | export const REDIS_URI_CONNECTION = process.env.REDIS_URI || "";
2 | export const REDIS_OPT_LIMITER_MAX = process.env.REDIS_OPT_LIMITER_MAX || 1;
3 | export const REDIS_OPT_LIMITER_DURATION =
4 | process.env.REDIS_OPT_LIMITER_DURATION || 3000;
5 |
--------------------------------------------------------------------------------
/backend/src/config/upload.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import multer from "multer";
3 |
4 | const publicFolder = __dirname.endsWith("/dist")
5 | ? path.resolve(__dirname, "..", "public")
6 | : path.resolve(__dirname, "..", "..", "public");
7 |
8 | export default {
9 | directory: publicFolder,
10 |
11 | storage: multer.diskStorage({
12 | destination: publicFolder,
13 | filename(req, file, cb) {
14 | const fileName = new Date().getTime() + path.extname(file.originalname);
15 |
16 | return cb(null, fileName);
17 | }
18 | })
19 | };
20 |
--------------------------------------------------------------------------------
/backend/src/controllers/ImportPhoneContactsController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from "express";
2 | import ImportContactsService from "../services/WbotServices/ImportContactsService";
3 |
4 | export const store = async (req: Request, res: Response): Promise => {
5 | const { companyId } = req.user;
6 | const { whatsappId } = req.body;
7 |
8 | await ImportContactsService(companyId, whatsappId);
9 |
10 | return res.status(200).json({ message: "contacts imported" });
11 | };
12 |
--------------------------------------------------------------------------------
/backend/src/controllers/SubscriptionController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from "express";
2 | import * as Yup from "yup";
3 | import AppError from "../errors/AppError";
4 | import { payGatewayCreateSubscription, payGatewayReceiveWebhook } from "../services/PaymentGatewayServices/PaymentGatewayServices";
5 |
6 | export const createSubscription = async (
7 | req: Request,
8 | res: Response
9 | ): Promise => {
10 |
11 | const schema = Yup.object().shape({
12 | price: Yup.string().required(),
13 | users: Yup.string().required(),
14 | connections: Yup.string().required()
15 | });
16 |
17 | if (!(await schema.isValid(req.body))) {
18 | throw new AppError("Validation fails", 400);
19 | }
20 |
21 | return payGatewayCreateSubscription(req, res);
22 | }
23 |
24 | export const webhook = async (
25 | req: Request,
26 | res: Response
27 | ): Promise => {
28 | return payGatewayReceiveWebhook(req, res);
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/controllers/VersionController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from "express";
2 | import GetPublicSettingService from "../services/SettingServices/GetPublicSettingService";
3 | import { GitInfo } from "../gitinfo";
4 |
5 | export const version = async (
6 | req: Request,
7 | res: Response
8 | ): Promise => {
9 | const appName = await GetPublicSettingService({ key: "appName" });
10 |
11 | const data = {
12 | name: appName || "Ticketz - Chat Based Ticket System",
13 | ...GitInfo
14 | };
15 |
16 | return res.status(200).json(data);
17 | };
18 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200717133431-add-uuid-ossp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes, Sequelize } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'),
7 | ]);
8 | },
9 |
10 | down: (_) => {
11 | return Promise.resolve();
12 | }
13 |
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Contacts", "email", {
6 | type: DataTypes.STRING,
7 | allowNull: false,
8 | defaultValue: ""
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Contacts", "email");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.removeColumn("Messages", "userId");
6 | },
7 |
8 | down: (queryInterface: QueryInterface) => {
9 | return queryInterface.addColumn("Messages", "userId", {
10 | type: DataTypes.INTEGER,
11 | references: { model: "Users", key: "id" },
12 | onUpdate: "CASCADE",
13 | onDelete: "SET NULL"
14 | });
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "fromMe", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Messages", "fromMe");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.changeColumn("Tickets", "lastMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.changeColumn("Tickets", "lastMessage", {
12 | type: DataTypes.STRING
13 | });
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Users", "profile", {
6 | type: DataTypes.STRING,
7 | allowNull: false,
8 | defaultValue: "admin"
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Users", "profile");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200903215941-create-settings.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.createTable("Settings", {
6 | key: {
7 | type: DataTypes.STRING,
8 | primaryKey: true,
9 | allowNull: false
10 | },
11 | value: {
12 | type: DataTypes.TEXT,
13 | allowNull: false
14 | },
15 | createdAt: {
16 | type: DataTypes.DATE,
17 | allowNull: false
18 | },
19 | updatedAt: {
20 | type: DataTypes.DATE,
21 | allowNull: false
22 | }
23 | });
24 | },
25 |
26 | down: (queryInterface: QueryInterface) => {
27 | return queryInterface.dropTable("Settings");
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "name", {
6 | type: DataTypes.STRING,
7 | allowNull: false,
8 | unique: true
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Whatsapps", "name");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "default", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Whatsapps", "default");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tickets", "whatsappId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Whatsapps", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Tickets", "whatsappId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.renameColumn("Whatsapps", "default", "isDefault");
6 | },
7 |
8 | down: (queryInterface: QueryInterface) => {
9 | return queryInterface.renameColumn("Whatsapps", "isDefault", "default");
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200927220708-add-isDeleted-column-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "isDeleted", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Messages", "isDeleted");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200929145451-add-user-tokenVersion-column.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Users", "tokenVersion", {
6 | type: DataTypes.INTEGER,
7 | allowNull: false,
8 | defaultValue: 0
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Users", "tokenVersion");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200930162323-add-isGroup-column-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tickets", "isGroup", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Tickets", "isGroup");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20200930194808-add-isGroup-column-to-contacts.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Contacts", "isGroup", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Contacts", "isGroup");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20201004150008-add-contactId-column-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "contactId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Contacts", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "CASCADE"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Messages", "contactId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20201004155719-add-vcardContactId-column-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "vcardContactId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Contacts", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "CASCADE"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Messages", "vcardContactId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20201004955719-remove-vcardContactId-column-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.removeColumn("Messages", "vcardContactId");
6 | },
7 |
8 | down: (queryInterface: QueryInterface) => {
9 | return queryInterface.addColumn("Messages", "vcardContactId", {
10 | type: DataTypes.INTEGER,
11 | references: { model: "Contacts", key: "id" },
12 | onUpdate: "CASCADE",
13 | onDelete: "CASCADE"
14 | });
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20201026215410-add-retries-to-whatsapps.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "retries", {
6 | type: DataTypes.INTEGER,
7 | defaultValue: 0,
8 | allowNull: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Whatsapps", "retries");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20201028124427-add-quoted-msg-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "quotedMsgId", {
6 | type: DataTypes.STRING,
7 | references: { model: "Messages", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Messages", "quotedMsgId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210108001431-add-unreadMessages-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tickets", "unreadMessages", {
6 | type: DataTypes.INTEGER
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Tickets", "unreadMessages");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210108164504-add-queueId-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tickets", "queueId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Queues", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Tickets", "queueId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210108174594-associate-whatsapp-queue.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.createTable("WhatsappQueues", {
6 | whatsappId: {
7 | type: DataTypes.INTEGER,
8 | primaryKey: true
9 | },
10 | queueId: {
11 | type: DataTypes.INTEGER,
12 | primaryKey: true
13 | },
14 | createdAt: {
15 | type: DataTypes.DATE,
16 | allowNull: false
17 | },
18 | updatedAt: {
19 | type: DataTypes.DATE,
20 | allowNull: false
21 | }
22 | });
23 | },
24 |
25 | down: (queryInterface: QueryInterface) => {
26 | return queryInterface.dropTable("WhatsappQueues");
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210108204708-associate-users-queue.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.createTable("UserQueues", {
6 | userId: {
7 | type: DataTypes.INTEGER,
8 | primaryKey: true
9 | },
10 | queueId: {
11 | type: DataTypes.INTEGER,
12 | primaryKey: true
13 | },
14 | createdAt: {
15 | type: DataTypes.DATE,
16 | allowNull: false
17 | },
18 | updatedAt: {
19 | type: DataTypes.DATE,
20 | allowNull: false
21 | }
22 | });
23 | },
24 |
25 | down: (queryInterface: QueryInterface) => {
26 | return queryInterface.dropTable("UserQueues");
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192513-add-greetingMessage-to-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "greetingMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Whatsapps", "greetingMessage");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192515-add-column-companyId-to-Settings-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Settings", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Settings", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192516-add-column-companyId-to-Users-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Users", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Users", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192517-add-column-companyId-to-Contacts-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Contacts", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Contacts", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192518-add-column-companyId-to-Messages-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Messages", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192519-add-column-companyId-to-Queues-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Queues", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Queues", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192520-add-column-companyId-to-Whatsapps-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Whatsapps", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192521-add-column-companyId-to-Tickets-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tickets", "companyId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Companies", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Tickets", "companyId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192523-add-column-planId-to-Companies.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Companies", "planId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Plans", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Companies", "planId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192523-add-column-status-and-schedules-to-Companies.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("Companies", "status", {
7 | type: DataTypes.BOOLEAN,
8 | defaultValue: true
9 | }),
10 | queryInterface.addColumn("Companies", "schedules", {
11 | type: DataTypes.JSONB,
12 | defaultValue: []
13 | })
14 | ]);
15 | },
16 |
17 | down: (queryInterface: QueryInterface) => {
18 | return Promise.all([
19 | queryInterface.removeColumn("Companies", "schedules"),
20 | queryInterface.removeColumn("Companies", "status")
21 | ]);
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192525-add-column-complationMessage-to-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "complationMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Whatsapps", "complationMessage");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192526-add-column-outOfHoursMessage-to-whatsapp .ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "outOfHoursMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Whatsapps", "outOfHoursMessage");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192527-add-column-super-to-Users-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Users", "super", {
6 | type: DataTypes.BOOLEAN,
7 | defaultValue: false
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Users", "super");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192528-change-column-message-to-quick-messages-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.changeColumn("QuickMessages", "message", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 | down: (queryInterface: QueryInterface) => {
10 | return queryInterface.changeColumn("QuickMessages", "message", {
11 | type: DataTypes.STRING
12 | });
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192530-add-unique-constraint-to-Contacts-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addConstraint("Contacts", { fields: ["number", "companyId"],
6 | type: "unique",
7 | name: "number_companyid_unique"
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeConstraint(
13 | "Contacts",
14 | "number_companyid_unique"
15 | );
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192532-add-column-online-to-Users-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Users", "online", {
6 | type: DataTypes.BOOLEAN,
7 | defaultValue: false
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Users", "online");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192534-add-rated-to-TicketTraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("TicketTraking", "ratingAt", {
7 | type: DataTypes.DATE,
8 | allowNull: true,
9 | defaultValue: null
10 | }),
11 | queryInterface.addColumn("TicketTraking", "rated", {
12 | type: DataTypes.BOOLEAN,
13 | defaultValue: false
14 | })
15 | ]);
16 | },
17 |
18 | down: (queryInterface: QueryInterface) => {
19 | return Promise.all([
20 | queryInterface.removeColumn("TicketTraking", "ratingAt"),
21 | queryInterface.removeColumn("TicketTraking", "rated")
22 | ]);
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192535-add-column-ratingMessage-to-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "ratingMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Whatsapps", "ratingMessage");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210109192536-add-unique-constraint-to-Tickets-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addConstraint("Tickets", { fields: ["contactId", "companyId"],
6 | type: "unique",
7 | name: "contactid_companyid_unique"
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeConstraint(
13 | "Tickets",
14 | "contactid_companyid_unique"
15 | );
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210818102606-add-uuid-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes, Sequelize } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("Tickets", "uuid", {
7 | type: DataTypes.UUID,
8 | allowNull: true,
9 | defaultValue: Sequelize.literal('uuid_generate_v4()')
10 | })
11 | ]);
12 | },
13 |
14 | down: (queryInterface: QueryInterface) => {
15 | return queryInterface.removeColumn("Tickets", "uuid");
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210818102607-remove-unique-indexes-to-Contacts-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.removeConstraint("Contacts","number_companyid_unique" )
6 |
7 | },
8 |
9 | down: async (queryInterface: QueryInterface) => {
10 | await queryInterface.addConstraint("Contacts", { fields: ["number", "companyId"],
11 | type: "unique",
12 | name: "number_companyid_unique"
13 | });
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210818102607-remove-unique-indexes-to-Queues-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.removeConstraint("Queues", "Queues_color_key"),
7 | queryInterface.removeConstraint("Queues", "Queues_name_key"),
8 | queryInterface.removeIndex("Queues", "Queues_color_key"),
9 | queryInterface.removeIndex("Queues", "Queues_name_key"),
10 | ]);
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return Promise.all([
15 | queryInterface.addConstraint("Queues", { fields: ["color"],
16 | name: "Queues_color_key",
17 | type: 'unique'
18 | }),
19 | queryInterface.addConstraint("Queues", { fields: ["name"],
20 | name: "Queues_name_key",
21 | type: 'unique'
22 | }),
23 | ]);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210818102608-add-unique-indexes-to-Queues-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addConstraint("Queues", { fields: ["color", "companyId"],
7 | name: "Queues_color_key",
8 | type: 'unique'
9 | }),
10 | queryInterface.addConstraint("Queues", { fields: ["name", "companyId"],
11 | name: "Queues_name_key",
12 | type: 'unique'
13 | }),
14 | ]);
15 | },
16 |
17 | down: (queryInterface: QueryInterface) => {
18 | return Promise.all([
19 | queryInterface.removeConstraint("Queues", "Queues_color_key"),
20 | queryInterface.removeConstraint("Queues", "Queues_name_key"),
21 | ]);
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20210818102609-add-token-to-Whatsapps.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "token", {
6 | type: DataTypes.TEXT,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "token");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20211212125704-add-chatbot-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("Tickets", "chatbot", {
7 | type: DataTypes.BOOLEAN,
8 | allowNull: true,
9 | defaultValue: false
10 | }),
11 | queryInterface.addColumn("Tickets", "queueOptionId", {
12 | type: DataTypes.INTEGER,
13 | references: { model: "QueueOptions", key: "id" },
14 | onUpdate: "SET null",
15 | onDelete: "SET null",
16 | allowNull: true
17 | })
18 | ]);
19 | },
20 |
21 | down: (queryInterface: QueryInterface) => {
22 | return queryInterface.removeColumn("Tickets", "chatbot"),
23 | queryInterface.removeColumn("Tickets", "queueOptionId");
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220115114088-add-column-userId-to-QuickMessages-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("QuickMessages", "userId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Users", key: "id" },
8 | onUpdate: "CASCADE",
9 | onDelete: "CASCADE"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("QuickMessages", "userId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220122160900-add-status-to-schedules.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Schedules", "status", {
6 | type: DataTypes.STRING,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Schedules", "status");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220220014719-add-farewellMessage-to-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "farewellMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Whatsapps", "farewellMessage");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220221014717-add-provider-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "provider", {
6 | type: DataTypes.TEXT,
7 | defaultValue: "stable"
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "provider");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220221014718-add-remoteJid-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "remoteJid", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Messages", "remoteJid");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220221014719-add-jsonMessage-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "dataJson", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Messages", "dataJson");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220221014720-add-participant-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "participant", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Messages", "participant");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220404000000-add-column-queueId-to-Messages-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "queueId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Queues", key: "id" },
8 | onUpdate: "SET NULL",
9 | onDelete: "SET NULL"
10 | });
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return queryInterface.removeColumn("Messages", "queueId");
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220406000000-add-column-dueDate-to-Companies.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Companies", "dueDate", {
6 | type: DataTypes.DATE,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Companies", "dueDate");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220406000001-add-column-recurrence-to-Companies.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Companies", "recurrence", {
6 | type: DataTypes.STRING,
7 | allowNull: true,
8 | defaultValue: ""
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Companies", "recurrence");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220411000002-add-column-schedules-and-outOfHoursMessage-to-Queues.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("Queues", "schedules", {
7 | type: DataTypes.JSONB,
8 | defaultValue: []
9 | }),
10 | queryInterface.addColumn("Queues", "outOfHoursMessage", {
11 | type: DataTypes.TEXT,
12 | allowNull: true
13 | })
14 | ]);
15 | },
16 |
17 | down: (queryInterface: QueryInterface) => {
18 | return Promise.all([
19 | queryInterface.removeColumn("Queues", "schedules"),
20 | queryInterface.removeColumn("Queues", "outOfHoursMessage")
21 | ]);
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20220814000000-add-value-to-plans.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("Plans", "value", {
7 | type: DataTypes.INTEGER,
8 | allowNull: true,
9 | defaultValue: 199.99
10 | })
11 | ]);
12 | },
13 |
14 | down: (queryInterface: QueryInterface) => {
15 | return queryInterface.removeColumn("Plans", "value");
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-channel-session.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "channel", {
6 | type: DataTypes.TEXT,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "channel");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-channel-to-contacts.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Contacts", "channel", {
6 | type: DataTypes.TEXT,
7 | defaultValue: "whatsapp",
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Contacts", "channel");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-channel-to-message.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "channel", {
6 | type: DataTypes.TEXT,
7 | defaultValue: "whatsapp",
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Messages", "channel");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-channel-to-ticket.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tickets", "channel", {
6 | type: DataTypes.TEXT,
7 | defaultValue: "whatsapp",
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Tickets", "channel");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-channel-token.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "facebookUserToken", {
6 | type: DataTypes.TEXT,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "facebookUserToken");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-channel-tokenUser.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "tokenMeta", {
6 | type: DataTypes.TEXT,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "tokenMeta");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-facebookPageUserId-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "facebookPageUserId", {
6 | type: DataTypes.TEXT,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "facebookPageUserId");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20222016014719-add-facebookUserId-whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "facebookUserId", {
6 | type: DataTypes.TEXT,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Whatsapps", "facebookUserId");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230528202116-add-transferMessage-field-to-Whatsapp.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Whatsapps", "transferMessage", {
6 | type: DataTypes.TEXT
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeColumn("Whatsapps", "transferMessage");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230723301001-add-kanban-to-Tags.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Tags", "kanban", {
6 | type: DataTypes.INTEGER,
7 | allowNull: true
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Tags", "kanban");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230813114236-change-ticket-lastMessage-column-type.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.changeColumn("Tickets", "lastMessage", {
6 | defaultValue: "",
7 | type: DataTypes.TEXT
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.changeColumn("Tickets", "lastMessage", {
13 | defaultValue: "",
14 | type: DataTypes.TEXT
15 | });
16 | }
17 | };
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230904212800-alter-value-to-plans.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.changeColumn("Plans", "value", {
7 | type: DataTypes.FLOAT,
8 | allowNull: true,
9 | })
10 | ]);
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return Promise.all([
15 | queryInterface.changeColumn("Plans", "value", {
16 | type: DataTypes.INTEGER,
17 | allowNull: true,
18 | defaultValue: 199.99
19 | })
20 | ]);
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230915212800-add-public-to-plans.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Plans", "isPublic", {
6 | type: DataTypes.BOOLEAN,
7 | defaultValue: true,
8 | allowNull: false
9 | });
10 | },
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Plans", "isPublic");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230918122800-add-media-to-Queues.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Queues", "mediaName", {
6 | type: DataTypes.TEXT,
7 | defaultValue: "",
8 | allowNull: true
9 | }),
10 | queryInterface.addColumn("Queues", "mediaPath", {
11 | type: DataTypes.TEXT,
12 | defaultValue: "",
13 | allowNull: true
14 | });
15 | },
16 | down: (queryInterface: QueryInterface) => {
17 | return queryInterface.removeColumn("Queues", "mediaName"),
18 | queryInterface.removeColumn("Queues", "mediaPath");
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20230918142800-add-media-to-QueueOptions.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("QueueOptions", "mediaName", {
6 | type: DataTypes.TEXT,
7 | defaultValue: "",
8 | allowNull: true
9 | }),
10 | queryInterface.addColumn("QueueOptions", "mediaPath", {
11 | type: DataTypes.TEXT,
12 | defaultValue: "",
13 | allowNull: true
14 | });
15 | },
16 | down: (queryInterface: QueryInterface) => {
17 | return queryInterface.removeColumn("QueueOptions", "mediaName"),
18 | queryInterface.removeColumn("QueueOptions", "mediaPath");
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20231219153800-add-isEdited-column-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Messages", "isEdited", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: (queryInterface: QueryInterface) => {
13 | return queryInterface.removeColumn("Messages", "isEdited");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20231230100900-remove-unique-constraint-to-ticket-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.removeConstraint(
6 | "Tickets",
7 | "contactid_companyid_unique"
8 | );
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.addConstraint("Tickets", { fields: ["contactId", "companyId"],
13 | type: "unique",
14 | name: "contactid_companyid_unique"
15 | });
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20232016014719-add-column-mediaType-to-ChatMessages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 | module.exports = {
3 | up: (queryInterface: QueryInterface) => {
4 | return Promise.all([
5 | queryInterface.addColumn("ChatMessages", "mediaType", {
6 | type: DataTypes.TEXT,
7 | allowNull: true,
8 | }),
9 | ]);
10 | },
11 | down: (queryInterface: QueryInterface) => {
12 | return Promise.all([
13 | queryInterface.removeColumn("ChatMessages", "mediaType"),
14 | ]);
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20240417150500-add-unique-constrait-to-Contacts-table.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | module.exports = {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addConstraint("Contacts", { fields: ["number", "companyId"],
6 | type: "unique",
7 | name: "number_companyid_unique"
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeConstraint(
13 | "Contacts",
14 | "number_companyid_unique"
15 | );
16 | }
17 | };
--------------------------------------------------------------------------------
/backend/src/database/migrations/20240522165800-add-disablebot-to-contact.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Contacts", "disableBot", {
6 | type: DataTypes.BOOLEAN,
7 | defaultValue: false
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Contacts", "disableBot");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20240524011900-add-presence-to-contact.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("Contacts", "presence", {
6 | type: DataTypes.STRING,
7 | defaultValue: "available"
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("Contacts", "presence");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20240820082900-add-expired-to-TicketTraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addColumn("TicketTraking", "expired", {
6 | type: DataTypes.BOOLEAN,
7 | defaultValue: false
8 | });
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.removeColumn("TicketTraking", "expired");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20240820082900-set-expired-to-TicketTraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, Op } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.bulkUpdate(
6 | "TicketTraking",
7 | { expired: true },
8 | {
9 | rated: false,
10 | ratingAt: { [Op.not]: null }
11 | }
12 | );
13 | },
14 |
15 | down: () => {
16 | return Promise.resolve(true);
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20240827171200-add-thumbnailurl-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addColumn("Messages", "thumbnailUrl", {
7 | type: DataTypes.STRING,
8 | allowNull: true
9 | })
10 | ]);
11 | },
12 |
13 | down: (queryInterface: QueryInterface) => {
14 | return Promise.all([
15 | queryInterface.removeColumn("Messages", "thumbnailUrl")
16 | ]);
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20241013110200-whatsapps-channel-default.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | return queryInterface.changeColumn("Whatsapps", "channel", {
6 | type: DataTypes.STRING,
7 | allowNull: false,
8 | defaultValue: "whatsapp"
9 | });
10 | },
11 |
12 | down: async (queryInterface: QueryInterface) => {
13 | return queryInterface.changeColumn("Whatsapps", "channel", {
14 | type: DataTypes.STRING,
15 | allowNull: true,
16 | defaultValue: null
17 | });
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20241110080800-change-QueueOptions-forwardQueueId-column.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.changeColumn("QueueOptions", "forwardQueueId", {
6 | type: DataTypes.INTEGER,
7 | references: { model: "Queues", key: "id" },
8 | onDelete: "SET NULL",
9 | onUpdate: "CASCADE",
10 | allowNull: true
11 | });
12 | },
13 |
14 | down: async (queryInterface: QueryInterface) => {
15 | await queryInterface.changeColumn("QueueOptions", "forwardQueueId", {
16 | type: DataTypes.INTEGER,
17 | references: { model: "Queues", key: "id" },
18 | allowNull: true
19 | });
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20241128175500-create-insensitive-index-for-users-email.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.addIndex("Users", {
6 | fields: [
7 | queryInterface.sequelize.fn(
8 | "LOWER",
9 | queryInterface.sequelize.col("email")
10 | )
11 | ],
12 | name: "idx_lower_email"
13 | });
14 | },
15 |
16 | down: async (queryInterface: QueryInterface) => {
17 | await queryInterface.removeIndex("Users", "idx_lower_email");
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20241128175800-add-save-messages-to-schedules.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.addColumn("Schedules", "saveMessage", {
6 | type: DataTypes.BOOLEAN,
7 | allowNull: false,
8 | defaultValue: false
9 | });
10 | },
11 |
12 | down: async (queryInterface: QueryInterface) => {
13 | await queryInterface.removeColumn("Schedules", "saveMessage");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20241201102700-add-unaccent-extension.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.sequelize.query("CREATE EXTENSION IF NOT EXISTS unaccent")
7 | ]);
8 | },
9 |
10 | down: _ => {
11 | return Promise.resolve();
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250226163500-fix-queueoptions-constraints.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.removeConstraint(
6 | "QueueOptions",
7 | "QueueOptions_forwardQueueId_fkey"
8 | );
9 | },
10 |
11 | down: (queryInterface: QueryInterface) => {
12 | return queryInterface.addConstraint("QueueOptions", {
13 | fields: ["forwardQueueId"],
14 | type: "foreign key",
15 | name: "QueueOptions_forwardQueueId_fkey",
16 | references: {
17 | table: "Queues",
18 | field: "id"
19 | },
20 | onUpdate: "NO ACTION",
21 | onDelete: "NO ACTION"
22 | });
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250321122300-add-indexes-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.addIndex("Messages", ["companyId", "contactId"], {
6 | name: "idx_messages_companyid_contactid"
7 | });
8 |
9 | await queryInterface.addIndex("Messages", ["ticketId", "companyId"], {
10 | name: "idx_messages_ticketid_companyid"
11 | });
12 | },
13 |
14 | down: async (queryInterface: QueryInterface) => {
15 | await queryInterface.removeIndex(
16 | "Messages",
17 | "idx_messages_companyid_contactid"
18 | );
19 | await queryInterface.removeIndex(
20 | "Messages",
21 | "idx_messages_ticketid_companyid"
22 | );
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250325080200-add-index-id-fromme-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | return queryInterface.addIndex("Messages", ["id", "fromMe"], {
6 | name: "idx_messages_id_fromme"
7 | });
8 | },
9 |
10 | down: async (queryInterface: QueryInterface) => {
11 | return queryInterface.removeIndex("Messages", "idx_messages_id_fromme");
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250326110400-add-indexes-to-baileyskeys.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | return queryInterface.addIndex(
6 | "BaileysKeys",
7 | ["whatsappId", "type", "key"],
8 | {
9 | name: "idx_baileyskeys_whatsappid_type_key"
10 | }
11 | );
12 | },
13 |
14 | down: async (queryInterface: QueryInterface) => {
15 | return queryInterface.removeIndex(
16 | "BaileysKeys",
17 | "idx_baileyskeys_whatsappid_type_key"
18 | );
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250327164700-add-index-quotedMsgId-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addIndex("Messages", ["ticketId", "quotedMsgId"], {
6 | name: "idx_messages_ticketid_quotedmsgid"
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeIndex(
12 | "Messages",
13 | "idx_messages_ticketid_quotedmsgid"
14 | );
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250410205700-add-index-ticketId-to-ticketTraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return queryInterface.addIndex("TicketTraking", ["ticketId"], {
6 | name: "idx_tickettraking_ticketid"
7 | });
8 | },
9 |
10 | down: (queryInterface: QueryInterface) => {
11 | return queryInterface.removeIndex(
12 | "TicketTraking",
13 | "idx_tickettraking_ticketid"
14 | );
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250415163900-add-index-timestamps-to-tickets.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addIndex("Tickets", ["createdAt"], {
7 | name: "idx_tickets_createdat"
8 | }),
9 | queryInterface.addIndex("Tickets", ["updatedAt"], {
10 | name: "idx_tickets_updatedat"
11 | })
12 | ]);
13 | },
14 |
15 | down: (queryInterface: QueryInterface) => {
16 | return Promise.all([
17 | queryInterface.removeIndex("Tickets", "idx_tickets_createdat"),
18 | queryInterface.removeIndex("Tickets", "idx_tickets_updatedat")
19 | ]);
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250415164000-add-index-timestamps-to-tickettraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addIndex("TicketTraking", ["createdAt"], {
7 | name: "idx_tickettraking_createdat"
8 | }),
9 | queryInterface.addIndex("TicketTraking", ["updatedAt"], {
10 | name: "idx_tickettraking_updatedat"
11 | })
12 | ]);
13 | },
14 |
15 | down: (queryInterface: QueryInterface) => {
16 | return Promise.all([
17 | queryInterface.removeIndex(
18 | "TicketTraking",
19 | "idx_tickettraking_createdat"
20 | ),
21 | queryInterface.removeIndex("TicketTraking", "idx_tickettraking_updatedat")
22 | ]);
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250415164200-add-index-timestamps-to-messages.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: (queryInterface: QueryInterface) => {
5 | return Promise.all([
6 | queryInterface.addIndex("Messages", ["createdAt"], {
7 | name: "idx_messages_createdat"
8 | }),
9 | queryInterface.addIndex("Messages", ["updatedAt"], {
10 | name: "idx_messages_updatedat"
11 | })
12 | ]);
13 | },
14 |
15 | down: (queryInterface: QueryInterface) => {
16 | return Promise.all([
17 | queryInterface.removeIndex("Messages", "idx_messages_createdat"),
18 | queryInterface.removeIndex("Messages", "idx_messages_updatedat")
19 | ]);
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250506155200-fix-groq-typo-on-settings.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.sequelize.query(`
6 | UPDATE "Settings"
7 | SET "value" = 'groq'
8 | WHERE "key" = 'aiProvider' AND "value" = 'grok';
9 | `);
10 | },
11 |
12 | down: async (queryInterface: QueryInterface) => {
13 | await queryInterface.sequelize.query(`
14 | UPDATE "Settings"
15 | SET "value" = 'grok'
16 | WHERE "key" = 'aiProvider' AND "value" = 'groq';
17 | `);
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250516095500-add-chatbotendat-to-tickettraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface, DataTypes } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.addColumn("TicketTraking", "chatbotendAt", {
6 | type: DataTypes.DATE,
7 | allowNull: true,
8 | comment: "Timestamp indicating when the chatbot interaction ended"
9 | });
10 | },
11 |
12 | down: async (queryInterface: QueryInterface) => {
13 | await queryInterface.removeColumn("TicketTraking", "chatbotendAt");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250520150600-change-duedate-to-dateonly.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | // Change the column to DATE using a raw SQL query to preserve UTC date
6 | await queryInterface.sequelize.query(`
7 | ALTER TABLE "Companies"
8 | ALTER COLUMN "dueDate"
9 | TYPE DATE
10 | USING ("dueDate" AT TIME ZONE 'UTC')::DATE;
11 | `);
12 | },
13 | down: async (queryInterface: QueryInterface) => {
14 | // Revert the column to TIMESTAMP using a raw SQL query
15 | await queryInterface.sequelize.query(`
16 | ALTER TABLE "Companies"
17 | ALTER COLUMN "dueDate"
18 | TYPE TIMESTAMP WITH TIME ZONE
19 | USING "dueDate"::timestamp with time zone;
20 | `);
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/backend/src/database/migrations/20250522093400-fix-finidsedAt-on-TicketTraking.ts:
--------------------------------------------------------------------------------
1 | import { QueryInterface } from "sequelize";
2 |
3 | export default {
4 | up: async (queryInterface: QueryInterface) => {
5 | await queryInterface.sequelize.query(`
6 | UPDATE "TicketTraking"
7 | SET "finishedAt" = "ratingAt"
8 | WHERE "ratingAt" IS NOT NULL;
9 | `);
10 | },
11 |
12 | down: async () => {
13 | // no operation
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/errors/AppError.ts:
--------------------------------------------------------------------------------
1 | class AppError {
2 | public readonly message: string;
3 |
4 | public readonly statusCode: number;
5 |
6 | public readonly level: string;
7 |
8 | constructor(message: string, statusCode = 400, level = "warn") {
9 | this.message = message;
10 | this.statusCode = statusCode;
11 | this.level = level;
12 | }
13 | }
14 |
15 | export default AppError;
16 |
--------------------------------------------------------------------------------
/backend/src/gitinfo.ts:
--------------------------------------------------------------------------------
1 | export const GitInfo = {
2 | commitHash: "",
3 | commitTimestamp: "",
4 | branchName: "",
5 | tagName: "v1.0.x",
6 | buildTimestamp: "custom build"
7 | };
8 |
--------------------------------------------------------------------------------
/backend/src/helpers/CheckContactOpenTickets.ts:
--------------------------------------------------------------------------------
1 | import { Op, WhereOptions } from "sequelize";
2 | import AppError from "../errors/AppError";
3 | import Ticket from "../models/Ticket";
4 |
5 | const CheckContactOpenTickets = async (
6 | contactId: number,
7 | whatsappId?: number,
8 | returnTicket = false
9 | ): Promise => {
10 | const where: WhereOptions = {
11 | contactId,
12 | status: { [Op.or]: ["open", "pending"] }
13 | };
14 |
15 | if (whatsappId) {
16 | where.whatsappId = whatsappId;
17 | }
18 |
19 | const ticket = await Ticket.findOne({
20 | where
21 | });
22 |
23 | if (ticket && !returnTicket) {
24 | throw new AppError("ERR_OTHER_OPEN_TICKET");
25 | }
26 |
27 | return ticket;
28 | };
29 |
30 | export default CheckContactOpenTickets;
31 |
--------------------------------------------------------------------------------
/backend/src/helpers/CheckContactSomeTicket.ts:
--------------------------------------------------------------------------------
1 | import { Op } from "sequelize";
2 | import AppError from "../errors/AppError";
3 | import Ticket from "../models/Ticket";
4 |
5 | const CheckContactSomeTickets = async (
6 | contactId: number,
7 | companyId: number
8 | ): Promise => {
9 | const ticket = await Ticket.findOne({
10 | where: { contactId, companyId }
11 | });
12 |
13 | if (ticket) {
14 | throw new AppError("ERR_OTHER_OPEN_TICKET");
15 | }
16 | };
17 |
18 | export default CheckContactSomeTickets;
19 |
--------------------------------------------------------------------------------
/backend/src/helpers/CreateTokens.ts:
--------------------------------------------------------------------------------
1 | import { sign } from "jsonwebtoken";
2 | import authConfig from "../config/auth";
3 | import User from "../models/User";
4 |
5 | export const createAccessToken = (user: User): string => {
6 | const { secret, expiresIn } = authConfig;
7 |
8 | return sign(
9 | {
10 | username: user.name,
11 | profile: user.profile,
12 | super: user.super,
13 | id: user.id,
14 | companyId: user.companyId
15 | },
16 | secret,
17 | {
18 | expiresIn
19 | }
20 | );
21 | };
22 |
23 | export const createRefreshToken = (user: User): string => {
24 | const { refreshSecret, refreshExpiresIn } = authConfig;
25 |
26 | return sign(
27 | { id: user.id, tokenVersion: user.tokenVersion, companyId: user.companyId },
28 | refreshSecret,
29 | {
30 | expiresIn: refreshExpiresIn
31 | }
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/backend/src/helpers/GetPublicPath.ts:
--------------------------------------------------------------------------------
1 | // get public folder
2 |
3 | import path from "path";
4 |
5 | export const getPublicPath = () => {
6 | const publicFolder = __dirname.endsWith("/dist")
7 | ? path.resolve(__dirname, "..", "public")
8 | : path.resolve(__dirname, "..", "..", "public");
9 |
10 | return publicFolder;
11 | };
12 |
--------------------------------------------------------------------------------
/backend/src/helpers/GetTicketWbot.ts:
--------------------------------------------------------------------------------
1 | import { getWbot, Session } from "../libs/wbot";
2 | import GetDefaultWhatsApp from "./GetDefaultWhatsApp";
3 | import Ticket from "../models/Ticket";
4 |
5 | const GetTicketWbot = async (ticket: Ticket): Promise => {
6 | if (!ticket.whatsappId) {
7 | const defaultWhatsapp = await GetDefaultWhatsApp(ticket.companyId);
8 |
9 | await ticket.$set("whatsapp", defaultWhatsapp);
10 | }
11 |
12 | const wbot = getWbot(ticket.whatsappId);
13 |
14 | return wbot;
15 | };
16 |
17 | export default GetTicketWbot;
18 |
--------------------------------------------------------------------------------
/backend/src/helpers/GetWhatsappWbot.ts:
--------------------------------------------------------------------------------
1 | import { getWbot } from "../libs/wbot";
2 | import Whatsapp from "../models/Whatsapp";
3 |
4 | const GetWhatsappWbot = async (whatsapp: Whatsapp) => {
5 | const wbot = await getWbot(whatsapp.id);
6 | return wbot;
7 | };
8 |
9 | export default GetWhatsappWbot;
10 |
--------------------------------------------------------------------------------
/backend/src/helpers/MakeRandomId.ts:
--------------------------------------------------------------------------------
1 | // generate random id string for file names, function got from: https://stackoverflow.com/a/1349426/1851801
2 | export function makeRandomId(length: number) {
3 | let result = "";
4 | const characters =
5 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
6 | const charactersLength = characters.length;
7 | let counter = 0;
8 | while (counter < length) {
9 | result += characters.charAt(Math.floor(Math.random() * charactersLength));
10 | counter += 1;
11 | }
12 | return result;
13 | }
14 |
--------------------------------------------------------------------------------
/backend/src/helpers/SendRefreshToken.ts:
--------------------------------------------------------------------------------
1 | import { CookieOptions, Response } from "express";
2 |
3 | export const SendRefreshToken = (res: Response, token: string): void => {
4 | const cookieOptions: CookieOptions = {
5 | httpOnly: true,
6 | expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
7 | };
8 |
9 | if (process.env.BACKEND_URL.startsWith("https:")) {
10 | cookieOptions.sameSite = "none";
11 | cookieOptions.secure = true;
12 | }
13 |
14 | res.cookie("jrt", token, cookieOptions);
15 | };
16 |
--------------------------------------------------------------------------------
/backend/src/helpers/SerializeUser.ts:
--------------------------------------------------------------------------------
1 | import Queue from "../models/Queue";
2 | import Company from "../models/Company";
3 | import User from "../models/User";
4 | import Setting from "../models/Setting";
5 |
6 | interface SerializedUser {
7 | id: number;
8 | name: string;
9 | email: string;
10 | profile: string;
11 | companyId: number;
12 | company: Company | null;
13 | super: boolean;
14 | queues: Queue[];
15 | }
16 |
17 | export const SerializeUser = async (user: User): Promise => {
18 | return {
19 | id: user.id,
20 | name: user.name,
21 | email: user.email,
22 | profile: user.profile,
23 | companyId: user.companyId,
24 | company: user.company,
25 | super: user.super,
26 | queues: user.queues
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/backend/src/helpers/SerializeWbotMsgId.ts:
--------------------------------------------------------------------------------
1 | import Message from "../models/Message";
2 | import Ticket from "../models/Ticket";
3 |
4 | const SerializeWbotMsgId = (ticket: Ticket, message: Message): string => {
5 | const serializedMsgId = `${message.fromMe}_${ticket.contact.number}@${
6 | ticket.isGroup ? "g" : "c"
7 | }.us_${message.id}`;
8 |
9 | return serializedMsgId;
10 | };
11 |
12 | export default SerializeWbotMsgId;
13 |
--------------------------------------------------------------------------------
/backend/src/helpers/UpdateDeletedUserOpenTicketsStatus.ts:
--------------------------------------------------------------------------------
1 | import Ticket from "../models/Ticket";
2 | import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
3 |
4 | const UpdateDeletedUserOpenTicketsStatus = async (
5 | tickets: Ticket[],
6 | companyId: number
7 | ): Promise => {
8 | tickets.forEach(async t => {
9 | const ticketId = t.id.toString();
10 |
11 | await UpdateTicketService({
12 | ticketData: { status: "pending" },
13 | ticketId: Number.parseInt(ticketId, 10),
14 | companyId
15 | });
16 | });
17 | };
18 |
19 | export default UpdateDeletedUserOpenTicketsStatus;
20 |
--------------------------------------------------------------------------------
/backend/src/helpers/parseToMilliseconds.ts:
--------------------------------------------------------------------------------
1 | export function parseToMilliseconds(seconds: number) {
2 | return seconds * 1000;
3 | }
4 |
--------------------------------------------------------------------------------
/backend/src/helpers/randomValue.ts:
--------------------------------------------------------------------------------
1 | export function randomValue(min, max) {
2 | return Math.floor(Math.random() * max) + min;
3 | }
4 |
--------------------------------------------------------------------------------
/backend/src/helpers/replaceExtension.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Replace the file extension of a given filename.
3 | *
4 | * Up to 4 characters are considered for the original extension.
5 | *
6 | * @param {string} filename - The original filename.
7 | * @param {string} newExtension - The new file extension (without the dot).
8 | * @returns {string} - The filename with the new extension.
9 | */
10 | export function replaceFileExtension(
11 | filename: string,
12 | newExtension: string
13 | ): string {
14 | return filename.replace(/(\.[^/.]{0,4})?$/, `.${newExtension}`);
15 | }
16 |
--------------------------------------------------------------------------------
/backend/src/middleware/envTokenAuth.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from "express";
2 |
3 | import AppError from "../errors/AppError";
4 |
5 | type TokenPayload = {
6 | token: string | undefined;
7 | };
8 |
9 | const envTokenAuth = (
10 | req: Request,
11 | res: Response,
12 | next: NextFunction
13 | ): void => {
14 | try {
15 | const { token: bodyToken } = req.body as TokenPayload;
16 | const { token: queryToken } = req.query as TokenPayload;
17 |
18 | if (queryToken === process.env.ENV_TOKEN) {
19 | return next();
20 | }
21 |
22 | if (bodyToken === process.env.ENV_TOKEN) {
23 | return next();
24 | }
25 | } catch (e) {
26 | console.log(e);
27 | }
28 |
29 | throw new AppError("Token inválido", 403);
30 | };
31 |
32 | export default envTokenAuth;
33 |
--------------------------------------------------------------------------------
/backend/src/middleware/isAdmin.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from "express";
2 | import AppError from "../errors/AppError";
3 | import User from "../models/User";
4 |
5 | const isAdmin = async (
6 | req: Request,
7 | res: Response,
8 | next: NextFunction
9 | ): Promise => {
10 | const { profile } = await User.findByPk(req.user.id);
11 | if (profile !== "admin") {
12 | throw new AppError("Acesso não permitido", 403);
13 | }
14 |
15 | return next();
16 | };
17 |
18 | export default isAdmin;
19 |
--------------------------------------------------------------------------------
/backend/src/middleware/isCompliant.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from "express";
2 | import AppError from "../errors/AppError";
3 | import { checkCompanyCompliant } from "../helpers/CheckCompanyCompliant";
4 |
5 | const isCompliant = async (
6 | req: Request,
7 | res: Response,
8 | next: NextFunction
9 | ): Promise => {
10 | if (!req.companyId) {
11 | throw new AppError("ERR_UNAUTHORIZED", 401);
12 | }
13 | if (!(await checkCompanyCompliant(req.companyId))) {
14 | throw new AppError("ERR_SUBSCRIPTION_EXPIRED", 402);
15 | }
16 |
17 | return next();
18 | };
19 |
20 | export default isCompliant;
21 |
--------------------------------------------------------------------------------
/backend/src/middleware/isSuper.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from "express";
2 | import AppError from "../errors/AppError";
3 | import User from "../models/User";
4 |
5 | const isSuper = async (
6 | req: Request,
7 | res: Response,
8 | next: NextFunction
9 | ): Promise => {
10 | const { super: isSuperUser } = await User.findByPk(req.user.id);
11 | if (!isSuperUser) {
12 | throw new AppError("Acesso não permitido", 401);
13 | }
14 |
15 | return next();
16 | };
17 |
18 | export default isSuper;
19 |
--------------------------------------------------------------------------------
/backend/src/middleware/tokenAuth.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from "express";
2 |
3 | import AppError from "../errors/AppError";
4 | import Whatsapp from "../models/Whatsapp";
5 |
6 | const tokenAuth = async (
7 | req: Request,
8 | res: Response,
9 | next: NextFunction
10 | ): Promise => {
11 | try {
12 | const token = req.headers.authorization.replace("Bearer ", "");
13 | const whatsapp = await Whatsapp.findOne({ where: { token } });
14 | if (whatsapp) {
15 | req.params = {
16 | whatsappId: whatsapp.id.toString()
17 | };
18 | req.companyId = whatsapp.companyId;
19 | } else {
20 | throw new Error();
21 | }
22 | } catch (err) {
23 | throw new AppError("Acesso não permitido", 401);
24 | }
25 |
26 | return next();
27 | };
28 |
29 | export default tokenAuth;
30 |
--------------------------------------------------------------------------------
/backend/src/models/Baileys.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | AutoIncrement,
9 | Default,
10 | ForeignKey
11 | } from "sequelize-typescript";
12 | import Whatsapp from "./Whatsapp";
13 |
14 | @Table
15 | class Baileys extends Model {
16 | @PrimaryKey
17 | @AutoIncrement
18 | @Column
19 | id: number;
20 |
21 | @Default(null)
22 | @Column
23 | contacts: string;
24 |
25 | @Default(null)
26 | @Column
27 | chats: string;
28 |
29 | @CreatedAt
30 | createdAt: Date;
31 |
32 | @UpdatedAt
33 | updatedAt: Date;
34 |
35 | @ForeignKey(() => Whatsapp)
36 | @Column
37 | whatsappId: number;
38 | }
39 |
40 | export default Baileys;
41 |
--------------------------------------------------------------------------------
/backend/src/models/BaileysKeys.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | Model,
5 | ForeignKey,
6 | BelongsTo,
7 | DataType,
8 | PrimaryKey,
9 | } from "sequelize-typescript";
10 | import Whatsapp from "./Whatsapp";
11 |
12 | @Table({ timestamps: false })
13 | class BaileysKeys extends Model {
14 | @PrimaryKey
15 | @ForeignKey(() => Whatsapp)
16 | @Column(DataType.INTEGER)
17 | whatsappId: number;
18 |
19 | @BelongsTo(() => Whatsapp)
20 | whatsapp: Whatsapp;
21 |
22 | @PrimaryKey
23 | @Column(DataType.TEXT)
24 | type: string;
25 |
26 | @PrimaryKey
27 | @Column(DataType.TEXT)
28 | key: string;
29 |
30 | @Column(DataType.TEXT)
31 | value: string;
32 | }
33 |
34 | export default BaileysKeys;
35 |
--------------------------------------------------------------------------------
/backend/src/models/CampaignSetting.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | AutoIncrement,
9 | ForeignKey,
10 | BelongsTo
11 | } from "sequelize-typescript";
12 | import Company from "./Company";
13 |
14 | @Table({ tableName: "CampaignSettings" })
15 | class CampaignSetting extends Model {
16 | @PrimaryKey
17 | @AutoIncrement
18 | @Column
19 | id: number;
20 |
21 | @Column
22 | key: string;
23 |
24 | @Column
25 | value: string;
26 |
27 | @CreatedAt
28 | createdAt: Date;
29 |
30 | @UpdatedAt
31 | updatedAt: Date;
32 |
33 | @ForeignKey(() => Company)
34 | @Column
35 | companyId: number;
36 |
37 | @BelongsTo(() => Company)
38 | company: Company;
39 | }
40 |
41 | export default CampaignSetting;
42 |
--------------------------------------------------------------------------------
/backend/src/models/ChatUser.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | AutoIncrement,
9 | BelongsTo,
10 | ForeignKey
11 | } from "sequelize-typescript";
12 | import User from "./User";
13 | import Chat from "./Chat";
14 |
15 | @Table({ tableName: "ChatUsers" })
16 | class ChatUser extends Model {
17 | @PrimaryKey
18 | @AutoIncrement
19 | @Column
20 | id: number;
21 |
22 | @ForeignKey(() => Chat)
23 | @Column
24 | chatId: number;
25 |
26 | @ForeignKey(() => User)
27 | @Column
28 | userId: number;
29 |
30 | @Column
31 | unreads: number;
32 |
33 | @CreatedAt
34 | createdAt: Date;
35 |
36 | @UpdatedAt
37 | updatedAt: Date;
38 |
39 | @BelongsTo(() => Chat)
40 | chat: Chat;
41 |
42 | @BelongsTo(() => User)
43 | user: User;
44 | }
45 |
46 | export default ChatUser;
47 |
--------------------------------------------------------------------------------
/backend/src/models/ContactCustomField.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | AutoIncrement,
9 | ForeignKey,
10 | BelongsTo
11 | } from "sequelize-typescript";
12 | import Contact from "./Contact";
13 |
14 | @Table
15 | class ContactCustomField extends Model {
16 | @PrimaryKey
17 | @AutoIncrement
18 | @Column
19 | id: number;
20 |
21 | @Column
22 | name: string;
23 |
24 | @Column
25 | value: string;
26 |
27 | @ForeignKey(() => Contact)
28 | @Column
29 | contactId: number;
30 |
31 | @BelongsTo(() => Contact)
32 | contact: Contact;
33 |
34 | @CreatedAt
35 | createdAt: Date;
36 |
37 | @UpdatedAt
38 | updatedAt: Date;
39 | }
40 |
41 | export default ContactCustomField;
42 |
--------------------------------------------------------------------------------
/backend/src/models/ContactTag.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | ForeignKey,
8 | BelongsTo
9 | } from "sequelize-typescript";
10 | import Tag from "./Tag";
11 | import Contact from "./Contact";
12 |
13 | @Table({
14 | tableName: "ContactTags"
15 | })
16 | class ContactTag extends Model {
17 | @ForeignKey(() => Contact)
18 | @Column
19 | contactId: number;
20 |
21 | @ForeignKey(() => Tag)
22 | @Column
23 | tagId: number;
24 |
25 | @CreatedAt
26 | createdAt: Date;
27 |
28 | @UpdatedAt
29 | updatedAt: Date;
30 |
31 | @BelongsTo(() => Contact)
32 | contact: Contact;
33 |
34 | @BelongsTo(() => Tag)
35 | tag: Tag;
36 | }
37 |
38 | export default ContactTag;
39 |
--------------------------------------------------------------------------------
/backend/src/models/Help.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | AutoIncrement
9 | } from "sequelize-typescript";
10 |
11 | @Table({
12 | tableName: "Helps"
13 | })
14 | class Help extends Model {
15 | @PrimaryKey
16 | @AutoIncrement
17 | @Column
18 | id: number;
19 |
20 | @Column
21 | title: string;
22 |
23 | @Column
24 | description: string;
25 |
26 | @Column
27 | video: string;
28 |
29 | @Column
30 | link: string;
31 |
32 | @CreatedAt
33 | createdAt: Date;
34 |
35 | @UpdatedAt
36 | updatedAt: Date;
37 | }
38 |
39 | export default Help;
40 |
--------------------------------------------------------------------------------
/backend/src/models/OutOfTicketMessages.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | ForeignKey,
9 | BelongsTo,
10 | DataType
11 | } from "sequelize-typescript";
12 |
13 | import Whatsapp from "./Whatsapp";
14 |
15 | @Table
16 | class OutOfTicketMessage extends Model {
17 | @PrimaryKey
18 | @Column
19 | id: string;
20 |
21 | @Column(DataType.STRING)
22 | dataJson: string;
23 |
24 | @CreatedAt
25 | @Column(DataType.DATE(6))
26 | createdAt: Date;
27 |
28 | @UpdatedAt
29 | @Column(DataType.DATE(6))
30 | updatedAt: Date;
31 |
32 | @ForeignKey(() => Whatsapp)
33 | @Column
34 | whatsappId: string;
35 |
36 | @BelongsTo(() => Whatsapp)
37 | whatsapp: Whatsapp;
38 | }
39 |
40 | export default OutOfTicketMessage;
41 |
--------------------------------------------------------------------------------
/backend/src/models/Plan.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | AutoIncrement,
9 | AllowNull,
10 | Unique,
11 | Default
12 | } from "sequelize-typescript";
13 |
14 | @Table
15 | class Plan extends Model {
16 | @PrimaryKey
17 | @AutoIncrement
18 | @Column
19 | id: number;
20 |
21 | @AllowNull(false)
22 | @Unique
23 | @Column
24 | name: string;
25 |
26 | @Column
27 | users: number;
28 |
29 | @Column
30 | connections: number;
31 |
32 | @Column
33 | queues: number;
34 |
35 | @Column
36 | value: number;
37 |
38 | @CreatedAt
39 | createdAt: Date;
40 |
41 | @UpdatedAt
42 | updatedAt: Date;
43 |
44 | @Default(true)
45 | @Column
46 | isPublic: boolean;
47 | }
48 |
49 | export default Plan;
50 |
--------------------------------------------------------------------------------
/backend/src/models/QuickMessage.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | ForeignKey,
9 | BelongsTo,
10 | AutoIncrement
11 | } from "sequelize-typescript";
12 |
13 | import Company from "./Company";
14 | import User from "./User";
15 |
16 | @Table
17 | class QuickMessage extends Model {
18 | @PrimaryKey
19 | @AutoIncrement
20 | @Column
21 | id: number;
22 |
23 | @Column
24 | shortcode: string;
25 |
26 | @Column
27 | message: string;
28 |
29 | @ForeignKey(() => Company)
30 | @Column
31 | companyId: number;
32 |
33 | @ForeignKey(() => User)
34 | @Column
35 | userId: number;
36 |
37 | @BelongsTo(() => Company)
38 | company: Company;
39 |
40 | @BelongsTo(() => User)
41 | user: User;
42 |
43 | @CreatedAt
44 | createdAt: Date;
45 |
46 | @UpdatedAt
47 | updatedAt: Date;
48 | }
49 |
50 | export default QuickMessage;
51 |
--------------------------------------------------------------------------------
/backend/src/models/Setting.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | PrimaryKey,
8 | ForeignKey,
9 | BelongsTo,
10 | AutoIncrement
11 | } from "sequelize-typescript";
12 |
13 | import Company from "./Company";
14 |
15 | @Table
16 | class Setting extends Model {
17 | @PrimaryKey
18 | @AutoIncrement
19 | @Column
20 | id: number;
21 |
22 | @Column
23 | key: string;
24 |
25 | @Column
26 | value: string;
27 |
28 | @CreatedAt
29 | createdAt: Date;
30 |
31 | @UpdatedAt
32 | updatedAt: Date;
33 |
34 | @ForeignKey(() => Company)
35 | @Column
36 | companyId: number;
37 |
38 | @BelongsTo(() => Company)
39 | company: Company;
40 | }
41 |
42 | export default Setting;
43 |
--------------------------------------------------------------------------------
/backend/src/models/TicketTag.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | ForeignKey,
8 | BelongsTo
9 | } from "sequelize-typescript";
10 | import Tag from "./Tag";
11 | import Ticket from "./Ticket";
12 |
13 | @Table({
14 | tableName: 'TicketTags'
15 | })
16 | class TicketTag extends Model {
17 | @ForeignKey(() => Ticket)
18 | @Column
19 | ticketId: number;
20 |
21 | @ForeignKey(() => Tag)
22 | @Column
23 | tagId: number;
24 |
25 | @CreatedAt
26 | createdAt: Date;
27 |
28 | @UpdatedAt
29 | updatedAt: Date;
30 |
31 | @BelongsTo(() => Ticket)
32 | ticket: Ticket;
33 |
34 | @BelongsTo(() => Tag)
35 | tag: Tag;
36 | }
37 |
38 | export default TicketTag;
39 |
--------------------------------------------------------------------------------
/backend/src/models/UserQueue.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | ForeignKey
8 | } from "sequelize-typescript";
9 | import Queue from "./Queue";
10 | import User from "./User";
11 |
12 | @Table
13 | class UserQueue extends Model {
14 | @ForeignKey(() => User)
15 | @Column
16 | userId: number;
17 |
18 | @ForeignKey(() => Queue)
19 | @Column
20 | queueId: number;
21 |
22 | @CreatedAt
23 | createdAt: Date;
24 |
25 | @UpdatedAt
26 | updatedAt: Date;
27 | }
28 |
29 | export default UserQueue;
30 |
--------------------------------------------------------------------------------
/backend/src/models/UserSocketSession.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | Model,
5 | DataType,
6 | ForeignKey,
7 | PrimaryKey,
8 | Default,
9 | CreatedAt,
10 | UpdatedAt,
11 | BelongsTo
12 | } from "sequelize-typescript";
13 | import User from "./User"; // Importação da model User
14 |
15 | @Table
16 | class UserSocketSession extends Model {
17 | @PrimaryKey
18 | @Column(DataType.STRING)
19 | id: string;
20 |
21 | @ForeignKey(() => User)
22 | @Column
23 | userId: number;
24 |
25 | @Default(true)
26 | @Column(DataType.BOOLEAN)
27 | active: boolean;
28 |
29 | @CreatedAt
30 | createdAt: Date;
31 |
32 | @UpdatedAt
33 | updatedAt: Date;
34 |
35 | @BelongsTo(() => User)
36 | user: User; // Associação com User
37 | }
38 |
39 | export default UserSocketSession;
40 |
--------------------------------------------------------------------------------
/backend/src/models/WhatsappQueue.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Table,
3 | Column,
4 | CreatedAt,
5 | UpdatedAt,
6 | Model,
7 | ForeignKey,
8 | BelongsTo
9 | } from "sequelize-typescript";
10 | import Queue from "./Queue";
11 | import Whatsapp from "./Whatsapp";
12 |
13 | @Table
14 | class WhatsappQueue extends Model {
15 | @ForeignKey(() => Whatsapp)
16 | @Column
17 | whatsappId: number;
18 |
19 | @ForeignKey(() => Queue)
20 | @Column
21 | queueId: number;
22 |
23 | @CreatedAt
24 | createdAt: Date;
25 |
26 | @UpdatedAt
27 | updatedAt: Date;
28 |
29 | @BelongsTo(() => Queue)
30 | queue: Queue;
31 | }
32 |
33 | export default WhatsappQueue;
34 |
--------------------------------------------------------------------------------
/backend/src/routes/authRoutes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 | import * as SessionController from "../controllers/SessionController";
3 | import isAuth from "../middleware/isAuth";
4 | import isSuper from "../middleware/isAdmin";
5 |
6 | const authRoutes = Router();
7 |
8 | authRoutes.post("/login", SessionController.store);
9 | authRoutes.get(
10 | "/impersonate/:companyId",
11 | isAuth,
12 | isSuper,
13 | SessionController.impersonate
14 | );
15 | authRoutes.post("/refresh_token", SessionController.update);
16 | authRoutes.delete("/logout", isAuth, SessionController.remove);
17 | authRoutes.get("/me", isAuth, SessionController.me);
18 |
19 | export default authRoutes;
20 |
--------------------------------------------------------------------------------
/backend/src/routes/campaignSettingRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as CampaignSettingController from "../controllers/CampaignSettingController";
5 | import multer from "multer";
6 | import uploadConfig from "../config/upload";
7 |
8 | const upload = multer(uploadConfig);
9 |
10 | const routes = express.Router();
11 |
12 | routes.get("/campaign-settings", isAuth, CampaignSettingController.index);
13 |
14 | routes.post("/campaign-settings", isAuth, CampaignSettingController.store);
15 |
16 | export default routes;
17 |
--------------------------------------------------------------------------------
/backend/src/routes/dashboardRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as DashboardController from "../controllers/DashboardController";
5 | import isAdmin from "../middleware/isAdmin";
6 | import isCompliant from "../middleware/isCompliant";
7 |
8 | const routes = express.Router();
9 |
10 | routes.get(
11 | "/dashboard/status",
12 | isAuth,
13 | isAdmin,
14 | isCompliant,
15 | DashboardController.statusSummary
16 | );
17 |
18 | routes.get(
19 | "/dashboard/tickets",
20 | isAuth,
21 | isAdmin,
22 | isCompliant,
23 | DashboardController.ticketsStatistic
24 | );
25 |
26 | routes.get(
27 | "/dashboard/users",
28 | isAuth,
29 | isAdmin,
30 | isCompliant,
31 | DashboardController.usersReport
32 | );
33 |
34 | export default routes;
35 |
--------------------------------------------------------------------------------
/backend/src/routes/helpRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 | import isSuper from "../middleware/isSuper";
4 |
5 | import * as HelpController from "../controllers/HelpController";
6 |
7 | const routes = express.Router();
8 |
9 | routes.get("/helps/list", isAuth, HelpController.findList);
10 |
11 | routes.get("/helps", isAuth, HelpController.index);
12 |
13 | routes.get("/helps/:id", isAuth, HelpController.show);
14 |
15 | routes.post("/helps", isAuth, isSuper, HelpController.store);
16 |
17 | routes.put("/helps/:id", isAuth, isSuper, HelpController.update);
18 |
19 | routes.delete("/helps/:id", isAuth, isSuper, HelpController.remove);
20 |
21 | export default routes;
22 |
--------------------------------------------------------------------------------
/backend/src/routes/invoicesRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 | import * as QueueOptionController from "../controllers/QueueOptionController";
4 | import * as InvoicesController from "../controllers/InvoicesController"
5 |
6 | const invoiceRoutes = express.Router();
7 |
8 | invoiceRoutes.get("/invoices", isAuth, InvoicesController.index);
9 | invoiceRoutes.get("/invoices/list", isAuth, InvoicesController.list);
10 | invoiceRoutes.get("/invoices/all", isAuth, InvoicesController.list);
11 | invoiceRoutes.get("/invoices/:Invoiceid", isAuth, InvoicesController.show);
12 | invoiceRoutes.put("/invoices/:id", isAuth, InvoicesController.update);
13 |
14 | export default invoiceRoutes;
15 |
--------------------------------------------------------------------------------
/backend/src/routes/planRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 | import isSuper from "../middleware/isSuper";
4 |
5 | import * as PlanController from "../controllers/PlanController";
6 |
7 | const planRoutes = express.Router();
8 |
9 | planRoutes.get("/plans", isAuth, isSuper, PlanController.index);
10 |
11 | planRoutes.get("/plans/list", isAuth, isSuper, PlanController.list);
12 | planRoutes.get("/plans/listpublic", PlanController.listPublic);
13 |
14 | planRoutes.get("/plans/all", isAuth, isSuper, PlanController.list);
15 |
16 | planRoutes.get("/plans/:id", isAuth, PlanController.show);
17 |
18 | planRoutes.post("/plans", isAuth, isSuper, PlanController.store);
19 |
20 | planRoutes.put("/plans/:id", isAuth, isSuper, PlanController.update);
21 |
22 | planRoutes.delete("/plans/:id", isAuth, isSuper, PlanController.remove);
23 |
24 | export default planRoutes;
25 |
--------------------------------------------------------------------------------
/backend/src/routes/pwaRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import * as PwaController from "../controllers/PwaController";
3 |
4 | const pwaRoutes = express.Router();
5 |
6 | pwaRoutes.get("/manifest.json", PwaController.manifest);
7 | pwaRoutes.get("/favicon.ico", PwaController.favicon);
8 |
9 | export default pwaRoutes;
10 |
--------------------------------------------------------------------------------
/backend/src/routes/quickMessageRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as QuickMessageController from "../controllers/QuickMessageController";
5 |
6 | const routes = express.Router();
7 |
8 | routes.get("/quick-messages/list", isAuth, QuickMessageController.findList);
9 |
10 | routes.get("/quick-messages", isAuth, QuickMessageController.index);
11 |
12 | routes.get("/quick-messages/:id", isAuth, QuickMessageController.show);
13 |
14 | routes.post("/quick-messages", isAuth, QuickMessageController.store);
15 |
16 | routes.put("/quick-messages/:id", isAuth, QuickMessageController.update);
17 |
18 | routes.delete("/quick-messages/:id", isAuth, QuickMessageController.remove);
19 |
20 | export default routes;
21 |
--------------------------------------------------------------------------------
/backend/src/routes/scheduleRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as ScheduleController from "../controllers/ScheduleController";
5 |
6 | const scheduleRoutes = express.Router();
7 |
8 | scheduleRoutes.get("/schedules", isAuth, ScheduleController.index);
9 |
10 | scheduleRoutes.post("/schedules", isAuth, ScheduleController.store);
11 |
12 | scheduleRoutes.put("/schedules/:scheduleId", isAuth, ScheduleController.update);
13 |
14 | scheduleRoutes.get("/schedules/:scheduleId", isAuth, ScheduleController.show);
15 |
16 | scheduleRoutes.delete(
17 | "/schedules/:scheduleId",
18 | isAuth,
19 | ScheduleController.remove
20 | );
21 |
22 | export default scheduleRoutes;
23 |
--------------------------------------------------------------------------------
/backend/src/routes/subScriptionRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as SubscriptionController from "../controllers/SubscriptionController";
5 |
6 | const subscriptionRoutes = express.Router();
7 | subscriptionRoutes.post("/subscription", isAuth, SubscriptionController.createSubscription);
8 | subscriptionRoutes.post("/subscription/ticketz/webhook/:type?", SubscriptionController.webhook);
9 |
10 | export default subscriptionRoutes;
11 |
--------------------------------------------------------------------------------
/backend/src/routes/tagRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as TagController from "../controllers/TagController";
5 |
6 | const tagRoutes = express.Router();
7 |
8 | tagRoutes.get("/tags/list", isAuth, TagController.list);
9 |
10 | tagRoutes.get("/tags/kanban", isAuth, TagController.kanban);
11 |
12 | tagRoutes.get("/tags", isAuth, TagController.index);
13 |
14 | tagRoutes.post("/tags", isAuth, TagController.store);
15 |
16 | tagRoutes.put("/tags/:tagId", isAuth, TagController.update);
17 |
18 | tagRoutes.get("/tags/:tagId", isAuth, TagController.show);
19 |
20 | tagRoutes.delete("/tags/:tagId", isAuth, TagController.remove);
21 |
22 | tagRoutes.post("/tags/sync", isAuth, TagController.syncTags);
23 |
24 | export default tagRoutes;
25 |
--------------------------------------------------------------------------------
/backend/src/routes/ticketNoteRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as TicketNoteController from "../controllers/TicketNoteController";
5 |
6 | const ticketNoteRoutes = express.Router();
7 |
8 | ticketNoteRoutes.get(
9 | "/ticket-notes/list",
10 | isAuth,
11 | TicketNoteController.findFilteredList
12 | );
13 |
14 | ticketNoteRoutes.get("/ticket-notes", isAuth, TicketNoteController.index);
15 |
16 | ticketNoteRoutes.get("/ticket-notes/:id", isAuth, TicketNoteController.show);
17 |
18 | ticketNoteRoutes.post("/ticket-notes", isAuth, TicketNoteController.store);
19 |
20 | ticketNoteRoutes.put("/ticket-notes/:id", isAuth, TicketNoteController.update);
21 |
22 | ticketNoteRoutes.delete(
23 | "/ticket-notes/:id",
24 | isAuth,
25 | TicketNoteController.remove
26 | );
27 |
28 | export default ticketNoteRoutes;
29 |
--------------------------------------------------------------------------------
/backend/src/routes/ticketTagRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import isAuth from "../middleware/isAuth";
3 |
4 | import * as TicketTagController from "../controllers/TicketTagController";
5 |
6 | const ticketTagRoutes = express.Router();
7 |
8 | ticketTagRoutes.put(
9 | "/ticket-tags/:ticketId/:tagId",
10 | isAuth,
11 | TicketTagController.store
12 | );
13 |
14 | ticketTagRoutes.delete(
15 | "/ticket-tags/:ticketId/:tagId",
16 | isAuth,
17 | TicketTagController.remove
18 | );
19 |
20 | ticketTagRoutes.delete(
21 | "/ticket-tags/:ticketId",
22 | isAuth,
23 | TicketTagController.removeAll
24 | );
25 |
26 | export default ticketTagRoutes;
27 |
--------------------------------------------------------------------------------
/backend/src/routes/ticketzOSSRoutes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 |
3 | import isAuth from "../middleware/isAuth";
4 | import isSuper from "../middleware/isAdmin";
5 | import * as TicketzOSSController from "../controllers/TicketzOSSController";
6 |
7 | const ticketzOSSRoutes = Router();
8 |
9 | ticketzOSSRoutes.get(
10 | "/ticketz/registry",
11 | isAuth,
12 | isSuper,
13 | TicketzOSSController.show
14 | );
15 |
16 | ticketzOSSRoutes.post(
17 | "/ticketz/registry",
18 | isAuth,
19 | isSuper,
20 | TicketzOSSController.store
21 | );
22 |
23 | export default ticketzOSSRoutes;
24 |
--------------------------------------------------------------------------------
/backend/src/routes/userRoutes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 |
3 | import isAuth from "../middleware/isAuth";
4 | import * as UserController from "../controllers/UserController";
5 |
6 | const userRoutes = Router();
7 |
8 | userRoutes.get("/users", isAuth, UserController.index);
9 |
10 | userRoutes.get("/users/list", isAuth, UserController.list);
11 |
12 | userRoutes.post("/users", isAuth, UserController.store);
13 |
14 | userRoutes.put("/users/:userId", isAuth, UserController.update);
15 |
16 | userRoutes.get("/users/:userId", isAuth, UserController.show);
17 |
18 | userRoutes.delete("/users/:userId", isAuth, UserController.remove);
19 |
20 | export default userRoutes;
21 |
--------------------------------------------------------------------------------
/backend/src/routes/versionRoutes.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import * as VersionController from "../controllers/VersionController";
3 |
4 | const versionRoutes = express.Router();
5 |
6 | versionRoutes.get("/", VersionController.version);
7 |
8 | export default versionRoutes;
9 |
--------------------------------------------------------------------------------
/backend/src/services/AnnouncementService/CreateService.ts:
--------------------------------------------------------------------------------
1 | import * as Yup from "yup";
2 | import AppError from "../../errors/AppError";
3 | import Announcement from "../../models/Announcement";
4 |
5 | interface Data {
6 | priority: number;
7 | title: string;
8 | text: string;
9 | status: boolean;
10 | companyId: number;
11 | }
12 |
13 | const CreateService = async (data: Data): Promise => {
14 | const { title, text } = data;
15 |
16 | const ticketnoteSchema = Yup.object().shape({
17 | title: Yup.string().required("ERR_ANNOUNCEMENT_REQUIRED"),
18 | text: Yup.string().required("ERR_ANNOUNCEMENT_REQUIRED")
19 | });
20 |
21 | try {
22 | await ticketnoteSchema.validate({ title, text });
23 | } catch (err: any) {
24 | throw new AppError(err.message);
25 | }
26 |
27 | const record = await Announcement.create(data);
28 |
29 | return record;
30 | };
31 |
32 | export default CreateService;
33 |
--------------------------------------------------------------------------------
/backend/src/services/AnnouncementService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import Announcement from "../../models/Announcement";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await Announcement.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_ANNOUNCEMENT_FOUND", 404);
11 | }
12 |
13 | await record.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/AnnouncementService/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import Announcement from "../../models/Announcement";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: Announcement[] = await Announcement.findAll({
5 | order: [["createdAt", "DESC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/AnnouncementService/FindService.ts:
--------------------------------------------------------------------------------
1 | import Announcement from "../../models/Announcement";
2 | import Company from "../../models/Company";
3 |
4 | type Params = {
5 | companyId: string;
6 | };
7 |
8 | const FindService = async ({ companyId }: Params): Promise => {
9 | const notes: Announcement[] = await Announcement.findAll({
10 | where: {
11 | companyId
12 | },
13 | include: [{ model: Company, as: "company", attributes: ["id", "name"] }],
14 | order: [["createdAt", "DESC"]]
15 | });
16 |
17 | return notes;
18 | };
19 |
20 | export default FindService;
21 |
--------------------------------------------------------------------------------
/backend/src/services/AnnouncementService/ShowService.ts:
--------------------------------------------------------------------------------
1 | import Announcement from "../../models/Announcement";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowService = async (id: string | number): Promise => {
5 | const record = await Announcement.findByPk(id);
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_ANNOUNCEMENT_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/AnnouncementService/UpdateService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Announcement from "../../models/Announcement";
3 |
4 | interface Data {
5 | id: number;
6 | priority: number;
7 | title: string;
8 | text: string;
9 | status: boolean;
10 | companyId: number;
11 | }
12 |
13 | const UpdateService = async (data: Data): Promise => {
14 | const { id } = data;
15 |
16 | const record = await Announcement.findByPk(id);
17 |
18 | if (!record) {
19 | throw new AppError("ERR_NO_ANNOUNCEMENT_FOUND", 404);
20 | }
21 |
22 | await record.update(data);
23 |
24 | return record;
25 | };
26 |
27 | export default UpdateService;
28 |
--------------------------------------------------------------------------------
/backend/src/services/AuthServices/FindUserFromToken.ts:
--------------------------------------------------------------------------------
1 | import { verify } from "jsonwebtoken";
2 | import ShowUserService from "../UserServices/ShowUserService";
3 | import authConfig from "../../config/auth";
4 | import User from "../../models/User";
5 |
6 | interface RefreshTokenPayload {
7 | id: string;
8 | tokenVersion: number;
9 | companyId: number;
10 | }
11 |
12 | export default async function FindUserFromToken(token: string): Promise {
13 | const decoded = verify(token, authConfig.refreshSecret);
14 | const { id } = decoded as RefreshTokenPayload;
15 |
16 | const user = await ShowUserService(id);
17 | return user;
18 | }
19 |
--------------------------------------------------------------------------------
/backend/src/services/BaileysServices/DeleteBaileysService.ts:
--------------------------------------------------------------------------------
1 | import Baileys from "../../models/Baileys";
2 |
3 | const DeleteBaileysService = async (id: string | number): Promise => {
4 | const baileysData = await Baileys.findOne({
5 | where: {
6 | whatsappId: id
7 | }
8 | });
9 |
10 | if (baileysData) {
11 | await baileysData.destroy();
12 | }
13 | };
14 |
15 | export default DeleteBaileysService;
16 |
--------------------------------------------------------------------------------
/backend/src/services/BaileysServices/ShowBaileysService.ts:
--------------------------------------------------------------------------------
1 | import Baileys from "../../models/Baileys";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowBaileysService = async (id: string | number): Promise => {
5 | const baileysData = await Baileys.findOne({
6 | where: {
7 | whatsappId: id
8 | }
9 | });
10 |
11 | if (!baileysData) {
12 | throw new AppError("ERR_NO_BAILEYS_DATA_FOUND", 404);
13 | }
14 |
15 | return baileysData;
16 | };
17 |
18 | export default ShowBaileysService;
19 |
--------------------------------------------------------------------------------
/backend/src/services/CampaignService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import Campaign from "../../models/Campaign";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await Campaign.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_CAMPAIGN_FOUND", 404);
11 | }
12 |
13 | if (record.status === "EM_ANDAMENTO") {
14 | throw new AppError("Não é permitido excluir campanha em andamento", 400);
15 | }
16 |
17 | await record.destroy();
18 | };
19 |
20 | export default DeleteService;
21 |
--------------------------------------------------------------------------------
/backend/src/services/CampaignService/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import Campaign from "../../models/Campaign";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: Campaign[] = await Campaign.findAll({
5 | order: [["name", "ASC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/CampaignService/FindService.ts:
--------------------------------------------------------------------------------
1 | import Campaign from "../../models/Campaign";
2 | import Company from "../../models/Company";
3 |
4 | type Params = {
5 | companyId: string;
6 | };
7 |
8 | const FindService = async ({ companyId }: Params): Promise => {
9 | const notes: Campaign[] = await Campaign.findAll({
10 | where: {
11 | companyId
12 | },
13 | include: [{ model: Company, as: "company", attributes: ["id", "name"] }],
14 | order: [["name", "ASC"]]
15 | });
16 |
17 | return notes;
18 | };
19 |
20 | export default FindService;
21 |
--------------------------------------------------------------------------------
/backend/src/services/CampaignService/RestartService.ts:
--------------------------------------------------------------------------------
1 | import Campaign from "../../models/Campaign";
2 | import { campaignQueue } from "../../queues/campaign";
3 |
4 | export async function RestartService(id: number) {
5 | const campaign = await Campaign.findByPk(id);
6 | await campaign.update({ status: "EM_ANDAMENTO" });
7 |
8 | await campaignQueue.add("ProcessCampaign", {
9 | id: campaign.id,
10 | delay: 3000
11 | });
12 | }
13 |
--------------------------------------------------------------------------------
/backend/src/services/CampaignSettingServices/ListService.ts:
--------------------------------------------------------------------------------
1 | import { Op, fn, col, where } from "sequelize";
2 | import Campaign from "../../models/Campaign";
3 | import { isEmpty } from "lodash";
4 | import ContactList from "../../models/ContactList";
5 | import Whatsapp from "../../models/Whatsapp";
6 | import CampaignSetting from "../../models/CampaignSetting";
7 |
8 | interface Request {
9 | companyId: number | string;
10 | searchParam?: string;
11 | pageNumber?: string;
12 | }
13 |
14 | interface Response {
15 | records: Campaign[];
16 | count: number;
17 | hasMore: boolean;
18 | }
19 |
20 | const ListService = async ({
21 | companyId
22 | }: Request): Promise => {
23 | let whereCondition: any = {
24 | companyId
25 | };
26 |
27 | const records = await CampaignSetting.findAll({
28 | where: whereCondition
29 | });
30 |
31 | return records;
32 | };
33 |
34 | export default ListService;
35 |
--------------------------------------------------------------------------------
/backend/src/services/ChatService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import Chat from "../../models/Chat";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await Chat.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_CHAT_FOUND", 404);
11 | }
12 |
13 | await record.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/ChatService/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import Chat from "../../models/Chat";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: Chat[] = await Chat.findAll({
5 | order: [["createdAt", "DESC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/ChatService/FindService.ts:
--------------------------------------------------------------------------------
1 | import Chat from "../../models/Chat";
2 | import Company from "../../models/Company";
3 | import User from "../../models/User";
4 |
5 | type Params = {
6 | companyId: number;
7 | ownerId?: number;
8 | };
9 |
10 | const FindService = async ({ ownerId, companyId }: Params): Promise => {
11 | const chats: Chat[] = await Chat.findAll({
12 | where: {
13 | ownerId,
14 | companyId
15 | },
16 | include: [
17 | { model: Company, as: "company", attributes: ["id", "name"] },
18 | { model: User, as: "owner", attributes: ["id", "name"] }
19 | ],
20 | order: [["createdAt", "DESC"]]
21 | });
22 |
23 | return chats;
24 | };
25 |
26 | export default FindService;
27 |
--------------------------------------------------------------------------------
/backend/src/services/ChatService/ShowFromUuidService.ts:
--------------------------------------------------------------------------------
1 | import Chat from "../../models/Chat";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowFromUuidService = async (uuid: string): Promise => {
5 | const record = await Chat.findOne({ where: { uuid } });
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_CHAT_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowFromUuidService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/ChatService/ShowService.ts:
--------------------------------------------------------------------------------
1 | import Chat from "../../models/Chat";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowService = async (id: string | number): Promise => {
5 | const record = await Chat.findByPk(id);
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_CHAT_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/CompanyService/DeleteCompanyService.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import { join } from "path";
3 | import Company from "../../models/Company";
4 | import AppError from "../../errors/AppError";
5 | import { getPublicPath } from "../../helpers/GetPublicPath";
6 |
7 | const DeleteCompanyService = async (id: string): Promise => {
8 | const company = await Company.findOne({
9 | where: { id }
10 | });
11 |
12 | if (!company) {
13 | throw new AppError("ERR_NO_COMPANY_FOUND", 404);
14 | }
15 |
16 | await company.destroy();
17 |
18 | const companyMediaPath = join(getPublicPath(), "media", id);
19 |
20 | // recursively remove company media folder
21 | fs.rmSync(companyMediaPath, { recursive: true });
22 | };
23 |
24 | export default DeleteCompanyService;
25 |
--------------------------------------------------------------------------------
/backend/src/services/CompanyService/FindAllCompaniesService.ts:
--------------------------------------------------------------------------------
1 | import Company from "../../models/Company";
2 | import Plan from "../../models/Plan";
3 | import Setting from "../../models/Setting";
4 |
5 | const FindAllCompanyService = async (): Promise => {
6 | const companies = await Company.findAll({
7 | order: [["name", "ASC"]],
8 | include: [
9 | { model: Plan, as: "plan", attributes: ["id", "name", "value"] },
10 | { model: Setting, as: "settings" }
11 | ]
12 | });
13 | return companies;
14 | };
15 |
16 | export default FindAllCompanyService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/CompanyService/ShowCompanyService.ts:
--------------------------------------------------------------------------------
1 | import Company from "../../models/Company";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowCompanyService = async (id: string | number): Promise => {
5 | const company = await Company.findByPk(id);
6 |
7 | if (!company) {
8 | throw new AppError("ERR_NO_COMPANY_FOUND", 404);
9 | }
10 |
11 | return company;
12 | };
13 |
14 | export default ShowCompanyService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/CompanyService/UpdateSchedulesService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Company from "../../models/Company";
3 |
4 | type ScheduleData = {
5 | id: number | string;
6 | schedules: [];
7 | };
8 |
9 | const UpdateSchedulesService = async ({
10 | id,
11 | schedules
12 | }: ScheduleData): Promise => {
13 | const company = await Company.findByPk(id);
14 |
15 | if (!company) {
16 | throw new AppError("ERR_NO_COMPANY_FOUND", 404);
17 | }
18 |
19 | await company.update({
20 | schedules
21 | });
22 |
23 | return company;
24 | };
25 |
26 | export default UpdateSchedulesService;
27 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListItemService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import ContactListItem from "../../models/ContactListItem";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await ContactListItem.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_CONTACTLISTITEM_FOUND", 404);
11 | }
12 |
13 | await record.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListItemService/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import ContactListItem from "../../models/ContactListItem";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: ContactListItem[] = await ContactListItem.findAll({
5 | order: [["name", "ASC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListItemService/FindService.ts:
--------------------------------------------------------------------------------
1 | import ContactListItem from "../../models/ContactListItem";
2 | import Company from "../../models/Company";
3 |
4 | type Params = {
5 | companyId: number;
6 | contactListId: number;
7 | };
8 |
9 | const FindService = async ({
10 | companyId,
11 | contactListId
12 | }: Params): Promise => {
13 | let where: any = {
14 | companyId
15 | };
16 |
17 | if (contactListId) {
18 | where = {
19 | ...where,
20 | contactListId
21 | };
22 | }
23 |
24 | const notes: ContactListItem[] = await ContactListItem.findAll({
25 | where,
26 | include: [{ model: Company, as: "company", attributes: ["id", "name"] }],
27 | order: [["name", "ASC"]]
28 | });
29 |
30 | return notes;
31 | };
32 |
33 | export default FindService;
34 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListItemService/ShowService.ts:
--------------------------------------------------------------------------------
1 | import ContactListItem from "../../models/ContactListItem";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowService = async (id: string | number): Promise => {
5 | const record = await ContactListItem.findByPk(id);
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_CONTACTLISTITEM_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListService/CreateService.ts:
--------------------------------------------------------------------------------
1 | import * as Yup from "yup";
2 | import AppError from "../../errors/AppError";
3 | import ContactList from "../../models/ContactList";
4 |
5 | interface Data {
6 | name: string;
7 | companyId: number;
8 | }
9 |
10 | const CreateService = async (data: Data): Promise => {
11 | const { name } = data;
12 |
13 | const ticketnoteSchema = Yup.object().shape({
14 | name: Yup.string()
15 | .min(3, "ERR_CONTACTLIST_INVALID_NAME")
16 | .required("ERR_CONTACTLIST_REQUIRED")
17 | });
18 |
19 | try {
20 | await ticketnoteSchema.validate({ name });
21 | } catch (err: any) {
22 | throw new AppError(err.message);
23 | }
24 |
25 | const record = await ContactList.create(data);
26 |
27 | return record;
28 | };
29 |
30 | export default CreateService;
31 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import ContactList from "../../models/ContactList";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await ContactList.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_CONTACTLIST_FOUND", 404);
11 | }
12 |
13 | await record.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListService/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import ContactList from "../../models/ContactList";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: ContactList[] = await ContactList.findAll({
5 | order: [["name", "ASC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListService/FindService.ts:
--------------------------------------------------------------------------------
1 | import ContactList from "../../models/ContactList";
2 | import Company from "../../models/Company";
3 |
4 | type Params = {
5 | companyId: string;
6 | };
7 |
8 | const FindService = async ({ companyId }: Params): Promise => {
9 | const notes: ContactList[] = await ContactList.findAll({
10 | where: {
11 | companyId
12 | },
13 | include: [{ model: Company, as: "company", attributes: ["id", "name"] }],
14 | order: [["name", "ASC"]]
15 | });
16 |
17 | return notes;
18 | };
19 |
20 | export default FindService;
21 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListService/ShowService.ts:
--------------------------------------------------------------------------------
1 | import ContactList from "../../models/ContactList";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowService = async (id: string | number): Promise => {
5 | const record = await ContactList.findByPk(id);
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/ContactListService/UpdateService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import ContactList from "../../models/ContactList";
3 |
4 | interface Data {
5 | id: number | string;
6 | name: string;
7 | }
8 |
9 | const UpdateService = async (data: Data): Promise => {
10 | const { id, name } = data;
11 |
12 | const record = await ContactList.findByPk(id);
13 |
14 | if (!record) {
15 | throw new AppError("ERR_NO_CONTACTLIST_FOUND", 404);
16 | }
17 |
18 | await record.update({
19 | name
20 | });
21 |
22 | return record;
23 | };
24 |
25 | export default UpdateService;
26 |
--------------------------------------------------------------------------------
/backend/src/services/ContactServices/DeleteContactService.ts:
--------------------------------------------------------------------------------
1 | import { join } from "path";
2 | import fs from "fs";
3 | import Contact from "../../models/Contact";
4 | import AppError from "../../errors/AppError";
5 | import { getPublicPath } from "../../helpers/GetPublicPath";
6 |
7 | const DeleteContactService = async (id: string): Promise => {
8 | const contact = await Contact.findOne({
9 | where: { id }
10 | });
11 |
12 | if (!contact) {
13 | throw new AppError("ERR_NO_CONTACT_FOUND", 404);
14 | }
15 |
16 | const contactMediaPath = join(
17 | getPublicPath(),
18 | "media",
19 | `${contact.companyId}/${contact.id}`
20 | );
21 |
22 | // recursively remove contact media folder
23 | fs.rmSync(contactMediaPath, { recursive: true, force: true });
24 |
25 | await contact.destroy();
26 | };
27 |
28 | export default DeleteContactService;
29 |
--------------------------------------------------------------------------------
/backend/src/services/ContactServices/ShowContactService.ts:
--------------------------------------------------------------------------------
1 | import Contact from "../../models/Contact";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowContactService = async (
5 | id: string | number,
6 | companyId?: number
7 | ): Promise => {
8 | const contact = await Contact.findByPk(id, {
9 | include: ["tags", "extraInfo"]
10 | });
11 |
12 | if (companyId && contact?.companyId !== companyId) {
13 | throw new AppError("Não é possível consultar registro de outra empresa");
14 | }
15 |
16 | if (!contact) {
17 | throw new AppError("ERR_NO_CONTACT_FOUND", 404);
18 | }
19 |
20 | return contact;
21 | };
22 |
23 | export default ShowContactService;
24 |
--------------------------------------------------------------------------------
/backend/src/services/HelpServices/CreateService.ts:
--------------------------------------------------------------------------------
1 | import * as Yup from "yup";
2 | import AppError from "../../errors/AppError";
3 | import Help from "../../models/Help";
4 |
5 | interface Data {
6 | title: string;
7 | description?: string;
8 | video?: string;
9 | link?: string;
10 | }
11 |
12 | const CreateService = async (data: Data): Promise => {
13 | const { title, description } = data;
14 |
15 | const helpSchema = Yup.object().shape({
16 | title: Yup.string()
17 | .min(3, "ERR_HELP_INVALID_NAME")
18 | .required("ERR_HELP_REQUIRED"),
19 | description: Yup.string().min(3, "ERR_HELP_INVALID_NAME")
20 | });
21 |
22 | try {
23 | await helpSchema.validate({ title, description });
24 | } catch (err) {
25 | throw new AppError(err.message);
26 | }
27 |
28 | const record = await Help.create(data);
29 |
30 | return record;
31 | };
32 |
33 | export default CreateService;
34 |
--------------------------------------------------------------------------------
/backend/src/services/HelpServices/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import Help from "../../models/Help";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await Help.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_HELP_FOUND", 404);
11 | }
12 |
13 | await record.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/HelpServices/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import Help from "../../models/Help";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: Help[] = await Help.findAll({
5 | order: [["title", "ASC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/HelpServices/FindService.ts:
--------------------------------------------------------------------------------
1 | import Help from "../../models/Help";
2 |
3 | const FindService = async (): Promise => {
4 | const notes: Help[] = await Help.findAll({
5 | order: [["title", "ASC"]]
6 | });
7 |
8 | return notes;
9 | };
10 |
11 | export default FindService;
12 |
--------------------------------------------------------------------------------
/backend/src/services/HelpServices/ShowService.ts:
--------------------------------------------------------------------------------
1 | import Help from "../../models/Help";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowService = async (id: string | number): Promise => {
5 | const record = await Help.findByPk(id);
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_HELP_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/HelpServices/UpdateService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Help from "../../models/Help";
3 |
4 | interface Data {
5 | id: number;
6 | title: string;
7 | description?: string;
8 | video?: string;
9 | link?: string;
10 | }
11 |
12 | const UpdateService = async (data: Data): Promise => {
13 | const { id } = data;
14 |
15 | const record = await Help.findByPk(id);
16 |
17 | if (!record) {
18 | throw new AppError("ERR_NO_HELP_FOUND", 404);
19 | }
20 |
21 | await record.update(data);
22 |
23 | return record;
24 | };
25 |
26 | export default UpdateService;
27 |
--------------------------------------------------------------------------------
/backend/src/services/InvoicesService/FindAllInvoiceService.ts:
--------------------------------------------------------------------------------
1 | import Invoices from "../../models/Invoices";
2 |
3 | interface Request {
4 | companyId: number;
5 | }
6 |
7 | const FindAllPlanService = async (companyId: number): Promise => {
8 | const invoice = await Invoices.findAll({
9 | attributes: [ "id", "detail", "value", "dueDate", "status", "createdAt", "updatedAt" ],
10 | where: {
11 | companyId
12 | },
13 | order: [["id", "ASC"]]
14 | });
15 | return invoice;
16 | };
17 |
18 | export default FindAllPlanService;
19 |
--------------------------------------------------------------------------------
/backend/src/services/InvoicesService/ShowInvoiceService.ts:
--------------------------------------------------------------------------------
1 | import Invoice from "../../models/Invoices";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowInvoceService = async (Invoiceid: string | number): Promise => {
5 | const invoice = await Invoice.findByPk(Invoiceid, {
6 | attributes: ["id", "detail", "value", "dueDate", "status", "createdAt", "updatedAt"],
7 | });
8 |
9 | if (!invoice) {
10 | throw new AppError("ERR_NO_PLAN_FOUND", 404);
11 | }
12 |
13 | return invoice;
14 | };
15 |
16 | export default ShowInvoceService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/InvoicesService/UpdateInvoiceService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Invoice from "../../models/Invoices";
3 |
4 | interface InvoiceData {
5 | status: string;
6 | id?: number | string;
7 | }
8 |
9 | const UpdateInvoiceService = async (InvoiceData: InvoiceData): Promise => {
10 | const { id, status } = InvoiceData;
11 |
12 | const invoice = await Invoice.findByPk(id);
13 |
14 | if (!invoice) {
15 | throw new AppError("ERR_NO_PLAN_FOUND", 404);
16 | }
17 |
18 | await invoice.update({
19 | status,
20 | });
21 |
22 | return invoice;
23 | };
24 |
25 | export default UpdateInvoiceService;
26 |
--------------------------------------------------------------------------------
/backend/src/services/MessageServices/GetMessagesService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Message from "../../models/Message";
3 |
4 | interface Request {
5 | id: string;
6 | ticketId: number;
7 | }
8 |
9 | const GetMessageService = async ({
10 | id,
11 | ticketId
12 | }: Request): Promise => {
13 | const messageExists = await Message.findOne({
14 | where: { id, ticketId }
15 | });
16 |
17 | if (!messageExists) {
18 | throw new AppError("MESSAGE_NOT_FIND");
19 | }
20 |
21 | return messageExists;
22 | };
23 |
24 | export default GetMessageService;
25 |
--------------------------------------------------------------------------------
/backend/src/services/PlanService/DeletePlanService.ts:
--------------------------------------------------------------------------------
1 | import Plan from "../../models/Plan";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeletePlanService = async (id: string): Promise => {
5 | const plan = await Plan.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!plan) {
10 | throw new AppError("ERR_NO_PLAN_FOUND", 404);
11 | }
12 |
13 | await plan.destroy();
14 | };
15 |
16 | export default DeletePlanService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/PlanService/FindAllPlanService.ts:
--------------------------------------------------------------------------------
1 | import Plan from "../../models/Plan";
2 |
3 | const FindAllPlanService = async (listPublic: boolean): Promise => {
4 | let plan: Plan[];
5 | if (listPublic)
6 | {
7 | plan = await Plan.findAll({
8 | where: {
9 | isPublic: true
10 | },
11 | order: [["name", "ASC"]]
12 | });
13 | } else {
14 | plan = await Plan.findAll({
15 | order: [["name", "ASC"]]
16 | });
17 | }
18 | return plan;
19 | };
20 |
21 | export default FindAllPlanService;
22 |
--------------------------------------------------------------------------------
/backend/src/services/PlanService/ShowPlanService.ts:
--------------------------------------------------------------------------------
1 | import Plan from "../../models/Plan";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowPlanService = async (id: string | number): Promise => {
5 | const plan = await Plan.findByPk(id);
6 |
7 | if (!plan) {
8 | throw new AppError("ERR_NO_PLAN_FOUND", 404);
9 | }
10 |
11 | return plan;
12 | };
13 |
14 | export default ShowPlanService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/PlanService/UpdatePlanService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Plan from "../../models/Plan";
3 |
4 | interface PlanData {
5 | name: string;
6 | id?: number | string;
7 | users?: number;
8 | connections?: number;
9 | queues?: number;
10 | value?: number;
11 | isPublic?: boolean;
12 |
13 | }
14 |
15 | const UpdatePlanService = async (planData: PlanData): Promise => {
16 | const { id, name, users, connections, queues, value, isPublic } = planData;
17 |
18 | const plan = await Plan.findByPk(id);
19 |
20 | if (!plan) {
21 | throw new AppError("ERR_NO_PLAN_FOUND", 404);
22 | }
23 |
24 | await plan.update({
25 | name,
26 | users,
27 | connections,
28 | queues,
29 | value,
30 | isPublic
31 | });
32 |
33 | return plan;
34 | };
35 |
36 | export default UpdatePlanService;
37 |
--------------------------------------------------------------------------------
/backend/src/services/QueueOptionService/CreateService.ts:
--------------------------------------------------------------------------------
1 | import QueueOption from "../../models/QueueOption";
2 |
3 | interface QueueOptionData {
4 | queueId: number;
5 | title: string;
6 | option: string;
7 | message?: string;
8 | parentId?: number;
9 | }
10 |
11 | const CreateService = async (queueOptionData: QueueOptionData): Promise => {
12 | const queueOption = await QueueOption.create(queueOptionData);
13 | return queueOption;
14 | };
15 |
16 | export default CreateService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/QueueOptionService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import ShowService from "./ShowService";
2 |
3 | const DeleteService = async (queueOptionId: number | string): Promise => {
4 | const queueOption = await ShowService(queueOptionId);
5 |
6 | await queueOption.destroy();
7 | };
8 |
9 | export default DeleteService;
10 |
--------------------------------------------------------------------------------
/backend/src/services/QueueOptionService/ShowService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import QueueOption from "../../models/QueueOption";
3 |
4 | const ShowService = async (queueOptionId: number | string): Promise => {
5 | const queue = await QueueOption.findOne({
6 | where: {
7 | id: queueOptionId
8 | },
9 | include: [
10 | {
11 | model: QueueOption,
12 | as: 'parent',
13 | where: { parentId: queueOptionId },
14 | required: false
15 | },
16 | ]
17 | });
18 |
19 | if (!queue) {
20 | throw new AppError("ERR_QUEUE_NOT_FOUND");
21 | }
22 |
23 | return queue;
24 | };
25 |
26 | export default ShowService;
27 |
--------------------------------------------------------------------------------
/backend/src/services/QueueOptionService/UpdateService.ts:
--------------------------------------------------------------------------------
1 | import QueueOption from "../../models/QueueOption";
2 | import ShowService from "./ShowService";
3 |
4 | interface QueueData {
5 | queueId?: number;
6 | title?: string;
7 | option?: string;
8 | message?: string;
9 | parentId?: number;
10 | }
11 |
12 | const UpdateService = async (
13 | queueOptionId: number | string,
14 | queueOptionData: QueueData
15 | ): Promise => {
16 |
17 | const queueOption = await ShowService(queueOptionId);
18 |
19 | await queueOption.update(queueOptionData);
20 |
21 | return queueOption;
22 | };
23 |
24 | export default UpdateService;
25 |
--------------------------------------------------------------------------------
/backend/src/services/QueueService/DeleteQueueService.ts:
--------------------------------------------------------------------------------
1 | import ShowQueueService from "./ShowQueueService";
2 |
3 | const DeleteQueueService = async (
4 | queueId: number | string,
5 | companyId: number
6 | ): Promise => {
7 | const queue = await ShowQueueService(queueId, companyId);
8 |
9 | await queue.destroy();
10 | };
11 |
12 | export default DeleteQueueService;
13 |
--------------------------------------------------------------------------------
/backend/src/services/QueueService/ListQueuesService.ts:
--------------------------------------------------------------------------------
1 | import Queue from "../../models/Queue";
2 |
3 | interface Request {
4 | companyId: number;
5 | }
6 |
7 | const ListQueuesService = async ({ companyId }: Request): Promise => {
8 | const queues = await Queue.findAll({
9 | where: {
10 | companyId
11 | },
12 | order: [["name", "ASC"]]
13 | });
14 |
15 | return queues;
16 | };
17 |
18 | export default ListQueuesService;
19 |
--------------------------------------------------------------------------------
/backend/src/services/QueueService/ShowQueueService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import Queue from "../../models/Queue";
3 |
4 | const ShowQueueService = async (
5 | queueId: number | string,
6 | companyId: number
7 | ): Promise => {
8 | const queue = await Queue.findByPk(queueId);
9 |
10 | if (queue?.companyId !== companyId) {
11 | throw new AppError("Não é possível consultar registros de outra empresa");
12 | }
13 |
14 | if (!queue) {
15 | throw new AppError("ERR_QUEUE_NOT_FOUND");
16 | }
17 |
18 | return queue;
19 | };
20 |
21 | export default ShowQueueService;
22 |
--------------------------------------------------------------------------------
/backend/src/services/QuickMessageService/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import QuickMessage from "../../models/QuickMessage";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string): Promise => {
5 | const record = await QuickMessage.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!record) {
10 | throw new AppError("ERR_NO_QUICKMESSAGE_FOUND", 404);
11 | }
12 |
13 | await record.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/QuickMessageService/FindAllService.ts:
--------------------------------------------------------------------------------
1 | import QuickMessage from "../../models/QuickMessage";
2 |
3 | const FindAllService = async (): Promise => {
4 | const records: QuickMessage[] = await QuickMessage.findAll({
5 | order: [["shortcode", "ASC"]]
6 | });
7 | return records;
8 | };
9 |
10 | export default FindAllService;
11 |
--------------------------------------------------------------------------------
/backend/src/services/QuickMessageService/ShowService.ts:
--------------------------------------------------------------------------------
1 | import QuickMessage from "../../models/QuickMessage";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowService = async (id: string | number): Promise => {
5 | const record = await QuickMessage.findByPk(id);
6 |
7 | if (!record) {
8 | throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404);
9 | }
10 |
11 | return record;
12 | };
13 |
14 | export default ShowService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/QuickMessageService/UpdateService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import QuickMessage from "../../models/QuickMessage";
3 |
4 | interface Data {
5 | shortcode: string;
6 | message: string;
7 | userId: number;
8 | id?: number;
9 | }
10 |
11 | const UpdateService = async (data: Data): Promise => {
12 | const { id, shortcode, message, userId } = data;
13 |
14 | const record = await QuickMessage.findByPk(id);
15 |
16 | if (!record) {
17 | throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404);
18 | }
19 |
20 | await record.update({
21 | shortcode,
22 | message,
23 | userId
24 | });
25 |
26 | return record;
27 | };
28 |
29 | export default UpdateService;
30 |
--------------------------------------------------------------------------------
/backend/src/services/ScheduleServices/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import Schedule from "../../models/Schedule";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string | number, companyId: number): Promise => {
5 | const schedule = await Schedule.findOne({
6 | where: { id, companyId }
7 | });
8 |
9 | if (!schedule) {
10 | throw new AppError("ERR_NO_SCHEDULE_FOUND", 404);
11 | }
12 |
13 | await schedule.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/ScheduleServices/ShowService.ts:
--------------------------------------------------------------------------------
1 | import Schedule from "../../models/Schedule";
2 | import AppError from "../../errors/AppError";
3 | import Contact from "../../models/Contact";
4 | import User from "../../models/User";
5 |
6 | const ScheduleService = async (id: string | number, companyId: number): Promise => {
7 | const schedule = await Schedule.findByPk(id, {
8 | include: [
9 | { model: Contact, as: "contact", attributes: ["id", "name"] },
10 | { model: User, as: "user", attributes: ["id", "name"] },
11 | ]
12 | });
13 |
14 | if (schedule?.companyId !== companyId) {
15 | throw new AppError("Não é possível excluir registro de outra empresa");
16 | }
17 |
18 | if (!schedule) {
19 | throw new AppError("ERR_NO_SCHEDULE_FOUND", 404);
20 | }
21 |
22 | return schedule;
23 | };
24 |
25 | export default ScheduleService;
26 |
--------------------------------------------------------------------------------
/backend/src/services/SettingServices/GetPublicSettingService.ts:
--------------------------------------------------------------------------------
1 | import Setting from "../../models/Setting";
2 |
3 | interface Request {
4 | key: string;
5 | }
6 |
7 | const publicSettingsKeys = [
8 | "allowSignup",
9 | "primaryColorLight",
10 | "primaryColorDark",
11 | "appLogoLight",
12 | "appLogoDark",
13 | "appLogoFavicon",
14 | "appName"
15 | ];
16 |
17 | const GetPublicSettingService = async ({
18 | key
19 | }: Request): Promise => {
20 | if (!publicSettingsKeys.includes(key)) {
21 | return null;
22 | }
23 |
24 | const setting = await Setting.findOne({
25 | where: {
26 | companyId: 1,
27 | key
28 | }
29 | });
30 |
31 | return setting?.value || null;
32 | };
33 |
34 | export default GetPublicSettingService;
35 |
--------------------------------------------------------------------------------
/backend/src/services/SettingServices/GetSuperSettingService.ts:
--------------------------------------------------------------------------------
1 | import Setting from "../../models/Setting";
2 |
3 | interface Request {
4 | key: string;
5 | }
6 |
7 | const GetSuperSettingService = async ({
8 | key
9 | }: Request): Promise => {
10 |
11 | if (!key.startsWith("_")) {
12 | return null;
13 | }
14 |
15 | const setting = await Setting.findOne({
16 | where: {
17 | companyId: 1,
18 | key
19 | }
20 | });
21 |
22 | return setting?.value;
23 | };
24 |
25 | export default GetSuperSettingService;
26 |
--------------------------------------------------------------------------------
/backend/src/services/SettingServices/ListSettingsService.ts:
--------------------------------------------------------------------------------
1 | import { Op, WhereOptions } from "sequelize";
2 | import Setting from "../../models/Setting";
3 | import User from "../../models/User";
4 |
5 | interface Request {
6 | isSuper: boolean,
7 | companyId: number;
8 | }
9 |
10 | const ListSettingsService = async ({
11 | isSuper, companyId
12 | }: Request): Promise => {
13 | const where: WhereOptions = { companyId };
14 | if (!isSuper) {
15 | where.key = {
16 | [Op.notLike]: "\\_%"
17 | }
18 | }
19 |
20 | const settings = await Setting.findAll({
21 | where
22 | });
23 |
24 | return settings;
25 | };
26 |
27 | export default ListSettingsService;
28 |
--------------------------------------------------------------------------------
/backend/src/services/TagServices/DeleteService.ts:
--------------------------------------------------------------------------------
1 | import Tag from "../../models/Tag";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteService = async (id: string | number): Promise => {
5 | const tag = await Tag.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!tag) {
10 | throw new AppError("ERR_NO_TAG_FOUND", 404);
11 | }
12 |
13 | await tag.destroy();
14 | };
15 |
16 | export default DeleteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/TagServices/KanbanListService.ts:
--------------------------------------------------------------------------------
1 | import { Op } from "sequelize";
2 | import Tag from "../../models/Tag";
3 | import Ticket from "../../models/Ticket";
4 | import TicketTag from "../../models/TicketTag";
5 |
6 | interface Request {
7 | companyId: number;
8 | }
9 |
10 | const KanbanListService = async ({
11 | companyId
12 | }: Request): Promise => {
13 | const tags = await Tag.findAll({
14 | where: {
15 | kanban: 1,
16 | companyId: companyId,
17 | },
18 | order: [["id", "ASC"]],
19 | raw: true,
20 | });
21 | //console.log(tags);
22 | return tags;
23 | };
24 |
25 | export default KanbanListService;
--------------------------------------------------------------------------------
/backend/src/services/TagServices/ShowService.ts:
--------------------------------------------------------------------------------
1 | import Tag from "../../models/Tag";
2 | import AppError from "../../errors/AppError";
3 |
4 | const TagService = async (id: string | number): Promise => {
5 | const tag = await Tag.findByPk(id);
6 |
7 | if (!tag) {
8 | throw new AppError("ERR_NO_TAG_FOUND", 404);
9 | }
10 |
11 | return tag;
12 | };
13 |
14 | export default TagService;
15 |
--------------------------------------------------------------------------------
/backend/src/services/TagServices/SimpleListService.ts:
--------------------------------------------------------------------------------
1 | import { Op, Sequelize } from "sequelize";
2 | import Tag from "../../models/Tag";
3 | import Ticket from "../../models/Ticket";
4 | import TicketTag from "../../models/TicketTag";
5 |
6 | interface Request {
7 | companyId: number;
8 | searchParam?: string;
9 | }
10 |
11 | const ListService = async ({
12 | companyId,
13 | searchParam
14 | }: Request): Promise => {
15 | let whereCondition = {};
16 |
17 | if (searchParam) {
18 | whereCondition = {
19 | [Op.or]: [
20 | { name: { [Op.like]: `%${searchParam}%` } },
21 | { color: { [Op.like]: `%${searchParam}%` } },
22 | // { kanban: { [Op.like]: `%${searchParam}%` } }
23 | ]
24 | };
25 | }
26 |
27 | const tags = await Tag.findAll({
28 | where: { ...whereCondition, companyId },
29 | order: [["name", "ASC"]]
30 | });
31 |
32 | return tags;
33 | };
34 |
35 | export default ListService;
36 |
--------------------------------------------------------------------------------
/backend/src/services/TicketNoteService/DeleteTicketNoteService.ts:
--------------------------------------------------------------------------------
1 | import TicketNote from "../../models/TicketNote";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteTicketNoteService = async (id: string): Promise => {
5 | const ticketnote = await TicketNote.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!ticketnote) {
10 | throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404);
11 | }
12 |
13 | await ticketnote.destroy();
14 | };
15 |
16 | export default DeleteTicketNoteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/TicketNoteService/FindAllTicketNotesService.ts:
--------------------------------------------------------------------------------
1 | import TicketNote from "../../models/TicketNote";
2 |
3 | const FindAllTicketNotesService = async (): Promise => {
4 | const ticketNote = await TicketNote.findAll();
5 | return ticketNote;
6 | };
7 |
8 | export default FindAllTicketNotesService;
9 |
--------------------------------------------------------------------------------
/backend/src/services/TicketNoteService/ShowTicketNoteService.ts:
--------------------------------------------------------------------------------
1 | import TicketNote from "../../models/TicketNote";
2 | import AppError from "../../errors/AppError";
3 |
4 | const ShowTicketNoteService = async (
5 | id: string | number
6 | ): Promise => {
7 | const ticketNote = await TicketNote.findByPk(id);
8 |
9 | if (!ticketNote) {
10 | throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404);
11 | }
12 |
13 | return ticketNote;
14 | };
15 |
16 | export default ShowTicketNoteService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/TicketNoteService/UpdateTicketNoteService.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import TicketNote from "../../models/TicketNote";
3 |
4 | interface TicketNoteData {
5 | note: string;
6 | id?: number | string;
7 | }
8 |
9 | const UpdateTicketNoteService = async (
10 | ticketNoteData: TicketNoteData
11 | ): Promise => {
12 | const { id, note } = ticketNoteData;
13 |
14 | const ticketNote = await TicketNote.findByPk(id);
15 |
16 | if (!ticketNote) {
17 | throw new AppError("ERR_NO_TICKETNOTE_FOUND", 404);
18 | }
19 |
20 | await ticketNote.update({
21 | note
22 | });
23 |
24 | return ticketNote;
25 | };
26 |
27 | export default UpdateTicketNoteService;
28 |
--------------------------------------------------------------------------------
/backend/src/services/UserServices/SimpleListService.ts:
--------------------------------------------------------------------------------
1 | import User from "../../models/User";
2 | import AppError from "../../errors/AppError";
3 | import Queue from "../../models/Queue";
4 |
5 | interface Params {
6 | companyId: string | number;
7 | }
8 |
9 | const SimpleListService = async ({ companyId }: Params): Promise => {
10 | const users = await User.findAll({
11 | where: {
12 | companyId
13 | },
14 | attributes: ["name", "id", "email"],
15 | include: [
16 | { model: Queue, as: 'queues' }
17 | ],
18 | order: [["id", "ASC"]]
19 | });
20 |
21 | if (!users) {
22 | throw new AppError("ERR_NO_USER_FOUND", 404);
23 | }
24 |
25 | return users;
26 | };
27 |
28 | export default SimpleListService;
29 |
--------------------------------------------------------------------------------
/backend/src/services/WbotServices/CheckIsValidContact.ts:
--------------------------------------------------------------------------------
1 | import AppError from "../../errors/AppError";
2 | import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp";
3 | import { getWbot } from "../../libs/wbot";
4 |
5 | const CheckIsValidContact = async (
6 | number: string,
7 | companyId: number
8 | ): Promise => {
9 | const defaultWhatsapp = await GetDefaultWhatsApp(companyId);
10 |
11 | const wbot = getWbot(defaultWhatsapp.id);
12 |
13 | try {
14 | const isValidNumber = await wbot.onWhatsApp(`${number}`);
15 | if (!isValidNumber) {
16 | throw new AppError("invalidNumber");
17 | }
18 | } catch (err: any) {
19 | if (err.message === "invalidNumber") {
20 | throw new AppError("ERR_WAPP_INVALID_CONTACT");
21 | }
22 | throw new AppError("ERR_WAPP_CHECK_CONTACT");
23 | }
24 | };
25 |
26 | export default CheckIsValidContact;
27 |
--------------------------------------------------------------------------------
/backend/src/services/WbotServices/GetProfilePicUrl.ts:
--------------------------------------------------------------------------------
1 | import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp";
2 | import { getWbot } from "../../libs/wbot";
3 |
4 | const GetProfilePicUrl = async (
5 | number: string,
6 | companyId: number
7 | ): Promise => {
8 | const defaultWhatsapp = await GetDefaultWhatsApp(companyId);
9 |
10 | const wbot = getWbot(defaultWhatsapp.id);
11 |
12 | let profilePicUrl: string;
13 | try {
14 | profilePicUrl = await wbot.profilePictureUrl(`${number}@s.whatsapp.net`);
15 | } catch (error) {
16 | profilePicUrl = `${process.env.FRONTEND_URL}/nopicture.png`;
17 | }
18 |
19 | return profilePicUrl;
20 | };
21 |
22 | export default GetProfilePicUrl;
23 |
--------------------------------------------------------------------------------
/backend/src/services/WbotServices/StartAllWhatsAppsSessions.ts:
--------------------------------------------------------------------------------
1 | import ListWhatsAppsService from "../WhatsappService/ListWhatsAppsService";
2 | import { StartWhatsAppSession } from "./StartWhatsAppSession";
3 | import * as Sentry from "@sentry/node";
4 |
5 | export const StartAllWhatsAppsSessions = async (
6 | companyId: number
7 | ): Promise => {
8 | try {
9 | const whatsapps = await ListWhatsAppsService({ companyId, });
10 | if (whatsapps.length > 0) {
11 | whatsapps.forEach(whatsapp => {
12 | if(whatsapp.channel === 'whatsapp') {
13 | StartWhatsAppSession(whatsapp, companyId);
14 | }
15 | });
16 | }
17 | } catch (e) {
18 | Sentry.captureException(e);
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/backend/src/services/WhatsappService/AssociateWhatsappQueue.ts:
--------------------------------------------------------------------------------
1 | import Whatsapp from "../../models/Whatsapp";
2 |
3 | const AssociateWhatsappQueue = async (
4 | whatsapp: Whatsapp,
5 | queueIds: number[]
6 | ): Promise => {
7 | await whatsapp.$set("queues", queueIds);
8 |
9 | await whatsapp.reload();
10 | };
11 |
12 | export default AssociateWhatsappQueue;
13 |
--------------------------------------------------------------------------------
/backend/src/services/WhatsappService/DeleteWhatsAppService.ts:
--------------------------------------------------------------------------------
1 | import Whatsapp from "../../models/Whatsapp";
2 | import AppError from "../../errors/AppError";
3 |
4 | const DeleteWhatsAppService = async (id: string): Promise => {
5 | const whatsapp = await Whatsapp.findOne({
6 | where: { id }
7 | });
8 |
9 | if (!whatsapp) {
10 | throw new AppError("ERR_NO_WAPP_FOUND", 404);
11 | }
12 |
13 | await whatsapp.destroy();
14 | };
15 |
16 | export default DeleteWhatsAppService;
17 |
--------------------------------------------------------------------------------
/backend/src/services/WhatsappService/ListWhatsAppsService.ts:
--------------------------------------------------------------------------------
1 | import { FindOptions } from "sequelize/types";
2 | import Queue from "../../models/Queue";
3 | import Whatsapp from "../../models/Whatsapp";
4 |
5 | interface Request {
6 | companyId: number;
7 | session?: number | string;
8 | }
9 |
10 | const ListWhatsAppsService = async ({
11 | session,
12 | companyId
13 | }: Request): Promise => {
14 | const options: FindOptions = {
15 | where: {
16 | companyId
17 | },
18 | include: [
19 | {
20 | model: Queue,
21 | as: "queues",
22 | attributes: ["id", "name", "color", "greetingMessage"]
23 | }
24 | ]
25 | };
26 |
27 | if (session !== undefined && session == 0) {
28 | options.attributes = { exclude: ["session"] };
29 | }
30 |
31 | const whatsapps = await Whatsapp.findAll(options);
32 |
33 | return whatsapps;
34 | };
35 |
36 | export default ListWhatsAppsService;
37 |
--------------------------------------------------------------------------------
/backend/src/services/services.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/backend/src/services/services.zip
--------------------------------------------------------------------------------
/backend/src/waversion.json:
--------------------------------------------------------------------------------
1 | [2, 3000, 1023333981]
2 |
--------------------------------------------------------------------------------
/backend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "outDir": "./dist",
6 | "strict": false,
7 | "strictPropertyInitialization": false,
8 | "esModuleInterop": true,
9 | "experimentalDecorators": true,
10 | "emitDecoratorMetadata": true,
11 | "skipLibCheck": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "typeRoots": ["./node_modules/@types", "./src/@types"],
14 | "resolveJsonModule": true,
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/confs/nginx-ticketz.conf:
--------------------------------------------------------------------------------
1 | client_max_body_size 64m;
2 |
--------------------------------------------------------------------------------
/confs/pgadmin4-servers.json:
--------------------------------------------------------------------------------
1 | {
2 | "Servers": {
3 | "1": {
4 | "Name": "Ticketz Postgres",
5 | "Group": "Servers",
6 | "Port": 5432,
7 | "Username": "ticketz",
8 | "Host": "postgres",
9 | "SSLMode": "prefer",
10 | "MaintenanceDB": "postgres"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | public/config.json
3 | dist
4 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | /.well-known
4 | /.env
5 | /build
6 | /.project
7 | /public/config.json
8 | /public/socket-admin
9 | /public/ogv
10 | /public/opus-recorder
11 |
--------------------------------------------------------------------------------
/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine as build-deps
2 |
3 | WORKDIR /usr/src/app
4 | COPY . .
5 |
6 | ENV NODE_OPTIONS --openssl-legacy-provider
7 |
8 | RUN --mount=type=cache,target=/root/.npm \
9 | npm ci && npm run build
10 |
11 | FROM ghcr.io/ticketz-oss/nginx-alpine
12 |
13 | WORKDIR /usr/share/nginx/html
14 |
15 | COPY --from=build-deps /usr/src/app/build /var/www/public
16 | COPY --from=build-deps /usr/src/app/node_modules/@socket.io/admin-ui/ui/dist /var/www/public/socket-admin
17 | COPY nginx /etc/nginx
18 |
19 | CMD (echo "{" && while IFS='=' read -r name value; do \
20 | printf '\t"%s": "%s"\n' "$name" "$value"; \
21 | done < <(env) | sed '$!s/$/,/' && echo "}") > /var/www/public/config.json \
22 | && if [ -n "$BACKEND_SERVICE" ]; then \
23 | BACKEND_IP=$(getent hosts $BACKEND_SERVICE | awk '{ print $1 }') \
24 | && echo "$BACKEND_IP backend" >> /etc/hosts; \
25 | fi \
26 | && nginx -g "daemon off;"
27 |
--------------------------------------------------------------------------------
/frontend/nginx/conf.d/default.conf:
--------------------------------------------------------------------------------
1 | client_max_body_size 20M;
2 |
3 | server {
4 | index index.html;
5 | root /var/www/public/;
6 |
7 | server_name _;
8 |
9 | include sites.d/frontend.conf;
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/nginx/include.d/allcache.conf:
--------------------------------------------------------------------------------
1 | expires 1y;
2 | add_header Cache-Control "public";
3 | access_log off;
4 |
--------------------------------------------------------------------------------
/frontend/nginx/include.d/nocache.conf:
--------------------------------------------------------------------------------
1 | add_header Last-Modified $date_gmt;
2 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
3 | if_modified_since off;
4 | expires off;
5 | etag off;
--------------------------------------------------------------------------------
/frontend/nginx/include.d/spa.conf:
--------------------------------------------------------------------------------
1 | # X-Frame-Options is to prevent from clickJacking attack
2 | add_header X-Frame-Options SAMEORIGIN;
3 |
4 | # disable content-type sniffing on some browsers.
5 | add_header X-Content-Type-Options nosniff;
6 |
7 | # This header enables the Cross-site scripting (XSS) filter
8 | add_header X-XSS-Protection "1; mode=block";
9 |
10 | # This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack
11 | add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
12 |
13 | add_header Referrer-Policy "no-referrer-when-downgrade";
14 |
15 | # Enables response header of "Vary: Accept-Encoding"
16 | gzip_vary on;
--------------------------------------------------------------------------------
/frontend/nginx/sites.d/frontend.conf:
--------------------------------------------------------------------------------
1 | location / {
2 | try_files $uri $uri/ /index.html;
3 | include include.d/nocache.conf;
4 | }
5 |
6 | location /static {
7 | alias /var/www/public/static/;
8 | include include.d/allcache.conf;
9 | }
10 |
11 | location /manifest.json {
12 | proxy_pass http://backend:3000/manifest.json;
13 | }
14 |
15 | location /socket.io/ {
16 | proxy_pass http://backend:3000;
17 | proxy_http_version 1.1;
18 | proxy_set_header Upgrade $http_upgrade;
19 | proxy_set_header Connection "Upgrade";
20 | proxy_set_header Host $host;
21 | }
22 |
23 | location /backend/public/ {
24 | add_header Content-Disposition 'attachment';
25 | alias /var/www/backend-public/;
26 | include include.d/allcache.conf;
27 | }
28 |
29 | location /backend/ {
30 | rewrite ^/backend/(.*) /$1 break;
31 | proxy_pass http://backend:3000;
32 | }
33 |
34 | include ticketz.d/*.conf;
35 |
36 | include "include.d/spa.conf";
37 |
--------------------------------------------------------------------------------
/frontend/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/frontend/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/frontend/public/config-dev-example.json:
--------------------------------------------------------------------------------
1 | {
2 | "BACKEND_PROTOCOL": "http",
3 | "BACKEND_HOST": "localhost",
4 | "BACKEND_PORT": "8080",
5 | "LOG_LEVEL": "debug"
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/favicon-16x16.png
--------------------------------------------------------------------------------
/frontend/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/favicon-32x32.png
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/favicon.png
--------------------------------------------------------------------------------
/frontend/public/gitinfo.json:
--------------------------------------------------------------------------------
1 | {
2 | "commitHash": "custom",
3 | "commitTimestamp": "",
4 | "branchName": "",
5 | "tagName": "v1.0.x",
6 | "buildTimestamp": "custom build"
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name" : "Ticketz",
3 | "name" : "Ticketz - Chat Based Ticket System",
4 | "icons" : [{
5 | "src" : "vector/favicon.ico",
6 | "sizes" : "512x512 192x192 64x64 32x32 24x24 16x16",
7 | "type" : "image/svg+xml"
8 | },{
9 | "src" : "favicon.ico",
10 | "sizes" : "64x64 32x32 24x24 16x16",
11 | "type" : "image/x-icon"
12 | }, {
13 | "src" : "/android-chrome-192x192.png",
14 | "sizes" : "192x192",
15 | "type" : "image/png"
16 | }
17 | ],
18 | "start_url" : ".",
19 | "display" : "standalone",
20 | "theme_color" : "#000000",
21 | "background_color" : "#ffffff"
22 | }
--------------------------------------------------------------------------------
/frontend/public/mercadopago.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/mercadopago.png
--------------------------------------------------------------------------------
/frontend/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/mstile-150x150.png
--------------------------------------------------------------------------------
/frontend/public/ticketzpix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/public/ticketzpix.png
--------------------------------------------------------------------------------
/frontend/src/assets/chat_notify.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/chat_notify.mp3
--------------------------------------------------------------------------------
/frontend/src/assets/planilha.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/planilha.xlsx
--------------------------------------------------------------------------------
/frontend/src/assets/sacmais.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/sacmais.png
--------------------------------------------------------------------------------
/frontend/src/assets/sound.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/sound.mp3
--------------------------------------------------------------------------------
/frontend/src/assets/sound.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/sound.ogg
--------------------------------------------------------------------------------
/frontend/src/assets/wa-background-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/wa-background-dark.png
--------------------------------------------------------------------------------
/frontend/src/assets/wa-background-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/wa-background-light.png
--------------------------------------------------------------------------------
/frontend/src/assets/wa-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ticketz-oss/ticketz/d3256619ba6706ab978778e8a7c183b363d506e2/frontend/src/assets/wa-background.png
--------------------------------------------------------------------------------
/frontend/src/components/BackdropLoading/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Backdrop from "@material-ui/core/Backdrop";
4 | import CircularProgress from "@material-ui/core/CircularProgress";
5 | import { makeStyles } from "@material-ui/core/styles";
6 |
7 | const useStyles = makeStyles(theme => ({
8 | backdrop: {
9 | zIndex: theme.zIndex.drawer + 1,
10 | color: "#fff",
11 | },
12 | }));
13 |
14 | const BackdropLoading = () => {
15 | const classes = useStyles();
16 | return (
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default BackdropLoading;
24 |
--------------------------------------------------------------------------------
/frontend/src/components/ButtonWithSpinner/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import { green } from "@material-ui/core/colors";
5 | import { CircularProgress, Button } from "@material-ui/core";
6 |
7 | const useStyles = makeStyles(theme => ({
8 | button: {
9 | position: "relative",
10 | },
11 |
12 | buttonProgress: {
13 | color: green[500],
14 | position: "absolute",
15 | top: "50%",
16 | left: "50%",
17 | marginTop: -12,
18 | marginLeft: -12,
19 | },
20 | }));
21 |
22 | const ButtonWithSpinner = ({ loading, children, ...rest }) => {
23 | const classes = useStyles();
24 |
25 | return (
26 |
32 | );
33 | };
34 |
35 | export default ButtonWithSpinner;
36 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/CheckoutSuccess/index.js:
--------------------------------------------------------------------------------
1 | import CheckoutSuccess from './CheckoutSuccess';
2 | export default CheckoutSuccess;
3 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/FormModel/formInitialValues.js:
--------------------------------------------------------------------------------
1 | import checkoutFormModel from './checkoutFormModel';
2 | const {
3 | formField: {
4 | firstName,
5 | lastName,
6 | address1,
7 | city,
8 | state,
9 | zipcode,
10 | country,
11 | useAddressForPaymentDetails,
12 | nameOnCard,
13 | cardNumber,
14 | invoiceId,
15 | cvv
16 | }
17 | } = checkoutFormModel;
18 |
19 | export default {
20 | [firstName.name]: '',
21 | [lastName.name]: '',
22 | [address1.name]: '',
23 | [city.name]: '',
24 | [state.name]: '',
25 | [zipcode.name]: '',
26 | [country.name]: '',
27 | [useAddressForPaymentDetails.name]: false,
28 | [nameOnCard.name]: '',
29 | [cardNumber.name]: '',
30 | [invoiceId.name]: '',
31 | [cvv.name]: ''
32 | };
33 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/FormModel/validationSchema.js:
--------------------------------------------------------------------------------
1 | import * as Yup from 'yup';
2 | import checkoutFormModel from './checkoutFormModel';
3 | const {
4 | formField: {
5 | firstName,
6 | address1,
7 | city,
8 | zipcode,
9 | country,
10 | }
11 | } = checkoutFormModel;
12 |
13 |
14 | export default [
15 | Yup.object().shape({
16 | [firstName.name]: Yup.string().required(`${firstName.requiredErrorMsg}`),
17 | [address1.name]: Yup.string().required(`${address1.requiredErrorMsg}`),
18 | [city.name]: Yup.string()
19 | .nullable()
20 | .required(`${city.requiredErrorMsg}`),
21 | [zipcode.name]: Yup.string()
22 | .required(`${zipcode.requiredErrorMsg}`),
23 |
24 | [country.name]: Yup.string()
25 | .nullable()
26 | .required(`${country.requiredErrorMsg}`)
27 | }),
28 |
29 | ];
30 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/ReviewOrder/ReviewOrder.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useFormikContext } from 'formik';
3 | import { Typography, Grid } from '@material-ui/core';
4 | import ShippingDetails from './ShippingDetails';
5 |
6 | export default function ReviewOrder() {
7 | const { values: formValues } = useFormikContext();
8 | return (
9 |
10 |
11 | Resumo da assinatura
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/ReviewOrder/index.js:
--------------------------------------------------------------------------------
1 | import ReviewOrder from './ReviewOrder';
2 | export default ReviewOrder;
3 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/ReviewOrder/styles.js:
--------------------------------------------------------------------------------
1 | import { makeStyles } from '@material-ui/core/styles';
2 | export default makeStyles(theme => ({
3 | listItem: {
4 | padding: theme.spacing(1, 0)
5 | },
6 | total: {
7 | fontWeight: '700'
8 | },
9 | title: {
10 | marginTop: theme.spacing(2)
11 | }
12 | }));
13 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/index.js:
--------------------------------------------------------------------------------
1 | import CheckoutPage from './CheckoutPage';
2 | export default CheckoutPage;
3 |
--------------------------------------------------------------------------------
/frontend/src/components/CheckoutPage/styles.js:
--------------------------------------------------------------------------------
1 | import { makeStyles } from '@material-ui/core/styles';
2 | export default makeStyles(theme => ({
3 | stepper: {
4 | padding: theme.spacing(3, 0, 5)
5 | },
6 | buttons: {
7 | display: 'flex',
8 | justifyContent: 'flex-end'
9 | },
10 | button: {
11 | marginTop: theme.spacing(3),
12 | marginLeft: theme.spacing(1)
13 | },
14 | wrapper: {
15 | margin: theme.spacing(1),
16 | position: 'relative'
17 | },
18 | buttonProgress: {
19 | position: 'absolute',
20 | top: '50%',
21 | left: '50%'
22 | }
23 | }));
24 |
--------------------------------------------------------------------------------
/frontend/src/components/FormFields/InputField.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { at } from 'lodash';
3 | import { useField } from 'formik';
4 | import { TextField } from '@material-ui/core';
5 |
6 | export default function InputField(props) {
7 | const { errorText, ...rest } = props;
8 | const [field, meta] = useField(props);
9 |
10 | function _renderHelperText() {
11 | const [touched, error] = at(meta, 'touched', 'error');
12 | if (touched && error) {
13 | return error;
14 | }
15 | }
16 |
17 | return (
18 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/src/components/FormFields/index.js:
--------------------------------------------------------------------------------
1 | import InputField from './InputField';
2 | import CheckboxField from './CheckboxField';
3 | import SelectField from './SelectField';
4 | import DatePickerField from './DatePickerField';
5 | export { InputField, CheckboxField, SelectField, DatePickerField };
6 |
--------------------------------------------------------------------------------
/frontend/src/components/GoogleAnalytics/index.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Helmet } from 'react-helmet';
3 |
4 | const ga4Tag = "G-VXMW3XS5BD";
5 | const scriptSrc = `https://www.googletagmanager.com/gtag/js?id={ga4Tag}`;
6 |
7 | const GoogleAnalytics = () => {
8 | useEffect(() => {
9 | // Inicialização do GA4
10 | window.dataLayer = window.dataLayer || [];
11 | function gtag(){window.dataLayer.push(arguments);}
12 | gtag('js', new Date());
13 |
14 | gtag('config', ga4Tag, {
15 | 'page_title': window.location.host
16 | } );
17 | }, []);
18 |
19 | return (
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default GoogleAnalytics;
27 |
--------------------------------------------------------------------------------
/frontend/src/components/MainHeader/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import whatsBackground from "../../assets/wa-background.png"
5 | import whatsBackgroundDark from "../../assets/wa-background-dark.png";
6 |
7 | const useStyles = makeStyles(theme => ({
8 | contactsHeader: {
9 | display: "flex",
10 | alignItems: "center",
11 | padding: "0px 6px 6px 6px",
12 | backgroundImage: theme.mode === 'light' ? `url(${whatsBackground})` : `url(${whatsBackgroundDark})`,
13 | backgroundPosition: 'center',
14 | backgroundSize: 'cover',
15 | backgroundRepeat: 'no-repeat',
16 | },
17 | }));
18 |
19 | const MainHeader = ({ children }) => {
20 | const classes = useStyles();
21 |
22 | return {children}
;
23 | };
24 |
25 | export default MainHeader;
26 |
--------------------------------------------------------------------------------
/frontend/src/components/MainHeaderButtonsWrapper/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { makeStyles } from "@material-ui/core/styles";
4 |
5 | const useStyles = makeStyles(theme => ({
6 | MainHeaderButtonsWrapper: {
7 | flex: "none",
8 | marginLeft: "auto",
9 | "& > *": {
10 | margin: theme.spacing(1),
11 | },
12 | },
13 | }));
14 |
15 | const MainHeaderButtonsWrapper = ({ children }) => {
16 | const classes = useStyles();
17 |
18 | return {children}
;
19 | };
20 |
21 | export default MainHeaderButtonsWrapper;
22 |
--------------------------------------------------------------------------------
/frontend/src/components/MessageOptionsMenu/style.js:
--------------------------------------------------------------------------------
1 | import { makeStyles } from '@material-ui/core'
2 |
3 | export const useStyles = makeStyles(theme => ({
4 | emojiButton: {
5 | cursor: "pointer",
6 | borderRadius: 5,
7 | '&:hover': {
8 | backgroundColor: '#888a'
9 | },
10 | flexBasis: '25%',
11 | margin: 0,
12 | textAlign: 'center',
13 | },
14 | flexContainer: {
15 | display: 'flex',
16 | flexWrap: 'wrap',
17 | justifyContent: 'space-around',
18 | },
19 | }))
--------------------------------------------------------------------------------
/frontend/src/components/OnlyForSuperUser/index.js:
--------------------------------------------------------------------------------
1 | const OnlyForSuperUser = ({ user, yes, no }) => user?.super ? yes() : no();
2 |
3 | OnlyForSuperUser.defaultProps = {
4 | user: {},
5 | yes: () => null,
6 | no: () => null,
7 | };
8 |
9 | export default OnlyForSuperUser;
--------------------------------------------------------------------------------
/frontend/src/components/SubscriptionStepper/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function index() {
4 | return (
5 | index
6 | )
7 | }
8 |
9 | export default index
--------------------------------------------------------------------------------
/frontend/src/components/TabPanel/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const TabPanel = ({ children, value, name, ...rest }) => {
4 | if (value === name) {
5 | return (
6 |
12 | <>{children}>
13 |
14 | );
15 | } else return null;
16 | };
17 |
18 | export default TabPanel;
19 |
--------------------------------------------------------------------------------
/frontend/src/components/TicketAdvancedLayout/index.js:
--------------------------------------------------------------------------------
1 | import { styled } from '@material-ui/core/styles';
2 | import Paper from '@material-ui/core/Paper';
3 |
4 | const TicketAdvancedLayout = styled(Paper)({
5 | height: `calc(100% - 48px)`,
6 | display: "grid",
7 | gridTemplateRows: "56px 1fr"
8 | })
9 |
10 | export default TicketAdvancedLayout;
--------------------------------------------------------------------------------
/frontend/src/components/TicketHeader/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { Card } from "@material-ui/core";
4 | import { makeStyles } from "@material-ui/core/styles";
5 | import TicketHeaderSkeleton from "../TicketHeaderSkeleton";
6 |
7 | const useStyles = makeStyles(theme => ({
8 | ticketHeader: {
9 | display: "flex",
10 | flex: "none",
11 | borderBottom: "1px solid rgba(0, 0, 0, 0.12)",
12 | },
13 | }));
14 |
15 | const TicketHeader = ({ loading, children }) => {
16 | const classes = useStyles();
17 |
18 | return (
19 | <>
20 | {loading ? (
21 |
22 | ) : (
23 |
24 | {children}
25 |
26 | )}
27 | >
28 | );
29 | };
30 |
31 | export default TicketHeader;
32 |
--------------------------------------------------------------------------------
/frontend/src/components/Title/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Typography from "@material-ui/core/Typography";
3 |
4 | export default function Title(props) {
5 | return (
6 |
7 | {props.children}
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/context/Auth/AuthContext.js:
--------------------------------------------------------------------------------
1 | import React, { createContext } from "react";
2 |
3 | import useAuth from "../../hooks/useAuth.js";
4 |
5 | const AuthContext = createContext();
6 |
7 | const AuthProvider = ({ children }) => {
8 | const { loading, user, isAuth, handleLogin, handleImpersonate, handleLogout } = useAuth();
9 |
10 | return (
11 |
14 | {children}
15 |
16 | );
17 | };
18 |
19 | export { AuthContext, AuthProvider };
20 |
--------------------------------------------------------------------------------
/frontend/src/context/EditingMessage/EditingMessageContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, createContext } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const EditMessageContext = createContext();
5 |
6 | const EditMessageProvider = ({ children }) => {
7 | const [editingMessage, setEditingMessage] = useState(null);
8 |
9 | return (
10 |
13 | {children}
14 |
15 | );
16 | };
17 |
18 | export { EditMessageContext, EditMessageProvider};
19 |
--------------------------------------------------------------------------------
/frontend/src/context/ReplyingMessage/ReplyingMessageContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, createContext } from "react";
2 |
3 | const ReplyMessageContext = createContext();
4 |
5 | const ReplyMessageProvider = ({ children }) => {
6 | const [replyingMessage, setReplyingMessage] = useState(null);
7 |
8 | return (
9 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | export { ReplyMessageContext, ReplyMessageProvider };
18 |
--------------------------------------------------------------------------------
/frontend/src/context/Tickets/TicketsContext.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, createContext } from "react";
2 | import { useHistory } from "react-router-dom";
3 |
4 | const TicketsContext = createContext();
5 |
6 | const TicketsContextProvider = ({ children }) => {
7 | const [currentTicket, setCurrentTicket] = useState({ id: null, code: null });
8 | const history = useHistory();
9 |
10 | useEffect(() => {
11 | if (currentTicket?.uuid) {
12 | history.push(`/tickets/${currentTicket.uuid}`);
13 | }
14 | // eslint-disable-next-line react-hooks/exhaustive-deps
15 | }, [currentTicket])
16 |
17 | return (
18 |
21 | {children}
22 |
23 | );
24 | };
25 |
26 | export { TicketsContext, TicketsContextProvider };
27 |
--------------------------------------------------------------------------------
/frontend/src/context/WhatsApp/WhatsAppsContext.js:
--------------------------------------------------------------------------------
1 | import React, { createContext } from "react";
2 |
3 | import useWhatsApps from "../../hooks/useWhatsApps";
4 |
5 | const WhatsAppsContext = createContext();
6 |
7 | const WhatsAppsProvider = ({ children }) => {
8 | const { loading, whatsApps } = useWhatsApps();
9 |
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | export { WhatsAppsContext, WhatsAppsProvider };
18 |
--------------------------------------------------------------------------------
/frontend/src/helpers/copyToClipboard.js:
--------------------------------------------------------------------------------
1 | const copyToClipboard = (text) => {
2 | console.log('text', text)
3 | var textField = document.createElement('textarea')
4 | textField.innerText = text
5 | document.body.appendChild(textField)
6 | textField.select()
7 | document.execCommand('copy')
8 | textField.remove()
9 | }
10 |
11 | export { copyToClipboard };
12 |
--------------------------------------------------------------------------------
/frontend/src/helpers/exportCsv.js:
--------------------------------------------------------------------------------
1 | import { unparse } from 'papaparse';
2 | import { toast } from "react-toastify";
3 |
4 | export function exportCsv(data, filename) {
5 | const result = unparse(data);
6 | if (!result) {
7 | toast.error("Error generating CSV");
8 | return;
9 | }
10 |
11 | const blob = new Blob([result], { type: "text/csv;charset=utf-8;" });
12 | const url = URL.createObjectURL(blob);
13 | const a = document.createElement("a");
14 | a.href = url;
15 | a.download = filename || "report.csv";
16 | a.click();
17 | URL.revokeObjectURL(url);
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/src/helpers/generateSecureToken.js:
--------------------------------------------------------------------------------
1 | const generateSecureToken = function (length) {
2 | const array = new Uint8Array(length);
3 | window.crypto.getRandomValues(array);
4 | return btoa(String.fromCharCode.apply(null, array));
5 | };
6 |
7 | export { generateSecureToken };
8 |
--------------------------------------------------------------------------------
/frontend/src/helpers/getInitials.js:
--------------------------------------------------------------------------------
1 |
2 | var getInitials = function(string) {
3 | if (!string) {
4 | return "";
5 | }
6 |
7 | let initials = "";
8 |
9 | var names = string.trim().split(' ');
10 |
11 | if (names.length > 0) {
12 | initials = Array.from(names[0])[0];
13 | }
14 |
15 | if (names.length > 1) {
16 | initials += Array.from(names[names.length - 1])[0];
17 | }
18 |
19 | if (!initials) {
20 | return "👤";
21 | }
22 |
23 | return initials.toUpperCase();
24 | };
25 |
26 | export { getInitials };
27 |
--------------------------------------------------------------------------------
/frontend/src/helpers/i18nToast.js:
--------------------------------------------------------------------------------
1 | import { toast as realToast } from 'react-toastify';
2 | import { i18n } from '../translate/i18n';
3 |
4 | export const i18nToast = {
5 | error: (message, options) => {
6 | return realToast.error(i18n.t(message), options);
7 | },
8 | success: (message, options) => {
9 | return realToast.success(i18n.t(message), options);
10 | },
11 | info: (message, options) => {
12 | return realToast.info(i18n.t(message), options);
13 | },
14 | warn: (message, options) => {
15 | return realToast.warn(i18n.t(message), options);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/frontend/src/helpers/isMobile.js:
--------------------------------------------------------------------------------
1 | export function isMobile() {
2 | return /Mobi|Android|iPhone/i.test(navigator.userAgent);
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/src/helpers/loadJSON.js:
--------------------------------------------------------------------------------
1 | // Load text with Ajax synchronously: takes path to file and optional MIME type
2 | function loadTextFileAjaxSync(filePath, mimeType)
3 | {
4 | var xmlhttp=new XMLHttpRequest();
5 | xmlhttp.open("GET",filePath,false);
6 | if (mimeType != null) {
7 | if (xmlhttp.overrideMimeType) {
8 | xmlhttp.overrideMimeType(mimeType);
9 | }
10 | }
11 | xmlhttp.send();
12 | if (xmlhttp.status === 200 && xmlhttp.readyState === 4 )
13 | {
14 | return xmlhttp.responseText;
15 | }
16 | else {
17 | // TODO Throw exception
18 | return null;
19 | }
20 | }
21 |
22 | var loadJSON = function(filePath) {
23 | try {
24 | // Load json file;
25 | var json = loadTextFileAjaxSync(filePath, "application/json");
26 | // Parse json
27 | return JSON.parse(json);
28 | } catch (e) {
29 | return null;
30 | }
31 | }
32 |
33 | export { loadJSON };
34 |
--------------------------------------------------------------------------------
/frontend/src/hooks/useLocalStorage/index.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import toastError from "../../errors/toastError";
3 |
4 | export function useLocalStorage(key, initialValue) {
5 | const [storedValue, setStoredValue] = useState(() => {
6 | try {
7 | const item = localStorage.getItem(key);
8 | return item ? JSON.parse(item) : initialValue;
9 | } catch (error) {
10 | toastError(error);
11 | return initialValue;
12 | }
13 | });
14 |
15 | const setValue = value => {
16 | try {
17 | const valueToStore =
18 | value instanceof Function ? value(storedValue) : value;
19 |
20 | setStoredValue(valueToStore);
21 |
22 | localStorage.setItem(key, JSON.stringify(valueToStore));
23 | } catch (error) {
24 | toastError(error);
25 | }
26 | };
27 |
28 | return [storedValue, setValue];
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/hooks/useQueues/index.js:
--------------------------------------------------------------------------------
1 | import api from "../../services/api";
2 |
3 | const useQueues = () => {
4 | const findAll = async () => {
5 | const { data } = await api.get("/queue");
6 | return data;
7 | }
8 |
9 | return { findAll };
10 | };
11 |
12 | export default useQueues;
13 |
--------------------------------------------------------------------------------
/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import CssBaseline from "@material-ui/core/CssBaseline";
4 | import App from "./App";
5 |
6 | ReactDOM.render(
7 | //
8 |
9 |
10 |
11 | //
12 | ,
13 | document.getElementById('root'),
14 | () => {
15 | window.finishProgress();
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/frontend/src/layout/themeContext.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ColorModeContext = React.createContext({
4 | toggleColorMode: () => { },
5 | setPrimaryColorLight: (_) => { },
6 | setPrimaryColorDark: (_) => { },
7 | setAppLogoLight: (_) => { },
8 | setAppLogoDark: (_) => { },
9 | setAppLogoFavicon: (_) => { },
10 | });
11 |
12 | export default ColorModeContext;
--------------------------------------------------------------------------------
/frontend/src/pages/Dashboard/CustomTooltip.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { i18n } from '../../translate/i18n';
3 |
4 | const CustomTooltip = ({ payload, label, active, i18nBase }) => {
5 | if (active && payload && payload.length) {
6 | return (
7 |
8 |
{label}
9 | {payload.map((item, index) => (
10 |
11 | {`${i18nBase ? i18n.t(i18nBase+"."+item.name) : item.name}: ${item.value}`}
12 |
13 | ))}
14 |
15 | );
16 | }
17 | return null;
18 | };
19 |
20 | export default CustomTooltip;
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/Dashboard/Title.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Typography from "@material-ui/core/Typography";
3 |
4 | const Title = props => {
5 | return (
6 |
7 | {props.children}
8 |
9 | );
10 | };
11 |
12 | export default Title;
13 |
--------------------------------------------------------------------------------
/frontend/src/pages/TicketResponsiveContainer/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import withWidth, { isWidthUp } from '@material-ui/core/withWidth';
3 |
4 | import Tickets from "../TicketsCustom"
5 | import TicketAdvanced from "../TicketsAdvanced";
6 |
7 | function TicketResponsiveContainer (props) {
8 | if (isWidthUp('md', props.width)) {
9 | return ;
10 | }
11 | return
12 | }
13 |
14 | export default withWidth()(TicketResponsiveContainer);
--------------------------------------------------------------------------------
/frontend/src/rules.js:
--------------------------------------------------------------------------------
1 | const rules = {
2 | user: {
3 | static: [],
4 | },
5 |
6 | admin: {
7 | static: [
8 | //"dashboard:view",
9 | "drawer-admin-items:view",
10 | "tickets-manager:showall",
11 | "user-modal:editProfile",
12 | "user-modal:editQueues",
13 | "ticket-options:deleteTicket",
14 | "contacts-page:deleteContact",
15 | "connections-page:actionButtons",
16 | "connections-page:addConnection",
17 | "connections-page:editOrDeleteConnection"
18 | ],
19 | },
20 | };
21 |
22 | export default rules;
23 |
--------------------------------------------------------------------------------
/frontend/src/services/api.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { getBackendURL } from "../services/config";
3 |
4 | const api = axios.create({
5 | baseURL: getBackendURL(),
6 | withCredentials: true,
7 | });
8 |
9 | export const openApi = axios.create({
10 | baseURL: getBackendURL()
11 | });
12 |
13 | export default api;
14 |
--------------------------------------------------------------------------------
/frontend/src/services/socket.js:
--------------------------------------------------------------------------------
1 | export function socketConnection(_) {
2 | throw new Error("socketConnection not supported anymore. Change to SocketContext");
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/src/translate/i18n.js:
--------------------------------------------------------------------------------
1 | import i18n from "i18next";
2 | import LanguageDetector from "i18next-browser-languagedetector";
3 |
4 | import { messages } from "./languages";
5 |
6 | i18n.use(LanguageDetector).init({
7 | debug: false,
8 | detection: {
9 | order: ['localStorage', 'navigator'],
10 | lookupLocalStorage: 'language',
11 | caches: ['localStorage'],
12 | },
13 | defaultNS: ["translations"],
14 | fallbackLng: "en",
15 | ns: ["translations"],
16 | resources: messages,
17 | });
18 |
19 | export { i18n };
20 |
--------------------------------------------------------------------------------
/frontend/src/translate/languages/index.js:
--------------------------------------------------------------------------------
1 | import { messages as portugueseMessages } from "./pt";
2 | import { messages as portuguesePortugalMessages } from "./pt_PT";
3 | import { messages as englishMessages } from "./en";
4 | import { messages as spanishMessages } from "./es";
5 | import { messages as frenchMessages } from "./fr";
6 | import { messages as germanMessages } from "./de";
7 | import { messages as italianMessages } from "./it";
8 | import { messages as indonesianMessages } from "./id";
9 |
10 | const messages = {
11 | ...portugueseMessages,
12 | ...portuguesePortugalMessages,
13 | ...englishMessages,
14 | ...spanishMessages,
15 | ...frenchMessages,
16 | ...germanMessages,
17 | ...italianMessages,
18 | ...indonesianMessages,
19 | };
20 |
21 | export { messages };
22 |
--------------------------------------------------------------------------------