├── backend ├── .eslintignore ├── src │ ├── config │ │ ├── version.ts │ │ ├── auth.ts │ │ ├── upload.ts │ │ └── database.ts │ ├── @types │ │ ├── qrcode-terminal.d.ts │ │ └── express.d.ts │ ├── bootstrap.ts │ ├── services │ │ ├── ClientStatusServices │ │ │ ├── DeleteAllService.ts │ │ │ └── DeleteService.ts │ │ ├── QueueService │ │ │ ├── ListQueuesService.ts │ │ │ ├── DeleteQueueService.ts │ │ │ └── ShowQueueService.ts │ │ ├── SettingServices │ │ │ ├── ListSettingsService.ts │ │ │ ├── ListSettingsServiceOne.ts │ │ │ ├── ListSettingByValueService.ts │ │ │ └── UpdateSettingService.ts │ │ ├── PersonalizationServices │ │ │ ├── ListPersonalizationsService.ts │ │ │ └── DeletePersonalizationService.ts │ │ ├── IntegrationServices │ │ │ ├── ListIntegrationsService.ts │ │ │ └── UpdateIntegrationService.ts │ │ ├── WhatsappService │ │ │ ├── AssociateWhatsappQueue.ts │ │ │ ├── DeleteWhatsAppService.ts │ │ │ ├── ListWhatsAppsService.ts │ │ │ ├── RestartWhatsAppService.ts │ │ │ └── ShowWhatsAppService.ts │ │ ├── ApiTokenService │ │ │ ├── FindByNameService.ts │ │ │ ├── ShowApiTokenService.ts │ │ │ ├── DeleteApiTokenService.ts │ │ │ ├── FindByTokenService.ts │ │ │ ├── CreateApiTokenService.ts │ │ │ └── ListApiTokenService.ts │ │ ├── TagServices │ │ │ ├── DeleteAllService.ts │ │ │ └── DeleteService.ts │ │ ├── HubServices │ │ │ ├── UpdateMessageHubAck.ts │ │ │ ├── ListHubChannels.ts │ │ │ └── CreateHubChannelsService.ts │ │ ├── ErrorLogServices │ │ │ ├── ShowErrorLogService.ts │ │ │ ├── DeleteOldErrorLogsService.ts │ │ │ └── CreateErrorLogService.ts │ │ ├── ContactServices │ │ │ ├── DeleteAllContactService.ts │ │ │ ├── DeleteContactService.ts │ │ │ └── ShowContactService.ts │ │ ├── QuickAnswerService │ │ │ ├── ShowQuickAnswerService.ts │ │ │ ├── DeleteAllQuickAnswerService.ts │ │ │ ├── DeleteQuickAnswerService.ts │ │ │ └── CreateQuickAnswerService.ts │ │ ├── VideoServices │ │ │ ├── DeleteVideoService.ts │ │ │ └── ShowVideoService.ts │ │ ├── WbotServices │ │ │ ├── GetProfilePicUrl.ts │ │ │ ├── CheckNumber.ts │ │ │ ├── StartAllWhatsAppsSessions.ts │ │ │ ├── CheckIsValidContact.ts │ │ │ ├── StartWhatsAppSession.ts │ │ │ └── DeleteWhatsAppMessage.ts │ │ ├── UserServices │ │ │ ├── FindAdminUsersService.ts │ │ │ ├── DeleteUserService.ts │ │ │ └── ShowUserService.ts │ │ ├── ActivityLogService │ │ │ ├── ListActionsService.ts │ │ │ └── CreateActivityLogService.ts │ │ └── TicketServices │ │ │ ├── DeleteTicketService.ts │ │ │ └── EmitTicketCounterService.ts │ ├── errors │ │ └── AppError.ts │ ├── __tests__ │ │ ├── utils │ │ │ └── database.ts │ │ └── unit │ │ │ └── User │ │ │ └── ListUserService.spec.ts │ ├── routes │ │ ├── userMonitorRoutes.ts │ │ ├── queueMonitorRoutes.ts │ │ ├── systemHealthRoutes.ts │ │ ├── networkMonitorRoutes.ts │ │ ├── settingRoutes.ts │ │ ├── hubChannelRoutes.ts │ │ ├── healthCheckRoutes.ts │ │ ├── apiRoutes.ts │ │ ├── pollVoteRoutes.ts │ │ ├── cacheRoutes.ts │ │ ├── hubWebhookRoutes.ts │ │ ├── integrationRoutes.ts │ │ ├── errorLogRoutes.ts │ │ ├── apiTokenRoutes.ts │ │ ├── systemCleanupRoutes.ts │ │ ├── videoRoutes.ts │ │ ├── userRoutes.ts │ │ ├── queueRoutes.ts │ │ ├── backupRoutes.ts │ │ ├── hubMessageRoutes.ts │ │ ├── whatsappNotificationRoutes.ts │ │ ├── fileManagerRoutes.ts │ │ ├── whatsappSessionRoutes.ts │ │ ├── versionRoutes.ts │ │ ├── tagRoutes.ts │ │ ├── activityLogRoutes.ts │ │ ├── groupEventRoutes.ts │ │ ├── systemUpdateRoutes.ts │ │ ├── clientStatusRoutes.ts │ │ ├── authRoutes.ts │ │ ├── ticketRoutes.ts │ │ ├── whatsappRoutes.ts │ │ ├── quickAnswerRoutes.ts │ │ └── systemRoutes.ts │ ├── helpers │ │ ├── SendRefreshToken.ts │ │ ├── CheckSettings.ts │ │ ├── showHubToken.ts │ │ ├── SerializeWbotMsgId.ts │ │ ├── UpdateDeletedUserOpenTicketsStatus.ts │ │ ├── CreateTokens.ts │ │ ├── GetDefaultWhatsAppByUser.ts │ │ ├── CheckContactOpenTickets.ts │ │ ├── GetTicketWbot.ts │ │ ├── SerializeUser.ts │ │ ├── GetDefaultWhatsApp.ts │ │ ├── Debounce.ts │ │ ├── ConvertMp3ToMp4.ts │ │ └── downloadHubFiles.ts │ ├── database │ │ ├── migrations │ │ │ ├── 20200919124112-update-default-column-name-on-whatsappp.ts │ │ │ ├── 20240924161020-add-type-to-whatsapp.ts │ │ │ ├── 20221128234000-add-number-to-whatsapp.ts │ │ │ ├── 20210108001431-add-unreadMessages-to-tickets.ts │ │ │ ├── 20210109192513-add-greetingMessage-to-whatsapp.ts │ │ │ ├── 20211016014719-add-farewellMessage-to-whatsapp.ts │ │ │ ├── 20220619203500-add-endwork-queues.ts │ │ │ ├── 20230505232700-add-istricked-to-users.ts │ │ │ ├── 20220619203200-add-startwork-queues.ts │ │ │ ├── 20230527010200-remove-isTricked-column-to-users.ts │ │ │ ├── 20200904220257-add-name-to-whatsapp.ts │ │ │ ├── 20220619203900-add-absencemessage-queues.ts │ │ │ ├── 20221012212700-add-endwork-users.ts │ │ │ ├── 20250123194700-add-online-to-users.ts │ │ │ ├── 20251005154200-add-cpf-to-contacts.ts │ │ │ ├── 20200723202116-add-email-field-to-contacts.ts │ │ │ ├── 20200730153545-add-fromMe-to-messages.ts │ │ │ ├── 20221012212600-add-startwork-users.ts │ │ │ ├── 20251024000000-add-albumId-to-messages.ts │ │ │ ├── 20200901235509-add-profile-column-to-users.ts │ │ │ ├── 20201026215410-add-retries-to-whatsapps.ts │ │ │ ├── 20241227211300-add-address-field-to-contacts.ts │ │ │ ├── 20251105000000-add-fileSize-to-messages.ts │ │ │ ├── 20200813114236-change-ticket-lastMessage-column-type.ts │ │ │ ├── 20200929145451-add-user-tokenVersion-column.ts │ │ │ ├── 20200930162323-add-isGroup-column-to-tickets.ts │ │ │ ├── 20200930194808-add-isGroup-column-to-contacts.ts │ │ │ ├── 20221023085500-add-isdisplay-to-whatsapp.ts │ │ │ ├── 20230527010400-add-istricked-column-to-users.ts │ │ │ ├── 20251128104200-add-ip-to-activity-logs.ts │ │ │ ├── 20200906122228-add-name-default-field-to-whatsapp.ts │ │ │ ├── 20200927220708-add-isDeleted-column-to-messages.ts │ │ │ ├── 20241026162841-add-isEdited-column-to-messages.ts │ │ │ ├── 20250120000000-add-mediaPath-to-quickanswers.ts │ │ │ ├── 20250124201400-add-last-activity-to-user-sessions.ts │ │ │ ├── 20241028230052-add-color-whatsapp.ts │ │ │ ├── 20251020040000-update-null-type-to-wwebjs.ts │ │ │ ├── 20210108164504-add-queueId-to-tickets.ts │ │ │ ├── 20250212094700-alter-profilePicUrl-length.ts │ │ │ ├── 20200730153237-remove-user-association-from-messages.ts │ │ │ ├── 20200906155658-add-whatsapp-field-to-tickets.ts │ │ │ ├── 20201004150008-add-contactId-column-to-messages.ts │ │ │ ├── 20201028124427-add-quoted-msg-to-messages.ts │ │ │ ├── 20220223095932-add-whatsapp-to-user.ts │ │ │ ├── 20201004155719-add-vcardContactId-column-to-messages.ts │ │ │ ├── 20201004955719-remove-vcardContactId-column-to-messages.ts │ │ │ ├── 20250301175500-add-userId-to-messages.ts │ │ │ ├── 20241230140500-remove-whatsapp-to-user.ts │ │ │ ├── 20240924161124-change-column-number-to-allownull.ts │ │ │ ├── 20250310102500-add-active-to-users.ts │ │ │ ├── 20240926143622-alter-contact-email-nullable.ts │ │ │ ├── 20250301175700-update-messages-userId-from-tickets.ts │ │ │ ├── 20251020031500-remove-battery-plugged-from-whatsapps.ts │ │ │ ├── 20250110154405-add-password-reset-fields-to-users.ts │ │ │ ├── 20210108204708-associate-users-queue.ts │ │ │ ├── 20200903215941-create-settings.ts │ │ │ ├── 20210108174594-associate-whatsapp-queue.ts │ │ │ ├── 20230503231400-create-integrations.ts │ │ │ ├── 20220906150400-create-tags.ts │ │ │ ├── 20210818102605-create-quickAnswers.ts │ │ │ ├── 20250610104928-add-break-time-to-queues.ts │ │ │ ├── 20251011000000-create-client-status.ts │ │ │ ├── 20250704224800-create-videos.ts │ │ │ ├── 20240921112934-alter-userId-foreign-key-on-tickets.ts │ │ │ ├── 20200717144403-create-contacts.ts │ │ │ ├── 20220906150600-create-associate-contacttags.ts │ │ │ ├── 20200717133438-create-users.ts │ │ │ ├── 20210108164404-create-queues.ts │ │ │ ├── 20200717170223-create-whatsapps.ts │ │ │ ├── 20241026164855-create-oldmessages.ts │ │ │ ├── 20250115234200-add-session-fields-to-users.ts │ │ │ ├── 20250704224900-create-video-users.ts │ │ │ └── 20250707000000-create-whatsapp-notification.ts │ │ └── seeds │ │ │ ├── 20230326221400-create-ASC-settings.ts │ │ │ ├── 20230326221600-create-created-settings.ts │ │ │ ├── 20230507221800-create-n8n-integrations.ts │ │ │ ├── 20241014161200-create-apiMaps-integrations.ts │ │ │ ├── 20230130004700-create-alltickets-settings.ts │ │ │ ├── 20240924161629-create-hubToken-integrations.ts │ │ │ ├── 20241008203900-create-quickAnswer-settings.ts │ │ │ ├── 20241228083100-create-opentickets-settings.ts │ │ │ ├── 20250124202200-create-signOption-settings.ts │ │ │ ├── 20250210085100-create-listItemSpy-settings.ts │ │ │ ├── 20250308170200-create-queueLength-settings.ts │ │ │ ├── 20200904070006-create-apiToken-settings.ts │ │ │ ├── 20230501010700-create-openai-settings.ts │ │ │ ├── 20200904070004-create-default-users.ts │ │ │ ├── 20250210061900-create-tabsSettings-settings.ts │ │ │ ├── 20241118200400-create-masteradmin-user.ts │ │ │ └── 20251021150000-create-call-rejection-settings.ts │ ├── models │ │ ├── Setting.ts │ │ ├── Integration.ts │ │ ├── UserQueue.ts │ │ ├── UserWhatsapp.ts │ │ ├── ContactTag.ts │ │ ├── QuickAnswer.ts │ │ ├── WhatsappQueue.ts │ │ ├── VideoUser.ts │ │ ├── ClientStatus.ts │ │ ├── MessageReaction.ts │ │ ├── WhatsappNotification.ts │ │ ├── ContactCustomField.ts │ │ ├── Video.ts │ │ ├── Tag.ts │ │ ├── UserSession.ts │ │ ├── OldMessage.ts │ │ ├── PollVote.ts │ │ ├── ActivityLog.ts │ │ └── ErrorLog.ts │ ├── controllers │ │ ├── CpuUsageController.ts │ │ ├── VersionController.ts │ │ ├── SystemHealthController.ts │ │ ├── NetworkMonitorController.ts │ │ ├── QueueMonitorController.ts │ │ ├── UserMonitorController.ts │ │ ├── MemoryUsageController.ts │ │ ├── DatabaseMonitorController.ts │ │ ├── HealthCheckController.ts │ │ ├── WebhookHubController.ts │ │ └── PollVoteController.ts │ ├── middleware │ │ ├── isMasterAdmin.ts │ │ ├── errorLogger.ts │ │ └── isAuthApi.ts │ └── server.ts ├── prettier.config.js ├── config │ └── update_status.json ├── sequelize-cli.json ├── .editorconfig ├── .gitignore ├── .sequelizerc └── tsconfig.json ├── README.md ├── img └── image.png ├── frontend ├── public │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── assets │ │ └── favicon.ico │ ├── default-profile.png │ ├── mstile-150x150.png │ └── manifest.json ├── src │ ├── assets │ │ ├── logo.jpg │ │ ├── send.png │ │ ├── sound.mp3 │ │ ├── sound.ogg │ │ ├── receive.png │ │ ├── logoTicket.jpg │ │ ├── backgroundDark.jpg │ │ └── backgroundLight.png │ ├── context │ │ ├── ForwardingMessage │ │ │ └── ForwardingMessageContext.js │ │ ├── Auth │ │ │ └── AuthContext.js │ │ ├── ReplyingMessage │ │ │ └── ReplyingMessageContext.js │ │ ├── WhatsApp │ │ │ └── WhatsAppsContext.js │ │ └── EditingMessage │ │ │ └── EditingMessageContext.js │ ├── components │ │ ├── Title │ │ │ └── index.js │ │ ├── MainHeader │ │ │ └── index.js │ │ ├── TabPanel │ │ │ └── index.js │ │ ├── WithSkeleton │ │ │ └── index.js │ │ ├── FormikTextField │ │ │ └── index.js │ │ ├── MainHeaderButtonsWrapper │ │ │ └── index.js │ │ ├── MainContainer │ │ │ └── index.js │ │ ├── OutlinedDiv │ │ │ └── index.js │ │ ├── ContactTag │ │ │ └── index.js │ │ ├── TicketHeaderSkeleton │ │ │ └── index.js │ │ ├── BackdropLoading │ │ │ └── index.js │ │ └── Can │ │ │ └── index.js │ ├── hooks │ │ ├── useQueues │ │ │ └── index.js │ │ ├── useMessageVariables │ │ │ └── index.js │ │ ├── useLocalStorage │ │ │ └── index.js │ │ ├── useSettings │ │ │ └── index.js │ │ └── useUsers │ │ │ └── index.js │ ├── themes │ │ └── themeConfig.js │ ├── pages │ │ ├── Dashboard │ │ │ ├── Title.js │ │ │ └── CustomTooltip.js │ │ └── ApiDocs │ │ │ └── index.js │ ├── errors │ │ ├── toastSuccess.js │ │ └── toastError.js │ ├── translate │ │ └── i18n.js │ ├── routes │ │ ├── PublicRoute.js │ │ ├── PrivateRoute.js │ │ ├── AdminRoute.js │ │ ├── MasterAdminRoute.js │ │ └── Route.js │ ├── helpers │ │ └── imageHelper.js │ ├── index.js │ ├── config.js │ ├── config │ │ └── messageVariables.js │ └── services │ │ └── versionService.js ├── .env.example └── .gitignore ├── .gitignore ├── .gitattributes ├── .github └── stale.yml └── LICENSE /backend/.eslintignore: -------------------------------------------------------------------------------- 1 | /*.js 2 | node_modules 3 | dist 4 | -------------------------------------------------------------------------------- /backend/src/config/version.ts: -------------------------------------------------------------------------------- 1 | export const systemVersion = "v1.14.0"; 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/README.md -------------------------------------------------------------------------------- /backend/src/@types/qrcode-terminal.d.ts: -------------------------------------------------------------------------------- 1 | declare module "qrcode-terminal"; 2 | -------------------------------------------------------------------------------- /img/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/img/image.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/public/logo512.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .docker/data/ 2 | ssl/ 3 | .env 4 | .wwebjs_auth 5 | .vscode 6 | 7 | estrutura_projeto.txt 8 | log/ 9 | -------------------------------------------------------------------------------- /frontend/src/assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/logo.jpg -------------------------------------------------------------------------------- /frontend/src/assets/send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/send.png -------------------------------------------------------------------------------- /frontend/src/assets/sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/sound.mp3 -------------------------------------------------------------------------------- /frontend/src/assets/sound.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/sound.ogg -------------------------------------------------------------------------------- /frontend/src/assets/receive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/receive.png -------------------------------------------------------------------------------- /frontend/public/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/public/assets/favicon.ico -------------------------------------------------------------------------------- /frontend/public/default-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/public/default-profile.png -------------------------------------------------------------------------------- /frontend/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/public/mstile-150x150.png -------------------------------------------------------------------------------- /frontend/src/assets/logoTicket.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/logoTicket.jpg -------------------------------------------------------------------------------- /backend/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: false, 3 | trailingComma: "none", 4 | arrowParens: "avoid" 5 | }; 6 | -------------------------------------------------------------------------------- /frontend/src/assets/backgroundDark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/backgroundDark.jpg -------------------------------------------------------------------------------- /frontend/src/assets/backgroundLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtenorioh/Press-Ticket/HEAD/frontend/src/assets/backgroundLight.png -------------------------------------------------------------------------------- /backend/config/update_status.json: -------------------------------------------------------------------------------- 1 | {"status":"idle","progress":0,"message":"Sistema está atualizado","lastUpdateCheck":"2025-08-28T19:38:25.365Z"} -------------------------------------------------------------------------------- /backend/src/@types/express.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Express { 2 | export interface Request { 3 | user: { id: string; profile: string }; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_BACKEND_URL=http://localhost:8080 2 | REACT_APP_HOURS_CLOSE_TICKETS_AUTO= 3 | PORT=3000 4 | REACT_APP_PAGE_TITLE=PressTicket -------------------------------------------------------------------------------- /backend/sequelize-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": "src/config/database.ts", 3 | "migrations-path": "src/database/migrations", 4 | "models-path": "src/models", 5 | "seeders-path": "src/database/seeders" 6 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /frontend/src/context/ForwardingMessage/ForwardingMessageContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | const ForwardingMessageContext = createContext({}); 4 | 5 | export { ForwardingMessageContext }; 6 | -------------------------------------------------------------------------------- /backend/src/config/auth.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | secret: process.env.JWT_SECRET || "mysecret", 3 | expiresIn: "8h", 4 | refreshSecret: process.env.JWT_REFRESH_SECRET || "myanothersecret", 5 | refreshExpiresIn: "1d" 6 | }; 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf 3 | Dockerfile eol=lf 4 | docker-compose.yml eol=lf 5 | docker-compose.*.yml eol=lf 6 | *.jpg binary 7 | *.png binary 8 | *.gif binary 9 | *.woff binary 10 | *.tff binary 11 | *.eot binary 12 | *.otf binary -------------------------------------------------------------------------------- /backend/src/services/ClientStatusServices/DeleteAllService.ts: -------------------------------------------------------------------------------- 1 | import ClientStatus from "../../models/ClientStatus"; 2 | 3 | const DeleteAllService = async (): Promise => { 4 | await ClientStatus.destroy({ where: {}, truncate: true }); 5 | }; 6 | 7 | export default DeleteAllService; 8 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/* 3 | dist 4 | !public/.gitkeep 5 | .env 6 | .env.test 7 | .wwebjs_auth 8 | .wwebjs_cache 9 | sequelizeData.json 10 | 11 | yarn.lock 12 | yarn-error.log 13 | 14 | /src/config/sentry.js 15 | 16 | # Ignore test-related files 17 | /coverage.data 18 | /coverage/ 19 | -------------------------------------------------------------------------------- /frontend/src/components/Title/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Typography from "@mui/material/Typography"; 3 | 4 | export default function Title(props) { 5 | return ( 6 | 7 | {props.children} 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /backend/src/services/QueueService/ListQueuesService.ts: -------------------------------------------------------------------------------- 1 | import Queue from "../../models/Queue"; 2 | 3 | const ListQueuesService = async (): Promise => { 4 | const queues = await Queue.findAll({ order: [["name", "ASC"]] }); 5 | 6 | return queues; 7 | }; 8 | 9 | export default ListQueuesService; 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /backend/src/errors/AppError.ts: -------------------------------------------------------------------------------- 1 | class AppError { 2 | public readonly message: string; 3 | 4 | public readonly statusCode: number; 5 | 6 | constructor(message: string, statusCode = 400) { 7 | this.message = message; 8 | this.statusCode = statusCode; 9 | } 10 | } 11 | 12 | export default AppError; 13 | -------------------------------------------------------------------------------- /backend/src/services/SettingServices/ListSettingsService.ts: -------------------------------------------------------------------------------- 1 | import Setting from "../../models/Setting"; 2 | 3 | const ListSettingsService = async (): Promise => { 4 | const settings = await Setting.findAll(); 5 | 6 | return settings; 7 | }; 8 | 9 | export default ListSettingsService; 10 | -------------------------------------------------------------------------------- /frontend/src/themes/themeConfig.js: -------------------------------------------------------------------------------- 1 | import getDarkTheme from './darkTheme'; 2 | import getLightTheme from './lightTheme'; 3 | 4 | const loadThemeConfig = (theme, config, locale) => { 5 | return theme === 'light' ? getLightTheme(config, locale) : getDarkTheme(config, locale); 6 | }; 7 | 8 | export default loadThemeConfig; 9 | -------------------------------------------------------------------------------- /backend/src/services/QueueService/DeleteQueueService.ts: -------------------------------------------------------------------------------- 1 | import ShowQueueService from "./ShowQueueService"; 2 | 3 | const DeleteQueueService = async (queueId: number | string): Promise => { 4 | const queue = await ShowQueueService(queueId); 5 | 6 | await queue.destroy(); 7 | }; 8 | 9 | export default DeleteQueueService; 10 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/Title.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Typography from "@mui/material/Typography"; 3 | 4 | const Title = props => { 5 | return ( 6 | 7 | {props.children} 8 | 9 | ); 10 | }; 11 | 12 | export default Title; 13 | -------------------------------------------------------------------------------- /backend/src/services/PersonalizationServices/ListPersonalizationsService.ts: -------------------------------------------------------------------------------- 1 | import Personalization from "../../models/Personalization"; 2 | 3 | const listPersonalizations = async (): Promise => { 4 | const personalizations = await Personalization.findAll(); 5 | return personalizations; 6 | }; 7 | 8 | export default listPersonalizations; 9 | -------------------------------------------------------------------------------- /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/src/__tests__/utils/database.ts: -------------------------------------------------------------------------------- 1 | import database from "../../database"; 2 | 3 | const truncate = async (): Promise => { 4 | await database.truncate({ force: true, cascade: true }); 5 | }; 6 | 7 | const disconnect = async (): Promise => { 8 | return database.connectionManager.close(); 9 | }; 10 | 11 | export { truncate, disconnect }; 12 | -------------------------------------------------------------------------------- /frontend/src/errors/toastSuccess.js: -------------------------------------------------------------------------------- 1 | import { toast } from "react-toastify"; 2 | 3 | const toastSuccess = (msg) => { 4 | toast.success(msg, { 5 | toastId: msg, 6 | autoClose: 2000, 7 | hideProgressBar: false, 8 | closeOnClick: true, 9 | pauseOnHover: true, 10 | draggable: true, 11 | }); 12 | }; 13 | 14 | export default toastSuccess; 15 | -------------------------------------------------------------------------------- /backend/src/services/IntegrationServices/ListIntegrationsService.ts: -------------------------------------------------------------------------------- 1 | import Integration from "../../models/Integration"; 2 | 3 | const ListIntegrationsService = async (): Promise< 4 | Integration[] | undefined 5 | > => { 6 | const integrations = await Integration.findAll(); 7 | 8 | return integrations; 9 | }; 10 | 11 | export default ListIntegrationsService; 12 | -------------------------------------------------------------------------------- /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/routes/userMonitorRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as UserMonitorController from "../controllers/UserMonitorController"; 4 | 5 | const userMonitorRoutes = Router(); 6 | 7 | userMonitorRoutes.get("/user-monitor", isAuth, UserMonitorController.index); 8 | 9 | export default userMonitorRoutes; 10 | -------------------------------------------------------------------------------- /backend/src/services/ApiTokenService/FindByNameService.ts: -------------------------------------------------------------------------------- 1 | import ApiToken from "../../models/ApiToken"; 2 | 3 | const FindByNameService = async (name: string): Promise => { 4 | const token = await ApiToken.findOne({ 5 | where: { 6 | name: name 7 | } 8 | }); 9 | return token; 10 | }; 11 | 12 | export default FindByNameService; -------------------------------------------------------------------------------- /backend/src/routes/queueMonitorRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as QueueMonitorController from "../controllers/QueueMonitorController"; 4 | 5 | const queueMonitorRoutes = express.Router(); 6 | 7 | queueMonitorRoutes.get("/queue-monitor", isAuth, QueueMonitorController.index); 8 | 9 | export default queueMonitorRoutes; 10 | -------------------------------------------------------------------------------- /backend/src/routes/systemHealthRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as SystemHealthController from "../controllers/SystemHealthController"; 4 | 5 | const systemHealthRoutes = express.Router(); 6 | 7 | systemHealthRoutes.get("/system-health", isAuth, SystemHealthController.index); 8 | 9 | export default systemHealthRoutes; 10 | -------------------------------------------------------------------------------- /backend/src/routes/networkMonitorRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as NetworkMonitorController from "../controllers/NetworkMonitorController"; 4 | 5 | const networkMonitorRoutes = express.Router(); 6 | 7 | networkMonitorRoutes.get("/network-status", isAuth, NetworkMonitorController.index); 8 | 9 | export default networkMonitorRoutes; 10 | -------------------------------------------------------------------------------- /frontend/src/hooks/useMessageVariables/index.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { defaultMessageVariables } from "../../config/messageVariables"; 3 | 4 | const useMessageVariables = (customVariables = []) => { 5 | return useMemo(() => { 6 | return [...defaultMessageVariables, ...customVariables]; 7 | }, [customVariables]); 8 | }; 9 | 10 | export default useMessageVariables; 11 | -------------------------------------------------------------------------------- /backend/src/services/TagServices/DeleteAllService.ts: -------------------------------------------------------------------------------- 1 | import Tag from "../../models/Tag"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const DeleteAllService = async (): Promise => { 5 | await Tag.findAll(); 6 | 7 | if (!Tag) { 8 | throw new AppError("ERR_NO_TAG_FOUND", 404); 9 | } 10 | 11 | await Tag.destroy({ where: {} }); 12 | }; 13 | 14 | export default DeleteAllService; 15 | -------------------------------------------------------------------------------- /backend/src/helpers/SendRefreshToken.ts: -------------------------------------------------------------------------------- 1 | import { Response } from "express"; 2 | 3 | export const SendRefreshToken = (res: Response, token: string): void => { 4 | res.cookie("jrt", token, { 5 | httpOnly: true, 6 | secure: process.env.NODE_ENV === "production", 7 | sameSite: "strict", 8 | path: "/auth/refresh_token", 9 | maxAge: 8 * 60 * 60 * 1000 // 8 horas em milissegundos 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /backend/src/services/HubServices/UpdateMessageHubAck.ts: -------------------------------------------------------------------------------- 1 | import Message from "../../models/Message"; 2 | 3 | export const UpdateMessageAck = async (messageId: string): Promise => { 4 | const message = await Message.findOne({ 5 | where: { 6 | id: messageId 7 | } 8 | }); 9 | 10 | if (!message) { 11 | return; 12 | } 13 | 14 | await message.update({ 15 | ack: 3 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /backend/src/routes/settingRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | 4 | import * as SettingController from "../controllers/SettingController"; 5 | 6 | const settingRoutes = Router(); 7 | 8 | settingRoutes.get("/settings", isAuth, SettingController.index); 9 | 10 | settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update); 11 | 12 | export default settingRoutes; 13 | -------------------------------------------------------------------------------- /frontend/src/components/MainHeader/index.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import React from "react"; 3 | 4 | const HeaderContainer = styled('div')(({ theme }) => ({ 5 | display: "flex", 6 | alignItems: "center", 7 | padding: theme.spacing(0, 1, 1, 1), 8 | })); 9 | 10 | const MainHeader = ({ children }) => { 11 | return {children}; 12 | }; 13 | 14 | export default MainHeader; 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /backend/src/services/QueueService/ShowQueueService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Queue from "../../models/Queue"; 3 | 4 | const ShowQueueService = async (queueId: number | string): Promise => { 5 | const queue = await Queue.findByPk(queueId); 6 | 7 | if (!queue) { 8 | throw new AppError("ERR_QUEUE_NOT_FOUND"); 9 | } 10 | 11 | return queue; 12 | }; 13 | 14 | export default ShowQueueService; 15 | -------------------------------------------------------------------------------- /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/helpers/CheckSettings.ts: -------------------------------------------------------------------------------- 1 | import Setting from "../models/Setting"; 2 | import AppError from "../errors/AppError"; 3 | 4 | const CheckSettings = async (key: string): Promise => { 5 | const setting = await Setting.findOne({ 6 | where: { key } 7 | }); 8 | 9 | if (!setting) { 10 | throw new AppError("ERR_NO_SETTING_FOUND", 404); 11 | } 12 | 13 | return setting.value; 14 | }; 15 | 16 | export default CheckSettings; 17 | -------------------------------------------------------------------------------- /backend/src/routes/hubChannelRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | 3 | import * as ChannelController from "../controllers/ChannelHubController"; 4 | import isAuth from "../middleware/isAuth"; 5 | 6 | const hubChannelRoutes = express.Router(); 7 | 8 | hubChannelRoutes.post("/hub-channel/", isAuth, ChannelController.store); 9 | hubChannelRoutes.get("/hub-channel/", isAuth, ChannelController.index); 10 | 11 | export default hubChannelRoutes; 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /frontend/src/components/WithSkeleton/index.js: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@mui/material"; 2 | import React from "react"; 3 | 4 | const WithSkeleton = ({ loading, children, fullWidth }) => { 5 | return ( 6 | <> 7 | {loading ? ( 8 | {children} 9 | ) : ( 10 | <>{children} 11 | )} 12 | 13 | ); 14 | }; 15 | 16 | export default WithSkeleton; 17 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20240924161020-add-type-to-whatsapp.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Whatsapps", "type", { 6 | type: DataTypes.TEXT 7 | }); 8 | }, 9 | 10 | down: (queryInterface: QueryInterface) => { 11 | return queryInterface.removeColumn("Whatsapps", "type"); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20221128234000-add-number-to-whatsapp.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Whatsapps", "number", { 6 | type: DataTypes.STRING 7 | }); 8 | }, 9 | 10 | down: (queryInterface: QueryInterface) => { 11 | return queryInterface.removeColumn("Whatsapps", "number"); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /backend/src/models/Setting.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Table, 3 | Column, 4 | CreatedAt, 5 | UpdatedAt, 6 | Model, 7 | PrimaryKey 8 | } from "sequelize-typescript"; 9 | 10 | @Table 11 | class Setting extends Model { 12 | @PrimaryKey 13 | @Column 14 | key: string; 15 | 16 | @Column 17 | value: string; 18 | 19 | @CreatedAt 20 | createdAt: Date; 21 | 22 | @UpdatedAt 23 | updatedAt: Date; 24 | } 25 | 26 | export default Setting; 27 | -------------------------------------------------------------------------------- /backend/src/services/ApiTokenService/ShowApiTokenService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import ApiToken from "../../models/ApiToken"; 3 | 4 | const ShowApiTokenService = async (id: number): Promise => { 5 | const token = await ApiToken.findByPk(id); 6 | 7 | if (!token) { 8 | throw new AppError("ERR_TOKEN_NOT_FOUND", 404); 9 | } 10 | 11 | return token; 12 | }; 13 | 14 | export default ShowApiTokenService; -------------------------------------------------------------------------------- /backend/src/services/ApiTokenService/DeleteApiTokenService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import ApiToken from "../../models/ApiToken"; 3 | 4 | const DeleteApiTokenService = async (id: number): Promise => { 5 | const token = await ApiToken.findByPk(id); 6 | 7 | if (!token) { 8 | throw new AppError("ERR_TOKEN_NOT_FOUND", 404); 9 | } 10 | 11 | await token.destroy(); 12 | }; 13 | 14 | export default DeleteApiTokenService; -------------------------------------------------------------------------------- /backend/src/models/Integration.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Table, 3 | Column, 4 | CreatedAt, 5 | UpdatedAt, 6 | Model, 7 | PrimaryKey 8 | } from "sequelize-typescript"; 9 | 10 | @Table 11 | class Integration extends Model { 12 | @PrimaryKey 13 | @Column 14 | key: string; 15 | 16 | @Column 17 | value: string; 18 | 19 | @CreatedAt 20 | createdAt: Date; 21 | 22 | @UpdatedAt 23 | updatedAt: Date; 24 | } 25 | 26 | export default Integration; 27 | -------------------------------------------------------------------------------- /backend/src/routes/healthCheckRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as HealthCheckController from "../controllers/HealthCheckController"; 4 | 5 | const healthCheckRoutes = express.Router(); 6 | 7 | healthCheckRoutes.get("/health-check", isAuth, HealthCheckController.index); 8 | healthCheckRoutes.get("/health-check/:whatsappId", isAuth, HealthCheckController.show); 9 | 10 | export default healthCheckRoutes; 11 | -------------------------------------------------------------------------------- /backend/src/services/ErrorLogServices/ShowErrorLogService.ts: -------------------------------------------------------------------------------- 1 | import ErrorLog from "../../models/ErrorLog"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const ShowErrorLogService = async (id: number): Promise => { 5 | const errorLog = await ErrorLog.findByPk(id); 6 | 7 | if (!errorLog) { 8 | throw new AppError("Log de erro não encontrado", 404); 9 | } 10 | 11 | return errorLog; 12 | }; 13 | 14 | export default ShowErrorLogService; 15 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /dist 14 | 15 | # misc 16 | .DS_Store 17 | .env 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | yarn.lock 27 | build 28 | -------------------------------------------------------------------------------- /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/helpers/showHubToken.ts: -------------------------------------------------------------------------------- 1 | import Setting from "../models/Integration"; 2 | 3 | export const showHubToken = async (): Promise => { 4 | const notificameHubToken = await Setting.findOne({ 5 | where: { 6 | key: "hubToken" 7 | } 8 | }); 9 | 10 | if (!notificameHubToken) { 11 | throw new Error("Notificame Hub token not found"); 12 | } 13 | 14 | if (notificameHubToken) { 15 | return notificameHubToken.value; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /backend/src/routes/apiRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import multer from "multer"; 3 | import uploadConfig from "../config/upload"; 4 | 5 | import * as ApiController from "../controllers/ApiController"; 6 | import isAuthApi from "../middleware/isAuthApi"; 7 | 8 | const upload = multer(uploadConfig); 9 | 10 | const ApiRoutes = express.Router(); 11 | 12 | ApiRoutes.post("/send", isAuthApi, upload.array("medias"), ApiController.index); 13 | 14 | export default ApiRoutes; -------------------------------------------------------------------------------- /backend/src/routes/pollVoteRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import * as PollVoteController from "../controllers/PollVoteController"; 3 | import isAuth from "../middleware/isAuth"; 4 | 5 | const pollVoteRoutes = express.Router(); 6 | 7 | pollVoteRoutes.get("/poll-votes/:pollMessageId/summary", isAuth, PollVoteController.getVotesSummary); 8 | pollVoteRoutes.get("/poll-votes/:pollMessageId", isAuth, PollVoteController.getVotes); 9 | 10 | export default pollVoteRoutes; 11 | -------------------------------------------------------------------------------- /backend/src/services/ContactServices/DeleteAllContactService.ts: -------------------------------------------------------------------------------- 1 | import Contact from "../../models/Contact"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const DeleteAllContactService = async (): Promise => { 5 | const contacts = await Contact.findAll(); 6 | 7 | if (contacts.length === 0) { 8 | throw new AppError("ERR_NO_CONTACT_FOUND", 404); 9 | } 10 | 11 | await Contact.destroy({ where: {} }) 12 | }; 13 | 14 | export default DeleteAllContactService; -------------------------------------------------------------------------------- /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/20211016014719-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/20220619203500-add-endwork-queues.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Queues", "endWork", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | }); 9 | }, 10 | 11 | down: (queryInterface: QueryInterface) => { 12 | return queryInterface.removeColumn("Queues", "endWork"); 13 | } 14 | }; -------------------------------------------------------------------------------- /backend/src/database/migrations/20230505232700-add-istricked-to-users.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Users", "isTricked", { 6 | type: DataTypes.BOOLEAN, 7 | defaultValue: true 8 | }); 9 | }, 10 | 11 | down: (queryInterface: QueryInterface) => { 12 | return queryInterface.removeColumn("Users", "isTricked"); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20220619203200-add-startwork-queues.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Queues", "startWork", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | }); 9 | }, 10 | 11 | down: (queryInterface: QueryInterface) => { 12 | return queryInterface.removeColumn("Queues", "startWork"); 13 | } 14 | }; -------------------------------------------------------------------------------- /backend/src/database/migrations/20230527010200-remove-isTricked-column-to-users.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.removeColumn("Users", "isTricked"); 6 | }, 7 | down: (queryInterface: QueryInterface) => { 8 | return queryInterface.addColumn("Users", "isTricked", { 9 | type: DataTypes.BOOLEAN, 10 | defaultValue: true 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /backend/src/routes/cacheRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as CacheController from "../controllers/CacheController"; 4 | 5 | const cacheRoutes = express.Router(); 6 | 7 | cacheRoutes.get("/cache/stats", isAuth, CacheController.getCacheStats); 8 | cacheRoutes.get("/cache/keys", isAuth, CacheController.getCacheKeys); 9 | cacheRoutes.post("/cache/flush", isAuth, CacheController.flushCache); 10 | 11 | export default cacheRoutes; 12 | -------------------------------------------------------------------------------- /backend/src/services/ContactServices/DeleteContactService.ts: -------------------------------------------------------------------------------- 1 | import Contact from "../../models/Contact"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const DeleteContactService = async (id: string): Promise => { 5 | const contact = await Contact.findOne({ 6 | where: { id } 7 | }); 8 | 9 | if (!contact) { 10 | throw new AppError("ERR_NO_CONTACT_FOUND", 404); 11 | } 12 | 13 | await contact.destroy(); 14 | }; 15 | 16 | export default DeleteContactService; 17 | -------------------------------------------------------------------------------- /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; -------------------------------------------------------------------------------- /backend/src/services/QuickAnswerService/ShowQuickAnswerService.ts: -------------------------------------------------------------------------------- 1 | import QuickAnswer from "../../models/QuickAnswer"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const ShowQuickAnswerService = async (id: string): Promise => { 5 | const quickAnswer = await QuickAnswer.findByPk(id); 6 | 7 | if (!quickAnswer) { 8 | throw new AppError("ERR_NO_QUICK_ANSWERS_FOUND", 404); 9 | } 10 | 11 | return quickAnswer; 12 | }; 13 | 14 | export default ShowQuickAnswerService; 15 | -------------------------------------------------------------------------------- /backend/src/routes/hubWebhookRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import multer from "multer"; 3 | import uploadConfig from "../config/upload"; 4 | 5 | import * as WebhookController from "../controllers/WebhookHubController"; 6 | 7 | const hubWebhookRoutes = express.Router(); 8 | const upload = multer(uploadConfig); 9 | 10 | hubWebhookRoutes.post( 11 | "/hub-webhook/:channelId", 12 | upload.array("medias"), 13 | WebhookController.listen 14 | ); 15 | 16 | export default hubWebhookRoutes; 17 | -------------------------------------------------------------------------------- /backend/src/services/ApiTokenService/FindByTokenService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import ApiToken from "../../models/ApiToken"; 3 | 4 | const FindByTokenService = async (token: string): Promise => { 5 | const apiToken = await ApiToken.findOne({ 6 | where: { token } 7 | }); 8 | 9 | if (!apiToken) { 10 | throw new AppError("ERR_TOKEN_NOT_FOUND", 404); 11 | } 12 | 13 | return apiToken; 14 | }; 15 | 16 | export default FindByTokenService; -------------------------------------------------------------------------------- /backend/src/services/VideoServices/DeleteVideoService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Video from "../../models/Video"; 3 | 4 | interface Request { 5 | id: string; 6 | } 7 | 8 | const DeleteVideoService = async ({ id }: Request): Promise => { 9 | const video = await Video.findByPk(id); 10 | 11 | if (!video) { 12 | throw new AppError("Vídeo não encontrado", 404); 13 | } 14 | 15 | await video.destroy(); 16 | }; 17 | 18 | export default DeleteVideoService; 19 | -------------------------------------------------------------------------------- /backend/src/controllers/CpuUsageController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { getCpuUsageInfo } from '../services/CpuUsageService'; 3 | 4 | export const cpuUsage = async ( 5 | req: Request, 6 | res: Response 7 | ): Promise => { 8 | try { 9 | const cpuInfo = await getCpuUsageInfo(); 10 | return res.status(200).json(cpuInfo); 11 | } catch (error) { 12 | return res.status(500).json({ error: 'Erro ao obter informações de uso de CPU' }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /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/20220619203900-add-absencemessage-queues.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Queues", "absenceMessage", { 6 | type: DataTypes.TEXT, 7 | allowNull: true, 8 | }); 9 | }, 10 | 11 | down: (queryInterface: QueryInterface) => { 12 | return queryInterface.removeColumn("Queues", "absenceMessage"); 13 | } 14 | }; -------------------------------------------------------------------------------- /backend/src/database/migrations/20221012212700-add-endwork-users.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Users", "endWork", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: "23:59" 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Users", "endWork"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20250123194700-add-online-to-users.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 | allowNull: false 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Users", "online"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /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 cleanNumber = ticket.contact.number.replace(/@(c|g)\.us$/, ''); 6 | 7 | const serializedMsgId = `${message.fromMe}_${cleanNumber}@${ 8 | ticket.isGroup ? "g" : "c" 9 | }.us_${message.id}`; 10 | 11 | return serializedMsgId; 12 | }; 13 | 14 | export default SerializeWbotMsgId; 15 | -------------------------------------------------------------------------------- /backend/src/services/QuickAnswerService/DeleteAllQuickAnswerService.ts: -------------------------------------------------------------------------------- 1 | import QuickAnswer from "../../models/QuickAnswer"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const DeleteAllQuickAnswerService = async (): Promise => { 5 | await QuickAnswer.findAll(); 6 | 7 | if (!QuickAnswer) { 8 | throw new AppError("ERR_NO_QUICK_ANSWER_FOUND", 404); 9 | } 10 | 11 | await QuickAnswer.destroy({ 12 | where: { } 13 | }); 14 | }; 15 | 16 | export default DeleteAllQuickAnswerService; -------------------------------------------------------------------------------- /backend/src/database/migrations/20251005154200-add-cpf-to-contacts.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface) => { 5 | await queryInterface.addColumn("Contacts", "cpf", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: "" 9 | }); 10 | }, 11 | 12 | down: async (queryInterface: QueryInterface) => { 13 | await queryInterface.removeColumn("Contacts", "cpf"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/middleware/isMasterAdmin.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from "express"; 2 | import AppError from "../errors/AppError"; 3 | 4 | const isMasterAdmin = (req: Request, res: Response, next: NextFunction): void => { 5 | const { profile } = req.user; 6 | 7 | if (profile?.toUpperCase() !== "MASTERADMIN") { 8 | throw new AppError("ERR_ACCESS_DENIED: Only Master Admin can access this resource", 403); 9 | } 10 | 11 | return next(); 12 | }; 13 | 14 | export default isMasterAdmin; 15 | -------------------------------------------------------------------------------- /backend/src/routes/integrationRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | 4 | import * as IntegrationController from "../controllers/IntegrationController"; 5 | 6 | const integrationRoutes = Router(); 7 | 8 | integrationRoutes.get("/integrations", isAuth, IntegrationController.index); 9 | 10 | integrationRoutes.put( 11 | "/integrations/:integrationKey", 12 | isAuth, 13 | IntegrationController.update 14 | ); 15 | 16 | export default integrationRoutes; 17 | -------------------------------------------------------------------------------- /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/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/20221012212600-add-startwork-users.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Users", "startWork", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: "00:00" 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Users", "startWork"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20251024000000-add-albumId-to-messages.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Messages", "albumId", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: null 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Messages", "albumId"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/server.ts: -------------------------------------------------------------------------------- 1 | import gracefulShutdown from "http-graceful-shutdown"; 2 | import app from "./app"; 3 | import { initIO } from "./libs/socket"; 4 | import { logger } from "./utils/logger"; 5 | import { StartAllWhatsAppsSessions } from "./services/WbotServices/StartAllWhatsAppsSessions"; 6 | 7 | 8 | const server = app.listen(process.env.PORT, () => { 9 | logger.info(`Servidor iniciado na porta: ${process.env.PORT}`); 10 | }); 11 | 12 | initIO(server); 13 | StartAllWhatsAppsSessions(); 14 | gracefulShutdown(server); -------------------------------------------------------------------------------- /backend/src/services/WbotServices/GetProfilePicUrl.ts: -------------------------------------------------------------------------------- 1 | import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; 2 | import { getWbot } from "../../libs/wbot"; 3 | 4 | const GetProfilePicUrl = async (number: string): Promise => { 5 | const defaultWhatsapp = await GetDefaultWhatsApp(); 6 | 7 | const wbot = getWbot(defaultWhatsapp.id); 8 | 9 | const profilePicUrl = await wbot.getProfilePicUrl(`${number}@c.us`); 10 | 11 | return profilePicUrl; 12 | }; 13 | 14 | export default GetProfilePicUrl; 15 | -------------------------------------------------------------------------------- /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/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/20241227211300-add-address-field-to-contacts.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Contacts", "address", { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | defaultValue: "" 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Contacts", "address"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20251105000000-add-fileSize-to-messages.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Messages", "fileSize", { 6 | type: DataTypes.BIGINT, 7 | allowNull: true, 8 | defaultValue: null 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Messages", "fileSize"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/services/ClientStatusServices/DeleteService.ts: -------------------------------------------------------------------------------- 1 | import ClientStatus from "../../models/ClientStatus"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const DeleteService = async (id: string | number): Promise => { 5 | const clientStatus = await ClientStatus.findOne({ 6 | where: { id } 7 | }); 8 | 9 | if (!clientStatus) { 10 | throw new AppError("ERR_NO_CLIENT_STATUS_FOUND", 404); 11 | } 12 | 13 | await clientStatus.destroy(); 14 | }; 15 | 16 | export default DeleteService; 17 | -------------------------------------------------------------------------------- /backend/src/services/UserServices/FindAdminUsersService.ts: -------------------------------------------------------------------------------- 1 | import { Op } from "sequelize"; 2 | import User from "../../models/User"; 3 | 4 | interface Request { 5 | profiles: string[]; 6 | } 7 | 8 | const FindAdminUsersService = async ({ 9 | profiles 10 | }: Request): Promise => { 11 | const users = await User.findAll({ 12 | where: { 13 | profile: { 14 | [Op.in]: profiles 15 | } 16 | } 17 | }); 18 | 19 | return users; 20 | }; 21 | 22 | export default FindAdminUsersService; 23 | -------------------------------------------------------------------------------- /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/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/20221023085500-add-isdisplay-to-whatsapp.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Whatsapps", "isDisplay", { 6 | type: DataTypes.BOOLEAN, 7 | allowNull: false, 8 | defaultValue: false 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Whatsapps", "isDisplay"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20230527010400-add-istricked-column-to-users.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Users", "isTricked", { 6 | type: DataTypes.STRING, 7 | allowNull: false, 8 | defaultValue: "enabled" 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("Users", "isTricked"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20251128104200-add-ip-to-activity-logs.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface) => { 5 | await queryInterface.addColumn("ActivityLogs", "ip", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: null 9 | }); 10 | }, 11 | 12 | down: async (queryInterface: QueryInterface) => { 13 | await queryInterface.removeColumn("ActivityLogs", "ip"); 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/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/20241026162841-add-isEdited-column-to-messages.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } 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/config/upload.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import multer from "multer"; 3 | 4 | const publicFolder = path.resolve(__dirname, "..", "..", "public"); 5 | export default { 6 | directory: publicFolder, 7 | 8 | storage: multer.diskStorage({ 9 | destination: publicFolder, 10 | filename(req, file, cb) { 11 | const fileName = new Date().getTime() + "_" + file.originalname; 12 | 13 | return cb(null, fileName); 14 | } 15 | }), 16 | 17 | limits: { 18 | fileSize: 500 * 1024 * 1024 19 | } 20 | }; -------------------------------------------------------------------------------- /backend/src/database/migrations/20250120000000-add-mediaPath-to-quickanswers.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("QuickAnswers", "mediaPath", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: null 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("QuickAnswers", "mediaPath"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/services/QuickAnswerService/DeleteQuickAnswerService.ts: -------------------------------------------------------------------------------- 1 | import QuickAnswer from "../../models/QuickAnswer"; 2 | import AppError from "../../errors/AppError"; 3 | 4 | const DeleteQuickAnswerService = async (id: string): Promise => { 5 | const quickAnswer = await QuickAnswer.findOne({ 6 | where: { id } 7 | }); 8 | 9 | if (!quickAnswer) { 10 | throw new AppError("ERR_NO_QUICK_ANSWER_FOUND", 404); 11 | } 12 | 13 | await quickAnswer.destroy(); 14 | }; 15 | 16 | export default DeleteQuickAnswerService; 17 | -------------------------------------------------------------------------------- /backend/src/services/WbotServices/CheckNumber.ts: -------------------------------------------------------------------------------- 1 | import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; 2 | import { getWbot } from "../../libs/wbot"; 3 | 4 | const CheckContactNumber = async (number: string): Promise => { 5 | const defaultWhatsapp = await GetDefaultWhatsApp(); 6 | 7 | const wbot = getWbot(defaultWhatsapp.id); 8 | 9 | const validNumber: any = await wbot.getNumberId(`${number}@c.us`); 10 | return validNumber ? validNumber.user : number; 11 | }; 12 | 13 | export default CheckContactNumber; 14 | -------------------------------------------------------------------------------- /backend/src/controllers/VersionController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import * as VersionService from "../services/VersionService/VersionService"; 3 | 4 | export const getVersion = async (req: Request, res: Response): Promise => { 5 | try { 6 | const versionInfo = await VersionService.getVersionInfo(); 7 | return res.status(200).json(versionInfo); 8 | } catch (err) { 9 | console.error(err); 10 | return res.status(500).json({ error: "Erro ao verificar versão do sistema" }); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /backend/src/routes/errorLogRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as ErrorLogController from "../controllers/ErrorLogController"; 4 | 5 | const errorLogRoutes = Router(); 6 | 7 | errorLogRoutes.post("/", ErrorLogController.store); 8 | errorLogRoutes.get("/", isAuth, ErrorLogController.index); 9 | errorLogRoutes.get("/:id", isAuth, ErrorLogController.show); 10 | errorLogRoutes.delete("/cleanup", isAuth, ErrorLogController.cleanupOldLogs); 11 | 12 | export default errorLogRoutes; 13 | -------------------------------------------------------------------------------- /backend/src/services/ApiTokenService/CreateApiTokenService.ts: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | import ApiToken from "../../models/ApiToken"; 3 | 4 | interface TokenData { 5 | name: string; 6 | permissions: string; 7 | } 8 | 9 | const CreateApiTokenService = async ({ name, permissions }: TokenData): Promise => { 10 | const token = await ApiToken.create({ 11 | name, 12 | token: uuidv4(), 13 | permissions 14 | }); 15 | 16 | return token; 17 | }; 18 | 19 | export default CreateApiTokenService; -------------------------------------------------------------------------------- /frontend/src/translate/i18n.js: -------------------------------------------------------------------------------- 1 | import i18n from "i18next"; 2 | import LanguageDetector from "i18next-browser-languagedetector"; 3 | import { initReactI18next } from "react-i18next"; 4 | import { messages } from "./languages"; 5 | 6 | i18n 7 | .use(LanguageDetector) 8 | .use(initReactI18next) 9 | .init({ 10 | debug: false, 11 | fallbackLng: "pt", 12 | defaultNS: "translations", 13 | ns: ["translations"], 14 | resources: messages, 15 | interpolation: { 16 | escapeValue: false, 17 | }, 18 | }); 19 | 20 | export default i18n; 21 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20250124201400-add-last-activity-to-user-sessions.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("UserSessions", "lastActivity", { 6 | type: DataTypes.DATE, 7 | allowNull: false, 8 | defaultValue: new Date() 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.removeColumn("UserSessions", "lastActivity"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/services/PersonalizationServices/DeletePersonalizationService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Personalization from "../../models/Personalization"; 3 | 4 | const deletePersonalization = async (theme: string): Promise => { 5 | const personalization = await Personalization.findOne({ where: { theme } }); 6 | 7 | if (!personalization) { 8 | throw new AppError("ERR_NO_PERSONALIZATION_FOUND", 404); 9 | } 10 | await personalization.destroy(); 11 | }; 12 | 13 | export default deletePersonalization; 14 | -------------------------------------------------------------------------------- /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, handleLogout } = useAuth(); 9 | 10 | return ( 11 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export { AuthContext, AuthProvider }; 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20241028230052-add-color-whatsapp.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface): Promise => { 5 | await queryInterface.addColumn("Whatsapps", "color", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: "#5C59A0" 9 | }); 10 | }, 11 | 12 | down: async (queryInterface: QueryInterface): Promise => { 13 | await queryInterface.removeColumn("Whatsapps", "color"); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/services/ErrorLogServices/DeleteOldErrorLogsService.ts: -------------------------------------------------------------------------------- 1 | import ErrorLog from "../../models/ErrorLog"; 2 | import { Op } from "sequelize"; 3 | 4 | const DeleteOldErrorLogsService = async (daysToKeep = 30): Promise => { 5 | const date = new Date(); 6 | date.setDate(date.getDate() - daysToKeep); 7 | 8 | const result = await ErrorLog.destroy({ 9 | where: { 10 | createdAt: { 11 | [Op.lt]: date 12 | } 13 | } 14 | }); 15 | 16 | return result; 17 | }; 18 | 19 | export default DeleteOldErrorLogsService; 20 | -------------------------------------------------------------------------------- /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, fetchWhatsApps } = useWhatsApps(); 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | }; 16 | 17 | export { WhatsAppsContext, WhatsAppsProvider }; 18 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20251020040000-update-null-type-to-wwebjs.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface) => { 5 | await queryInterface.sequelize.query( 6 | "UPDATE `Whatsapps` SET `type` = 'wwebjs' WHERE `type` IS NULL OR `type` = ''" 7 | ); 8 | }, 9 | 10 | down: async (queryInterface: QueryInterface) => { 11 | await queryInterface.sequelize.query( 12 | "UPDATE `Whatsapps` SET `type` = NULL WHERE `type` = 'wwebjs'" 13 | ); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /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 | ): Promise => { 7 | tickets.forEach(async t => { 8 | const ticketId = t.id.toString(); 9 | 10 | await UpdateTicketService({ 11 | ticketData: { status: "pending" }, 12 | ticketId 13 | }); 14 | }); 15 | }; 16 | 17 | export default UpdateDeletedUserOpenTicketsStatus; 18 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Press Ticket®", 3 | "name": "Press Ticket®", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } -------------------------------------------------------------------------------- /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/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/routes/apiTokenRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import * as ApiTokenController from "../controllers/ApiTokenController"; 3 | import isAuth from "../middleware/isAuth"; 4 | 5 | const apiTokenRoutes = Router(); 6 | 7 | apiTokenRoutes.get("/api-tokens", isAuth, ApiTokenController.index); 8 | apiTokenRoutes.get("/api-tokens/:id", isAuth, ApiTokenController.show); 9 | apiTokenRoutes.post("/api-tokens", isAuth, ApiTokenController.store); 10 | apiTokenRoutes.delete("/api-tokens/:id", isAuth, ApiTokenController.remove); 11 | 12 | export default apiTokenRoutes; -------------------------------------------------------------------------------- /backend/src/services/SettingServices/ListSettingsServiceOne.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Setting from "../../models/Setting"; 3 | 4 | interface Request { 5 | key: string; 6 | } 7 | 8 | const ListSettingsServiceOne = async ({ 9 | key 10 | }: Request): Promise => { 11 | const setting = await Setting.findOne({ 12 | where: { key } 13 | }); 14 | 15 | if (!setting) { 16 | throw new AppError("ERR_NO_SETTING_FOUND", 404); 17 | } 18 | 19 | return setting; 20 | }; 21 | 22 | export default ListSettingsServiceOne; -------------------------------------------------------------------------------- /backend/src/routes/systemCleanupRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as SystemCleanupController from "../controllers/SystemCleanupController"; 4 | 5 | const systemCleanupRoutes = express.Router(); 6 | 7 | systemCleanupRoutes.get("/system-cleanup", isAuth, SystemCleanupController.index); 8 | systemCleanupRoutes.post("/system-cleanup", isAuth, SystemCleanupController.store); 9 | systemCleanupRoutes.post("/system-cleanup/execute", isAuth, SystemCleanupController.execute); 10 | 11 | export default systemCleanupRoutes; 12 | -------------------------------------------------------------------------------- /backend/src/services/WhatsappService/ListWhatsAppsService.ts: -------------------------------------------------------------------------------- 1 | import Queue from "../../models/Queue"; 2 | import Whatsapp from "../../models/Whatsapp"; 3 | 4 | const ListWhatsAppsService = async (): Promise => { 5 | const whatsapps = await Whatsapp.findAll({ 6 | include: [ 7 | { 8 | model: Queue, 9 | as: "queues", 10 | attributes: ["id", "name", "color", "greetingMessage", "startWork", "endWork", "absenceMessage"] 11 | } 12 | ] 13 | }); 14 | 15 | return whatsapps; 16 | }; 17 | 18 | export default ListWhatsAppsService; 19 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20250212094700-alter-profilePicUrl-length.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.changeColumn("Contacts", "profilePicUrl", { 6 | type: DataTypes.STRING(500), 7 | allowNull: true, 8 | }); 9 | }, 10 | 11 | down: (queryInterface: QueryInterface) => { 12 | return queryInterface.changeColumn("Contacts", "profilePicUrl", { 13 | type: DataTypes.STRING, 14 | allowNull: true, 15 | }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /backend/src/controllers/SystemHealthController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getSystemHealth } from "../services/SystemHealthService"; 3 | import { logger } from "../utils/logger"; 4 | 5 | export const index = async (req: Request, res: Response): Promise => { 6 | try { 7 | const systemHealth = await getSystemHealth(); 8 | return res.status(200).json(systemHealth); 9 | } catch (err: any) { 10 | logger.error(`Erro ao obter saúde do sistema: ${err.message}`); 11 | return res.status(500).json({ error: err.message }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /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/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/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/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/controllers/NetworkMonitorController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getNetworkStatus } from "../services/NetworkMonitorService"; 3 | import { logger } from "../utils/logger"; 4 | 5 | export const index = async (req: Request, res: Response): Promise => { 6 | try { 7 | const networkStatus = await getNetworkStatus(); 8 | return res.status(200).json(networkStatus); 9 | } catch (err: any) { 10 | logger.error(`Erro ao obter status da rede: ${err.message}`); 11 | return res.status(500).json({ error: err.message }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /backend/src/routes/videoRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as VideoController from "../controllers/VideoController"; 4 | 5 | const videoRoutes = Router(); 6 | 7 | videoRoutes.get("/videos", isAuth, VideoController.index); 8 | videoRoutes.get("/videos/:id", isAuth, VideoController.show); 9 | videoRoutes.post("/videos", isAuth, VideoController.store); 10 | videoRoutes.put("/videos/:id", isAuth, VideoController.update); 11 | videoRoutes.delete("/videos/:id", isAuth, VideoController.remove); 12 | 13 | export default videoRoutes; 14 | -------------------------------------------------------------------------------- /frontend/src/components/FormikTextField/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TextField } from "@mui/material"; 3 | import { Field } from "formik"; 4 | 5 | const FormikTextField = ({ 6 | name, 7 | values, 8 | touched, 9 | errors, 10 | isSubmitting, 11 | loading, 12 | ...rest 13 | }) => { 14 | return ( 15 | 22 | ); 23 | }; 24 | 25 | export default FormikTextField; 26 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20220223095932-add-whatsapp-to-user.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface, DataTypes } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.addColumn("Users", "whatsappId", { 6 | type: DataTypes.INTEGER, 7 | references: { model: "Whatsapps", key: "id" }, 8 | onUpdate: "CASCADE", 9 | onDelete: "SET NULL", 10 | allowNull: true 11 | },); 12 | }, 13 | 14 | down: (queryInterface: QueryInterface) => { 15 | return queryInterface.removeColumn("Users", "whatsappId"); 16 | } 17 | }; -------------------------------------------------------------------------------- /backend/src/database/seeds/20230326221400-create-ASC-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "ASC", 10 | value: "disabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/models/UserWhatsapp.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Column, 3 | CreatedAt, 4 | ForeignKey, 5 | Model, 6 | Table, 7 | UpdatedAt 8 | } from "sequelize-typescript"; 9 | import User from "./User"; 10 | import Whatsapp from "./Whatsapp"; 11 | 12 | @Table 13 | class UserWhatsapp extends Model { 14 | @ForeignKey(() => User) 15 | @Column 16 | userId: number; 17 | 18 | @ForeignKey(() => Whatsapp) 19 | @Column 20 | whatsappId: number; 21 | 22 | @CreatedAt 23 | createdAt: Date; 24 | 25 | @UpdatedAt 26 | updatedAt: Date; 27 | } 28 | 29 | export default UserWhatsapp; 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /frontend/src/routes/PublicRoute.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { Navigate, Outlet } from "react-router-dom"; 3 | import { AuthContext } from "../context/Auth/AuthContext"; 4 | import BackdropLoading from "../components/BackdropLoading"; 5 | 6 | const PublicRoute = ({ element }) => { 7 | const { isAuth, loading } = useContext(AuthContext); 8 | 9 | if (loading) { 10 | return ; 11 | } 12 | 13 | if (isAuth) { 14 | return ; 15 | } 16 | 17 | return element || ; 18 | }; 19 | 20 | export default PublicRoute; 21 | -------------------------------------------------------------------------------- /backend/src/config/database.ts: -------------------------------------------------------------------------------- 1 | require("../bootstrap"); 2 | 3 | module.exports = { 4 | define: { 5 | charset: "utf8mb4", 6 | collate: "utf8mb4_general_ci" 7 | }, 8 | dialect: process.env.DB_DIALECT || "mysql", 9 | timezone: process.env.DB_TIMEZONE || "-03:00", 10 | host: process.env.DB_HOST || "localhost", 11 | database: process.env.DB_NAME || "press-ticket", 12 | username: process.env.DB_USER || "root", 13 | password: process.env.DB_PASS, 14 | port: process.env.DB_PORT || 3306, 15 | logging: false, 16 | seederStorage: "json", 17 | seederStoragePath: "sequelizeData.json" 18 | }; 19 | -------------------------------------------------------------------------------- /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/20250301175500-add-userId-to-messages.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface) => { 5 | await queryInterface.addColumn("Messages", "userId", { 6 | type: DataTypes.INTEGER, 7 | allowNull: true, 8 | references: { model: "Users", key: "id" }, 9 | onUpdate: "CASCADE", 10 | onDelete: "SET NULL" 11 | }); 12 | }, 13 | 14 | down: async (queryInterface: QueryInterface) => { 15 | await queryInterface.removeColumn("Messages", "userId"); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20230326221600-create-created-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "created", 10 | value: "disabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20230507221800-create-n8n-integrations.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Integrations", 7 | [ 8 | { 9 | key: "urlApiN8N", 10 | value: "", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Integrations", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20241014161200-create-apiMaps-integrations.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Integrations", 7 | [ 8 | { 9 | key: "apiMaps", 10 | value: "", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Integrations", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20230130004700-create-alltickets-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "allTicket", 10 | value: "disabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20240924161629-create-hubToken-integrations.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Integrations", 7 | [ 8 | { 9 | key: "hubToken", 10 | value: "", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Integrations", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20241008203900-create-quickAnswer-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "quickAnswer", 10 | value: "disabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20241228083100-create-opentickets-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "openTickets", 10 | value: "disabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20250124202200-create-signOption-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "signOption", 10 | value: "enabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20250210085100-create-listItemSpy-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "listItemSpy", 10 | value: "enabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20250308170200-create-queueLength-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.bulkInsert( 6 | "Settings", 7 | [ 8 | { 9 | key: "queueLength", 10 | value: "disabled", 11 | createdAt: new Date(), 12 | updatedAt: new Date() 13 | } 14 | ], 15 | {} 16 | ); 17 | }, 18 | 19 | down: (queryInterface: QueryInterface) => { 20 | return queryInterface.bulkDelete("Settings", {}); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /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.post("/users", isAuth, UserController.store); 11 | 12 | userRoutes.put("/users/:userId", isAuth, UserController.update); 13 | 14 | userRoutes.get("/users/:userId", isAuth, UserController.show); 15 | 16 | userRoutes.delete("/users/:userId", isAuth, UserController.remove); 17 | 18 | export default userRoutes; 19 | -------------------------------------------------------------------------------- /frontend/src/routes/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { Navigate, Outlet } from "react-router-dom"; 3 | import { AuthContext } from "../context/Auth/AuthContext"; 4 | import BackdropLoading from "../components/BackdropLoading"; 5 | 6 | const PrivateRoute = ({ element }) => { 7 | const { isAuth, loading } = useContext(AuthContext); 8 | 9 | if (loading) { 10 | return ; 11 | } 12 | 13 | if (!isAuth) { 14 | return ; 15 | } 16 | 17 | return element || ; 18 | }; 19 | 20 | export default PrivateRoute; 21 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20241230140500-remove-whatsapp-to-user.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface) => { 5 | await queryInterface.removeColumn("Users", "whatsappId"); 6 | }, 7 | 8 | down: async (queryInterface: QueryInterface) => { 9 | await queryInterface.addColumn("Users", "whatsappId", { 10 | type: DataTypes.INTEGER, 11 | allowNull: true, 12 | references: { model: "Whatsapps", key: "id" }, 13 | onUpdate: "CASCADE", 14 | onDelete: "CASCADE", 15 | }); 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /backend/src/controllers/QueueMonitorController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { getQueueMonitorData } from "../services/QueueMonitorService"; 3 | import { logger } from "../utils/logger"; 4 | 5 | export const index = async (req: Request, res: Response): Promise => { 6 | try { 7 | const queueMonitorData = await getQueueMonitorData(); 8 | return res.status(200).json(queueMonitorData); 9 | } catch (err: any) { 10 | logger.error(`Erro ao obter dados de monitoramento de setores: ${err.message}`); 11 | return res.status(500).json({ error: err.message }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20240924161124-change-column-number-to-allownull.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: (queryInterface: QueryInterface) => { 5 | return queryInterface.changeColumn("Contacts", "number", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | unique: true 9 | }); 10 | }, 11 | 12 | down: (queryInterface: QueryInterface) => { 13 | return queryInterface.changeColumn("Contacts", "number", { 14 | type: DataTypes.STRING, 15 | allowNull: false, 16 | unique: true 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /backend/src/models/ContactTag.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Table, 3 | Column, 4 | CreatedAt, 5 | UpdatedAt, 6 | Model, 7 | ForeignKey 8 | } from "sequelize-typescript"; 9 | import Tag from "./Tag"; 10 | import Contact from "./Contact"; 11 | 12 | @Table({ 13 | tableName: "ContactTags" 14 | }) 15 | class ContactTag extends Model { 16 | @ForeignKey(() => Contact) 17 | @Column 18 | contactId: number; 19 | 20 | @ForeignKey(() => Tag) 21 | @Column 22 | tagId: number; 23 | 24 | @CreatedAt 25 | createdAt: Date; 26 | 27 | @UpdatedAt 28 | updatedAt: Date; 29 | } 30 | 31 | export default ContactTag; 32 | -------------------------------------------------------------------------------- /backend/src/controllers/UserMonitorController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import UserMonitorService from "../services/UserServices/UserMonitorService"; 3 | 4 | export const index = async (req: Request, res: Response): Promise => { 5 | try { 6 | const { userId } = req.query; 7 | 8 | const data = await UserMonitorService(userId ? parseInt(userId as string) : undefined); 9 | 10 | return res.status(200).json(data); 11 | } catch (error: any) { 12 | console.error("[USER_MONITOR_CONTROLLER] Erro:", error); 13 | return res.status(500).json({ error: error.message }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/routes/queueRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | 4 | import * as QueueController from "../controllers/QueueController"; 5 | 6 | const queueRoutes = Router(); 7 | 8 | queueRoutes.get("/queue", isAuth, QueueController.index); 9 | 10 | queueRoutes.post("/queue", isAuth, QueueController.store); 11 | 12 | queueRoutes.get("/queue/:queueId", isAuth, QueueController.show); 13 | 14 | queueRoutes.put("/queue/:queueId", isAuth, QueueController.update); 15 | 16 | queueRoutes.delete("/queue/:queueId", isAuth, QueueController.remove); 17 | 18 | export default queueRoutes; 19 | -------------------------------------------------------------------------------- /backend/src/routes/backupRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as BackupController from "../controllers/BackupController"; 4 | 5 | const backupRoutes = express.Router(); 6 | 7 | backupRoutes.get("/backups", isAuth, BackupController.index); 8 | backupRoutes.post("/backups", isAuth, BackupController.store); 9 | backupRoutes.get("/backups/:filename", isAuth, BackupController.show); 10 | backupRoutes.post("/backups/:filename/restore", isAuth, BackupController.update); 11 | backupRoutes.delete("/backups/:filename", isAuth, BackupController.remove); 12 | 13 | export default backupRoutes; 14 | -------------------------------------------------------------------------------- /backend/src/routes/hubMessageRoutes.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import multer from "multer"; 3 | import uploadConfig from "../config/upload"; 4 | 5 | import * as MessageController from "../controllers/MessageHubController"; 6 | import isAuth from "../middleware/isAuth"; 7 | 8 | const hubMessageRoutes = express.Router(); 9 | const upload = multer(uploadConfig); 10 | 11 | hubMessageRoutes.post( 12 | "/hub-message/:ticketId", 13 | isAuth, 14 | upload.array("medias"), 15 | MessageController.send 16 | ); 17 | 18 | hubMessageRoutes.post("/hub-ticket", isAuth, MessageController.store); 19 | 20 | export default hubMessageRoutes; 21 | -------------------------------------------------------------------------------- /backend/src/services/SettingServices/ListSettingByValueService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Setting from "../../models/Setting"; 3 | 4 | interface Response { 5 | key: string; 6 | value: string; 7 | } 8 | const ListSettingByKeyService = async ( 9 | value: string 10 | ): Promise => { 11 | const settings = await Setting.findOne({ 12 | where: { value } 13 | }); 14 | 15 | if (!settings) { 16 | throw new AppError("ERR_NO_API_TOKEN_FOUND", 404); 17 | } 18 | 19 | return { key: settings.key, value: settings.value }; 20 | }; 21 | 22 | export default ListSettingByKeyService; 23 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20250310102500-add-active-to-users.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | module.exports = { 4 | up: async (queryInterface: QueryInterface) => { 5 | await queryInterface.addColumn("Users", "active", { 6 | type: DataTypes.BOOLEAN, 7 | defaultValue: true, 8 | allowNull: false 9 | }); 10 | 11 | await queryInterface.bulkUpdate( 12 | "Users", 13 | { active: true }, 14 | { active: null } 15 | ); 16 | }, 17 | 18 | down: async (queryInterface: QueryInterface) => { 19 | await queryInterface.removeColumn("Users", "active"); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /backend/src/database/seeds/20200904070006-create-apiToken-settings.ts: -------------------------------------------------------------------------------- 1 | import { QueryInterface } from "sequelize"; 2 | import { v4 as uuidv4 } from "uuid"; 3 | 4 | module.exports = { 5 | up: (queryInterface: QueryInterface) => { 6 | return queryInterface.bulkInsert( 7 | "Settings", 8 | [ 9 | { 10 | key: "userApiToken", 11 | value: uuidv4(), 12 | createdAt: new Date(), 13 | updatedAt: new Date() 14 | } 15 | ], 16 | {} 17 | ); 18 | }, 19 | 20 | down: (queryInterface: QueryInterface) => { 21 | return queryInterface.bulkDelete("Settings", {}); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /backend/src/services/SettingServices/UpdateSettingService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Setting from "../../models/Setting"; 3 | 4 | interface Request { 5 | key: string; 6 | value: string; 7 | } 8 | 9 | const UpdateSettingService = async ({ 10 | key, 11 | value 12 | }: Request): Promise => { 13 | const setting = await Setting.findOne({ 14 | where: { key } 15 | }); 16 | 17 | if (!setting) { 18 | throw new AppError("ERR_NO_SETTING_FOUND", 404); 19 | } 20 | 21 | await setting.update({ value }); 22 | 23 | return setting; 24 | }; 25 | 26 | export default UpdateSettingService; 27 | -------------------------------------------------------------------------------- /backend/src/database/migrations/20240926143622-alter-contact-email-nullable.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes, QueryInterface } from "sequelize"; 2 | 3 | export default { 4 | up: async (queryInterface: QueryInterface): Promise => { 5 | await queryInterface.changeColumn("Contacts", "email", { 6 | type: DataTypes.STRING, 7 | allowNull: true, 8 | defaultValue: "" 9 | }); 10 | }, 11 | 12 | down: async (queryInterface: QueryInterface): Promise => { 13 | await queryInterface.changeColumn("Contacts", "email", { 14 | type: DataTypes.STRING, 15 | allowNull: false, 16 | defaultValue: "" 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /backend/src/routes/whatsappNotificationRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import isAuth from "../middleware/isAuth"; 3 | import * as WhatsappNotificationController from "../controllers/WhatsappNotificationController"; 4 | 5 | const whatsappNotificationRoutes = Router(); 6 | 7 | whatsappNotificationRoutes.post( 8 | "/whatsapp-notification/:whatsappId", 9 | isAuth, 10 | WhatsappNotificationController.notifyChannel 11 | ); 12 | 13 | whatsappNotificationRoutes.post( 14 | "/whatsapp-notification/check/all", 15 | isAuth, 16 | WhatsappNotificationController.notifyAllDisconnected 17 | ); 18 | 19 | export default whatsappNotificationRoutes; 20 | -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "strict": true, 7 | "useUnknownInCatchVariables": false, 8 | "strictPropertyInitialization": false, 9 | "esModuleInterop": true, 10 | "experimentalDecorators": true, 11 | "emitDecoratorMetadata": true, 12 | "skipLibCheck": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "resolveJsonModule": true, 15 | "removeComments": false 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "src/__tests__/**/*" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/components/MainHeaderButtonsWrapper/index.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | import React from "react"; 3 | 4 | const HeaderButtonsWrapperDiv = styled('div')(({ theme }) => ({ 5 | display: "flex", 6 | marginLeft: "auto", 7 | alignItems: "center", 8 | justifyContent: "center", 9 | gap: theme.spacing(2), 10 | "& > *": { 11 | margin: 0, 12 | }, 13 | [theme.breakpoints.down('sm')]: { 14 | gap: theme.spacing(1), 15 | } 16 | })); 17 | 18 | const HeaderButtonsWrapper = ({ children }) => { 19 | return {children}; 20 | }; 21 | 22 | export default HeaderButtonsWrapper; 23 | -------------------------------------------------------------------------------- /frontend/src/context/EditingMessage/EditingMessageContext.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | import React, { createContext, useState } from "react"; 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 | EditMessageProvider.propTypes = { 19 | children: PropTypes.array 20 | } 21 | 22 | export { EditMessageContext, EditMessageProvider }; 23 | -------------------------------------------------------------------------------- /backend/src/models/QuickAnswer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Table, 3 | Column, 4 | DataType, 5 | CreatedAt, 6 | UpdatedAt, 7 | Model, 8 | PrimaryKey, 9 | AutoIncrement 10 | } from "sequelize-typescript"; 11 | 12 | @Table 13 | class QuickAnswer extends Model { 14 | @PrimaryKey 15 | @AutoIncrement 16 | @Column 17 | id: number; 18 | 19 | @Column(DataType.TEXT) 20 | shortcut: string; 21 | 22 | @Column(DataType.TEXT) 23 | message: string; 24 | 25 | @Column(DataType.STRING) 26 | mediaPath: string; 27 | 28 | @CreatedAt 29 | createdAt: Date; 30 | 31 | @UpdatedAt 32 | updatedAt: Date; 33 | } 34 | 35 | export default QuickAnswer; 36 | -------------------------------------------------------------------------------- /backend/src/routes/fileManagerRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import * as FileManagerController from '../controllers/FileManagerController'; 3 | import isAuth from '../middleware/isAuth'; 4 | 5 | const fileManagerRoutes = Router(); 6 | 7 | fileManagerRoutes.get('/file-manager/stats', isAuth, FileManagerController.getPublicFolderStats); 8 | fileManagerRoutes.post('/file-manager/delete', isAuth, FileManagerController.deleteFiles); 9 | fileManagerRoutes.get('/file-manager/download', isAuth, FileManagerController.downloadFile); 10 | fileManagerRoutes.get('/file-manager/view', isAuth, FileManagerController.viewFile); 11 | 12 | export default fileManagerRoutes; 13 | -------------------------------------------------------------------------------- /backend/src/controllers/MemoryUsageController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { logger } from "../utils/logger"; 3 | import { getMemoryUsageInfo } from "../services/MemoryUsageService"; 4 | 5 | export const getMemoryUsage = async ( 6 | req: Request, 7 | res: Response 8 | ): Promise => { 9 | try { 10 | const memoryInfo = await getMemoryUsageInfo(); 11 | return res.status(200).json(memoryInfo); 12 | } catch (error) { 13 | logger.error(`Erro ao obter informações de uso de memória: ${error}`); 14 | return res.status(500).json({ 15 | error: "Erro ao obter informações de uso de memória" 16 | }); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/CustomTooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CustomTooltip = ({ payload, label, active }) => { 4 | if (active && payload && payload.length) { 5 | return ( 6 |
7 |
{label}
8 |
{`${payload[0].name}: ${payload[0].value}`}
9 |
10 | ); 11 | } 12 | return null; 13 | }; 14 | 15 | export default CustomTooltip; 16 | -------------------------------------------------------------------------------- /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/services/HubServices/ListHubChannels.ts: -------------------------------------------------------------------------------- 1 | import { showHubToken } from "../../helpers/showHubToken"; 2 | 3 | const { Client } = require("notificamehubsdk"); 4 | require("dotenv").config(); 5 | 6 | const ListChannels = async () => { 7 | try { 8 | const notificameHubToken = await showHubToken(); 9 | 10 | if (!notificameHubToken) { 11 | throw new Error("NOTIFICAMEHUB_TOKEN_NOT_FOUND"); 12 | } 13 | 14 | const client = new Client(notificameHubToken); 15 | 16 | const response = await client.listChannels(); 17 | return response; 18 | } catch (error) { 19 | throw new Error("Error"); 20 | } 21 | }; 22 | 23 | export default ListChannels; 24 | -------------------------------------------------------------------------------- /backend/src/controllers/DatabaseMonitorController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { logger } from "../utils/logger"; 3 | import { getDatabaseInfo } from "../services/DatabaseMonitorService"; 4 | 5 | export const getDatabaseStatus = async ( 6 | req: Request, 7 | res: Response 8 | ): Promise => { 9 | try { 10 | const databaseInfo = await getDatabaseInfo(); 11 | return res.status(200).json(databaseInfo); 12 | } catch (error) { 13 | logger.error(`Erro ao obter informações do banco de dados: ${error}`); 14 | return res.status(500).json({ 15 | error: "Erro ao obter informações do banco de dados" 16 | }); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /backend/src/services/ContactServices/ShowContactService.ts: -------------------------------------------------------------------------------- 1 | import Contact from "../../models/Contact"; 2 | import AppError from "../../errors/AppError"; 3 | import Tag from "../../models/Tag"; 4 | 5 | const ShowContactService = async (id: string | number): Promise => { 6 | const contact = await Contact.findByPk(id, { 7 | include: [ 8 | "extraInfo", 9 | { 10 | model: Tag, 11 | as: "tags", 12 | attributes: ["id", "name", "color"] 13 | } 14 | ] 15 | }); 16 | 17 | if (!contact) { 18 | throw new AppError("ERR_NO_CONTACT_FOUND", 404); 19 | } 20 | 21 | return contact; 22 | }; 23 | 24 | export default ShowContactService; 25 | -------------------------------------------------------------------------------- /backend/src/services/VideoServices/ShowVideoService.ts: -------------------------------------------------------------------------------- 1 | import AppError from "../../errors/AppError"; 2 | import Video from "../../models/Video"; 3 | import User from "../../models/User"; 4 | 5 | interface Request { 6 | id: string; 7 | } 8 | 9 | const ShowVideoService = async ({ id }: Request): Promise