From 8be45a3702bea84bd90dc1d2a9d0e8d7050ab3a9 Mon Sep 17 00:00:00 2001 From: Robson Tenorio Henriques Date: Mon, 23 Sep 2024 23:27:30 -0300 Subject: [PATCH] Implementando gerador de CodeSnippet --- backend/.eslintrc.json | 12 +- ...934-alter-userId-foreign-key-on-tickets.ts | 33 +++++ ...22-alter-queueId-foreign-key-on-tickets.ts | 43 ++++++ backend/src/models/Ticket.ts | 24 ++-- .../WbotServices/wbotMessageListener.ts | 4 +- .../CodeSnippetGenerator/codeSnippets.js | 118 ++++++++++++++++ .../components/CodeSnippetGenerator/index.js | 131 ++++++++++++++++++ frontend/src/pages/Api/index.js | 19 ++- 8 files changed, 368 insertions(+), 16 deletions(-) create mode 100644 backend/src/database/migrations/20240921112934-alter-userId-foreign-key-on-tickets.ts create mode 100644 backend/src/database/migrations/20240921153422-alter-queueId-foreign-key-on-tickets.ts create mode 100644 frontend/src/components/CodeSnippetGenerator/codeSnippets.js create mode 100644 frontend/src/components/CodeSnippetGenerator/index.js diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index aa015faa..84a92ece 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -15,13 +15,19 @@ "ecmaVersion": 12, "sourceType": "module" }, - "plugins": ["@typescript-eslint", "prettier"], + "plugins": [ + "@typescript-eslint", + "prettier" + ], "rules": { "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-unused-vars": [ "error", - { "argsIgnorePattern": "_" } + { + "argsIgnorePattern": "_" + } ], + "prefer-const": "off", "import/prefer-default-export": "off", "no-console": "off", "no-param-reassign": "off", @@ -46,4 +52,4 @@ "typescript": {} } } -} +} \ No newline at end of file diff --git a/backend/src/database/migrations/20240921112934-alter-userId-foreign-key-on-tickets.ts b/backend/src/database/migrations/20240921112934-alter-userId-foreign-key-on-tickets.ts new file mode 100644 index 00000000..32fcdfa2 --- /dev/null +++ b/backend/src/database/migrations/20240921112934-alter-userId-foreign-key-on-tickets.ts @@ -0,0 +1,33 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: async (queryInterface: QueryInterface) => { + await queryInterface.removeConstraint("tickets", "tickets_ibfk_2"); + + await queryInterface.addConstraint("tickets", ["userId"], { + type: "foreign key", + name: "tickets_ibfk_2", + references: { + table: "users", + field: "id" + }, + onDelete: "CASCADE", + onUpdate: "CASCADE" + }); + }, + + down: async (queryInterface: QueryInterface) => { + await queryInterface.removeConstraint("tickets", "tickets_ibfk_2"); + + await queryInterface.addConstraint("tickets", ["userId"], { + type: "foreign key", + name: "tickets_ibfk_2", + references: { + table: "users", + field: "id" + }, + onDelete: "SET NULL", + onUpdate: "CASCADE" + }); + } +}; diff --git a/backend/src/database/migrations/20240921153422-alter-queueId-foreign-key-on-tickets.ts b/backend/src/database/migrations/20240921153422-alter-queueId-foreign-key-on-tickets.ts new file mode 100644 index 00000000..a9c03112 --- /dev/null +++ b/backend/src/database/migrations/20240921153422-alter-queueId-foreign-key-on-tickets.ts @@ -0,0 +1,43 @@ +import { QueryInterface } from "sequelize"; + +export default { + up: async (queryInterface: QueryInterface) => { + // Remover a constraint antiga + await queryInterface.removeConstraint( + "tickets", + "Tickets_queueId_foreign_idx" + ); + + // Adicionar a nova constraint com SET NULL e CASCADE + await queryInterface.addConstraint("tickets", ["queueId"], { + type: "foreign key", + name: "Tickets_queueId_foreign_idx", + references: { + table: "queues", + field: "id" + }, + onDelete: "SET NULL", // Ao deletar, coloca NULL no campo queueId + onUpdate: "CASCADE" // Ao atualizar o id na tabela queues, atualiza o queueId em tickets + }); + }, + + down: async (queryInterface: QueryInterface) => { + // Remover a constraint recém adicionada + await queryInterface.removeConstraint( + "tickets", + "Tickets_queueId_foreign_idx" + ); + + // Restaurar a constraint antiga + await queryInterface.addConstraint("tickets", ["queueId"], { + type: "foreign key", + name: "Tickets_queueId_foreign_idx", + references: { + table: "queues", + field: "id" + }, + onDelete: "RESTRICT", + onUpdate: "CASCADE" + }); + } +}; diff --git a/backend/src/models/Ticket.ts b/backend/src/models/Ticket.ts index 8de43756..0ee6fcd6 100644 --- a/backend/src/models/Ticket.ts +++ b/backend/src/models/Ticket.ts @@ -1,15 +1,15 @@ import { - Table, + AutoIncrement, + BelongsTo, Column, CreatedAt, - UpdatedAt, - Model, - PrimaryKey, + Default, ForeignKey, - BelongsTo, HasMany, - AutoIncrement, - Default + Model, + PrimaryKey, + Table, + UpdatedAt } from "sequelize-typescript"; import Contact from "./Contact"; @@ -48,7 +48,10 @@ class Ticket extends Model { @Column userId: number; - @BelongsTo(() => User) + @BelongsTo(() => User, { + onDelete: "CASCADE", + onUpdate: "CASCADE" + }) user: User; @ForeignKey(() => Contact) @@ -69,7 +72,10 @@ class Ticket extends Model { @Column queueId: number; - @BelongsTo(() => Queue) + @BelongsTo(() => Queue, { + onDelete: "SET NULL", + onUpdate: "CASCADE" + }) queue: Queue; @HasMany(() => Message) diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index ecbee770..cc8e93f7 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -428,8 +428,8 @@ const handleMessage = async ( try { let msgContact: WbotContact; let groupContact: Contact | undefined; - const userId = 0; - const queueId = 0; + let userId; + let queueId; if (msg.fromMe) { // messages sent automatically by wbot have a special character in front of it diff --git a/frontend/src/components/CodeSnippetGenerator/codeSnippets.js b/frontend/src/components/CodeSnippetGenerator/codeSnippets.js new file mode 100644 index 00000000..415ea0e5 --- /dev/null +++ b/frontend/src/components/CodeSnippetGenerator/codeSnippets.js @@ -0,0 +1,118 @@ +const codeSnippets = { + HTTP: (number, body, userId, queueId, whatsappId, token) => `POST ${process.env.REACT_APP_BACKEND_URL}/api/messages/send HTTP/1.1 +User-Agent: vscode-restclient +Authorization: Bearer ${token} +Content-Type: application/json +Host: localhost:4000 +Content-Length: ${85 + body.length} + +{ + "number": "${number}", + "body": "${body}", + "userId": "${userId}", + "queueId": "${queueId}", + "whatsappId": "${whatsappId}" + }`, + JavaScript_JQuery: (number, body, userId, queueId, whatsappId, token) => `const settings = { + "async": true, + "crossDomain": true, + "url": "${process.env.REACT_APP_BACKEND_URL}/api/messages/send}", + "method": "POST", + "headers": { + "user-agent": "vscode-restclient", + "authorization": "Bearer ${token}", + "content-type": "application/json" + }, + "processData": false, + "data": "{\"number\": \"${number}\",\"body\": \"${body}\",\"userId\": \"${userId}\",\"queueId\": \"${queueId}\",\"whatsappId\": \"${whatsappId}\"}" +}; + +$.ajax(settings).done(function (response) { + console.log(response); +}); + `, + JavaScript_fetch: (number, body, userId, queueId, whatsappId, token) => ` + fetch("${process.env.REACT_APP_BACKEND_URL}/api/messages/send", { + "method": "POST", + "headers": { + "user-agent": "vscode-restclient", + "Content-Type": "application/json", + "Authorization": "Bearer ${token}", + }, + "body": { + number: "${number}", + body: '${body}', + userId: '${userId}', + queueId: '${queueId}', + whatsAppId: '${whatsappId}' + }, + }) + .then(response => { + console.log(response); +}) +.catch(err => { + console.error(err); +}); + `, + NODEjs_Request: (number, body, userId, queueId, whatsappId, token) => ` + const request = require('request'); + const options = { + method: 'POST', + url: '${process.env.REACT_APP_BACKEND_URL}/api/messages/send', + headers: { + 'user-agent': 'vscode-restclient', + 'Authorization': 'Bearer ${token}', + 'Content-Type': 'application/json', + }, + body: { + number: '${number}', + body: '${body}', + userId: '${userId}', + queueId: '${queueId}', + whatsAppId: '${whatsappId}', + }, + json: true + }; + + request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log('Response:', body); + });`, + PHP_cURL: (number, body, userId, queueId, whatsappId, token) => ` + "4000", + CURLOPT_URL => "${process.env.REACT_APP_BACKEND_URL}/api/messages/send", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 30, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => "{\"number\": \"${number}\",\"body\": \"${body}\",\"userId\": \"${userId}\",\"queueId\": \"${queueId}\",\"whatsappId\": \"${whatsappId}\"}", + CURLOPT_HTTPHEADER => [ + 'Authorization': 'Bearer ${token}', + "content-type: application/json", + "user-agent: vscode-restclient" + ], +]); + +$response = curl_exec($curl); +$err = curl_error($curl); + +curl_close($curl); + +if ($err) { + echo "cURL Error #:" . $err; +} else { + echo $response; +} + + `, +}; + +export default codeSnippets; \ No newline at end of file diff --git a/frontend/src/components/CodeSnippetGenerator/index.js b/frontend/src/components/CodeSnippetGenerator/index.js new file mode 100644 index 00000000..bb16204c --- /dev/null +++ b/frontend/src/components/CodeSnippetGenerator/index.js @@ -0,0 +1,131 @@ +import { + Button, + FormControl, + IconButton, + InputLabel, + MenuItem, + Modal, + Select, + TextField +} from "@material-ui/core"; +import { makeStyles } from "@material-ui/core/styles"; +import CloseIcon from "@material-ui/icons/Close"; +import React, { useState } from "react"; +import codeSnippets from './codeSnippets.js'; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + flexDirection: "column", + }, + modalContent: { + padding: theme.spacing(4), + maxWidth: 600, + backgroundColor: theme.palette.background.default, + borderRadius: "8px", + boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)", + outline: "none", + position: "relative", + }, + selectContainer: { + marginBottom: theme.spacing(2), + }, + snippetBox: { + marginTop: theme.spacing(2), + width: "100%", + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(2), + borderRadius: "4px", + overflowX: "auto", + fontFamily: "monospace", + maxHeight: "400px", + overflowY: "scroll", + }, + closeButton: { + position: "absolute", + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.secondary.main, + }, +})); + +const CodeSnippetGenerator = ({ number, body, userId, queueId, whatsappId, token }) => { + const classes = useStyles(); + const [selectedLanguage, setSelectedLanguage] = useState(""); + const [open, setOpen] = useState(false); + const [snippet, setSnippet] = useState(""); + + const handleOpen = () => { + const generateSnippet = codeSnippets[selectedLanguage]; + if (generateSnippet) { + setSnippet(generateSnippet(number, body, userId, queueId, whatsappId, token)); + setOpen(true); + } + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleChange = (e) => { + setSelectedLanguage(e.target.value); + }; + + return ( +
+ + Selecione uma linguagem (Apenas para envio de texto) + + + + + + +
+ {/* Botão de fechar */} + + + + +

Snippet de código para {selectedLanguage}

+ + +
+
+
+ ); +}; + +export default CodeSnippetGenerator; diff --git a/frontend/src/pages/Api/index.js b/frontend/src/pages/Api/index.js index e328fee5..daf763a1 100644 --- a/frontend/src/pages/Api/index.js +++ b/frontend/src/pages/Api/index.js @@ -6,6 +6,7 @@ import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import axios from "axios"; import React, { useEffect, useState } from "react"; +import CodeSnippetGenerator from "../../components/CodeSnippetGenerator"; // Import do componente import toastError from "../../errors/toastError"; import api from "../../services/api"; @@ -53,6 +54,11 @@ const useStyles = makeStyles(theme => ({ textP: { marginBottom: theme.spacing(2), }, + observacao: { + marginBottom: theme.spacing(2), + color: theme.palette.text.secondary, + fontSize: '0.9rem', + } })); const Api = () => { @@ -201,6 +207,9 @@ const Api = () => {

Envie Mensagens de Texto ou Mídia

+

+ Observação: Neste formulário, o token é puxado automaticamente. Para utilizar em outros locais, é necessário ter o token. +

{ ))} - { ))} - { ENVIAR MENSAGEM + 0 && getSettingValue("userApiToken")} + />