Add environment variable in frontend and ssl

This commit is contained in:
Ricardo Paes
2022-02-14 08:27:05 -03:00
parent 7a4e908421
commit ac84639687
30 changed files with 207 additions and 77 deletions

View File

@@ -1,7 +1,19 @@
# MYSQL
MYSQL_ROOT_PASSWORD=strongpassword
MYSQL_DATABASE=whaticket
TZ=America/Fortaleza
# BACKEND
BACKEND_PORT=8080
BACKEND_SERVER_NAME=api.mydomain.com
BACKEND_URL=https://api.mydomain.com/
JWT_SECRET=3123123213123
JWT_REFRESH_SECRET=75756756756
# FRONTEND
FRONTEND_PORT=3000
TZ=America/Fortaleza
FRONTEND_SERVER_NAME=myapp.mydomain.com
FRONTEND_URL=https://myapp.mydomain.com/
# BROWSERLESS
MAX_CONCURRENT_SESSIONS=1

1
.gitignore vendored
View File

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

View File

@@ -16,9 +16,11 @@ 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:8080}
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
- CHROME_ARGS=--no-sandbox --disable-setuid-sandbox
ports:
- ${BACKEND_PORT:-8080}:3000
networks:
- whaticket
@@ -29,7 +31,12 @@ services:
context: ./frontend
dockerfile: ./Dockerfile
environment:
- URL_BACKEND=http://backend:3000/
- URL_BACKEND=backend:3000
- REACT_APP_BACKEND_URL=${BACKEND_URL:-http://localhost:8080/}
- FRONTEND_SERVER_NAME=${FRONTEND_SERVER_NAME}
- BACKEND_SERVER_NAME=${BACKEND_SERVER_NAME}
volumes:
- ./ssl/:/etc/nginx/ssl/
networks:
- whaticket

View File

@@ -0,0 +1,27 @@
_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 'include "include.d/ssl.conf";' >> ${FILE_CONF};
echo "ssl_certificate ${SSL_CERTIFICATE};" >> ${FILE_CONF};
echo "ssl_certificate_key ${SSL_CERTIFICATE_KEY};" >> ${FILE_CONF};
fi;
}
_writeFrontendEnvVars;
_writeNginxEnvVars;
_addSslConfig 'backend'
_addSslConfig 'frontend'

View File

@@ -1,4 +1,18 @@
server {
server_name _;
include include.d/spa.conf;
upstream backend {
server {{ .Env.URL_BACKEND }};
}
server {
listen 80;
index index.html;
root /var/www/public/;
server_name {{ default .Env.FRONTEND_SERVER_NAME "_" }};
include sites.d/frontend.conf;
}
server {
listen 80;
server_name {{ default .Env.BACKEND_SERVER_NAME "_" }};
include sites.d/backend.conf;
}

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;
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;

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

@@ -5,20 +5,21 @@ 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

View File

@@ -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>

View File

@@ -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));

View File

@@ -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"));

View File

@@ -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) {

View File

@@ -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));

View File

@@ -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
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 { 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) {

View File

@@ -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) {

View File

@@ -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") {

View File

@@ -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") {

View File

@@ -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") {

View File

@@ -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") {

View File

@@ -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") {

View File

@@ -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") {

View File

@@ -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,
});

View File

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