├── .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 | --------------------------------------------------------------------------------