Merge pull request #377 from ricardoapaes/feature/docker-for-production-v2

Enhancing docker for production
This commit is contained in:
Cassio Santos
2022-02-17 22:00:49 -03:00
committed by GitHub
32 changed files with 328 additions and 86 deletions

View File

@@ -1,7 +1,24 @@
MYSQL_ROOT_PASSWORD=strongpassword # MYSQL
MYSQL_ENGINE=
MYSQL_VERSION=
MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE=whaticket MYSQL_DATABASE=whaticket
JWT_SECRET=3123123213123 MYSQL_PORT=
JWT_REFRESH_SECRET=75756756756 TZ=
FRONTEND_PORT=3000
TZ=America/Fortaleza # BACKEND
MAX_CONCURRENT_SESSIONS=1 BACKEND_PORT=
BACKEND_SERVER_NAME=api.mydomain.com
BACKEND_URL=https://api.mydomain.com
PROXY_PORT=443
JWT_SECRET=
JWT_REFRESH_SECRET=
# FRONTEND
FRONTEND_PORT=80
FRONTEND_SSL_PORT=443
FRONTEND_SERVER_NAME=myapp.mydomain.com
FRONTEND_URL=https://myapp.mydomain.com
# BROWSERLESS
MAX_CONCURRENT_SESSIONS=

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.docker/data/ .docker/data/
ssl/
.env .env

View File

@@ -45,6 +45,10 @@ _Note_: change MYSQL_DATABASE, MYSQL_PASSWORD, MYSQL_USER and MYSQL_ROOT_PASSWOR
```bash ```bash
docker run --name whaticketdb -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket --restart always -p 3306:3306 -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_bin docker run --name whaticketdb -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket --restart always -p 3306:3306 -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
# Or run using `docker-compose` as below
# Before copy .env.example to .env first and set the variables in the file.
docker-compose up -d mysql
``` ```
Install puppeteer dependencies: Install puppeteer dependencies:
@@ -119,7 +123,9 @@ npm start
- Wait for QR CODE button to appear, click it and read qr code. - Wait for QR CODE button to appear, click it and read qr code.
- Done. Every message received by your synced WhatsApp number will appear in Tickets List. - Done. Every message received by your synced WhatsApp number will appear in Tickets List.
## Basic production deployment (Ubuntu 20.04 VPS) ## Basic production deployment
### Using Ubuntu 20.04 VPS
All instructions below assumes you are NOT running as root, since it will give an error in puppeteer. So let's start creating a new user and granting sudo privileges to it: All instructions below assumes you are NOT running as root, since it will give an error in puppeteer. So let's start creating a new user and granting sudo privileges to it:
@@ -169,6 +175,10 @@ _Note_: change MYSQL_DATABASE, MYSQL_PASSWORD, MYSQL_USER and MYSQL_ROOT_PASSWOR
```bash ```bash
docker run --name whaticketdb -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket --restart always -p 3306:3306 -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_bin docker run --name whaticketdb -e MYSQL_ROOT_PASSWORD=strongpassword -e MYSQL_DATABASE=whaticket -e MYSQL_USER=whaticket -e MYSQL_PASSWORD=whaticket --restart always -p 3306:3306 -d mariadb:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
# Or run using `docker-compose` as below
# Before copy .env.example to .env first and set the variables in the file.
docker-compose up -d mysql
``` ```
Clone this repository: Clone this repository:
@@ -373,6 +383,83 @@ Enable SSL on nginx (Fill / Accept all information required):
sudo certbot --nginx sudo certbot --nginx
``` ```
### Using docker and docker-compose
To run WhaTicket using docker you must perform the following steps:
```bash
cp .env.example .env
```
Now it will be necessary to configure the .env using its information, the variables are the same as those mentioned in the deployment using ubuntu, with the exception of mysql settings that were not in the .env.
```bash
# MYSQL
MYSQL_ENGINE= # default: mariadb
MYSQL_VERSION= # default: 10.6
MYSQL_ROOT_PASSWORD=strongpassword # change it please
MYSQL_DATABASE=whaticket
MYSQL_PORT=3306 # default: 3306; Use this port to expose mysql server
TZ=America/Fortaleza # default: America/Fortaleza; Timezone for mysql
# BACKEND
BACKEND_PORT= # default: 8080; but access by host not use this port
BACKEND_SERVER_NAME=api.mydomain.com
BACKEND_URL=https://api.mydomain.com
PROXY_PORT=443
JWT_SECRET=3123123213123 # change it please
JWT_REFRESH_SECRET=75756756756 # change it please
# FRONTEND
FRONTEND_PORT=80 # default: 3000; Use port 80 to expose in production
FRONTEND_SSL_PORT=443 # default: 3001; Use port 443 to expose in production
FRONTEND_SERVER_NAME=myapp.mydomain.com
FRONTEND_URL=https://myapp.mydomain.com
# BROWSERLESS
MAX_CONCURRENT_SESSIONS= # default: 1; Use only if using browserless
```
After defining the variables, run the following command:
```bash
docker-compose up -d --build
```
On the `first` run it will be necessary to seed the database tables using the following command:
```bash
docker-compose exec backend npx sequelize db:seed:all
```
#### SSL Certificate
To deploy the ssl certificate, add it to the `ssl/certs` folder. Inside it there should be a `backend` and a `frontend` folder, and each of them should contain the files `fullchain.pem` and `privkey.pem`, as in the structure below:
```bash
.
├── certs
│   ├── backend
│   │   ├── fullchain.pem
│   │   └── privkey.pem
│   └── frontend
│   ├── fullchain.pem
│   └── privkey.pem
└── www
```
To generate the certificate files use `certbot` which can be installed using snap, I used the following command:
Note: The frontend container that runs nginx is already prepared to receive the request made by certboot to validate the certificate.
```bash
# FRONTEND
certbot certonly --cert-name backend --webroot --webroot-path ./ssl/www/ -d api.mydomain.com
# BACKEND
certbot certonly --cert-name frontend --webroot --webroot-path ./ssl/www/ -d myapp.mydomain.com
```
## Access Data ## Access Data
User: admin@whaticket.com User: admin@whaticket.com

View File

@@ -16,25 +16,35 @@ services:
- DB_NAME=${MYSQL_DATABASE:-whaticket} - DB_NAME=${MYSQL_DATABASE:-whaticket}
- JWT_SECRET=${JWT_SECRET:-3123123213123} - JWT_SECRET=${JWT_SECRET:-3123123213123}
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-75756756756} - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-75756756756}
- BACKEND_URL=${BACKEND_URL:-http://localhost:3000/api} - BACKEND_URL=${BACKEND_URL:-http://localhost}
- FRONTEND_URL=${BACKEND_URL:-http://localhost:3000} - FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
- PROXY_PORT=${PROXY_PORT:-8080}
- CHROME_ARGS=--no-sandbox --disable-setuid-sandbox - CHROME_ARGS=--no-sandbox --disable-setuid-sandbox
ports:
- ${BACKEND_PORT:-8080}:3000
networks: networks:
- whaticket - whaticket
frontend: frontend:
ports: ports:
- ${FRONTEND_PORT:-3000}:80 - ${FRONTEND_PORT:-3000}:80
- ${FRONTEND_SSL_PORT:-3001}:443
build: build:
context: ./frontend context: ./frontend
dockerfile: ./Dockerfile dockerfile: ./Dockerfile
environment: environment:
- URL_BACKEND=http://backend:3000/ - URL_BACKEND=backend:3000
- REACT_APP_BACKEND_URL=${BACKEND_URL:-http://localhost}:${PROXY_PORT:-8080}/
- FRONTEND_SERVER_NAME=${FRONTEND_SERVER_NAME}
- BACKEND_SERVER_NAME=${BACKEND_SERVER_NAME}
volumes:
- ./ssl/certs/:/etc/nginx/ssl/
- ./ssl/www/:/var/www/letsencrypt/
networks: networks:
- whaticket - whaticket
mysql: mysql:
image: mariadb:latest image: ${MYSQL_ENGINE:-mariadb}:${MYSQL_VERSION:-10.6}
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_bin command: --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
volumes: volumes:
- ./.docker/data/:/var/lib/mysql - ./.docker/data/:/var/lib/mysql
@@ -43,7 +53,7 @@ services:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-strongpassword} - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-strongpassword}
- TZ=${TZ:-America/Fortaleza} - TZ=${TZ:-America/Fortaleza}
ports: ports:
- 3306:3306 - ${MYSQL_PORT:-3306}:3306
restart: always restart: always
networks: networks:
- whaticket - whaticket

View File

@@ -0,0 +1,30 @@
_writeFrontendEnvVars() {
ENV_JSON="$(jq --compact-output --null-input 'env | with_entries(select(.key | startswith("REACT_APP_")))')"
ENV_JSON_ESCAPED="$(printf "%s" "${ENV_JSON}" | sed -e 's/[\&/]/\\&/g')"
sed -i "s/<noscript id=\"env-insertion-point\"><\/noscript>/<script>var ENV=${ENV_JSON_ESCAPED}<\/script>/g" ${PUBLIC_HTML}index.html
}
_writeNginxEnvVars() {
dockerize -template /etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
}
_addSslConfig() {
SSL_CERTIFICATE=/etc/nginx/ssl/${1}/fullchain.pem;
SSL_CERTIFICATE_KEY=/etc/nginx/ssl/${1}/privkey.pem;
FILE_CONF=/etc/nginx/sites.d/${1}.conf
if [ -f ${SSL_CERTIFICATE} ] && [ -f ${SSL_CERTIFICATE_KEY} ]; then
echo "saving ssl config in ${FILE_CONF}"
echo 'include "include.d/ssl.conf";' >> ${FILE_CONF};
echo "ssl_certificate ${SSL_CERTIFICATE};" >> ${FILE_CONF};
echo "ssl_certificate_key ${SSL_CERTIFICATE_KEY};" >> ${FILE_CONF};
else
echo "ssl ${1} not found >> ${SSL_CERTIFICATE} -> ${SSL_CERTIFICATE_KEY}"
fi;
}
_writeFrontendEnvVars;
_writeNginxEnvVars;
_addSslConfig 'backend'
_addSslConfig 'frontend'

View File

@@ -1,4 +1,27 @@
server { include include.d/ssl-redirect.conf;
server_name _;
include include.d/spa.conf; upstream backend {
server {{ .Env.URL_BACKEND }};
} }
server {
index index.html;
root /var/www/public/;
{{ if .Env.FRONTEND_SERVER_NAME }}
server_name {{ .Env.FRONTEND_SERVER_NAME }};
{{else}}
server_name _;
{{end}}
include sites.d/frontend.conf;
include include.d/letsencrypt.conf;
}
{{if .Env.BACKEND_SERVER_NAME}}
server {
server_name {{ .Env.BACKEND_SERVER_NAME }};
include sites.d/backend.conf;
include include.d/letsencrypt.conf;
}
{{end}}

View File

@@ -0,0 +1,45 @@
#############################################################################
# Configuration file for Let's Encrypt ACME Challenge location
# This file is already included in listen_xxx.conf files.
# Do NOT include it separately!
#############################################################################
#
# This config enables to access /.well-known/acme-challenge/xxxxxxxxxxx
# on all our sites (HTTP), including all subdomains.
# This is required by ACME Challenge (webroot authentication).
# You can check that this location is working by placing ping.txt here:
# /var/www/letsencrypt/.well-known/acme-challenge/ping.txt
# And pointing your browser to:
# http://xxx.domain.tld/.well-known/acme-challenge/ping.txt
#
# Sources:
# https://community.letsencrypt.org/t/howto-easy-cert-generation-and-renewal-with-nginx/3491
#
#############################################################################
# Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx)
# We use ^~ here, so that we don't check other regexes (for speed-up). We actually MUST cancel
# other regex checks, because in our other config files have regex rule that denies access to files with dotted names.
location ^~ /.well-known/acme-challenge/ {
# Set correct content type. According to this:
# https://community.letsencrypt.org/t/using-the-webroot-domain-verification-method/1445/29
# Current specification requires "text/plain" or no content header at all.
# It seems that "text/plain" is a safe option.
default_type "text/plain";
# This directory must be the same as in /etc/letsencrypt/cli.ini
# as "webroot-path" parameter. Also don't forget to set "authenticator" parameter
# there to "webroot".
# Do NOT use alias, use root! Target directory is located here:
# /var/www/common/letsencrypt/.well-known/acme-challenge/
root /var/www/letsencrypt;
autoindex on;
}
# Hide /acme-challenge subdirectory and return 404 on all requests.
# It is somewhat more secure than letting Nginx return 403.
# Ending slash is important!
location = /.well-known/acme-challenge/ {
return 404;
}

View File

@@ -1,16 +0,0 @@
# X-Frame-Options is to prevent from clickJacking attack
add_header X-Frame-Options SAMEORIGIN;
# disable content-type sniffing on some browsers.
add_header X-Content-Type-Options nosniff;
# This header enables the Cross-site scripting (XSS) filter
add_header X-XSS-Protection "1; mode=block";
# This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
add_header Referrer-Policy "no-referrer-when-downgrade";
# Enables response header of "Vary: Accept-Encoding"
gzip_vary on;

View File

@@ -1,28 +1,16 @@
listen 80; # X-Frame-Options is to prevent from clickJacking attack
index index.html; add_header X-Frame-Options SAMEORIGIN;
root /var/www/public/;
location /{{ default .Env.FOLDER_BACKEND "api" }}/ { # disable content-type sniffing on some browsers.
proxy_pass {{ .Env.URL_BACKEND }}; add_header X-Content-Type-Options nosniff;
}
location /socket.io/ { # This header enables the Cross-site scripting (XSS) filter
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; add_header X-XSS-Protection "1; mode=block";
proxy_set_header Host $host;
proxy_pass {{ .Env.URL_BACKEND }}socket.io/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / { # This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack
try_files $uri $uri/ /index.html; add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
include include.d/nocache.conf;
}
location /static { add_header Referrer-Policy "no-referrer-when-downgrade";
alias /var/www/public/static/;
include include.d/allcache.conf;
}
include include.d/root.conf; # Enables response header of "Vary: Accept-Encoding"
gzip_vary on;

View File

@@ -0,0 +1,5 @@
server {
listen 80;
listen [::]:80;
return 302 https://$host$request_uri;
}

View File

@@ -0,0 +1,2 @@
listen 443 ssl http2;
listen [::]:443 ssl http2;

View File

@@ -0,0 +1,11 @@
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}

View File

@@ -0,0 +1,11 @@
location / {
try_files $uri $uri/ /index.html;
include include.d/nocache.conf;
}
location /static {
alias /var/www/public/static/;
include include.d/allcache.conf;
}
include "include.d/spa.conf";

View File

@@ -1,24 +1,25 @@
FROM node:14-alpine as build-deps FROM node:14-alpine as build-deps
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY package.json package-lock.json ./ COPY package*.json ./
RUN npm install RUN npm install
COPY .env* ./ COPY .env* ./
COPY src/ ./src/ COPY src/ ./src/
COPY public/ ./public/ COPY public/ ./public/
RUN echo "REACT_APP_BACKEND_URL=/api/" > .env.production
RUN npm run build RUN npm run build
FROM nginx:alpine FROM nginx:alpine
RUN apk add --no-cache jq openssl
RUN apk add --no-cache openssl
ENV DOCKERIZE_VERSION v0.6.1 ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz
ENV PUBLIC_HTML=/var/www/public/
COPY .docker/nginx /etc/nginx/ COPY .docker/nginx /etc/nginx/
COPY --from=build-deps /usr/src/app/build /var/www/public/ COPY --from=build-deps /usr/src/app/build ${PUBLIC_HTML}
EXPOSE 80 EXPOSE 80
RUN echo "dockerize -template /etc/nginx/include.d/spa.conf:/etc/nginx/include.d/spa.conf" > /docker-entrypoint.d/01-change-url-backend.sh \ COPY .docker/add-env-vars.sh /docker-entrypoint.d/01-add-env-vars.sh
&& chmod +x /docker-entrypoint.d/01-change-url-backend.sh RUN chmod +x /docker-entrypoint.d/01-add-env-vars.sh

View File

@@ -15,6 +15,7 @@
name="viewport" name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width" content="minimum-scale=1, initial-scale=1, width=device-width"
/> />
<noscript id="env-insertion-point"></noscript>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useReducer, useRef } from "react"; import React, { useState, useEffect, useReducer, useRef } from "react";
import { isSameDay, parseISO, format } from "date-fns"; import { isSameDay, parseISO, format } from "date-fns";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import clsx from "clsx"; import clsx from "clsx";
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors";
@@ -358,7 +358,7 @@ const MessagesList = ({ ticketId, isGroup }) => {
}, [pageNumber, ticketId]); }, [pageNumber, ticketId]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("connect", () => socket.emit("joinChatBox", ticketId)); socket.on("connect", () => socket.emit("joinChatBox", ticketId));

View File

@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect, useContext } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { format } from "date-fns"; import { format } from "date-fns";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import useSound from "use-sound"; import useSound from "use-sound";
import Popover from "@material-ui/core/Popover"; import Popover from "@material-ui/core/Popover";
@@ -78,7 +78,7 @@ const NotificationsPopOver = () => {
}, [ticketIdUrl]); }, [ticketIdUrl]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("connect", () => socket.emit("joinNotification")); socket.on("connect", () => socket.emit("joinNotification"));

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import QRCode from "qrcode.react"; import QRCode from "qrcode.react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
import { Dialog, DialogContent, Paper, Typography } from "@material-ui/core"; import { Dialog, DialogContent, Paper, Typography } from "@material-ui/core";
@@ -26,7 +26,7 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => {
useEffect(() => { useEffect(() => {
if (!whatsAppId) return; if (!whatsAppId) return;
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("whatsappSession", data => { socket.on("whatsappSession", data => {
if (data.action === "update" && data.session.id === whatsAppId) { if (data.action === "update" && data.session.id === whatsAppId) {

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { useParams, useHistory } from "react-router-dom"; import { useParams, useHistory } from "react-router-dom";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import clsx from "clsx"; import clsx from "clsx";
import { Paper, makeStyles } from "@material-ui/core"; import { Paper, makeStyles } from "@material-ui/core";
@@ -104,7 +104,7 @@ const Ticket = () => {
}, [ticketId, history]); }, [ticketId, history]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("connect", () => socket.emit("joinChatBox", ticketId)); socket.on("connect", () => socket.emit("joinChatBox", ticketId));

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useReducer, useContext } from "react"; import React, { useState, useEffect, useReducer, useContext } from "react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List"; import List from "@material-ui/core/List";
@@ -182,7 +182,7 @@ const reducer = (state, action) => {
}, [tickets, status, searchParam]); }, [tickets, status, searchParam]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
const shouldUpdateTicket = ticket => const shouldUpdateTicket = ticket =>
(!ticket.userId || ticket.userId === user?.id || showAll) && (!ticket.userId || ticket.userId === user?.id || showAll) &&

16
frontend/src/config.js Normal file
View File

@@ -0,0 +1,16 @@
function getConfig(name, defaultValue=null) {
// If inside a docker container, use window.ENV
if( window.ENV !== undefined ) {
return window.ENV[name] || defaultValue;
}
return process.env[name] || defaultValue;
}
export function getBackendUrl() {
return getConfig('REACT_APP_BACKEND_URL');
}
export function getHoursCloseTicketsAuto() {
return getConfig('REACT_APP_HOURS_CLOSE_TICKETS_AUTO');
}

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -71,7 +71,7 @@ const useAuth = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("user", data => { socket.on("user", data => {
if (data.action === "update" && data.user.id === user.id) { if (data.action === "update" && data.user.id === user.id) {

View File

@@ -1,4 +1,5 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { getHoursCloseTicketsAuto } from "../../config";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
import api from "../../services/api"; import api from "../../services/api";
@@ -35,7 +36,7 @@ const useTickets = ({
}) })
setTickets(data.tickets) setTickets(data.tickets)
let horasFecharAutomaticamente = process.env.REACT_APP_HOURS_CLOSE_TICKETS_AUTO let horasFecharAutomaticamente = getHoursCloseTicketsAuto();
if (status === "open" && horasFecharAutomaticamente && horasFecharAutomaticamente !== "" && if (status === "open" && horasFecharAutomaticamente && horasFecharAutomaticamente !== "" &&
horasFecharAutomaticamente !== "0" && Number(horasFecharAutomaticamente) > 0) { horasFecharAutomaticamente !== "0" && Number(horasFecharAutomaticamente) > 0) {

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useReducer } from "react"; import { useState, useEffect, useReducer } from "react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
import api from "../../services/api"; import api from "../../services/api";
@@ -73,7 +73,7 @@ const useWhatsApps = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("whatsapp", data => { socket.on("whatsapp", data => {
if (data.action === "update") { if (data.action === "update") {

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useReducer, useContext } from "react"; import React, { useState, useEffect, useReducer, useContext } from "react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
@@ -130,7 +130,7 @@ const Contacts = () => {
}, [searchParam, pageNumber]); }, [searchParam, pageNumber]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("contact", (data) => { socket.on("contact", (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useReducer, useState } from "react"; import React, { useEffect, useReducer, useState } from "react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { import {
Button, Button,
@@ -111,7 +111,7 @@ const Queues = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("queue", (data) => { socket.on("queue", (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useReducer } from "react"; import React, { useState, useEffect, useReducer } from "react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { import {
Button, Button,
@@ -122,7 +122,7 @@ const QuickAnswers = () => {
}, [searchParam, pageNumber]); }, [searchParam, pageNumber]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("quickAnswer", (data) => { socket.on("quickAnswer", (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
@@ -55,7 +55,7 @@ const Settings = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("settings", data => { socket.on("settings", data => {
if (data.action === "update") { if (data.action === "update") {

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect, useReducer } from "react"; import React, { useState, useEffect, useReducer } from "react";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import openSocket from "socket.io-client"; import openSocket from "../../services/socket-io";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
@@ -122,7 +122,7 @@ const Users = () => {
}, [searchParam, pageNumber]); }, [searchParam, pageNumber]);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket();
socket.on("user", (data) => { socket.on("user", (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {

View File

@@ -1,7 +1,8 @@
import axios from "axios"; import axios from "axios";
import { getBackendUrl } from "../config";
const api = axios.create({ const api = axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL, baseURL: getBackendUrl(),
withCredentials: true, withCredentials: true,
}); });

View File

@@ -0,0 +1,8 @@
import openSocket from "socket.io-client";
import { getBackendUrl } from "../config";
function connectToSocket() {
return openSocket(getBackendUrl());
}
export default connectToSocket;