Adicionar arquivos de projeto.
9
backend/.editorconfig
Normal file
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
35
backend/.env.example
Normal file
@ -0,0 +1,35 @@
|
||||
NODE_ENV=
|
||||
BACKEND_URL=http://localhost
|
||||
FRONTEND_URL=http://localhost:3000
|
||||
PROXY_PORT=8080
|
||||
PORT=8080
|
||||
|
||||
DB_DIALECT=postgres
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_USER=user
|
||||
DB_PASS=senha
|
||||
DB_NAME=db_name
|
||||
|
||||
JWT_SECRET=kZaOTd+YZpjRUyyuQUpigJaEMk4vcW4YOymKPZX0Ts8=
|
||||
JWT_REFRESH_SECRET=dBSXqFg9TaNUEDXVp6fhMTRLBysP+j2DSqf7+raxD3A=
|
||||
|
||||
REDIS_URI=redis://:123456@127.0.0.1:6379
|
||||
REDIS_OPT_LIMITER_MAX=1
|
||||
REDIS_OPT_LIMITER_DURATION=3000
|
||||
|
||||
USER_LIMIT=10000
|
||||
CONNECTIONS_LIMIT=100000
|
||||
CLOSED_SEND_BY_ME=true
|
||||
|
||||
GERENCIANET_SANDBOX=false
|
||||
GERENCIANET_CLIENT_ID=Client_Id_Gerencianet
|
||||
GERENCIANET_CLIENT_SECRET=Client_Secret_Gerencianet
|
||||
GERENCIANET_PIX_CERT=certificado-Gerencianet
|
||||
GERENCIANET_PIX_KEY=chave pix gerencianet
|
||||
|
||||
MAIL_HOST="smtp.hostinger.com"
|
||||
MAIL_USER="contato@seusite.com"
|
||||
MAIL_PASS="senha"
|
||||
MAIL_FROM="Recuperar Senha <contato@seusite.com>"
|
||||
MAIL_PORT="465"
|
3
backend/.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
/*.js
|
||||
node_modules
|
||||
dist
|
49
backend/.eslintrc.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ "argsIgnorePattern": "_" }
|
||||
],
|
||||
"import/prefer-default-export": "off",
|
||||
"no-console": "off",
|
||||
"no-param-reassign": "off",
|
||||
"prettier/prettier": "error",
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"ts": "never"
|
||||
}
|
||||
],
|
||||
"quotes": [
|
||||
1,
|
||||
"double",
|
||||
{
|
||||
"avoidEscape": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"typescript": {}
|
||||
}
|
||||
}
|
||||
}
|
13
backend/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
node_modules
|
||||
public/*
|
||||
!public/logotipos/
|
||||
!public/.gitkeep
|
||||
dist
|
||||
.env
|
||||
.env.test
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
/src/config/sentry.js
|
||||
coverage.data
|
||||
coverage/
|
8
backend/.sequelizerc
Normal file
@ -0,0 +1,8 @@
|
||||
const { resolve } = require("path");
|
||||
|
||||
module.exports = {
|
||||
"config": resolve(__dirname, "dist", "config", "database.js"),
|
||||
"modules-path": resolve(__dirname, "dist", "models"),
|
||||
"migrations-path": resolve(__dirname, "dist", "database", "migrations"),
|
||||
"seeders-path": resolve(__dirname, "dist", "database", "seeds")
|
||||
};
|
1
backend/certs/coloque_seus_certificado_aqui
Normal file
@ -0,0 +1 @@
|
||||
|
9
backend/ecosystem.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = [{
|
||||
script: 'dist/server.js',
|
||||
name: 'beta-back',
|
||||
exec_mode: 'fork',
|
||||
cron_restart: '05 00 * * *',
|
||||
max_memory_restart: '769M', // Configuração para reiniciar quando atingir 769 MB de memória
|
||||
node_args: '--max-old-space-size=769', // Limite de memória do Node.js para 769 MB
|
||||
watch: false
|
||||
}]
|
186
backend/jest.config.js
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property, visit:
|
||||
* https://jestjs.io/docs/en/configuration.html
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
bail: 1,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/tmp/jest_rs",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
collectCoverage: true,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
collectCoverageFrom: ["<rootDir>/src/services/**/*.ts"],
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: "coverage",
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: "v8",
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
coverageReporters: ["text", "lcov"],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
preset: "ts-jest",
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: ["**/__tests__/**/*.spec.ts"]
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/",
|
||||
// "\\.pnp\\.[^\\/]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
113
backend/package.json
Normal file
@ -0,0 +1,113 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc --sourceMap false",
|
||||
"devbuild": "tsc --sourceMap true",
|
||||
"watch": "tsc -w",
|
||||
"start": "node --max-old-space-size=8096 dist/server.js",
|
||||
"dev": "tsc-watch --onSuccess \"node --max-old-space-size=8096 --trace-warnings ./dist/server.js\"",
|
||||
"dev:server": "node --max-old-space-size=8096 -r ts-node/register src/server.ts",
|
||||
"db:migrate": "npx sequelize db:migrate",
|
||||
"db:seed": "sequelize db:seed:all",
|
||||
"pretest": "NODE_ENV=test sequelize db:migrate && NODE_ENV=test sequelize db:seed:all",
|
||||
"test": "NODE_ENV=test jest",
|
||||
"posttest": "NODE_ENV=test sequelize db:migrate:undo:all",
|
||||
"lint": "eslint src/**/*.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@hapi/boom": "^9.1.4",
|
||||
"@sentry/node": "^6.18.1",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@whiskeysockets/baileys": "^6.7.9",
|
||||
"async-mutex": "^0.4.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.20.2",
|
||||
"bull": "^4.8.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"cron": "^2.1.0",
|
||||
"date-fns": "^2.28.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.17.3",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"gn-api-sdk-typescript": "^1.0.7",
|
||||
"http-graceful-shutdown": "^3.1.6",
|
||||
"image-size": "^1.2.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"microsoft-cognitiveservices-speech-sdk": "1.31.0",
|
||||
"multer": "^1.4.4",
|
||||
"mustache": "^4.2.0",
|
||||
"mysql2": "^2.2.5",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-cron": "^3.0.2",
|
||||
"nodemailer": "^6.8.0",
|
||||
"notificamehubsdk": "^0.0.19",
|
||||
"openai": "3.3.0",
|
||||
"pg": "^8.7.3",
|
||||
"pino": "^7.8.0",
|
||||
"pino-pretty": "^10.0.0",
|
||||
"puppeteer": "^19.4.0",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "2.88.2",
|
||||
"sequelize": "^5.22.3",
|
||||
"sequelize-cli": "^5.5.1",
|
||||
"sequelize-typescript": "^1.1.0",
|
||||
"socket.io": "^4.7.4",
|
||||
"uuid": "^8.3.2",
|
||||
"xlsx": "^0.18.3",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/bluebird": "^3.5.36",
|
||||
"@types/chance": "^1.1.3",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/factory-girl": "^5.0.8",
|
||||
"@types/faker": "^5.1.3",
|
||||
"@types/fluent-ffmpeg": "^2.1.26",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/image-size": "^0.7.0",
|
||||
"@types/ioredis": "^5.0.0",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/mustache": "^4.1.2",
|
||||
"@types/node": "^14.11.8",
|
||||
"@types/pino-pretty": "~4.7.1",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/validator": "^13.1.0",
|
||||
"@types/yup": "^0.29.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"chance": "^1.1.8",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"factory-girl": "^5.0.4",
|
||||
"faker": "^5.1.0",
|
||||
"jest": "^27.5.1",
|
||||
"nodemon": "^2.0.15",
|
||||
"prettier": "^2.5.1",
|
||||
"supertest": "^6.2.2",
|
||||
"ts-jest": "^27.1.3",
|
||||
"ts-node-dev": "^1.1.8",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
}
|
5
backend/prettier.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
singleQuote: false,
|
||||
trailingComma: "none",
|
||||
arrowParens: "avoid"
|
||||
};
|
BIN
backend/public/logotipos/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
backend/public/logotipos/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
backend/public/logotipos/facebook.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
backend/public/logotipos/favicon-16x16.png
Normal file
After Width: | Height: | Size: 503 B |
BIN
backend/public/logotipos/favicon-256x256.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
backend/public/logotipos/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
backend/public/logotipos/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
backend/public/logotipos/favicon.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/interno.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/login.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/logo.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/logo_meta.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
backend/public/logotipos/logo_typebot.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
backend/public/logotipos/logo_w.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/mstile-150x150.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
backend/public/logotipos/signup.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
backend/public/logotipos/tela-login.png
Normal file
After Width: | Height: | Size: 214 KiB |
5
backend/src/@types/express.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare namespace Express {
|
||||
export interface Request {
|
||||
user: { id: string; profile: string; companyId: number };
|
||||
}
|
||||
}
|
1
backend/src/@types/qrcode-terminal.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module "qrcode-terminal";
|
53
backend/src/app.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import * as Sentry from "@sentry/node";
|
||||
import cookieParser from "cookie-parser";
|
||||
import cors from "cors";
|
||||
import express, { NextFunction, Request, Response } from "express";
|
||||
import "express-async-errors";
|
||||
import "reflect-metadata";
|
||||
import "./bootstrap";
|
||||
|
||||
import bodyParser from 'body-parser';
|
||||
import uploadConfig from "./config/upload";
|
||||
import "./database";
|
||||
import AppError from "./errors/AppError";
|
||||
import { messageQueue, sendScheduledMessages } from "./queues";
|
||||
import routes from "./routes";
|
||||
import { logger } from "./utils/logger";
|
||||
|
||||
Sentry.init({ dsn: process.env.SENTRY_DSN });
|
||||
|
||||
const app = express();
|
||||
|
||||
app.set("queues", {
|
||||
messageQueue,
|
||||
sendScheduledMessages
|
||||
});
|
||||
|
||||
const bodyparser = require('body-parser');
|
||||
app.use(bodyParser.json({ limit: '10mb' }));
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
credentials: true,
|
||||
origin: process.env.FRONTEND_URL
|
||||
})
|
||||
);
|
||||
app.use(cookieParser());
|
||||
app.use(express.json());
|
||||
app.use(Sentry.Handlers.requestHandler());
|
||||
app.use("/public", express.static(uploadConfig.directory));
|
||||
app.use(routes);
|
||||
|
||||
app.use(Sentry.Handlers.errorHandler());
|
||||
|
||||
app.use(async (err: Error, req: Request, res: Response, _: NextFunction) => {
|
||||
if (err instanceof AppError) {
|
||||
logger.warn(err);
|
||||
return res.status(err.statusCode).json({ error: err.message });
|
||||
}
|
||||
|
||||
logger.error(err);
|
||||
return res.status(500).json({ error: "Internal server error" });
|
||||
});
|
||||
|
||||
export default app;
|
5
backend/src/bootstrap.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config({
|
||||
path: process.env.NODE_ENV === "test" ? ".env.test" : ".env"
|
||||
});
|
13
backend/src/config/Gn.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import path from "path";
|
||||
|
||||
const cert = path.join(
|
||||
__dirname,
|
||||
`../../certs/${process.env.GERENCIANET_PIX_CERT}.p12`
|
||||
);
|
||||
|
||||
export = {
|
||||
sandbox: false,
|
||||
client_id: process.env.GERENCIANET_CLIENT_ID as string,
|
||||
client_secret: process.env.GERENCIANET_CLIENT_SECRET as string,
|
||||
pix_cert: cert
|
||||
};
|
6
backend/src/config/auth.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
secret: process.env.JWT_SECRET || "mysecret",
|
||||
expiresIn: "15m",
|
||||
refreshSecret: process.env.JWT_REFRESH_SECRET || "myanothersecret",
|
||||
refreshExpiresIn: "7d"
|
||||
};
|
16
backend/src/config/database.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import "../bootstrap";
|
||||
|
||||
module.exports = {
|
||||
define: {
|
||||
charset: "utf8mb4",
|
||||
collate: "utf8mb4_bin"
|
||||
},
|
||||
dialect: process.env.DB_DIALECT || "mysql",
|
||||
timezone: "-03:00",
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT || 3306,
|
||||
database: process.env.DB_NAME,
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASS,
|
||||
logging: process.env.DB_DEBUG === "true"
|
||||
};
|
3
backend/src/config/redis.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const REDIS_URI_CONNECTION = process.env.REDIS_URI || "";
|
||||
export const REDIS_OPT_LIMITER_MAX = process.env.REDIS_OPT_LIMITER_MAX || 1;
|
||||
export const REDIS_OPT_LIMITER_DURATION = process.env.REDIS_OPT_LIMITER_DURATION || 3000;
|
50
backend/src/config/upload.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import path from "path";
|
||||
import multer from "multer";
|
||||
import fs from "fs";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import { isEmpty, isNil } from "lodash";
|
||||
|
||||
const publicFolder = path.resolve(__dirname, "..", "..", "public");
|
||||
|
||||
export default {
|
||||
directory: publicFolder,
|
||||
storage: multer.diskStorage({
|
||||
destination: async function (req, file, cb) {
|
||||
|
||||
let companyId;
|
||||
companyId = req.user?.companyId
|
||||
const { typeArch, fileId } = req.body;
|
||||
|
||||
if (companyId === undefined && isNil(companyId) && isEmpty(companyId)) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const whatsapp = await Whatsapp.findOne({ where: { token } });
|
||||
companyId = whatsapp.companyId;
|
||||
}
|
||||
let folder;
|
||||
|
||||
if (typeArch && typeArch !== "announcements" && typeArch !== "logo") {
|
||||
folder = path.resolve(publicFolder, `company${companyId}`, typeArch, fileId ? fileId : "")
|
||||
} else if (typeArch && typeArch === "announcements") {
|
||||
folder = path.resolve(publicFolder, typeArch)
|
||||
} else if (typeArch === "logo") {
|
||||
folder = path.resolve(publicFolder)
|
||||
}
|
||||
else {
|
||||
folder = path.resolve(publicFolder, `company${companyId}`)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(folder)) {
|
||||
fs.mkdirSync(folder, { recursive: true })
|
||||
fs.chmodSync(folder, 0o777)
|
||||
}
|
||||
return cb(null, folder);
|
||||
},
|
||||
filename(req, file, cb) {
|
||||
const { typeArch } = req.body;
|
||||
|
||||
const fileName = typeArch && typeArch !== "announcements" ? file.originalname.replace('/', '-').replace(/ /g, "_") : new Date().getTime() + '_' + file.originalname.replace('/', '-').replace(/ /g, "_");
|
||||
return cb(null, fileName);
|
||||
}
|
||||
})
|
||||
};
|
19
backend/src/config/uploadlogo.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import path from "path";
|
||||
import multer from "multer";
|
||||
|
||||
const publicFolder = path.resolve(__dirname, "..", "..", "public/logotipos");
|
||||
|
||||
export default {
|
||||
directory: publicFolder,
|
||||
|
||||
storage: multer.diskStorage({
|
||||
destination: publicFolder,
|
||||
filename(req, file, cb) {
|
||||
|
||||
|
||||
const desiredFileName = req.query.ref + path.extname(file.originalname);
|
||||
|
||||
return cb(null, desiredFileName);
|
||||
}
|
||||
})
|
||||
};
|
47
backend/src/config/uploadquick.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import path from "path";
|
||||
import multer from "multer";
|
||||
import fs from "fs";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import { isEmpty, isNil } from "lodash";
|
||||
|
||||
const publicFolder = path.resolve(__dirname, "..", "..", "public");
|
||||
|
||||
export default {
|
||||
directory: publicFolder,
|
||||
storage: multer.diskStorage({
|
||||
destination: async function (req, file, cb) {
|
||||
let companyId;
|
||||
|
||||
if (req.user?.companyId) {
|
||||
companyId = req.user.companyId;
|
||||
} else {
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const whatsapp = await Whatsapp.findOne({ where: { token } });
|
||||
companyId = whatsapp.companyId;
|
||||
}
|
||||
|
||||
const companyFolder = `${publicFolder}/company${companyId}`;
|
||||
|
||||
// Criar a pasta company{companyId} caso ela não exista
|
||||
if (!fs.existsSync(companyFolder)) {
|
||||
fs.mkdirSync(companyFolder, { recursive: true });
|
||||
fs.chmodSync(companyFolder, 0o777);
|
||||
}
|
||||
|
||||
const folder = `${companyFolder}/quick/`;
|
||||
|
||||
// Criar a pasta quick/ caso ela não exista
|
||||
if (!fs.existsSync(folder)) {
|
||||
fs.mkdirSync(folder, { recursive: true });
|
||||
fs.chmodSync(folder, 0o777);
|
||||
}
|
||||
|
||||
return cb(null, folder);
|
||||
},
|
||||
filename(req, file, cb) {
|
||||
const fileName = `${new Date().getTime()}_${file.originalname.replace("/", "-")}`;
|
||||
return cb(null, fileName);
|
||||
}
|
||||
})
|
||||
};
|
205
backend/src/controllers/AnnouncementController.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import ListService from "../services/AnnouncementService/ListService";
|
||||
import CreateService from "../services/AnnouncementService/CreateService";
|
||||
import ShowService from "../services/AnnouncementService/ShowService";
|
||||
import UpdateService from "../services/AnnouncementService/UpdateService";
|
||||
import DeleteService from "../services/AnnouncementService/DeleteService";
|
||||
import FindService from "../services/AnnouncementService/FindService";
|
||||
|
||||
import Announcement from "../models/Announcement";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
companyId: string | number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
priority: string;
|
||||
title: string;
|
||||
text: string;
|
||||
status: string;
|
||||
companyId: number;
|
||||
mediaPath?: string;
|
||||
mediaName?: string;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-announcement`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Announcement deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as FindParams;
|
||||
const records: Announcement[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const announcement = await Announcement.findByPk(id);
|
||||
|
||||
await announcement.update({
|
||||
mediaPath: file.filename,
|
||||
mediaName: file.originalname
|
||||
});
|
||||
await announcement.reload();
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "update",
|
||||
record: announcement
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Mensagem enviada" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const announcement = await Announcement.findByPk(id);
|
||||
const filePath = path.resolve("public", announcement.mediaPath);
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
await announcement.update({
|
||||
mediaPath: null,
|
||||
mediaName: null
|
||||
});
|
||||
await announcement.reload();
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "update",
|
||||
record: announcement
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Arquivo excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
205
backend/src/controllers/AnnouncementController_error.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import ListService from "../services/AnnouncementService/ListService";
|
||||
import CreateService from "../services/AnnouncementService/CreateService";
|
||||
import ShowService from "../services/AnnouncementService/ShowService";
|
||||
import UpdateService from "../services/AnnouncementService/UpdateService";
|
||||
import DeleteService from "../services/AnnouncementService/DeleteService";
|
||||
import FindService from "../services/AnnouncementService/FindService";
|
||||
|
||||
import Announcement from "../models/Announcement";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
companyId: string | number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
priority: string;
|
||||
title: string;
|
||||
text: string;
|
||||
status: string;
|
||||
companyId: number;
|
||||
mediaPath?: string;
|
||||
mediaName?: string;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-announcement`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Announcement deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as FindParams;
|
||||
const records: Announcement[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const announcement = await Announcement.findByPk(id);
|
||||
|
||||
await announcement.update({
|
||||
mediaPath: file.filename,
|
||||
mediaName: file.originalname
|
||||
});
|
||||
await announcement.reload();
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "update",
|
||||
record: announcement
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Mensagem enviada" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const announcement = await Announcement.findByPk(id);
|
||||
const filePath = path.resolve("public", announcement.mediaPath);
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
await announcement.update({
|
||||
mediaPath: null,
|
||||
mediaName: null
|
||||
});
|
||||
await announcement.reload();
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-announcement`, {
|
||||
action: "update",
|
||||
record: announcement
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Arquivo excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
289
backend/src/controllers/CampaignController.ts
Normal file
@ -0,0 +1,289 @@
|
||||
import { Request, Response } from "express";
|
||||
import fs from "fs";
|
||||
import { head } from "lodash";
|
||||
import path from "path";
|
||||
import * as Yup from "yup";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import CreateService from "../services/CampaignService/CreateService";
|
||||
import DeleteService from "../services/CampaignService/DeleteService";
|
||||
import FindService from "../services/CampaignService/FindService";
|
||||
import ListService from "../services/CampaignService/ListService";
|
||||
import ShowService from "../services/CampaignService/ShowService";
|
||||
import UpdateService from "../services/CampaignService/UpdateService";
|
||||
|
||||
import Campaign from "../models/Campaign";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
import Contact from "../models/Contact";
|
||||
import ContactList from "../models/ContactList";
|
||||
import ContactListItem from "../models/ContactListItem";
|
||||
import Ticket from "../models/Ticket";
|
||||
import TicketTag from "../models/TicketTag";
|
||||
import { CancelService } from "../services/CampaignService/CancelService";
|
||||
import { RestartService } from "../services/CampaignService/RestartService";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
companyId: string | number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
name: string;
|
||||
status: string;
|
||||
confirmation: boolean;
|
||||
scheduledAt: string;
|
||||
companyId: number;
|
||||
contactListId: number;
|
||||
tagListId: number | string;
|
||||
fileListId: number;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
console.log('data------- store:', data);
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
if (typeof data.tagListId === 'number') {
|
||||
|
||||
const tagId = data.tagListId;
|
||||
const campanhaNome = data.name;
|
||||
|
||||
async function createContactListFromTag(tagId) {
|
||||
|
||||
const currentDate = new Date();
|
||||
const formattedDate = currentDate.toISOString();
|
||||
|
||||
try {
|
||||
const ticketTags = await TicketTag.findAll({ where: { tagId } });
|
||||
const ticketIds = ticketTags.map((ticketTag) => ticketTag.ticketId);
|
||||
|
||||
const tickets = await Ticket.findAll({ where: { id: ticketIds } });
|
||||
const contactIds = tickets.map((ticket) => ticket.contactId);
|
||||
|
||||
const contacts = await Contact.findAll({ where: { id: contactIds } });
|
||||
|
||||
const randomName = `${campanhaNome} | TAG: ${tagId} - ${formattedDate}` // Implement your own function to generate a random name
|
||||
const contactList = await ContactList.create({ name: randomName, companyId: companyId });
|
||||
|
||||
const { id: contactListId } = contactList;
|
||||
|
||||
const contactListItems = contacts.map((contact) => ({
|
||||
name: contact.name,
|
||||
number: contact.number,
|
||||
email: contact.email,
|
||||
contactListId,
|
||||
companyId,
|
||||
isWhatsappValid: true,
|
||||
|
||||
}));
|
||||
|
||||
await ContactListItem.bulkCreate(contactListItems);
|
||||
|
||||
// Return the ContactList ID
|
||||
return contactListId;
|
||||
} catch (error) {
|
||||
console.error('Error creating contact list:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
createContactListFromTag(tagId)
|
||||
.then(async (contactListId) => {
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId,
|
||||
contactListId: contactListId,
|
||||
});
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
return res.status(200).json(record);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
return res.status(500).json({ error: 'Error creating contact list' });
|
||||
});
|
||||
|
||||
} else { // SAI DO CHECK DE TAG
|
||||
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
}
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const cancel = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
await CancelService(+id);
|
||||
|
||||
return res.status(204).json({ message: "Cancelamento realizado" });
|
||||
};
|
||||
|
||||
export const restart = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
await RestartService(+id);
|
||||
|
||||
return res.status(204).json({ message: "Reinício dos disparos" });
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaign`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Campaign deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as FindParams;
|
||||
const records: Campaign[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const campaign = await Campaign.findByPk(id);
|
||||
campaign.mediaPath = file.filename;
|
||||
campaign.mediaName = file.originalname;
|
||||
await campaign.save();
|
||||
return res.send({ mensagem: "Mensagem enviada" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const campaign = await Campaign.findByPk(id);
|
||||
const filePath = path.resolve("public", `company${companyId}`, campaign.mediaPath);
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
campaign.mediaPath = null;
|
||||
campaign.mediaName = null;
|
||||
await campaign.save();
|
||||
return res.send({ mensagem: "Arquivo excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
34
backend/src/controllers/CampaignSettingController.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import ListService from "../services/CampaignSettingServices/ListService";
|
||||
import CreateService from "../services/CampaignSettingServices/CreateService";
|
||||
|
||||
interface StoreData {
|
||||
settings: any;
|
||||
}
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
|
||||
const records = await ListService({
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json(records);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const record = await CreateService(data, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-campaignSettings`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
38
backend/src/controllers/ChannelHubController.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Request, Response } from "express";
|
||||
import CreateChannelsService from "../services/HubServices/CreateHubChannelsService";
|
||||
import { setChannelWebhook } from "../helpers/setChannelHubWebhook";
|
||||
import { getIO } from "../libs/socket";
|
||||
import ListChannels from "../services/HubServices/ListHubChannels";
|
||||
|
||||
export interface IChannel {
|
||||
name: string;
|
||||
status?: string;
|
||||
isDefault?: boolean;
|
||||
qrcode?: string;
|
||||
type?: string;
|
||||
channel?: string;
|
||||
id?:string;
|
||||
}
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
const { whatsapps } = await CreateChannelsService(req.body);
|
||||
|
||||
whatsapps.forEach(whatsapp => {
|
||||
setTimeout(() => {
|
||||
setChannelWebhook(whatsapp, whatsapp.id.toString());
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
return res.status(200).json(whatsapps);
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
try {
|
||||
const channels = await ListChannels();
|
||||
return res.status(200).json(channels);
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error });
|
||||
}
|
||||
};
|
205
backend/src/controllers/ChatController.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import CreateService from "../services/ChatService/CreateService";
|
||||
import ListService from "../services/ChatService/ListService";
|
||||
import ShowFromUuidService from "../services/ChatService/ShowFromUuidService";
|
||||
import DeleteService from "../services/ChatService/DeleteService";
|
||||
import FindMessages from "../services/ChatService/FindMessages";
|
||||
import UpdateService from "../services/ChatService/UpdateService";
|
||||
|
||||
import Chat from "../models/Chat";
|
||||
import CreateMessageService from "../services/ChatService/CreateMessageService";
|
||||
import User from "../models/User";
|
||||
import ChatUser from "../models/ChatUser";
|
||||
|
||||
type IndexQuery = {
|
||||
pageNumber: string;
|
||||
companyId: string | number;
|
||||
ownerId?: number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
users: any[];
|
||||
title: string;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: number;
|
||||
ownerId?: number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { pageNumber } = req.query as unknown as IndexQuery;
|
||||
const ownerId = +req.user.id;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
ownerId,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const ownerId = +req.user.id;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
ownerId,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
|
||||
record.users.forEach(user => {
|
||||
io.to(`user-${user.userId}`).emit(`company-${companyId}-chat-user-${user.userId}`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body;
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id: +id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
|
||||
record.users.forEach(user => {
|
||||
io.to(`user-${user.userId}`).emit(`company-${companyId}-chat-user-${user.userId}`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowFromUuidService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Chat deleted" });
|
||||
};
|
||||
|
||||
export const saveMessage = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const { message } = req.body;
|
||||
const { id } = req.params;
|
||||
const senderId = +req.user.id;
|
||||
const chatId = +id;
|
||||
|
||||
const newMessage = await CreateMessageService({
|
||||
chatId,
|
||||
senderId,
|
||||
message
|
||||
});
|
||||
|
||||
const chat = await Chat.findByPk(chatId, {
|
||||
include: [
|
||||
{ model: User, as: "owner" },
|
||||
{ model: ChatUser, as: "users" }
|
||||
]
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat-${chatId}`, {
|
||||
action: "new-message",
|
||||
newMessage,
|
||||
chat
|
||||
});
|
||||
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat`, {
|
||||
action: "new-message",
|
||||
newMessage,
|
||||
chat
|
||||
});
|
||||
|
||||
return res.json(newMessage);
|
||||
};
|
||||
|
||||
export const checkAsRead = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const { userId } = req.body;
|
||||
const { id } = req.params;
|
||||
|
||||
const chatUser = await ChatUser.findOne({ where: { chatId: id, userId } });
|
||||
await chatUser.update({ unreads: 0 });
|
||||
|
||||
const chat = await Chat.findByPk(id, {
|
||||
include: [
|
||||
{ model: User, as: "owner" },
|
||||
{ model: ChatUser, as: "users" }
|
||||
]
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat-${id}`, {
|
||||
action: "update",
|
||||
chat
|
||||
});
|
||||
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-chat`, {
|
||||
action: "update",
|
||||
chat
|
||||
});
|
||||
|
||||
return res.json(chat);
|
||||
};
|
||||
|
||||
export const messages = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { pageNumber } = req.query as unknown as IndexQuery;
|
||||
const { id: chatId } = req.params;
|
||||
const ownerId = +req.user.id;
|
||||
|
||||
const { records, count, hasMore } = await FindMessages({
|
||||
chatId,
|
||||
ownerId,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
200
backend/src/controllers/CompanyController.ts
Normal file
@ -0,0 +1,200 @@
|
||||
import { Request, Response } from "express";
|
||||
import * as Yup from "yup";
|
||||
// import { getIO } from "../libs/socket";
|
||||
import authConfig from "../config/auth";
|
||||
import AppError from "../errors/AppError";
|
||||
import Company from "../models/Company";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { verify } from "jsonwebtoken";
|
||||
import User from "../models/User";
|
||||
import CreateCompanyService from "../services/CompanyService/CreateCompanyService";
|
||||
import DeleteCompanyService from "../services/CompanyService/DeleteCompanyService";
|
||||
import FindAllCompaniesService from "../services/CompanyService/FindAllCompaniesService";
|
||||
import ListCompaniesPlanService from "../services/CompanyService/ListCompaniesPlanService";
|
||||
import ListCompaniesService from "../services/CompanyService/ListCompaniesService";
|
||||
import ShowCompanyService from "../services/CompanyService/ShowCompanyService";
|
||||
import ShowPlanCompanyService from "../services/CompanyService/ShowPlanCompanyService";
|
||||
import UpdateCompanyService from "../services/CompanyService/UpdateCompanyService";
|
||||
import UpdateSchedulesService from "../services/CompanyService/UpdateSchedulesService";
|
||||
|
||||
const publicFolder = path.resolve(__dirname, "..", "..", "public");
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
interface TokenPayload {
|
||||
id: string;
|
||||
username: string;
|
||||
profile: string;
|
||||
companyId: number;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
type CompanyData = {
|
||||
name: string;
|
||||
id?: number;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
status?: boolean;
|
||||
planId?: number;
|
||||
campaignsEnabled?: boolean;
|
||||
dueDate?: string;
|
||||
recurrence?: string;
|
||||
};
|
||||
|
||||
type SchedulesData = {
|
||||
schedules: [];
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { companies, count, hasMore } = await ListCompaniesService({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ companies, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const newCompany: CompanyData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(newCompany);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const company = await CreateCompanyService(newCompany);
|
||||
|
||||
return res.status(200).json(company);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const company = await ShowCompanyService(id);
|
||||
|
||||
return res.status(200).json(company);
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const companies: Company[] = await FindAllCompaniesService();
|
||||
|
||||
return res.status(200).json(companies);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const companyData: CompanyData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(companyData);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const company = await UpdateCompanyService({ id, ...companyData });
|
||||
|
||||
return res.status(200).json(company);
|
||||
};
|
||||
|
||||
export const updateSchedules = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { schedules }: SchedulesData = req.body;
|
||||
const { id } = req.params;
|
||||
|
||||
const company = await UpdateSchedulesService({
|
||||
id,
|
||||
schedules
|
||||
});
|
||||
|
||||
return res.status(200).json(company);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const userId = req.user.id;
|
||||
const requestUser = await User.findByPk(userId);
|
||||
|
||||
if (requestUser.super === false) {
|
||||
throw new AppError("você nao tem permissão para este consulta");
|
||||
}
|
||||
const { id } = req.params;
|
||||
|
||||
if (fs.existsSync(`${publicFolder}/company${id}/`)) {
|
||||
|
||||
const removefolder = await fs.rmdirSync(`${publicFolder}/company${id}/`, {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const company = await DeleteCompanyService(id);
|
||||
|
||||
|
||||
//fs.remove(`${publicFolder}/company${id}/`);
|
||||
|
||||
return res.status(200).json(company);
|
||||
};
|
||||
|
||||
export const listPlan = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { id: requestUserId, profile, companyId } = decoded as TokenPayload;
|
||||
const requestUser = await User.findByPk(requestUserId);
|
||||
|
||||
if (requestUser.super === true) {
|
||||
const company = await ShowPlanCompanyService(id);
|
||||
return res.status(200).json(company);
|
||||
} else if (companyId.toString() !== id) {
|
||||
return res.status(400).json({ error: "Você não possui permissão para acessar este recurso!" });
|
||||
} else {
|
||||
const company = await ShowPlanCompanyService(id);
|
||||
return res.status(200).json(company);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const indexPlan = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { id, profile, companyId } = decoded as TokenPayload;
|
||||
// const company = await Company.findByPk(companyId);
|
||||
const requestUser = await User.findByPk(id);
|
||||
|
||||
if (requestUser.super === true) {
|
||||
const companies = await ListCompaniesPlanService();
|
||||
return res.json({ companies });
|
||||
} else {
|
||||
return res.status(400).json({ error: "Você não possui permissão para acessar este recurso!" });
|
||||
}
|
||||
|
||||
};
|
259
backend/src/controllers/ContactController.ts
Normal file
@ -0,0 +1,259 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import Contact from "../models/Contact";
|
||||
import ListContactsService from "../services/ContactServices/ListContactsService";
|
||||
import CreateContactService from "../services/ContactServices/CreateContactService";
|
||||
import ShowContactService from "../services/ContactServices/ShowContactService";
|
||||
import UpdateContactService from "../services/ContactServices/UpdateContactService";
|
||||
import DeleteContactService from "../services/ContactServices/DeleteContactService";
|
||||
import GetContactService from "../services/ContactServices/GetContactService";
|
||||
|
||||
import CheckContactNumber from "../services/WbotServices/CheckNumber";
|
||||
import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact";
|
||||
import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
|
||||
import AppError from "../errors/AppError";
|
||||
import SimpleListService, {
|
||||
SearchContactParams
|
||||
} from "../services/ContactServices/SimpleListService";
|
||||
import ContactCustomField from "../models/ContactCustomField";
|
||||
import {head} from "lodash";
|
||||
import {ImportContacts} from "../services/ContactServices/ImportContacts";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type IndexGetContactQuery = {
|
||||
name: string;
|
||||
number: string;
|
||||
};
|
||||
|
||||
interface ExtraInfo extends ContactCustomField {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
interface ContactData {
|
||||
name: string;
|
||||
number: string;
|
||||
email?: string;
|
||||
extraInfo?: ExtraInfo[];
|
||||
}
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { contacts, count, hasMore } = await ListContactsService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ contacts, count, hasMore });
|
||||
};
|
||||
|
||||
export const getContact = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { name, number } = req.body as IndexGetContactQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const contact = await GetContactService({
|
||||
name,
|
||||
number,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const newContact: ContactData = req.body;
|
||||
newContact.number = newContact.number.replace("-", "").replace(" ", "");
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(),
|
||||
/*number: Yup.string()
|
||||
.required()
|
||||
.matches(/^\d+$/, "Invalid number format. Only numbers is allowed.")*/
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(newContact);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
await CheckIsValidContact(newContact.number, companyId);
|
||||
const validNumber = await CheckContactNumber(newContact.number, companyId);
|
||||
const number = validNumber.jid.replace(/\D/g, "");
|
||||
newContact.number = number;
|
||||
|
||||
// Check if the contact already exists
|
||||
const existingContact = await Contact.findOne({
|
||||
where: {
|
||||
number: newContact.number,
|
||||
companyId
|
||||
}
|
||||
});
|
||||
|
||||
if (existingContact) {
|
||||
// Contact already exists, send the existing contact data as the response
|
||||
return res.status(200).json({ alreadyExists: true, existingContact });
|
||||
}
|
||||
|
||||
/**
|
||||
* Código desabilitado por demora no retorno
|
||||
*/
|
||||
// const profilePicUrl = await GetProfilePicUrl(validNumber.jid, companyId);
|
||||
|
||||
const contact = await CreateContactService({
|
||||
...newContact,
|
||||
// profilePicUrl,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, {
|
||||
action: "create",
|
||||
contact
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { contactId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const contact = await ShowContactService(contactId, companyId);
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const contactData: ContactData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string(),
|
||||
/*number: Yup.string().matches(
|
||||
/^\d+$/,
|
||||
"Invalid number format. Only numbers is allowed."
|
||||
)*/
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(contactData);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
/*await CheckIsValidContact(contactData.number, companyId);
|
||||
const validNumber = await CheckContactNumber(contactData.number, companyId);
|
||||
const number = validNumber.jid.replace(/\D/g, "");
|
||||
contactData.number = number;
|
||||
*/
|
||||
const { contactId } = req.params;
|
||||
|
||||
const contact = await UpdateContactService({
|
||||
contactData,
|
||||
contactId,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, {
|
||||
action: "update",
|
||||
contact
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { contactId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await ShowContactService(contactId, companyId);
|
||||
|
||||
await DeleteContactService(contactId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, {
|
||||
action: "delete",
|
||||
contactId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Contact deleted" });
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { name } = req.query as unknown as SearchContactParams;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const contacts = await SimpleListService({ name, companyId });
|
||||
|
||||
return res.json(contacts);
|
||||
};
|
||||
|
||||
export const upload = async (req: Request, res: Response) => {
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file: Express.Multer.File = head(files) as Express.Multer.File;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const response = await ImportContacts(companyId, file);
|
||||
|
||||
const io = getIO();
|
||||
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-contact`, {
|
||||
action: "create",
|
||||
records: response
|
||||
});
|
||||
|
||||
return res.status(200).json(response);
|
||||
};
|
||||
|
||||
export const getContactVcard = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { name, number } = req.query as IndexGetContactQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
let vNumber = number;
|
||||
const numberDDI = vNumber.toString().substr(0, 2);
|
||||
const numberDDD = vNumber.toString().substr(2, 2);
|
||||
const numberUser = vNumber.toString().substr(-8, 8);
|
||||
|
||||
if (numberDDD <= '30' && numberDDI === '55') {
|
||||
console.log("menor 30")
|
||||
vNumber = `${numberDDI + numberDDD + 9 + numberUser}@s.whatsapp.net`;
|
||||
} else if (numberDDD > '30' && numberDDI === '55') {
|
||||
console.log("maior 30")
|
||||
vNumber = `${numberDDI + numberDDD + numberUser}@s.whatsapp.net`;
|
||||
} else {
|
||||
vNumber = `${number}@s.whatsapp.net`;
|
||||
}
|
||||
|
||||
console.log(vNumber);
|
||||
|
||||
const contact = await GetContactService({
|
||||
name,
|
||||
number,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
159
backend/src/controllers/ContactListController.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import ListService from "../services/ContactListService/ListService";
|
||||
import CreateService from "../services/ContactListService/CreateService";
|
||||
import ShowService from "../services/ContactListService/ShowService";
|
||||
import UpdateService from "../services/ContactListService/UpdateService";
|
||||
import DeleteService from "../services/ContactListService/DeleteService";
|
||||
import FindService from "../services/ContactListService/FindService";
|
||||
import { head } from "lodash";
|
||||
|
||||
import ContactList from "../models/ContactList";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
import { ImportContacts } from "../services/ContactListService/ImportContacts";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
companyId: string | number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
name: string;
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactList`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactList`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactList`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Contact list deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as FindParams;
|
||||
const records: ContactList[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
||||
|
||||
export const upload = async (req: Request, res: Response) => {
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file: Express.Multer.File = head(files) as Express.Multer.File;
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const response = await ImportContacts(+id, companyId, file);
|
||||
|
||||
const io = getIO();
|
||||
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem-${+id}`, {
|
||||
action: "reload",
|
||||
records: response
|
||||
});
|
||||
|
||||
return res.status(200).json(response);
|
||||
};
|
145
backend/src/controllers/ContactListItemController.ts
Normal file
@ -0,0 +1,145 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import ListService from "../services/ContactListItemService/ListService";
|
||||
import CreateService from "../services/ContactListItemService/CreateService";
|
||||
import ShowService from "../services/ContactListItemService/ShowService";
|
||||
import UpdateService from "../services/ContactListItemService/UpdateService";
|
||||
import DeleteService from "../services/ContactListItemService/DeleteService";
|
||||
import FindService from "../services/ContactListItemService/FindService";
|
||||
|
||||
import ContactListItem from "../models/ContactListItem";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
companyId: string | number;
|
||||
contactListId: string | number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
name: string;
|
||||
number: string;
|
||||
contactListId: number;
|
||||
companyId?: string;
|
||||
email?: string;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: number;
|
||||
contactListId: number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber, contactListId } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { contacts, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId,
|
||||
contactListId
|
||||
});
|
||||
|
||||
return res.json({ contacts, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-ContactListItem`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Contact deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as unknown as FindParams;
|
||||
const records: ContactListItem[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
55
backend/src/controllers/DashbardController.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { Request, Response } from 'express';
|
||||
import DashboardDataService, {
|
||||
DashboardData,
|
||||
Params,
|
||||
} from '../services/ReportService/DashbardDataService';
|
||||
import { TicketsAttendance } from '../services/ReportService/TicketsAttendance';
|
||||
import { TicketsDayService } from '../services/ReportService/TicketsDayService';
|
||||
|
||||
type IndexQuery = {
|
||||
initialDate: string;
|
||||
finalDate: string;
|
||||
companyId: number | any;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const params: Params = req.query;
|
||||
const { companyId } = req.user;
|
||||
let daysInterval = 3;
|
||||
|
||||
const dashboardData: DashboardData = await DashboardDataService(
|
||||
companyId,
|
||||
params,
|
||||
);
|
||||
return res.status(200).json(dashboardData);
|
||||
};
|
||||
|
||||
export const reportsUsers = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<Response> => {
|
||||
const { initialDate, finalDate, companyId } = req.query as IndexQuery;
|
||||
|
||||
const { data } = await TicketsAttendance({
|
||||
initialDate,
|
||||
finalDate,
|
||||
companyId,
|
||||
});
|
||||
|
||||
return res.json({ data });
|
||||
};
|
||||
|
||||
export const reportsDay = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<Response> => {
|
||||
const { initialDate, finalDate, companyId } = req.query as IndexQuery;
|
||||
|
||||
const { count, data } = await TicketsDayService({
|
||||
initialDate,
|
||||
finalDate,
|
||||
companyId,
|
||||
});
|
||||
|
||||
return res.json({ count, data });
|
||||
};
|
155
backend/src/controllers/FilesController.ts
Normal file
@ -0,0 +1,155 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
import { head } from "lodash";
|
||||
|
||||
import CreateService from "../services/FileServices/CreateService";
|
||||
import ListService from "../services/FileServices/ListService";
|
||||
import UpdateService from "../services/FileServices/UpdateService";
|
||||
import ShowService from "../services/FileServices/ShowService";
|
||||
import DeleteService from "../services/FileServices/DeleteService";
|
||||
import SimpleListService from "../services/FileServices/SimpleListService";
|
||||
import DeleteAllService from "../services/FileServices/DeleteAllService";
|
||||
import ShowTicketService from "../services/TicketServices/ShowTicketService";
|
||||
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
||||
import FilesOptions from "../models/FilesOptions";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam?: string;
|
||||
pageNumber?: string | number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { pageNumber, searchParam } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { files, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ files, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { name, message, options } = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const fileList = await CreateService({
|
||||
name,
|
||||
message,
|
||||
options,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company${companyId}-file`, {
|
||||
action: "create",
|
||||
fileList
|
||||
});
|
||||
|
||||
return res.status(200).json(fileList);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { fileId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const file = await ShowService(fileId, companyId);
|
||||
|
||||
return res.status(200).json(file);
|
||||
};
|
||||
|
||||
export const uploadMedias = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { fileId, id, mediaType } = req.body;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
|
||||
let fileOpt
|
||||
if (files.length > 0) {
|
||||
|
||||
for (const [index, file] of files.entries()) {
|
||||
fileOpt = await FilesOptions.findOne({
|
||||
where: {
|
||||
fileId,
|
||||
id: Array.isArray(id)? id[index] : id
|
||||
}
|
||||
});
|
||||
|
||||
fileOpt.update({
|
||||
path: file.filename.replace('/','-'),
|
||||
mediaType: Array.isArray(mediaType)? mediaType[index] : mediaType
|
||||
}) ;
|
||||
}
|
||||
}
|
||||
|
||||
return res.send({ mensagem: "Arquivos atualizados" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
const { fileId } = req.params;
|
||||
const fileData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const fileList = await UpdateService({ fileData, id: fileId, companyId });
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company${companyId}-file`, {
|
||||
action: "update",
|
||||
fileList
|
||||
});
|
||||
|
||||
return res.status(200).json(fileList);
|
||||
};
|
||||
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { fileId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(fileId, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company${companyId}-file`, {
|
||||
action: "delete",
|
||||
fileId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "File List deleted" });
|
||||
};
|
||||
|
||||
export const removeAll = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
await DeleteAllService(companyId);
|
||||
|
||||
return res.send();
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const ratings = await SimpleListService({ searchParam, companyId });
|
||||
|
||||
return res.json(ratings);
|
||||
};
|
25
backend/src/controllers/ForgotController.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { Request, Response } from "express";
|
||||
import SendMail from "../services/ForgotPassWordServices/SendMail";
|
||||
import ResetPassword from "../services/ResetPasswordService/ResetPassword";
|
||||
type IndexQuery = { email?: string; token?: string; password?: string };
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { email } = req.params as IndexQuery;
|
||||
const TokenSenha = uuid();
|
||||
const forgotPassword = await SendMail(email, TokenSenha);
|
||||
if (!forgotPassword) {
|
||||
return res.status(200).json({ message: "E-mail enviado com sucesso" });
|
||||
}
|
||||
return res.status(404).json({ error: "E-mail enviado com sucesso" });
|
||||
};
|
||||
export const resetPasswords = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { email, token, password } = req.params as IndexQuery;
|
||||
const resetPassword = await ResetPassword(email, token, password);
|
||||
if (!resetPassword) {
|
||||
return res.status(200).json({ message: "Senha redefinida com sucesso" });
|
||||
}
|
||||
return res.status(404).json({ error: "Verifique o Token informado" });
|
||||
};
|
131
backend/src/controllers/HelpController.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import ListService from "../services/HelpServices/ListService";
|
||||
import CreateService from "../services/HelpServices/CreateService";
|
||||
import ShowService from "../services/HelpServices/ShowService";
|
||||
import UpdateService from "../services/HelpServices/UpdateService";
|
||||
import DeleteService from "../services/HelpServices/DeleteService";
|
||||
import FindService from "../services/HelpServices/FindService";
|
||||
|
||||
import Help from "../models/Help";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
title: string;
|
||||
description: string;
|
||||
video?: string;
|
||||
link?: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-help`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-help`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-help`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Help deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const records: Help[] = await FindService();
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
148
backend/src/controllers/HubNotificaMeController.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import ListService from "../services/HubNotificaMeService/ListService";
|
||||
import CreateService from "../services/HubNotificaMeService/CreateService";
|
||||
import ShowService from "../services/HubNotificaMeService/ShowService";
|
||||
import UpdateService from "../services/HubNotificaMeService/UpdateService";
|
||||
import DeleteService from "../services/HubNotificaMeService/DeleteService";
|
||||
import FindService from "../services/HubNotificaMeService/FindService";
|
||||
|
||||
import HubNotificaMe from "../models/HubNotificaMe";
|
||||
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
nome: string;
|
||||
token: string;
|
||||
tipo: string;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
// Certifique-se de que o tipo foi fornecido
|
||||
const schema = Yup.object().shape({
|
||||
nome: Yup.string().required(),
|
||||
token: Yup.string().required(),
|
||||
tipo: Yup.string().oneOf(["Facebook", "Instagram"], "Tipo inválido").required("Tipo é obrigatório"), // Validação do tipo
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-hubnotificame`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
nome: Yup.string().required(),
|
||||
token: Yup.string().required(),
|
||||
tipo: Yup.string().oneOf(["Facebook", "Instagram"], "Tipo inválido").required("Tipo é obrigatório"), // Validação do tipo
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
id,
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-hubnotificame`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-hubnotificame`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Contact deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as FindParams;
|
||||
const records: HubNotificaMe[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
10
backend/src/controllers/ImportPhoneContactsController.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Request, Response } from "express";
|
||||
import ImportContactsService from "../services/WbotServices/ImportContactsService";
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
|
||||
await ImportContactsService(companyId);
|
||||
|
||||
return res.status(200).json({ message: "contacts imported" });
|
||||
};
|
172
backend/src/controllers/InvoicesController.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
// import { getIO } from "../libs/socket";
|
||||
import AppError from "../errors/AppError";
|
||||
import Invoices from "../models/Invoices";
|
||||
|
||||
import CreatePlanService from "../services/PlanService/CreatePlanService";
|
||||
import UpdatePlanService from "../services/PlanService/UpdatePlanService";
|
||||
import ShowPlanService from "../services/PlanService/ShowPlanService";
|
||||
import DeletePlanService from "../services/PlanService/DeletePlanService";
|
||||
|
||||
import FindAllInvoiceService from "../services/InvoicesService/FindAllInvoiceService";
|
||||
import ListInvoicesServices from "../services/InvoicesService/ListInvoicesServices";
|
||||
import ShowInvoceService from "../services/InvoicesService/ShowInvoiceService";
|
||||
import UpdateInvoiceService from "../services/InvoicesService/UpdateInvoiceService";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type StorePlanData = {
|
||||
name: string;
|
||||
id?: number | string;
|
||||
users: number | 0;
|
||||
connections: number | 0;
|
||||
queues: number | 0;
|
||||
value: number;
|
||||
};
|
||||
|
||||
type UpdateInvoiceData = {
|
||||
status: string;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { invoices, count, hasMore } = await ListInvoicesServices({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ invoices, count, hasMore });
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { Invoiceid } = req.params;
|
||||
|
||||
const invoice = await ShowInvoceService(Invoiceid);
|
||||
|
||||
return res.status(200).json(invoice);
|
||||
};
|
||||
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const invoice: Invoices[] = await FindAllInvoiceService(companyId);
|
||||
|
||||
return res.status(200).json(invoice);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const InvoiceData: UpdateInvoiceData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(InvoiceData);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id, status } = InvoiceData;
|
||||
|
||||
const plan = await UpdateInvoiceService({
|
||||
id,
|
||||
status,
|
||||
|
||||
});
|
||||
|
||||
// const io = getIO();
|
||||
// io.emit("plan", {
|
||||
// action: "update",
|
||||
// plan
|
||||
// });
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
/* export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const newPlan: StorePlanData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(newPlan);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const plan = await CreatePlanService(newPlan);
|
||||
|
||||
// const io = getIO();
|
||||
// io.emit("plan", {
|
||||
// action: "create",
|
||||
// plan
|
||||
// });
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const plan = await ShowPlanService(id);
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const planData: UpdateInvoiceData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(planData);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id, name, users, connections, queues, value } = planData;
|
||||
|
||||
const plan = await UpdatePlanService({
|
||||
id,
|
||||
name,
|
||||
users,
|
||||
connections,
|
||||
queues,
|
||||
value
|
||||
});
|
||||
|
||||
// const io = getIO();
|
||||
// io.emit("plan", {
|
||||
// action: "update",
|
||||
// plan
|
||||
// });
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const plan = await DeletePlanService(id);
|
||||
|
||||
return res.status(200).json(plan);
|
||||
}; */
|
370
backend/src/controllers/MessageController.ts
Normal file
@ -0,0 +1,370 @@
|
||||
import { Request, Response } from "express";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
import formatBody from "../helpers/Mustache";
|
||||
import SetTicketMessagesAsRead from "../helpers/SetTicketMessagesAsRead";
|
||||
import { getIO } from "../libs/socket";
|
||||
import Ticket from "../models/Ticket";
|
||||
import Message from "../models/Message";
|
||||
import Queue from "../models/Queue";
|
||||
import User from "../models/User";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import { lookup } from 'mime-types';
|
||||
import { isNil } from "lodash";
|
||||
import QuickMessage from "../models/QuickMessage";
|
||||
import CreateOrUpdateContactService from "../services/ContactServices/CreateOrUpdateContactService";
|
||||
import SendWhatsAppReaction from "../services/WbotServices/SendWhatsAppReaction";
|
||||
import ListMessagesService from "../services/MessageServices/ListMessagesService";
|
||||
import FindOrCreateTicketService from "../services/TicketServices/FindOrCreateTicketService";
|
||||
import ShowTicketService from "../services/TicketServices/ShowTicketService";
|
||||
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
||||
import CheckContactNumber from "../services/WbotServices/CheckNumber";
|
||||
import DeleteWhatsAppMessage from "../services/WbotServices/DeleteWhatsAppMessage";
|
||||
import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
|
||||
import ShowContactService from "../services/ContactServices/ShowContactService";
|
||||
import SendWhatsAppMedia from "../services/WbotServices/SendWhatsAppMedia";
|
||||
//import SendWhatsAppMediaInternal from "../services/WbotServices/SendWhatsAppMediaInternal";
|
||||
import path from "path";
|
||||
import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
|
||||
import EditWhatsAppMessage from "../services/WbotServices/EditWhatsAppMessage";
|
||||
import ShowMessageService, { GetWhatsAppFromMessage } from "../services/MessageServices/ShowMessageService";
|
||||
type IndexQuery = {
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type MessageData = {
|
||||
body: string;
|
||||
fromMe: boolean;
|
||||
read: boolean;
|
||||
quotedMsg?: Message;
|
||||
number?: string;
|
||||
closeTicket?: true;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { ticketId } = req.params;
|
||||
const { pageNumber } = req.query as IndexQuery;
|
||||
const { companyId, profile } = req.user;
|
||||
const queues: number[] = [];
|
||||
|
||||
if (profile !== "admin") {
|
||||
const user = await User.findByPk(req.user.id, {
|
||||
include: [{ model: Queue, as: "queues" }]
|
||||
});
|
||||
user.queues.forEach(queue => {
|
||||
queues.push(queue.id);
|
||||
});
|
||||
}
|
||||
|
||||
const { count, messages, ticket, hasMore } = await ListMessagesService({
|
||||
pageNumber,
|
||||
ticketId,
|
||||
companyId,
|
||||
queues
|
||||
});
|
||||
|
||||
SetTicketMessagesAsRead(ticket);
|
||||
|
||||
return res.json({ count, messages, ticket, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { ticketId } = req.params;
|
||||
const { body, quotedMsg }: MessageData = req.body;
|
||||
const medias = req.files as Express.Multer.File[];
|
||||
const { companyId } = req.user;
|
||||
|
||||
const ticket = await ShowTicketService(ticketId, companyId);
|
||||
|
||||
SetTicketMessagesAsRead(ticket);
|
||||
|
||||
console.log('bodyyyyyyyyyy:', body)
|
||||
if (medias) {
|
||||
await Promise.all(
|
||||
medias.map(async (media: Express.Multer.File, index) => {
|
||||
await SendWhatsAppMedia({ media, ticket, body: Array.isArray(body) ? body[index] : body });
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const send = await SendWhatsAppMessage({ body, ticket, quotedMsg });
|
||||
}
|
||||
|
||||
return res.send();
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { messageId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const message = await DeleteWhatsAppMessage(messageId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(message.ticketId.toString()).emit(`company-${companyId}-appMessage`, {
|
||||
action: "update",
|
||||
message
|
||||
});
|
||||
|
||||
return res.send();
|
||||
};
|
||||
|
||||
export const send = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { whatsappId } = req.params as unknown as { whatsappId: number };
|
||||
const messageData: MessageData = req.body;
|
||||
const medias = req.files as Express.Multer.File[];
|
||||
|
||||
console.log('messageData;', messageData)
|
||||
|
||||
try {
|
||||
const whatsapp = await Whatsapp.findByPk(whatsappId);
|
||||
|
||||
if (!whatsapp) {
|
||||
throw new Error("Não foi possível realizar a operação");
|
||||
}
|
||||
|
||||
if (messageData.number === undefined) {
|
||||
throw new Error("O número é obrigatório");
|
||||
}
|
||||
|
||||
const numberToTest = messageData.number;
|
||||
const body = messageData.body;
|
||||
|
||||
const companyId = whatsapp.companyId;
|
||||
|
||||
const CheckValidNumber = await CheckContactNumber(numberToTest, companyId);
|
||||
const number = CheckValidNumber.jid.replace(/\D/g, "");
|
||||
const profilePicUrl = await GetProfilePicUrl(
|
||||
number,
|
||||
companyId
|
||||
);
|
||||
const contactData = {
|
||||
name: `${number}`,
|
||||
number,
|
||||
profilePicUrl,
|
||||
isGroup: false,
|
||||
companyId
|
||||
};
|
||||
|
||||
const contact = await CreateOrUpdateContactService(contactData);
|
||||
|
||||
const ticket = await FindOrCreateTicketService(contact, whatsapp.id!, 0, companyId);
|
||||
|
||||
if (medias) {
|
||||
await Promise.all(
|
||||
medias.map(async (media: Express.Multer.File) => {
|
||||
await req.app.get("queues").messageQueue.add(
|
||||
"SendMessage",
|
||||
{
|
||||
whatsappId,
|
||||
data: {
|
||||
number,
|
||||
body: body ? formatBody(body, contact) : media.originalname,
|
||||
mediaPath: media.path,
|
||||
fileName: media.originalname
|
||||
}
|
||||
},
|
||||
{ removeOnComplete: true, attempts: 3 }
|
||||
);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
await SendWhatsAppMessage({ body: formatBody(body, contact), ticket });
|
||||
|
||||
await ticket.update({
|
||||
lastMessage: body,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (messageData.closeTicket) {
|
||||
setTimeout(async () => {
|
||||
await UpdateTicketService({
|
||||
ticketId: ticket.id,
|
||||
ticketData: { status: "closed" },
|
||||
companyId
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
SetTicketMessagesAsRead(ticket);
|
||||
|
||||
return res.send({ mensagem: "Mensagem enviada" });
|
||||
} catch (err: any) {
|
||||
if (Object.keys(err).length === 0) {
|
||||
throw new AppError(
|
||||
"Não foi possível enviar a mensagem, tente novamente em alguns instantes"
|
||||
);
|
||||
} else {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const addReaction = async (req: Request, res: Response): Promise<Response> => {
|
||||
try {
|
||||
const {messageId} = req.params;
|
||||
const {type} = req.body; // O tipo de reação, por exemplo, 'like', 'heart', etc.
|
||||
const {companyId, id} = req.user;
|
||||
|
||||
const message = await Message.findByPk(messageId);
|
||||
|
||||
const ticket = await Ticket.findByPk(message.ticketId, {
|
||||
include: ["contact"]
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
return res.status(404).send({message: "Mensagem não encontrada"});
|
||||
}
|
||||
|
||||
// Envia a reação via WhatsApp
|
||||
const reactionResult = await SendWhatsAppReaction({
|
||||
messageId: messageId,
|
||||
ticket: ticket,
|
||||
reactionType: type
|
||||
});
|
||||
|
||||
// Atualiza a mensagem com a nova reação no banco de dados (opcional, dependendo da necessidade)
|
||||
const updatedMessage = await message.update({
|
||||
reactions: [...message.reactions, {type: type, userId: id}]
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(message.ticketId.toString()).emit(`company-${companyId}-appMessage`, {
|
||||
action: "update",
|
||||
message
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Reação adicionada com sucesso!',
|
||||
reactionResult,
|
||||
reactions: updatedMessage.reactions
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao adicionar reação:', error);
|
||||
if (error instanceof AppError) {
|
||||
return res.status(400).send({message: error.message});
|
||||
}
|
||||
return res.status(500).send({message: 'Erro ao adicionar reação', error: error.message});
|
||||
}
|
||||
};
|
||||
|
||||
function obterNomeEExtensaoDoArquivo(url) {
|
||||
var urlObj = new URL(url);
|
||||
var pathname = urlObj.pathname;
|
||||
var filename = pathname.split('/').pop();
|
||||
var parts = filename.split('.');
|
||||
|
||||
var nomeDoArquivo = parts[0];
|
||||
var extensao = parts[1];
|
||||
|
||||
return `${nomeDoArquivo}.${extensao}`;
|
||||
}
|
||||
|
||||
export const forwardMessage = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
|
||||
const { quotedMsg, signMessage, messageId, contactId } = req.body;
|
||||
const { id: userId, companyId } = req.user;
|
||||
const requestUser = await User.findByPk(userId);
|
||||
|
||||
if (!messageId || !contactId) {
|
||||
return res.status(200).send("MessageId or ContactId not found");
|
||||
}
|
||||
const message = await ShowMessageService(messageId);
|
||||
const contact = await ShowContactService(contactId, companyId);
|
||||
|
||||
if (!message) {
|
||||
return res.status(404).send("Message not found");
|
||||
}
|
||||
if (!contact) {
|
||||
return res.status(404).send("Contact not found");
|
||||
}
|
||||
|
||||
const whatsAppConnectionId = await GetWhatsAppFromMessage(message);
|
||||
if (!whatsAppConnectionId) {
|
||||
return res.status(404).send('Whatsapp from message not found');
|
||||
}
|
||||
|
||||
const ticket = await ShowTicketService(message.ticketId, message.companyId);
|
||||
|
||||
const createTicket = await FindOrCreateTicketService(
|
||||
contact,
|
||||
ticket?.whatsappId,
|
||||
0,
|
||||
ticket.companyId,
|
||||
contact.isGroup ? contact : null,
|
||||
);
|
||||
|
||||
let ticketData;
|
||||
|
||||
if (isNil(createTicket?.queueId)) {
|
||||
ticketData = {
|
||||
status: createTicket.isGroup ? "group" : "open",
|
||||
userId: requestUser.id,
|
||||
queueId: ticket.queueId
|
||||
}
|
||||
} else {
|
||||
ticketData = {
|
||||
status: createTicket.isGroup ? "group" : "open",
|
||||
userId: requestUser.id
|
||||
}
|
||||
}
|
||||
|
||||
await UpdateTicketService({
|
||||
ticketData,
|
||||
ticketId: createTicket.id,
|
||||
companyId: createTicket.companyId
|
||||
});
|
||||
|
||||
let body = message.body;
|
||||
if (message.mediaType === 'conversation' || message.mediaType === 'extendedTextMessage') {
|
||||
await SendWhatsAppMessage({ body, ticket: createTicket, quotedMsg, isForwarded: message.fromMe ? false : true });
|
||||
} else {
|
||||
|
||||
const mediaUrl = message.mediaUrl.replace(`:${process.env.PORT}`, '');
|
||||
const fileName = obterNomeEExtensaoDoArquivo(mediaUrl);
|
||||
|
||||
if (body === fileName) {
|
||||
body = "";
|
||||
}
|
||||
|
||||
const publicFolder = path.join(__dirname, '..', '..', '..', 'backend', 'public');
|
||||
|
||||
const filePath = path.join(publicFolder, `company${createTicket.companyId}`, fileName)
|
||||
|
||||
const mediaSrc = {
|
||||
fieldname: 'medias',
|
||||
originalname: fileName,
|
||||
encoding: '7bit',
|
||||
mimetype: message.mediaType,
|
||||
filename: fileName,
|
||||
path: filePath
|
||||
} as Express.Multer.File
|
||||
|
||||
await SendWhatsAppMedia({ media: mediaSrc, ticket: createTicket, body, isForwarded: message.fromMe ? false : true });
|
||||
}
|
||||
|
||||
return res.send();
|
||||
}
|
||||
|
||||
export const edit = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { messageId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
const { body }: MessageData = req.body;
|
||||
console.log(body)
|
||||
const { ticket , message } = await EditWhatsAppMessage({messageId, body});
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-${companyId}-appMessage`, {
|
||||
action:"update",
|
||||
message,
|
||||
ticket: ticket,
|
||||
contact: ticket.contact,
|
||||
});
|
||||
|
||||
return res.send();
|
||||
}
|
105
backend/src/controllers/MessageHubController.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { Request, Response } from "express";
|
||||
import User from "../models/User";
|
||||
import { getIO } from "../libs/socket";
|
||||
import Contact from "../models/Contact";
|
||||
import Ticket from "../models/Ticket";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import { SendMediaMessageService } from "../services/HubServices/SendMediaMessageHubService";
|
||||
import { SendTextMessageService } from "../services/HubServices/SendTextMessageHubService";
|
||||
import CreateHubTicketService from "../services/HubServices/CreateHubTicketService";
|
||||
|
||||
|
||||
interface TicketData {
|
||||
contactId: number;
|
||||
status: string;
|
||||
queueId: number;
|
||||
userId: number;
|
||||
channel: string;
|
||||
companyId: number;
|
||||
}
|
||||
|
||||
export const send = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
const { companyId } = req.user;
|
||||
|
||||
console.log('CompanyId do usuário autenticado:', companyId); // Verifique se companyId está correto aqui
|
||||
|
||||
const { body: message } = req.body;
|
||||
const { ticketId } = req.params;
|
||||
const medias = req.files as Express.Multer.File[];
|
||||
|
||||
console.log("sending hub message controller");
|
||||
|
||||
const ticket = await Ticket.findOne({
|
||||
where: { id: ticketId, companyId }, // Filtro pelo companyId
|
||||
include: [
|
||||
{
|
||||
model: Contact,
|
||||
as: "contact",
|
||||
attributes: ["number", "messengerId", "instagramId"]
|
||||
},
|
||||
{
|
||||
model: Whatsapp,
|
||||
as: "whatsapp",
|
||||
attributes: ["qrcode", "type", "companyId"]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!ticket) {
|
||||
return res.status(404).json({ message: "Ticket not found" });
|
||||
}
|
||||
|
||||
try {
|
||||
if (medias) {
|
||||
await Promise.all(
|
||||
medias.map(async (media: Express.Multer.File) => {
|
||||
await SendMediaMessageService(
|
||||
media,
|
||||
message,
|
||||
ticket.id,
|
||||
ticket.contact,
|
||||
ticket.whatsapp,
|
||||
companyId
|
||||
);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
await SendTextMessageService(
|
||||
message,
|
||||
ticket.id,
|
||||
ticket.contact,
|
||||
ticket.whatsapp,
|
||||
companyId
|
||||
);
|
||||
}
|
||||
|
||||
return res.status(200).json({ message: "Message sent" });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return res.status(400).json({ message: error });
|
||||
}
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { contactId, status, userId, channel }: TicketData = req.body;
|
||||
|
||||
const { companyId } = req.user; // Obtendo o companyId do usuário autenticado
|
||||
|
||||
const ticket = await CreateHubTicketService({
|
||||
contactId,
|
||||
status,
|
||||
userId,
|
||||
channel,
|
||||
companyId // Passando o companyId na criação do ticket
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(ticket.status).emit("ticket", {
|
||||
action: "update",
|
||||
ticket
|
||||
});
|
||||
|
||||
return res.status(200).json(ticket);
|
||||
};
|
146
backend/src/controllers/PlanController.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
// import { getIO } from "../libs/socket";
|
||||
import AppError from "../errors/AppError";
|
||||
import Plan from "../models/Plan";
|
||||
|
||||
import ListPlansService from "../services/PlanService/ListPlansService";
|
||||
import CreatePlanService from "../services/PlanService/CreatePlanService";
|
||||
import UpdatePlanService from "../services/PlanService/UpdatePlanService";
|
||||
import ShowPlanService from "../services/PlanService/ShowPlanService";
|
||||
import FindAllPlanServiceRegister from "../services/PlanService/FindAllPlanServiceRegister";
|
||||
import FindAllPlanService from "../services/PlanService/FindAllPlanService";
|
||||
import DeletePlanService from "../services/PlanService/DeletePlanService";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type StorePlanData = {
|
||||
name: string;
|
||||
id?: number | string;
|
||||
users: number | 0;
|
||||
connections: number | 0;
|
||||
queues: number | 0;
|
||||
value: number;
|
||||
useCampaigns?: boolean;
|
||||
useSchedules?: boolean;
|
||||
useInternalChat?: boolean;
|
||||
useExternalApi?: boolean;
|
||||
useKanban?: boolean;
|
||||
useOpenAi?: boolean;
|
||||
useIntegrations?: boolean;
|
||||
useInternal?: boolean;
|
||||
};
|
||||
|
||||
type UpdatePlanData = {
|
||||
name: string;
|
||||
id?: number | string;
|
||||
users?: number;
|
||||
connections?: number;
|
||||
queues?: number;
|
||||
value?: number;
|
||||
useCampaigns?: boolean;
|
||||
useSchedules?: boolean;
|
||||
useInternalChat?: boolean;
|
||||
useExternalApi?: boolean;
|
||||
useKanban?: boolean;
|
||||
useOpenAi?: boolean;
|
||||
useIntegrations?: boolean;
|
||||
useInternal?: boolean;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { plans, count, hasMore } = await ListPlansService({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ plans, count, hasMore });
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const plans: Plan[] = await FindAllPlanService();
|
||||
|
||||
return res.status(200).json(plans);
|
||||
};
|
||||
|
||||
export const register = async (req: Request, res: Response): Promise<Response> => {
|
||||
const plans: Plan[] = await FindAllPlanServiceRegister();
|
||||
|
||||
return res.status(200).json(plans);
|
||||
};
|
||||
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const newPlan: StorePlanData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(newPlan);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const plan = await CreatePlanService(newPlan);
|
||||
|
||||
// const io = getIO();
|
||||
// io.emit("plan", {
|
||||
// action: "create",
|
||||
// plan
|
||||
// });
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const plan = await ShowPlanService(id);
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const planData: UpdatePlanData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(planData);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const plan = await UpdatePlanService(planData);
|
||||
|
||||
// const io = getIO();
|
||||
// io.emit("plan", {
|
||||
// action: "update",
|
||||
// plan
|
||||
// });
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const plan = await DeletePlanService(id);
|
||||
|
||||
return res.status(200).json(plan);
|
||||
};
|
114
backend/src/controllers/PromptController.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import CreatePromptService from "../services/PromptServices/CreatePromptService";
|
||||
import DeletePromptService from "../services/PromptServices/DeletePromptService";
|
||||
import ListPromptsService from "../services/PromptServices/ListPromptsService";
|
||||
import ShowPromptService from "../services/PromptServices/ShowPromptService";
|
||||
import UpdatePromptService from "../services/PromptServices/UpdatePromptService";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import { verify } from "jsonwebtoken";
|
||||
import authConfig from "../config/auth";
|
||||
|
||||
interface TokenPayload {
|
||||
id: string;
|
||||
username: string;
|
||||
profile: string;
|
||||
companyId: number;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam?: string;
|
||||
pageNumber?: string | number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { pageNumber, searchParam } = req.query as IndexQuery;
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { companyId } = decoded as TokenPayload;
|
||||
const { prompts, count, hasMore } = await ListPromptsService({ searchParam, pageNumber, companyId });
|
||||
|
||||
return res.status(200).json({ prompts, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { companyId } = decoded as TokenPayload;
|
||||
const { name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages,voice,voiceKey,voiceRegion } = req.body;
|
||||
const promptTable = await CreatePromptService({ name, apiKey, prompt, maxTokens, temperature, promptTokens, completionTokens, totalTokens, queueId, maxMessages, companyId,voice,voiceKey,voiceRegion });
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("prompt", {
|
||||
action: "update",
|
||||
prompt: promptTable
|
||||
});
|
||||
|
||||
return res.status(200).json(promptTable);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { promptId } = req.params;
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { companyId } = decoded as TokenPayload;
|
||||
const prompt = await ShowPromptService({ promptId, companyId });
|
||||
|
||||
return res.status(200).json(prompt);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { promptId } = req.params;
|
||||
const promptData = req.body;
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { companyId } = decoded as TokenPayload;
|
||||
|
||||
const prompt = await UpdatePromptService({ promptData, promptId: promptId, companyId });
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("prompt", {
|
||||
action: "update",
|
||||
prompt
|
||||
});
|
||||
|
||||
return res.status(200).json(prompt);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { promptId } = req.params;
|
||||
const authHeader = req.headers.authorization;
|
||||
const [, token] = authHeader.split(" ");
|
||||
const decoded = verify(token, authConfig.secret);
|
||||
const { companyId } = decoded as TokenPayload;
|
||||
try {
|
||||
const { count } = await Whatsapp.findAndCountAll({ where: { promptId: +promptId, companyId } });
|
||||
|
||||
if (count > 0) return res.status(200).json({ message: "Não foi possível excluir! Verifique se este prompt está sendo usado nas conexões Whatsapp!" });
|
||||
|
||||
await DeletePromptService(promptId, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("prompt", {
|
||||
action: "delete",
|
||||
intelligenceId: +promptId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Prompt deleted" });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ message: "Não foi possível excluir! Verifique se este prompt está sendo usado!" });
|
||||
}
|
||||
};
|
||||
|
157
backend/src/controllers/QueueController.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import CreateQueueService from "../services/QueueService/CreateQueueService";
|
||||
import DeleteQueueService from "../services/QueueService/DeleteQueueService";
|
||||
import ListQueuesService from "../services/QueueService/ListQueuesService";
|
||||
import ShowQueueService from "../services/QueueService/ShowQueueService";
|
||||
import UpdateQueueService from "../services/QueueService/UpdateQueueService";
|
||||
import { isNil } from "lodash";
|
||||
import Queue from "../models/Queue";
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type QueueFilter = {
|
||||
companyId: number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId: userCompanyId } = req.user;
|
||||
const { companyId: queryCompanyId } = req.query as unknown as QueueFilter;
|
||||
let companyId = userCompanyId;
|
||||
|
||||
if (!isNil(queryCompanyId)) {
|
||||
companyId = +queryCompanyId;
|
||||
}
|
||||
|
||||
const queues = await ListQueuesService({ companyId });
|
||||
|
||||
return res.status(200).json(queues);
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueId } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const queue = await Queue.findByPk(queueId);
|
||||
|
||||
queue.update({
|
||||
mediaPath: file.filename,
|
||||
mediaName: file.originalname
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Arquivo Salvo" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueId } = req.params;
|
||||
|
||||
try {
|
||||
const queue = await Queue.findByPk(queueId);
|
||||
const filePath = path.resolve("public", queue.mediaPath);
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
queue.mediaPath = null;
|
||||
queue.mediaName = null;
|
||||
await queue.save();
|
||||
return res.send({ mensagem: "Arquivo excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { name, color, greetingMessage, outOfHoursMessage, schedules, orderQueue, integrationId, promptId } =
|
||||
req.body;
|
||||
const { companyId } = req.user;
|
||||
console.log("queue", integrationId, promptId)
|
||||
const queue = await CreateQueueService({
|
||||
name,
|
||||
color,
|
||||
greetingMessage,
|
||||
companyId,
|
||||
outOfHoursMessage,
|
||||
schedules,
|
||||
orderQueue: orderQueue === "" ? null : orderQueue,
|
||||
integrationId: integrationId === "" ? null : integrationId,
|
||||
promptId: promptId === "" ? null : promptId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-${companyId}-queue`, {
|
||||
action: "update",
|
||||
queue
|
||||
});
|
||||
|
||||
return res.status(200).json(queue);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { queueId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const queue = await ShowQueueService(queueId, companyId);
|
||||
|
||||
return res.status(200).json(queue);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
const { name, color, greetingMessage, outOfHoursMessage, schedules, orderQueue, integrationId, promptId } =
|
||||
req.body;
|
||||
const queue = await UpdateQueueService(queueId, {
|
||||
name,
|
||||
color,
|
||||
greetingMessage,
|
||||
outOfHoursMessage,
|
||||
schedules,
|
||||
orderQueue: orderQueue === "" ? null : orderQueue,
|
||||
integrationId: integrationId === "" ? null : integrationId,
|
||||
promptId: promptId === "" ? null : promptId
|
||||
}, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-${companyId}-queue`, {
|
||||
action: "update",
|
||||
queue
|
||||
});
|
||||
|
||||
return res.status(201).json(queue);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteQueueService(queueId, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.emit(`company-${companyId}-queue`, {
|
||||
action: "delete",
|
||||
queueId: +queueId
|
||||
});
|
||||
|
||||
return res.status(200).send();
|
||||
};
|
99
backend/src/controllers/QueueIntegrationController.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import CreateQueueIntegrationService from "../services/QueueIntegrationServices/CreateQueueIntegrationService";
|
||||
import DeleteQueueIntegrationService from "../services/QueueIntegrationServices/DeleteQueueIntegrationService";
|
||||
import ListQueueIntegrationService from "../services/QueueIntegrationServices/ListQueueIntegrationService";
|
||||
import ShowQueueIntegrationService from "../services/QueueIntegrationServices/ShowQueueIntegrationService";
|
||||
import UpdateQueueIntegrationService from "../services/QueueIntegrationServices/UpdateQueueIntegrationService";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { queueIntegrations, count, hasMore } = await ListQueueIntegrationService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.status(200).json({ queueIntegrations, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { type, name, projectName, jsonContent, language, urlN8N,
|
||||
typebotExpires,
|
||||
typebotKeywordFinish,
|
||||
typebotSlug,
|
||||
typebotUnknownMessage,
|
||||
typebotKeywordRestart,
|
||||
typebotRestartMessage } = req.body;
|
||||
const { companyId } = req.user;
|
||||
const queueIntegration = await CreateQueueIntegrationService({
|
||||
type, name, projectName, jsonContent, language, urlN8N, companyId,
|
||||
typebotExpires,
|
||||
typebotKeywordFinish,
|
||||
typebotSlug,
|
||||
typebotUnknownMessage,
|
||||
typebotKeywordRestart,
|
||||
typebotRestartMessage
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queueIntegration`, {
|
||||
action: "create",
|
||||
queueIntegration
|
||||
});
|
||||
|
||||
return res.status(200).json(queueIntegration);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { integrationId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const queueIntegration = await ShowQueueIntegrationService(integrationId, companyId);
|
||||
|
||||
return res.status(200).json(queueIntegration);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { integrationId } = req.params;
|
||||
const integrationData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const queueIntegration = await UpdateQueueIntegrationService({ integrationData, integrationId, companyId });
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queueIntegration`, {
|
||||
action: "update",
|
||||
queueIntegration
|
||||
});
|
||||
|
||||
return res.status(201).json(queueIntegration);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { integrationId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteQueueIntegrationService(integrationId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-queueIntegration`, {
|
||||
action: "delete",
|
||||
integrationId: +integrationId
|
||||
});
|
||||
|
||||
return res.status(200).send();
|
||||
};
|
110
backend/src/controllers/QueueOptionController.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
import CreateService from "../services/QueueOptionService/CreateService";
|
||||
import ListService from "../services/QueueOptionService/ListService";
|
||||
import UpdateService from "../services/QueueOptionService/UpdateService";
|
||||
import ShowService from "../services/QueueOptionService/ShowService";
|
||||
import DeleteService from "../services/QueueOptionService/DeleteService";
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import AppError from "../errors/AppError";
|
||||
import QueueOption from "../models/QueueOption";
|
||||
|
||||
type FilterList = {
|
||||
queueId: string | number;
|
||||
queueOptionId: string | number;
|
||||
parentId: string | number | boolean;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { queueId, queueOptionId, parentId } = req.query as FilterList;
|
||||
|
||||
const queueOptions = await ListService({ queueId, queueOptionId, parentId });
|
||||
|
||||
return res.json(queueOptions);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const queueOptionData = req.body;
|
||||
|
||||
const queueOption = await CreateService(queueOptionData);
|
||||
|
||||
return res.status(200).json(queueOption);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { queueOptionId } = req.params;
|
||||
|
||||
const queueOption = await ShowService(queueOptionId);
|
||||
|
||||
return res.status(200).json(queueOption);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueOptionId } = req.params
|
||||
const queueOptionData = req.body;
|
||||
|
||||
const queueOption = await UpdateService(queueOptionId, queueOptionData);
|
||||
|
||||
return res.status(200).json(queueOption);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueOptionId } = req.params
|
||||
|
||||
await DeleteService(queueOptionId);
|
||||
|
||||
return res.status(200).json({ message: "Option Delected" });
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueOptionId } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const queue = await QueueOption.findByPk(queueOptionId);
|
||||
|
||||
queue.update({
|
||||
mediaPath: file.filename,
|
||||
mediaName: file.originalname
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Arquivo Salvo" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { queueOptionId } = req.params;
|
||||
|
||||
try {
|
||||
const queue = await QueueOption.findByPk(queueOptionId);
|
||||
const filePath = path.resolve("public", queue.mediaPath);
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
queue.mediaPath = null;
|
||||
queue.mediaName = null;
|
||||
await queue.save();
|
||||
return res.send({ mensagem: "Arquivo excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
223
backend/src/controllers/QuickMessageController.ts
Normal file
@ -0,0 +1,223 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import ListService from "../services/QuickMessageService/ListService";
|
||||
import CreateService from "../services/QuickMessageService/CreateService";
|
||||
import ShowService from "../services/QuickMessageService/ShowService";
|
||||
import UpdateService from "../services/QuickMessageService/UpdateService";
|
||||
import DeleteService from "../services/QuickMessageService/DeleteService";
|
||||
import FindService from "../services/QuickMessageService/FindService";
|
||||
|
||||
import QuickMessage from "../models/QuickMessage";
|
||||
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
userId: string | number;
|
||||
};
|
||||
|
||||
type StoreData = {
|
||||
shortcode: string;
|
||||
message: string;
|
||||
userId: number | number;
|
||||
geral: boolean;
|
||||
};
|
||||
|
||||
type FindParams = {
|
||||
companyId: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId, id: userId } = req.user;
|
||||
|
||||
const { records, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId,
|
||||
userId
|
||||
});
|
||||
|
||||
return res.json({ records, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const data = req.body as StoreData;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
shortcode: Yup.string().required(),
|
||||
message: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const record = await CreateService({
|
||||
...data,
|
||||
companyId,
|
||||
userId: req.user.id
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, {
|
||||
action: "create",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await ShowService(id);
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body as StoreData;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
shortcode: Yup.string().required(),
|
||||
message: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(data);
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
|
||||
const record = await UpdateService({
|
||||
...data,
|
||||
userId: req.user.id,
|
||||
id,
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, {
|
||||
action: "update",
|
||||
record
|
||||
});
|
||||
|
||||
return res.status(200).json(record);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(id);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-quickmessage`, {
|
||||
action: "delete",
|
||||
id
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Contact deleted" });
|
||||
};
|
||||
|
||||
export const findList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const params = req.query as FindParams;
|
||||
const records: QuickMessage[] = await FindService(params);
|
||||
|
||||
return res.status(200).json(records);
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const quickmessage = await QuickMessage.findByPk(id);
|
||||
|
||||
quickmessage.update ({
|
||||
mediaPath: file.filename,
|
||||
mediaName: file.originalname
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Arquivo Anexado" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const { companyId } = req.user
|
||||
|
||||
try {
|
||||
// Encontre a mensagem rápida
|
||||
const quickmessage = await QuickMessage.findByPk(id);
|
||||
|
||||
// Verifique se a mensagem foi encontrada
|
||||
if (!quickmessage) {
|
||||
throw new AppError("Arquivo não encontrado", 404);
|
||||
}
|
||||
|
||||
// Aplique a mesma lógica de renomeação para gerar o nome correto do arquivo
|
||||
let filename = quickmessage.mediaName;
|
||||
|
||||
// Se o filename já tiver sido alterado (adicionando timestamp), remova esse prefixo
|
||||
const timestampRegex = /^\d+_/;
|
||||
if (timestampRegex.test(filename)) {
|
||||
// Remover o timestamp do começo do nome do arquivo
|
||||
filename = filename.replace(timestampRegex, '');
|
||||
}
|
||||
|
||||
const filePath = path.resolve(
|
||||
"public",
|
||||
`company${companyId}`,
|
||||
"quick",
|
||||
filename
|
||||
);
|
||||
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath); // Exclui o arquivo
|
||||
}
|
||||
|
||||
// Atualiza os dados da mensagem no banco
|
||||
await quickmessage.update({
|
||||
mediaPath: null,
|
||||
mediaName: null
|
||||
});
|
||||
|
||||
return res.send({ mensagem: "Arquivo Excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
105
backend/src/controllers/ReportsController.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { QueryTypes } from 'sequelize';
|
||||
|
||||
import sequelize from '../database';
|
||||
|
||||
type RequestQueryProps = {
|
||||
companyId: string;
|
||||
initialDate: string;
|
||||
finalDate: string;
|
||||
};
|
||||
|
||||
export const appointmentsAtendent = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<Response> => {
|
||||
const { companyId, initialDate, finalDate } = req.query as RequestQueryProps;
|
||||
|
||||
const resultAppointmentsByAttendents = await sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
u."name" as user_name
|
||||
,COUNT(t.*) as total_tickets
|
||||
FROM "Users" u
|
||||
LEFT JOIN "TicketTraking" tt ON tt."userId" = u.id
|
||||
LEFT JOIN "Tickets" t ON t.id = tt."ticketId" AND t."createdAt" BETWEEN '${initialDate}' AND '${finalDate}'
|
||||
where u."companyId" = ${companyId}
|
||||
GROUP BY u."name"
|
||||
ORDER BY total_tickets ASC
|
||||
`,
|
||||
{ type: QueryTypes.SELECT },
|
||||
);
|
||||
|
||||
const resultTicketsByQueues = await sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
q."name"
|
||||
,COUNT(DISTINCT t.id) as total_tickets
|
||||
FROM "Queues" q
|
||||
LEFT JOIN "Messages" m ON m."queueId" = q.id
|
||||
LEFt JOIN "Tickets" t ON t.id = m."ticketId" AND t."createdAt" BETWEEN '${initialDate}' AND '${finalDate}'
|
||||
WHERE q."companyId" = ${companyId}
|
||||
GROUP BY q."name"
|
||||
ORDER BY total_tickets ASC
|
||||
`,
|
||||
{ type: QueryTypes.SELECT },
|
||||
);
|
||||
|
||||
return res.json({
|
||||
appointmentsByAttendents: resultAppointmentsByAttendents,
|
||||
ticketsByQueues: resultTicketsByQueues,
|
||||
});
|
||||
};
|
||||
|
||||
export const rushHour = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<Response> => {
|
||||
const { companyId, initialDate, finalDate } = req.query as RequestQueryProps;
|
||||
|
||||
const resultAppointmentsByHours = await sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
extract (hour from m."createdAt") AS message_hour,
|
||||
COUNT(m.id) AS message_count
|
||||
FROM "Messages" m
|
||||
LEFT JOIN "Tickets" t ON t.id = m."ticketId"
|
||||
WHERE t."companyId" = ${companyId}
|
||||
AND m."createdAt" BETWEEN '${initialDate}' AND '${finalDate}'
|
||||
GROUP BY
|
||||
extract (hour from m."createdAt")
|
||||
ORDER BY
|
||||
extract (hour from m."createdAt")
|
||||
`,
|
||||
{ type: QueryTypes.SELECT },
|
||||
);
|
||||
|
||||
return res.json(resultAppointmentsByHours);
|
||||
};
|
||||
|
||||
export const departamentRatings = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<Response> => {
|
||||
const { companyId, initialDate, finalDate } = req.query as RequestQueryProps;
|
||||
|
||||
const resultDepartamentRating = await sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
m."ticketId"
|
||||
,q."name"
|
||||
,round(avg(ur.rate), 2) AS total_rate
|
||||
FROM "Messages" m
|
||||
LEFT JOIN "Tickets" t ON t.id = m."ticketId"
|
||||
LEFT JOIN "UserRatings" ur ON ur."ticketId" = t.id
|
||||
LEFT JOIN "Queues" q ON q.id = m."queueId"
|
||||
WHERE m."queueId" IS NOT NULL
|
||||
AND m."companyId" = ${companyId}
|
||||
AND ur."createdAt" BETWEEN '${initialDate}' AND '${finalDate}'
|
||||
GROUP BY m."ticketId", q."name"
|
||||
`,
|
||||
{ type: QueryTypes.SELECT },
|
||||
);
|
||||
|
||||
return res.json(resultDepartamentRating);
|
||||
};
|
154
backend/src/controllers/ScheduleController.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
import CreateService from "../services/ScheduleServices/CreateService";
|
||||
import ListService from "../services/ScheduleServices/ListService";
|
||||
import UpdateService from "../services/ScheduleServices/UpdateService";
|
||||
import ShowService from "../services/ScheduleServices/ShowService";
|
||||
import DeleteService from "../services/ScheduleServices/DeleteService";
|
||||
import Schedule from "../models/Schedule";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { head } from "lodash";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam?: string;
|
||||
contactId?: number | string;
|
||||
userId?: number | string;
|
||||
pageNumber?: string | number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { contactId, userId, pageNumber, searchParam } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { schedules, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
contactId,
|
||||
userId,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ schedules, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const {
|
||||
body,
|
||||
sendAt,
|
||||
contactId,
|
||||
userId
|
||||
} = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schedule = await CreateService({
|
||||
body,
|
||||
sendAt,
|
||||
contactId,
|
||||
companyId,
|
||||
userId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("schedule", {
|
||||
action: "create",
|
||||
schedule
|
||||
});
|
||||
|
||||
return res.status(200).json(schedule);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { scheduleId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schedule = await ShowService(scheduleId, companyId);
|
||||
|
||||
return res.status(200).json(schedule);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
const { scheduleId } = req.params;
|
||||
const scheduleData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schedule = await UpdateService({ scheduleData, id: scheduleId, companyId });
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("schedule", {
|
||||
action: "update",
|
||||
schedule
|
||||
});
|
||||
|
||||
return res.status(200).json(schedule);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { scheduleId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await DeleteService(scheduleId, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("schedule", {
|
||||
action: "delete",
|
||||
scheduleId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Schedule deleted" });
|
||||
};
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
|
||||
try {
|
||||
const schedule = await Schedule.findByPk(id);
|
||||
schedule.mediaPath = file.filename;
|
||||
schedule.mediaName = file.originalname;
|
||||
|
||||
await schedule.save();
|
||||
return res.send({ mensagem: "Arquivo Anexado" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMedia = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const schedule = await Schedule.findByPk(id);
|
||||
const filePath = path.resolve("public", schedule.mediaPath);
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
if (fileExists) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
schedule.mediaPath = null;
|
||||
schedule.mediaName = null;
|
||||
await schedule.save();
|
||||
return res.send({ mensagem: "Arquivo Excluído" });
|
||||
} catch (err: any) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
};
|
80
backend/src/controllers/SessionController.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { Request, Response } from "express";
|
||||
import AppError from "../errors/AppError";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import AuthUserService from "../services/UserServices/AuthUserService";
|
||||
import { SendRefreshToken } from "../helpers/SendRefreshToken";
|
||||
import { RefreshTokenService } from "../services/AuthServices/RefreshTokenService";
|
||||
import FindUserFromToken from "../services/AuthServices/FindUserFromToken";
|
||||
import User from "../models/User";
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { email, password } = req.body;
|
||||
|
||||
const { token, serializedUser, refreshToken } = await AuthUserService({
|
||||
email,
|
||||
password
|
||||
});
|
||||
|
||||
SendRefreshToken(res, refreshToken);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`user-${serializedUser.id}`).emit(`company-${serializedUser.companyId}-auth`, {
|
||||
action: "update",
|
||||
user: {
|
||||
id: serializedUser.id,
|
||||
email: serializedUser.email,
|
||||
companyId: serializedUser.companyId
|
||||
}
|
||||
});
|
||||
|
||||
return res.status(200).json({
|
||||
token,
|
||||
user: serializedUser
|
||||
});
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const token: string = req.cookies.jrt;
|
||||
|
||||
if (!token) {
|
||||
throw new AppError("ERR_SESSION_EXPIRED", 401);
|
||||
}
|
||||
|
||||
const { user, newToken, refreshToken } = await RefreshTokenService(
|
||||
res,
|
||||
token
|
||||
);
|
||||
|
||||
SendRefreshToken(res, refreshToken);
|
||||
|
||||
return res.json({ token: newToken, user });
|
||||
};
|
||||
|
||||
export const me = async (req: Request, res: Response): Promise<Response> => {
|
||||
const token: string = req.cookies.jrt;
|
||||
const user = await FindUserFromToken(token);
|
||||
const { id, profile, super: superAdmin } = user;
|
||||
|
||||
if (!token) {
|
||||
throw new AppError("ERR_SESSION_EXPIRED", 401);
|
||||
}
|
||||
|
||||
return res.json({ id, profile, super: superAdmin });
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.user;
|
||||
const user = await User.findByPk(id);
|
||||
await user.update({ online: false });
|
||||
|
||||
res.clearCookie("jrt");
|
||||
|
||||
return res.send();
|
||||
};
|
158
backend/src/controllers/SettingController.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import { Request, Response } from "express";
|
||||
import authConfig from "../config/auth";
|
||||
import * as Yup from "yup";
|
||||
|
||||
import { getIO } from "../libs/socket";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
import { head } from "lodash";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import User from "../models/User";
|
||||
import Company from "../models/Company";
|
||||
|
||||
import UpdateSettingService from "../services/SettingServices/UpdateSettingService";
|
||||
import ListSettingsService from "../services/SettingServices/ListSettingsService";
|
||||
import ShowSettingsService from "../services/SettingServices/ShowSettingsService";
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
|
||||
//if (req.user.profile !== "admin") {
|
||||
//throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
//}
|
||||
|
||||
const settings = await ListSettingsService({ companyId });
|
||||
|
||||
return res.status(200).json(settings);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
const { settingKey: key } = req.params;
|
||||
const { value } = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const setting = await UpdateSettingService({
|
||||
key,
|
||||
value,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-settings`, {
|
||||
action: "update",
|
||||
setting
|
||||
});
|
||||
|
||||
return res.status(200).json(setting);
|
||||
};
|
||||
|
||||
|
||||
export const show = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
|
||||
//const { companyId } = req.user;
|
||||
const companyId = 1;
|
||||
const { settingKey } = req.params;
|
||||
|
||||
|
||||
const retornoData = await ShowSettingsService({ settingKey, companyId });
|
||||
|
||||
return res.status(200).json(retornoData);
|
||||
};
|
||||
|
||||
|
||||
export const mediaUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { body } = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const userId = req.user.id;
|
||||
const requestUser = await User.findByPk(userId);
|
||||
|
||||
if (requestUser.super === false) {
|
||||
throw new AppError("você nao tem permissão para esta ação!");
|
||||
}
|
||||
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
if (companyId !== 1) {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
console.log(file);
|
||||
return res.send({ mensagem: "Arquivo Anexado" });
|
||||
};
|
||||
|
||||
|
||||
export const certUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { body } = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const userId = req.user.id;
|
||||
const requestUser = await User.findByPk(userId);
|
||||
|
||||
if (requestUser.super === false) {
|
||||
throw new AppError("você nao tem permissão para esta ação!");
|
||||
}
|
||||
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
if (companyId !== 1) {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
console.log(file);
|
||||
return res.send({ mensagem: "Arquivo Anexado" });
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const docUpload = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { body } = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const userId = req.user.id;
|
||||
const requestUser = await User.findByPk(userId);
|
||||
|
||||
if (requestUser.super === false) {
|
||||
throw new AppError("você nao tem permissão para esta ação!");
|
||||
}
|
||||
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
if (companyId !== 1) {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
const files = req.files as Express.Multer.File[];
|
||||
const file = head(files);
|
||||
console.log(file);
|
||||
return res.send({ mensagem: "Arquivo Anexado" });
|
||||
};
|
199
backend/src/controllers/SubscriptionController.ts
Normal file
@ -0,0 +1,199 @@
|
||||
import { Request, Response } from "express";
|
||||
import express from "express";
|
||||
import * as Yup from "yup";
|
||||
import Gerencianet from "gn-api-sdk-typescript";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
import options from "../config/Gn";
|
||||
import Company from "../models/Company";
|
||||
import Invoices from "../models/Invoices";
|
||||
import Subscriptions from "../models/Subscriptions";
|
||||
import { getIO } from "../libs/socket";
|
||||
import UpdateUserService from "../services/UserServices/UpdateUserService";
|
||||
|
||||
const app = express();
|
||||
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const gerencianet = Gerencianet(options);
|
||||
return res.json(gerencianet.getSubscriptions());
|
||||
};
|
||||
|
||||
export const createSubscription = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const gerencianet = Gerencianet(options);
|
||||
const { companyId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
price: Yup.string().required(),
|
||||
users: Yup.string().required(),
|
||||
connections: Yup.string().required()
|
||||
});
|
||||
|
||||
if (!(await schema.isValid(req.body))) {
|
||||
throw new AppError("Validation fails", 400);
|
||||
}
|
||||
|
||||
const {
|
||||
firstName,
|
||||
price,
|
||||
users,
|
||||
connections,
|
||||
address2,
|
||||
city,
|
||||
state,
|
||||
zipcode,
|
||||
country,
|
||||
plan,
|
||||
invoiceId
|
||||
} = req.body;
|
||||
|
||||
|
||||
const body = {
|
||||
calendario: {
|
||||
expiracao: 3600
|
||||
},
|
||||
valor: {
|
||||
original: price.toLocaleString("pt-br", { minimumFractionDigits: 2 }).replace(",", ".")
|
||||
},
|
||||
chave: process.env.GERENCIANET_PIX_KEY,
|
||||
solicitacaoPagador: `#Fatura:${invoiceId}`
|
||||
};
|
||||
try {
|
||||
const pix = await gerencianet.pixCreateImmediateCharge(null, body);
|
||||
|
||||
const qrcode = await gerencianet.pixGenerateQRCode({
|
||||
id: pix.loc.id
|
||||
});
|
||||
|
||||
const updateCompany = await Company.findOne();
|
||||
|
||||
if (!updateCompany) {
|
||||
throw new AppError("Company not found", 404);
|
||||
}
|
||||
|
||||
|
||||
/* await Subscriptions.create({
|
||||
companyId,
|
||||
isActive: false,
|
||||
userPriceCents: users,
|
||||
whatsPriceCents: connections,
|
||||
lastInvoiceUrl: pix.location,
|
||||
lastPlanChange: new Date(),
|
||||
providerSubscriptionId: pix.loc.id,
|
||||
expiresAt: new Date()
|
||||
}); */
|
||||
|
||||
/* const { id } = req.user;
|
||||
const userData = {};
|
||||
const userId = id;
|
||||
const requestUserId = parseInt(id);
|
||||
const user = await UpdateUserService({ userData, userId, companyId, requestUserId }); */
|
||||
|
||||
/* const io = getIO();
|
||||
io.emit("user", {
|
||||
action: "update",
|
||||
user
|
||||
}); */
|
||||
|
||||
|
||||
return res.json({
|
||||
...pix,
|
||||
qrcode,
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
throw new AppError("Validation fails", 400);
|
||||
}
|
||||
};
|
||||
|
||||
export const createWebhook = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const schema = Yup.object().shape({
|
||||
chave: Yup.string().required(),
|
||||
url: Yup.string().required()
|
||||
});
|
||||
|
||||
if (!(await schema.isValid(req.body))) {
|
||||
throw new AppError("Validation fails", 400);
|
||||
}
|
||||
|
||||
const { chave, url } = req.body;
|
||||
|
||||
const body = {
|
||||
webhookUrl: url
|
||||
};
|
||||
|
||||
const params = {
|
||||
chave
|
||||
};
|
||||
|
||||
try {
|
||||
const gerencianet = Gerencianet(options);
|
||||
const create = await gerencianet.pixConfigWebhook(params, body);
|
||||
return res.json(create);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const webhook = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { type } = req.params;
|
||||
const { evento } = req.body;
|
||||
if (evento === "teste_webhook") {
|
||||
return res.json({ ok: true });
|
||||
}
|
||||
if (req.body.pix) {
|
||||
const gerencianet = Gerencianet(options);
|
||||
req.body.pix.forEach(async (pix: any) => {
|
||||
const detahe = await gerencianet.pixDetailCharge({
|
||||
txid: pix.txid
|
||||
});
|
||||
|
||||
if (detahe.status === "CONCLUIDA") {
|
||||
const { solicitacaoPagador } = detahe;
|
||||
const invoiceID = solicitacaoPagador.replace("#Fatura:", "");
|
||||
const invoices = await Invoices.findByPk(invoiceID);
|
||||
const companyId =invoices.companyId;
|
||||
const company = await Company.findByPk(companyId);
|
||||
|
||||
const expiresAt = new Date(company.dueDate);
|
||||
expiresAt.setDate(expiresAt.getDate() + 30);
|
||||
const date = expiresAt.toISOString().split("T")[0];
|
||||
|
||||
if (company) {
|
||||
await company.update({
|
||||
dueDate: date
|
||||
});
|
||||
const invoi = await invoices.update({
|
||||
id: invoiceID,
|
||||
status: 'paid'
|
||||
});
|
||||
await company.reload();
|
||||
const io = getIO();
|
||||
const companyUpdate = await Company.findOne({
|
||||
where: {
|
||||
id: companyId
|
||||
}
|
||||
});
|
||||
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-payment`, {
|
||||
action: detahe.status,
|
||||
company: companyUpdate
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return res.json({ ok: true });
|
||||
};
|
128
backend/src/controllers/TagController.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
import CreateService from "../services/TagServices/CreateService";
|
||||
import ListService from "../services/TagServices/ListService";
|
||||
import UpdateService from "../services/TagServices/UpdateService";
|
||||
import ShowService from "../services/TagServices/ShowService";
|
||||
import DeleteService from "../services/TagServices/DeleteService";
|
||||
import SimpleListService from "../services/TagServices/SimpleListService";
|
||||
import SyncTagService from "../services/TagServices/SyncTagsService";
|
||||
import KanbanListService from "../services/TagServices/KanbanListService";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam?: string;
|
||||
pageNumber?: string | number;
|
||||
kanban?: number;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { pageNumber, searchParam } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { tags, count, hasMore } = await ListService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId
|
||||
});
|
||||
|
||||
return res.json({ tags, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { name, color, kanban } = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const tag = await CreateService({
|
||||
name,
|
||||
color,
|
||||
companyId,
|
||||
kanban
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit("tag", {
|
||||
action: "create",
|
||||
tag
|
||||
});
|
||||
|
||||
return res.status(200).json(tag);
|
||||
};
|
||||
|
||||
export const kanban = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
|
||||
const tags = await KanbanListService({ companyId });
|
||||
|
||||
return res.json({lista:tags});
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { tagId } = req.params;
|
||||
|
||||
const tag = await ShowService(tagId);
|
||||
|
||||
return res.status(200).json(tag);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
const { tagId } = req.params;
|
||||
const tagData = req.body;
|
||||
|
||||
const tag = await UpdateService({ tagData, id: tagId });
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${req.user.companyId}-mainchannel`).emit("tag", {
|
||||
action: "update",
|
||||
tag
|
||||
});
|
||||
|
||||
return res.status(200).json(tag);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { tagId } = req.params;
|
||||
|
||||
await DeleteService(tagId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${req.user.companyId}-mainchannel`).emit("tag", {
|
||||
action: "delete",
|
||||
tagId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Tag deleted" });
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam } = req.query as IndexQuery;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const tags = await SimpleListService({ searchParam, companyId });
|
||||
|
||||
return res.json(tags);
|
||||
};
|
||||
|
||||
export const syncTags = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const data = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const tags = await SyncTagService({ ...data, companyId });
|
||||
|
||||
return res.json(tags);
|
||||
};
|
378
backend/src/controllers/TicketController.ts
Normal file
@ -0,0 +1,378 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import Ticket from "../models/Ticket";
|
||||
|
||||
import CreateTicketService from "../services/TicketServices/CreateTicketService";
|
||||
import DeleteTicketService from "../services/TicketServices/DeleteTicketService";
|
||||
import ListTicketsService from "../services/TicketServices/ListTicketsService";
|
||||
import ShowTicketUUIDService from "../services/TicketServices/ShowTicketFromUUIDService";
|
||||
import ShowTicketService from "../services/TicketServices/ShowTicketService";
|
||||
import ListTicketsServiceReport from "../services/TicketServices/ListTicketsServiceReport";
|
||||
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
||||
import ListTicketsServiceKanban from "../services/TicketServices/ListTicketsServiceKanban";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
status: string;
|
||||
date: string;
|
||||
updatedAt?: string;
|
||||
showAll: string;
|
||||
withUnreadMessages: string;
|
||||
queueIds: string;
|
||||
tags: string;
|
||||
users: string;
|
||||
};
|
||||
|
||||
interface TicketData {
|
||||
contactId: number;
|
||||
status: string;
|
||||
queueId: number;
|
||||
userId: number;
|
||||
whatsappId: string;
|
||||
useIntegration: boolean;
|
||||
promptId: number;
|
||||
integrationId: number;
|
||||
}
|
||||
|
||||
type IndexQueryReport = {
|
||||
searchParam: string;
|
||||
contactId: string;
|
||||
whatsappId: string;
|
||||
dateFrom: string;
|
||||
dateTo: string;
|
||||
status: string;
|
||||
//lastMessage: string;
|
||||
queueIds: string;
|
||||
tags: string;
|
||||
users: string;
|
||||
page: string;
|
||||
pageSize: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO INDEX EXECUTADA");
|
||||
const {
|
||||
pageNumber,
|
||||
status,
|
||||
date,
|
||||
updatedAt,
|
||||
searchParam,
|
||||
showAll,
|
||||
queueIds: queueIdsStringified,
|
||||
tags: tagIdsStringified,
|
||||
users: userIdsStringified,
|
||||
withUnreadMessages
|
||||
} = req.query as IndexQuery;
|
||||
|
||||
const userId = req.user.id;
|
||||
const { companyId } = req.user;
|
||||
|
||||
let queueIds: number[] = [];
|
||||
let tagsIds: number[] = [];
|
||||
let usersIds: number[] = [];
|
||||
|
||||
if (queueIdsStringified) {
|
||||
queueIds = JSON.parse(queueIdsStringified);
|
||||
}
|
||||
|
||||
if (tagIdsStringified) {
|
||||
tagsIds = JSON.parse(tagIdsStringified);
|
||||
}
|
||||
|
||||
if (userIdsStringified) {
|
||||
usersIds = JSON.parse(userIdsStringified);
|
||||
}
|
||||
|
||||
const { tickets, count, hasMore } = await ListTicketsService({
|
||||
searchParam,
|
||||
tags: tagsIds,
|
||||
users: usersIds,
|
||||
pageNumber,
|
||||
status,
|
||||
date,
|
||||
updatedAt,
|
||||
showAll,
|
||||
userId,
|
||||
queueIds,
|
||||
withUnreadMessages,
|
||||
companyId,
|
||||
|
||||
|
||||
});
|
||||
return res.status(200).json({ tickets, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO STORE EXECUTADA");
|
||||
|
||||
const { contactId, status, userId, queueId, whatsappId }: TicketData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const ticket = await CreateTicketService({
|
||||
contactId,
|
||||
status,
|
||||
userId,
|
||||
companyId,
|
||||
queueId,
|
||||
whatsappId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(ticket.status).emit(`company-${companyId}-ticket`, {
|
||||
action: "update",
|
||||
ticket
|
||||
});
|
||||
return res.status(200).json(ticket);
|
||||
};
|
||||
|
||||
export const kanban = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO KANBAN EXECUTADA");
|
||||
|
||||
const {
|
||||
pageNumber,
|
||||
status,
|
||||
date,
|
||||
updatedAt,
|
||||
searchParam,
|
||||
showAll,
|
||||
queueIds: queueIdsStringified,
|
||||
tags: tagIdsStringified,
|
||||
users: userIdsStringified,
|
||||
withUnreadMessages
|
||||
} = req.query as IndexQuery;
|
||||
|
||||
|
||||
const userId = req.user.id;
|
||||
const { companyId } = req.user;
|
||||
|
||||
let queueIds: number[] = [];
|
||||
let tagsIds: number[] = [];
|
||||
let usersIds: number[] = [];
|
||||
|
||||
if (queueIdsStringified) {
|
||||
queueIds = JSON.parse(queueIdsStringified);
|
||||
}
|
||||
|
||||
if (tagIdsStringified) {
|
||||
tagsIds = JSON.parse(tagIdsStringified);
|
||||
}
|
||||
|
||||
if (userIdsStringified) {
|
||||
usersIds = JSON.parse(userIdsStringified);
|
||||
}
|
||||
|
||||
const { tickets, count, hasMore } = await ListTicketsServiceKanban({
|
||||
searchParam,
|
||||
tags: tagsIds,
|
||||
users: usersIds,
|
||||
pageNumber,
|
||||
status,
|
||||
date,
|
||||
updatedAt,
|
||||
showAll,
|
||||
userId,
|
||||
queueIds,
|
||||
withUnreadMessages,
|
||||
companyId
|
||||
|
||||
});
|
||||
|
||||
return res.status(200).json({ tickets, count, hasMore });
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO SHOW EXECUTADA");
|
||||
|
||||
const { ticketId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
console.log("VALOR DE ticketId NA FUNÇÃO SHOW:", ticketId); // Novo log para ver o valor
|
||||
|
||||
if (!ticketId || ticketId === "undefined" || isNaN(+ticketId)) {
|
||||
console.log("ERRO: ticketId inválido detectado na função SHOW:", ticketId);
|
||||
return res.status(400).json({ error: "Ticket ID inválido" });
|
||||
}
|
||||
|
||||
const contact = await ShowTicketService(ticketId, companyId);
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const showFromUUID = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { uuid } = req.params;
|
||||
|
||||
const ticket: Ticket = await ShowTicketUUIDService(uuid);
|
||||
|
||||
return res.status(200).json(ticket);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO UPDATE EXECUTADA");
|
||||
|
||||
const { ticketId } = req.params;
|
||||
const ticketData: TicketData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
console.log("VALOR DE ticketId NA FUNÇÃO UPDATE:", ticketId); // Novo log
|
||||
console.log("DADOS ENVIADOS NA FUNÇÃO UPDATE:", ticketData); // Novo log
|
||||
|
||||
const { ticket } = await UpdateTicketService({
|
||||
ticketData,
|
||||
ticketId,
|
||||
companyId
|
||||
});
|
||||
|
||||
console.log("TICKET RETORNADO POR UPDATE:", ticket); // Novo log
|
||||
|
||||
return res.status(200).json(ticket);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO REMOVE EXECUTADA");
|
||||
|
||||
const { ticketId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await ShowTicketService(ticketId, companyId);
|
||||
|
||||
const ticket = await DeleteTicketService(ticketId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(ticketId)
|
||||
.to(`company-${companyId}-${ticket.status}`)
|
||||
.to(`company-${companyId}-notification`)
|
||||
.to(`queue-${ticket.queueId}-${ticket.status}`)
|
||||
.to(`queue-${ticket.queueId}-notification`)
|
||||
.emit(`company-${companyId}-ticket`, {
|
||||
action: "delete",
|
||||
ticketId: +ticketId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "ticket deleted" });
|
||||
};
|
||||
|
||||
|
||||
export const report = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO REPORT EXECUTADA");
|
||||
|
||||
const {
|
||||
searchParam,
|
||||
contactId,
|
||||
whatsappId: whatsappIdsStringified,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
status: statusStringified,
|
||||
//lastMessage,
|
||||
queueIds: queueIdsStringified,
|
||||
tags: tagIdsStringified,
|
||||
users: userIdsStringified,
|
||||
page: pageNumber,
|
||||
pageSize
|
||||
} = req.query as IndexQueryReport;
|
||||
|
||||
const userId = req.user.id;
|
||||
const { companyId } = req.user;
|
||||
|
||||
let queueIds: number[] = [];
|
||||
let whatsappIds: string[] = [];
|
||||
let tagsIds: number[] = [];
|
||||
let usersIds: number[] = [];
|
||||
let statusIds: string[] = [];
|
||||
|
||||
|
||||
if (statusStringified) {
|
||||
statusIds = JSON.parse(statusStringified);
|
||||
}
|
||||
|
||||
if (whatsappIdsStringified) {
|
||||
whatsappIds = JSON.parse(whatsappIdsStringified);
|
||||
}
|
||||
|
||||
if (queueIdsStringified) {
|
||||
queueIds = JSON.parse(queueIdsStringified);
|
||||
}
|
||||
|
||||
if (tagIdsStringified) {
|
||||
tagsIds = JSON.parse(tagIdsStringified);
|
||||
}
|
||||
|
||||
if (userIdsStringified) {
|
||||
usersIds = JSON.parse(userIdsStringified);
|
||||
}
|
||||
|
||||
const { tickets, totalTickets } = await ListTicketsServiceReport(
|
||||
companyId,
|
||||
{
|
||||
searchParam,
|
||||
queueIds,
|
||||
tags: tagsIds,
|
||||
users: usersIds,
|
||||
status: statusIds,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
userId,
|
||||
contactId,
|
||||
whatsappId: whatsappIds
|
||||
},
|
||||
+pageNumber,
|
||||
+pageSize
|
||||
);
|
||||
|
||||
return res.status(200).json({ tickets, totalTickets });
|
||||
};
|
||||
|
||||
export const closeAll = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
console.log("FUNÇÃO CLOSEALL EXECUTADA");
|
||||
|
||||
const { companyId } = req.user;
|
||||
const { status }: TicketData = req.body;
|
||||
const io = getIO();
|
||||
|
||||
const { rows: tickets } = await Ticket.findAndCountAll({
|
||||
where: { companyId: companyId, status: status },
|
||||
order: [["updatedAt", "DESC"]]
|
||||
});
|
||||
|
||||
tickets.forEach(async ticket => {
|
||||
|
||||
await ticket.update({
|
||||
status: "closed",
|
||||
useIntegration: false,
|
||||
promptId: null,
|
||||
integrationId: null,
|
||||
unreadMessages: 0
|
||||
})
|
||||
|
||||
const io = getIO();
|
||||
io.to(`${ticket.id}`)
|
||||
.to(`company-${companyId}-${ticket.status}`)
|
||||
.to(`company-${companyId}-notification`)
|
||||
.to(`queue-${ticket.queueId}-${ticket.status}`)
|
||||
.to(`queue-${ticket.queueId}-notification`)
|
||||
.emit(`company-${companyId}-ticket`, {
|
||||
action: "delete",
|
||||
ticketId: ticket.id
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return res.status(200).json();
|
||||
};
|
||||
|
138
backend/src/controllers/TicketNoteController.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import * as Yup from "yup";
|
||||
import { Request, Response } from "express";
|
||||
import AppError from "../errors/AppError";
|
||||
import TicketNote from "../models/TicketNote";
|
||||
|
||||
import ListTicketNotesService from "../services/TicketNoteService/ListTicketNotesService";
|
||||
import CreateTicketNoteService from "../services/TicketNoteService/CreateTicketNoteService";
|
||||
import UpdateTicketNoteService from "../services/TicketNoteService/UpdateTicketNoteService";
|
||||
import ShowTicketNoteService from "../services/TicketNoteService/ShowTicketNoteService";
|
||||
import FindAllTicketNotesService from "../services/TicketNoteService/FindAllTicketNotesService";
|
||||
import DeleteTicketNoteService from "../services/TicketNoteService/DeleteTicketNoteService";
|
||||
import FindNotesByContactIdAndTicketId from "../services/TicketNoteService/FindNotesByContactIdAndTicketId";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type StoreTicketNoteData = {
|
||||
note: string;
|
||||
userId: number;
|
||||
contactId: number | 0;
|
||||
ticketId: number | 0;
|
||||
id?: number | string;
|
||||
};
|
||||
|
||||
type UpdateTicketNoteData = {
|
||||
note: string;
|
||||
id?: number | string;
|
||||
userId?: number | 0;
|
||||
contactId?: number | 0;
|
||||
ticketId?: number | 0;
|
||||
};
|
||||
|
||||
type QueryFilteredNotes = {
|
||||
contactId: number | string;
|
||||
ticketId: number | string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
const { ticketNotes, count, hasMore } = await ListTicketNotesService({
|
||||
searchParam,
|
||||
pageNumber
|
||||
});
|
||||
|
||||
return res.json({ ticketNotes, count, hasMore });
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const ticketNotes: TicketNote[] = await FindAllTicketNotesService();
|
||||
|
||||
return res.status(200).json(ticketNotes);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const newTicketNote: StoreTicketNoteData = req.body;
|
||||
const { id: userId } = req.user;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
note: Yup.string().required()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(newTicketNote);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const ticketNote = await CreateTicketNoteService({
|
||||
...newTicketNote,
|
||||
userId
|
||||
});
|
||||
|
||||
return res.status(200).json(ticketNote);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
const ticketNote = await ShowTicketNoteService(id);
|
||||
|
||||
return res.status(200).json(ticketNote);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const ticketNote: UpdateTicketNoteData = req.body;
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
note: Yup.string()
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(ticketNote);
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const recordUpdated = await UpdateTicketNoteService(ticketNote);
|
||||
|
||||
return res.status(200).json(recordUpdated);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { id } = req.params;
|
||||
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
await DeleteTicketNoteService(id);
|
||||
|
||||
return res.status(200).json({ message: "Observação removida" });
|
||||
};
|
||||
|
||||
export const findFilteredList = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
try {
|
||||
const { contactId, ticketId } = req.query as QueryFilteredNotes;
|
||||
const notes: TicketNote[] = await FindNotesByContactIdAndTicketId({
|
||||
contactId,
|
||||
ticketId
|
||||
});
|
||||
|
||||
return res.status(200).json(notes);
|
||||
} catch (e) {
|
||||
return res.status(500).json({ message: e });
|
||||
}
|
||||
};
|
57
backend/src/controllers/TicketTagController.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { Request, Response } from "express";
|
||||
import AppError from "../errors/AppError";
|
||||
import TicketTag from '../models/TicketTag';
|
||||
import Tag from '../models/Tag'
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { ticketId, tagId } = req.params;
|
||||
|
||||
try {
|
||||
const ticketTag = await TicketTag.create({ ticketId, tagId });
|
||||
return res.status(201).json(ticketTag);
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: 'Failed to store ticket tag.' });
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
export const remove = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { ticketId } = req.params;
|
||||
|
||||
|
||||
|
||||
try {
|
||||
await TicketTag.destroy({ where: { ticketId } });
|
||||
return res.status(200).json({ message: 'Ticket tags removed successfully.' });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: 'Failed to remove ticket tags.' });
|
||||
}
|
||||
};
|
||||
*/
|
||||
export const remove = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { ticketId } = req.params;
|
||||
|
||||
|
||||
try {
|
||||
// Retrieve tagIds associated with the provided ticketId from TicketTags
|
||||
const ticketTags = await TicketTag.findAll({ where: { ticketId } });
|
||||
const tagIds = ticketTags.map((ticketTag) => ticketTag.tagId);
|
||||
|
||||
// Find the tagIds with kanban = 1 in the Tags table
|
||||
const tagsWithKanbanOne = await Tag.findAll({
|
||||
where: {
|
||||
id: tagIds,
|
||||
kanban: 1,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove the tagIds with kanban = 1 from TicketTags
|
||||
const tagIdsWithKanbanOne = tagsWithKanbanOne.map((tag) => tag.id);
|
||||
if (tagIdsWithKanbanOne)
|
||||
await TicketTag.destroy({ where: { ticketId, tagId: tagIdsWithKanbanOne } });
|
||||
|
||||
return res.status(200).json({ message: 'Ticket tags removed successfully.' });
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: 'Failed to remove ticket tags.' });
|
||||
}
|
||||
};
|
155
backend/src/controllers/UserController.ts
Normal file
@ -0,0 +1,155 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
|
||||
import CheckSettingsHelper from "../helpers/CheckSettings";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
import CreateUserService from "../services/UserServices/CreateUserService";
|
||||
import ListUsersService from "../services/UserServices/ListUsersService";
|
||||
import UpdateUserService from "../services/UserServices/UpdateUserService";
|
||||
import ShowUserService from "../services/UserServices/ShowUserService";
|
||||
import DeleteUserService from "../services/UserServices/DeleteUserService";
|
||||
import SimpleListService from "../services/UserServices/SimpleListService";
|
||||
import User from "../models/User";
|
||||
|
||||
type IndexQuery = {
|
||||
searchParam: string;
|
||||
pageNumber: string;
|
||||
};
|
||||
|
||||
type ListQueryParams = {
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
const { companyId, profile } = req.user;
|
||||
|
||||
const { users, count, hasMore } = await ListUsersService({
|
||||
searchParam,
|
||||
pageNumber,
|
||||
companyId,
|
||||
profile
|
||||
});
|
||||
|
||||
return res.json({ users, count, hasMore });
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
profile,
|
||||
companyId: bodyCompanyId,
|
||||
queueIds,
|
||||
whatsappId,
|
||||
allTicket
|
||||
} = req.body;
|
||||
|
||||
const whatsappNumber = typeof req.body.whatsappNumber === "string" ? req.body.whatsappNumber.trim() : null;
|
||||
|
||||
let userCompanyId: number | null = null;
|
||||
let requestUser: User | null = null;
|
||||
|
||||
if (req.user !== undefined) {
|
||||
const { companyId: cId } = req.user;
|
||||
userCompanyId = cId;
|
||||
requestUser = await User.findByPk(req.user.id);
|
||||
}
|
||||
|
||||
const newUserCompanyId = bodyCompanyId || userCompanyId;
|
||||
|
||||
if (req.url === "/signup") {
|
||||
if (await CheckSettingsHelper("userCreation") === "disabled") {
|
||||
throw new AppError("ERR_USER_CREATION_DISABLED", 403);
|
||||
}
|
||||
} else if (req.user?.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
} else if (newUserCompanyId !== req.user?.companyId && !requestUser?.super) {
|
||||
throw new AppError("ERR_NO_SUPER", 403);
|
||||
}
|
||||
|
||||
const user = await CreateUserService({
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
profile,
|
||||
companyId: newUserCompanyId,
|
||||
queueIds,
|
||||
whatsappId,
|
||||
allTicket,
|
||||
whatsappNumber
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${userCompanyId}-mainchannel`).emit(`company-${userCompanyId}-user`, {
|
||||
action: "create",
|
||||
user
|
||||
});
|
||||
|
||||
return res.status(200).json(user);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { userId } = req.params;
|
||||
const user = await ShowUserService(userId);
|
||||
return res.status(200).json(user);
|
||||
};
|
||||
|
||||
export const update = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { id: requestUserId, companyId } = req.user;
|
||||
const { userId } = req.params;
|
||||
const userData = req.body;
|
||||
|
||||
if (typeof userData.whatsappNumber !== "string") {
|
||||
userData.whatsappNumber = null;
|
||||
} else {
|
||||
userData.whatsappNumber = userData.whatsappNumber.trim();
|
||||
}
|
||||
|
||||
const user = await UpdateUserService({
|
||||
userData,
|
||||
userId,
|
||||
companyId,
|
||||
requestUserId: +requestUserId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-user`, {
|
||||
action: "update",
|
||||
user
|
||||
});
|
||||
|
||||
return res.status(200).json(user);
|
||||
};
|
||||
|
||||
export const remove = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { userId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
if (req.user.profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
await DeleteUserService(userId, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-user`, {
|
||||
action: "delete",
|
||||
userId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "User deleted" });
|
||||
};
|
||||
|
||||
export const list = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.query;
|
||||
const { companyId: userCompanyId } = req.user;
|
||||
|
||||
const users = await SimpleListService({
|
||||
companyId: companyId ? +companyId : userCompanyId
|
||||
});
|
||||
|
||||
return res.status(200).json(users);
|
||||
};
|
7
backend/src/controllers/VersionController.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
return res.status(200).json({
|
||||
version: "10.12.0"
|
||||
});
|
||||
};
|
28
backend/src/controllers/WebhookHubController.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Request, Response } from "express";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import HubMessageListener from "../services/HubServices/HubMessageListener";
|
||||
|
||||
export const listen = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
console.log("Webhook received");
|
||||
const medias = req.files as Express.Multer.File[];
|
||||
const { channelId } = req.params;
|
||||
|
||||
const connection = await Whatsapp.findOne({
|
||||
where: { qrcode: channelId }
|
||||
});
|
||||
|
||||
if (!connection) {
|
||||
return res.status(404).json({ message: "Whatsapp channel not found" });
|
||||
}
|
||||
|
||||
try {
|
||||
await HubMessageListener(req.body, connection, medias);
|
||||
|
||||
return res.status(200).json({ message: "Webhook received" });
|
||||
} catch (error) {
|
||||
return res.status(400).json({ message: error });
|
||||
}
|
||||
};
|
185
backend/src/controllers/WhatsAppController.ts
Normal file
@ -0,0 +1,185 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import { removeWbot, restartWbot } from "../libs/wbot";
|
||||
import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession";
|
||||
|
||||
import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService";
|
||||
import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService";
|
||||
import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService";
|
||||
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
|
||||
import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
interface WhatsappData {
|
||||
name: string;
|
||||
queueIds: number[];
|
||||
companyId: number;
|
||||
greetingMessage?: string;
|
||||
complationMessage?: string;
|
||||
outOfHoursMessage?: string;
|
||||
ratingMessage?: string;
|
||||
status?: string;
|
||||
isDefault?: boolean;
|
||||
token?: string;
|
||||
//sendIdQueue?: number;
|
||||
//timeSendQueue?: number;
|
||||
transferQueueId?: number;
|
||||
timeToTransfer?: number;
|
||||
promptId?: number;
|
||||
maxUseBotQueues?: number;
|
||||
timeUseBotQueues?: number;
|
||||
expiresTicket?: number;
|
||||
expiresInactiveMessage?: string;
|
||||
}
|
||||
|
||||
interface QueryParams {
|
||||
session?: number | string;
|
||||
}
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { companyId } = req.user;
|
||||
const { session } = req.query as QueryParams;
|
||||
const whatsapps = await ListWhatsAppsService({ companyId, session });
|
||||
|
||||
return res.status(200).json(whatsapps);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const {
|
||||
name,
|
||||
status,
|
||||
isDefault,
|
||||
greetingMessage,
|
||||
complationMessage,
|
||||
ratingMessage,
|
||||
outOfHoursMessage,
|
||||
queueIds,
|
||||
token,
|
||||
//timeSendQueue,
|
||||
//sendIdQueue,
|
||||
transferQueueId,
|
||||
timeToTransfer,
|
||||
promptId,
|
||||
maxUseBotQueues,
|
||||
timeUseBotQueues,
|
||||
expiresTicket,
|
||||
expiresInactiveMessage
|
||||
}: WhatsappData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { whatsapp, oldDefaultWhatsapp } = await CreateWhatsAppService({
|
||||
name,
|
||||
status,
|
||||
isDefault,
|
||||
greetingMessage,
|
||||
complationMessage,
|
||||
ratingMessage,
|
||||
outOfHoursMessage,
|
||||
queueIds,
|
||||
companyId,
|
||||
token,
|
||||
//timeSendQueue,
|
||||
//sendIdQueue,
|
||||
transferQueueId,
|
||||
timeToTransfer,
|
||||
promptId,
|
||||
maxUseBotQueues,
|
||||
timeUseBotQueues,
|
||||
expiresTicket,
|
||||
expiresInactiveMessage
|
||||
});
|
||||
|
||||
StartWhatsAppSession(whatsapp, companyId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, {
|
||||
action: "update",
|
||||
whatsapp
|
||||
});
|
||||
|
||||
if (oldDefaultWhatsapp) {
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, {
|
||||
action: "update",
|
||||
whatsapp: oldDefaultWhatsapp
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json(whatsapp);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { whatsappId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
const { session } = req.query;
|
||||
|
||||
const whatsapp = await ShowWhatsAppService(whatsappId, companyId, session);
|
||||
|
||||
return res.status(200).json(whatsapp);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { whatsappId } = req.params;
|
||||
const whatsappData = req.body;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const { whatsapp, oldDefaultWhatsapp } = await UpdateWhatsAppService({
|
||||
whatsappData,
|
||||
whatsappId,
|
||||
companyId
|
||||
});
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, {
|
||||
action: "update",
|
||||
whatsapp
|
||||
});
|
||||
|
||||
if (oldDefaultWhatsapp) {
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, {
|
||||
action: "update",
|
||||
whatsapp: oldDefaultWhatsapp
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json(whatsapp);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { whatsappId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
await ShowWhatsAppService(whatsappId, companyId);
|
||||
|
||||
await DeleteWhatsAppService(whatsappId);
|
||||
removeWbot(+whatsappId);
|
||||
|
||||
const io = getIO();
|
||||
io.to(`company-${companyId}-mainchannel`).emit(`company-${companyId}-whatsapp`, {
|
||||
action: "delete",
|
||||
whatsappId: +whatsappId
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "Whatsapp deleted." });
|
||||
};
|
||||
|
||||
|
||||
export const restart = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { companyId, profile } = req.user;
|
||||
|
||||
if (profile !== "admin") {
|
||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||
}
|
||||
|
||||
await restartWbot(companyId);
|
||||
|
||||
return res.status(200).json({ message: "Whatsapp restart." });
|
||||
};
|
44
backend/src/controllers/WhatsAppSessionController.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { Request, Response } from "express";
|
||||
import { getWbot } from "../libs/wbot";
|
||||
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
|
||||
import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession";
|
||||
import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService";
|
||||
|
||||
const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { whatsappId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const whatsapp = await ShowWhatsAppService(whatsappId, companyId);
|
||||
await StartWhatsAppSession(whatsapp, companyId);
|
||||
|
||||
return res.status(200).json({ message: "Starting session." });
|
||||
};
|
||||
|
||||
const update = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { whatsappId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
|
||||
const whatsapp = await ShowWhatsAppService(whatsappId, companyId);
|
||||
|
||||
await whatsapp.update({ session: "" });
|
||||
|
||||
await StartWhatsAppSession(whatsapp, companyId);
|
||||
|
||||
return res.status(200).json({ message: "Starting session." });
|
||||
};
|
||||
|
||||
const remove = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { whatsappId } = req.params;
|
||||
const { companyId } = req.user;
|
||||
const whatsapp = await ShowWhatsAppService(whatsappId, companyId);
|
||||
|
||||
if (whatsapp.session) {
|
||||
await whatsapp.update({ status: "DISCONNECTED", session: "" });
|
||||
const wbot = getWbot(whatsapp.id);
|
||||
await wbot.logout();
|
||||
}
|
||||
|
||||
return res.status(200).json({ message: "Session disconnected." });
|
||||
};
|
||||
|
||||
export default { store, remove, update };
|
93
backend/src/database/index.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { Sequelize } from "sequelize-typescript";
|
||||
import User from "../models/User";
|
||||
import Setting from "../models/Setting";
|
||||
import Contact from "../models/Contact";
|
||||
import Ticket from "../models/Ticket";
|
||||
import Whatsapp from "../models/Whatsapp";
|
||||
import ContactCustomField from "../models/ContactCustomField";
|
||||
import Message from "../models/Message";
|
||||
import Queue from "../models/Queue";
|
||||
import WhatsappQueue from "../models/WhatsappQueue";
|
||||
import UserQueue from "../models/UserQueue";
|
||||
import Company from "../models/Company";
|
||||
import Plan from "../models/Plan";
|
||||
import TicketNote from "../models/TicketNote";
|
||||
import QuickMessage from "../models/QuickMessage";
|
||||
import Help from "../models/Help";
|
||||
import TicketTraking from "../models/TicketTraking";
|
||||
import UserRating from "../models/UserRating";
|
||||
import QueueOption from "../models/QueueOption";
|
||||
import Schedule from "../models/Schedule";
|
||||
import Tag from "../models/Tag";
|
||||
import TicketTag from "../models/TicketTag";
|
||||
import ContactList from "../models/ContactList";
|
||||
import ContactListItem from "../models/ContactListItem";
|
||||
import Campaign from "../models/Campaign";
|
||||
import CampaignSetting from "../models/CampaignSetting";
|
||||
import Baileys from "../models/Baileys";
|
||||
import CampaignShipping from "../models/CampaignShipping";
|
||||
import Announcement from "../models/Announcement";
|
||||
import Chat from "../models/Chat";
|
||||
import ChatUser from "../models/ChatUser";
|
||||
import ChatMessage from "../models/ChatMessage";
|
||||
import Invoices from "../models/Invoices";
|
||||
import Subscriptions from "../models/Subscriptions";
|
||||
import BaileysChats from "../models/BaileysChats";
|
||||
import Files from "../models/Files";
|
||||
import FilesOptions from "../models/FilesOptions";
|
||||
import Prompt from "../models/Prompt";
|
||||
import QueueIntegrations from "../models/QueueIntegrations";
|
||||
|
||||
// Adicione a importação do modelo HubNotificaMe
|
||||
import HubNotificaMe from "../models/HubNotificaMe";
|
||||
|
||||
// eslint-disable-next-line
|
||||
const dbConfig = require("../config/database");
|
||||
|
||||
const sequelize = new Sequelize(dbConfig);
|
||||
|
||||
const models = [
|
||||
Company,
|
||||
User,
|
||||
Contact,
|
||||
Ticket,
|
||||
Message,
|
||||
Whatsapp,
|
||||
ContactCustomField,
|
||||
Setting,
|
||||
Queue,
|
||||
WhatsappQueue,
|
||||
UserQueue,
|
||||
Plan,
|
||||
TicketNote,
|
||||
QuickMessage,
|
||||
Help,
|
||||
TicketTraking,
|
||||
UserRating,
|
||||
QueueOption,
|
||||
Schedule,
|
||||
Tag,
|
||||
TicketTag,
|
||||
ContactList,
|
||||
ContactListItem,
|
||||
Campaign,
|
||||
CampaignSetting,
|
||||
Baileys,
|
||||
CampaignShipping,
|
||||
Announcement,
|
||||
Chat,
|
||||
ChatUser,
|
||||
ChatMessage,
|
||||
Invoices,
|
||||
Subscriptions,
|
||||
BaileysChats,
|
||||
Files,
|
||||
FilesOptions,
|
||||
Prompt,
|
||||
QueueIntegrations,
|
||||
HubNotificaMe,
|
||||
];
|
||||
|
||||
sequelize.addModels(models);
|
||||
|
||||
export default sequelize;
|
@ -0,0 +1,10 @@
|
||||
import { QueryInterface } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return Promise.all([
|
||||
queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'),
|
||||
]);
|
||||
},
|
||||
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Users", {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
passwordHash: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Users");
|
||||
}
|
||||
};
|
@ -0,0 +1,45 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Contacts", {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
number: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
profilePicUrl: {
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
messengerId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
instagramId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Contacts");
|
||||
}
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Tickets", {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: "pending",
|
||||
allowNull: false
|
||||
},
|
||||
lastMessage: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
contactId: {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Contacts", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "CASCADE"
|
||||
},
|
||||
userId: {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Users", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "SET NULL"
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE(6),
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE(6),
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Tickets");
|
||||
}
|
||||
};
|
@ -0,0 +1,58 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Messages", {
|
||||
id: {
|
||||
type: DataTypes.STRING,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
body: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false
|
||||
},
|
||||
ack: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
read: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
},
|
||||
mediaType: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
mediaUrl: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
userId: {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Users", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "SET NULL"
|
||||
},
|
||||
ticketId: {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Tickets", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "CASCADE",
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE(6),
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE(6),
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Messages");
|
||||
}
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Whatsapps", {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
session: {
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
qrcode: {
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
battery: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
plugged: {
|
||||
type: DataTypes.BOOLEAN
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.STRING
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Whatsapps");
|
||||
}
|
||||
};
|
@ -0,0 +1,41 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("ContactCustomFields", {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
value: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
contactId: {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Contacts", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "CASCADE",
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("ContactCustomFields");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Contacts", "email", {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: ""
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Contacts", "email");
|
||||
}
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Messages", "userId");
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Messages", "userId", {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Users", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "SET NULL"
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Messages", "fromMe", {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Messages", "fromMe");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.changeColumn("Tickets", "lastMessage", {
|
||||
type: DataTypes.TEXT
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.changeColumn("Tickets", "lastMessage", {
|
||||
type: DataTypes.STRING
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Users", "profile", {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: "admin"
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Users", "profile");
|
||||
}
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Settings", {
|
||||
key: {
|
||||
type: DataTypes.STRING,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
value: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Settings");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Whatsapps", "name", {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Whatsapps", "name");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Whatsapps", "default", {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Whatsapps", "default");
|
||||
}
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Tickets", "whatsappId", {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Whatsapps", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "SET NULL"
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Tickets", "whatsappId");
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
import { QueryInterface } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.renameColumn("Whatsapps", "default", "isDefault");
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.renameColumn("Whatsapps", "isDefault", "default");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Messages", "isDeleted", {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Messages", "isDeleted");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Users", "tokenVersion", {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Users", "tokenVersion");
|
||||
}
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Tickets", "isGroup", {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Tickets", "isGroup");
|
||||
}
|
||||
};
|