mirror of
https://github.com/cheveguerra/whaticket-community.git
synced 2026-04-17 19:37:02 +00:00
Merge pull request #377 from ricardoapaes/feature/docker-for-production-v2
Enhancing docker for production
This commit is contained in:
29
.env.example
29
.env.example
@@ -1,7 +1,24 @@
|
||||
MYSQL_ROOT_PASSWORD=strongpassword
|
||||
# MYSQL
|
||||
MYSQL_ENGINE=
|
||||
MYSQL_VERSION=
|
||||
MYSQL_ROOT_PASSWORD=
|
||||
MYSQL_DATABASE=whaticket
|
||||
JWT_SECRET=3123123213123
|
||||
JWT_REFRESH_SECRET=75756756756
|
||||
FRONTEND_PORT=3000
|
||||
TZ=America/Fortaleza
|
||||
MAX_CONCURRENT_SESSIONS=1
|
||||
MYSQL_PORT=
|
||||
TZ=
|
||||
|
||||
# BACKEND
|
||||
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
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.docker/data/
|
||||
ssl/
|
||||
.env
|
||||
89
README.md
89
README.md
@@ -45,6 +45,10 @@ _Note_: change MYSQL_DATABASE, MYSQL_PASSWORD, MYSQL_USER and MYSQL_ROOT_PASSWOR
|
||||
|
||||
```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
|
||||
|
||||
# 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:
|
||||
@@ -119,7 +123,9 @@ npm start
|
||||
- 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.
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -169,6 +175,10 @@ _Note_: change MYSQL_DATABASE, MYSQL_PASSWORD, MYSQL_USER and MYSQL_ROOT_PASSWOR
|
||||
|
||||
```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
|
||||
|
||||
# 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:
|
||||
@@ -373,6 +383,83 @@ Enable SSL on nginx (Fill / Accept all information required):
|
||||
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
|
||||
|
||||
User: admin@whaticket.com
|
||||
|
||||
@@ -16,25 +16,35 @@ services:
|
||||
- DB_NAME=${MYSQL_DATABASE:-whaticket}
|
||||
- JWT_SECRET=${JWT_SECRET:-3123123213123}
|
||||
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-75756756756}
|
||||
- BACKEND_URL=${BACKEND_URL:-http://localhost:3000/api}
|
||||
- FRONTEND_URL=${BACKEND_URL:-http://localhost:3000}
|
||||
- BACKEND_URL=${BACKEND_URL:-http://localhost}
|
||||
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
|
||||
- PROXY_PORT=${PROXY_PORT:-8080}
|
||||
- CHROME_ARGS=--no-sandbox --disable-setuid-sandbox
|
||||
ports:
|
||||
- ${BACKEND_PORT:-8080}:3000
|
||||
networks:
|
||||
- whaticket
|
||||
|
||||
frontend:
|
||||
ports:
|
||||
- ${FRONTEND_PORT:-3000}:80
|
||||
- ${FRONTEND_SSL_PORT:-3001}:443
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: ./Dockerfile
|
||||
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:
|
||||
- whaticket
|
||||
|
||||
mysql:
|
||||
image: mariadb:latest
|
||||
image: ${MYSQL_ENGINE:-mariadb}:${MYSQL_VERSION:-10.6}
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_bin
|
||||
volumes:
|
||||
- ./.docker/data/:/var/lib/mysql
|
||||
@@ -43,7 +53,7 @@ services:
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-strongpassword}
|
||||
- TZ=${TZ:-America/Fortaleza}
|
||||
ports:
|
||||
- 3306:3306
|
||||
- ${MYSQL_PORT:-3306}:3306
|
||||
restart: always
|
||||
networks:
|
||||
- whaticket
|
||||
30
frontend/.docker/add-env-vars.sh
Normal file
30
frontend/.docker/add-env-vars.sh
Normal 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'
|
||||
@@ -1,4 +1,27 @@
|
||||
server {
|
||||
server_name _;
|
||||
include include.d/spa.conf;
|
||||
include include.d/ssl-redirect.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}}
|
||||
45
frontend/.docker/nginx/include.d/letsencrypt.conf
Normal file
45
frontend/.docker/nginx/include.d/letsencrypt.conf
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,28 +1,16 @@
|
||||
listen 80;
|
||||
index index.html;
|
||||
root /var/www/public/;
|
||||
# X-Frame-Options is to prevent from clickJacking attack
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
|
||||
location /{{ default .Env.FOLDER_BACKEND "api" }}/ {
|
||||
proxy_pass {{ .Env.URL_BACKEND }};
|
||||
}
|
||||
# disable content-type sniffing on some browsers.
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
location /socket.io/ {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
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";
|
||||
}
|
||||
# This header enables the Cross-site scripting (XSS) filter
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
include include.d/nocache.conf;
|
||||
}
|
||||
# This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
|
||||
|
||||
location /static {
|
||||
alias /var/www/public/static/;
|
||||
include include.d/allcache.conf;
|
||||
}
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade";
|
||||
|
||||
include include.d/root.conf;
|
||||
# Enables response header of "Vary: Accept-Encoding"
|
||||
gzip_vary on;
|
||||
5
frontend/.docker/nginx/include.d/ssl-redirect.conf
Normal file
5
frontend/.docker/nginx/include.d/ssl-redirect.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
return 302 https://$host$request_uri;
|
||||
}
|
||||
2
frontend/.docker/nginx/include.d/ssl.conf
Normal file
2
frontend/.docker/nginx/include.d/ssl.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
11
frontend/.docker/nginx/sites.d/backend.conf
Normal file
11
frontend/.docker/nginx/sites.d/backend.conf
Normal 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;
|
||||
}
|
||||
11
frontend/.docker/nginx/sites.d/frontend.conf
Executable file
11
frontend/.docker/nginx/sites.d/frontend.conf
Executable 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";
|
||||
@@ -1,24 +1,25 @@
|
||||
FROM node:14-alpine as build-deps
|
||||
WORKDIR /usr/src/app
|
||||
COPY package.json package-lock.json ./
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY .env* ./
|
||||
COPY src/ ./src/
|
||||
COPY public/ ./public/
|
||||
RUN echo "REACT_APP_BACKEND_URL=/api/" > .env.production
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
RUN apk add --no-cache jq openssl
|
||||
|
||||
RUN apk add --no-cache openssl
|
||||
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 \
|
||||
&& tar -C /usr/local/bin -xzvf 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 --from=build-deps /usr/src/app/build /var/www/public/
|
||||
COPY --from=build-deps /usr/src/app/build ${PUBLIC_HTML}
|
||||
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 \
|
||||
&& chmod +x /docker-entrypoint.d/01-change-url-backend.sh
|
||||
COPY .docker/add-env-vars.sh /docker-entrypoint.d/01-add-env-vars.sh
|
||||
RUN chmod +x /docker-entrypoint.d/01-add-env-vars.sh
|
||||
@@ -15,6 +15,7 @@
|
||||
name="viewport"
|
||||
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||
/>
|
||||
<noscript id="env-insertion-point"></noscript>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect, useReducer, useRef } from "react";
|
||||
|
||||
import { isSameDay, parseISO, format } from "date-fns";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { green } from "@material-ui/core/colors";
|
||||
@@ -358,7 +358,7 @@ const MessagesList = ({ ticketId, isGroup }) => {
|
||||
}, [pageNumber, ticketId]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("connect", () => socket.emit("joinChatBox", ticketId));
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect, useContext } from "react";
|
||||
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { format } from "date-fns";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
import useSound from "use-sound";
|
||||
|
||||
import Popover from "@material-ui/core/Popover";
|
||||
@@ -78,7 +78,7 @@ const NotificationsPopOver = () => {
|
||||
}, [ticketIdUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("connect", () => socket.emit("joinNotification"));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import QRCode from "qrcode.react";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
import toastError from "../../errors/toastError";
|
||||
|
||||
import { Dialog, DialogContent, Paper, Typography } from "@material-ui/core";
|
||||
@@ -26,7 +26,7 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!whatsAppId) return;
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("whatsappSession", data => {
|
||||
if (data.action === "update" && data.session.id === whatsAppId) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { useParams, useHistory } from "react-router-dom";
|
||||
|
||||
import { toast } from "react-toastify";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { Paper, makeStyles } from "@material-ui/core";
|
||||
@@ -104,7 +104,7 @@ const Ticket = () => {
|
||||
}, [ticketId, history]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("connect", () => socket.emit("joinChatBox", ticketId));
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 List from "@material-ui/core/List";
|
||||
@@ -182,7 +182,7 @@ const reducer = (state, action) => {
|
||||
}, [tickets, status, searchParam]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
const shouldUpdateTicket = ticket =>
|
||||
(!ticket.userId || ticket.userId === user?.id || showAll) &&
|
||||
|
||||
16
frontend/src/config.js
Normal file
16
frontend/src/config.js
Normal 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');
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
@@ -71,7 +71,7 @@ const useAuth = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("user", data => {
|
||||
if (data.action === "update" && data.user.id === user.id) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { getHoursCloseTicketsAuto } from "../../config";
|
||||
import toastError from "../../errors/toastError";
|
||||
|
||||
import api from "../../services/api";
|
||||
@@ -35,7 +36,7 @@ const useTickets = ({
|
||||
})
|
||||
setTickets(data.tickets)
|
||||
|
||||
let horasFecharAutomaticamente = process.env.REACT_APP_HOURS_CLOSE_TICKETS_AUTO
|
||||
let horasFecharAutomaticamente = getHoursCloseTicketsAuto();
|
||||
|
||||
if (status === "open" && horasFecharAutomaticamente && horasFecharAutomaticamente !== "" &&
|
||||
horasFecharAutomaticamente !== "0" && Number(horasFecharAutomaticamente) > 0) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useReducer } from "react";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
import toastError from "../../errors/toastError";
|
||||
|
||||
import api from "../../services/api";
|
||||
@@ -73,7 +73,7 @@ const useWhatsApps = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("whatsapp", data => {
|
||||
if (data.action === "update") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { useHistory } from "react-router-dom";
|
||||
|
||||
@@ -130,7 +130,7 @@ const Contacts = () => {
|
||||
}, [searchParam, pageNumber]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("contact", (data) => {
|
||||
if (data.action === "update" || data.action === "create") {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useReducer, useState } from "react";
|
||||
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -111,7 +111,7 @@ const Queues = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("queue", (data) => {
|
||||
if (data.action === "update" || data.action === "create") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useReducer } from "react";
|
||||
import openSocket from "socket.io-client";
|
||||
import openSocket from "../../services/socket-io";
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -122,7 +122,7 @@ const QuickAnswers = () => {
|
||||
}, [searchParam, pageNumber]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("quickAnswer", (data) => {
|
||||
if (data.action === "update" || data.action === "create") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 Paper from "@material-ui/core/Paper";
|
||||
@@ -55,7 +55,7 @@ const Settings = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("settings", data => {
|
||||
if (data.action === "update") {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useReducer } from "react";
|
||||
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 Paper from "@material-ui/core/Paper";
|
||||
@@ -122,7 +122,7 @@ const Users = () => {
|
||||
}, [searchParam, pageNumber]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on("user", (data) => {
|
||||
if (data.action === "update" || data.action === "create") {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import axios from "axios";
|
||||
import { getBackendUrl } from "../config";
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: process.env.REACT_APP_BACKEND_URL,
|
||||
baseURL: getBackendUrl(),
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
|
||||
8
frontend/src/services/socket-io.js
Normal file
8
frontend/src/services/socket-io.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import openSocket from "socket.io-client";
|
||||
import { getBackendUrl } from "../config";
|
||||
|
||||
function connectToSocket() {
|
||||
return openSocket(getBackendUrl());
|
||||
}
|
||||
|
||||
export default connectToSocket;
|
||||
Reference in New Issue
Block a user