Имеется конфигурация для Nginx
server {
listen 80;
server_name alexandra-portfolio.proto-dev.ru www.alexandra-portfolio.proto-dev.ru;
location / {
root /var/www/develop/frontend/alexandra-portfolio;
try_files $uri /index.html;
}
location /admin {
alias /var/www/develop/frontend/alexandra-portfolio-admin/build;
index index.html index.htm;
try_files $uri /admin/index.html;
}
location /build {
root /var/www/develop/frontend/alexandra-portfolio-admin/build;
autoindex off;
}
location /swagger {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Имеется конфигурация webpack 4
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { DefinePlugin } = require('webpack');
const dotEnv = require('dotenv').config();
const isEnvProduction = process.env.NODE_ENV === 'production';
module.exports = {
mode: process.env.NODE_ENV,
entry: {
main: [ 'whatwg-fetch', '@babel/polyfill', './src/index.js' ],
},
output: {
path: path.resolve(__dirname, 'build'),
filename: isEnvProduction ? '[name].[hash:10].js' : '[name].js',
chunkFilename: isEnvProduction ? '[name].[hash:10].chunk.js' : '[name].chunk.js',
publicPath: './',
},
resolve: {
modules: [ path.resolve(__dirname, 'node_modules') ],
...require('./webpack.config.alias').resolve,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: [
/node_modules\/(?!ansi-regex).*/,
],
use: {
loader: 'babel-loader',
options: {
presets: [
[ '@babel/preset-env', { modules: false } ],
'@babel/preset-react',
],
plugins: [
[
'babel-plugin-styled-components',
{
ssr: false,
displayName: !isEnvProduction,
fileName: false,
minify: isEnvProduction,
transpileTemplateLiterals: isEnvProduction,
pure: false,
},
],
'@babel/plugin-transform-regenerator',
'@babel/plugin-proposal-class-properties',
],
},
},
},
{
test: /\.font\.js/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'webfonts-loader',
options: {
fileName: '[name].[hash:8].[ext]',
},
},
],
},
{
test: /\.(jpg|jpeg|png|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
},
},
{
test: /\.(ttf|eot|woff2?)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
{
test: /\.css$/i,
use: [ 'style-loader', 'css-loader' ],
},
],
},
devServer: {
open: true,
overlay: true,
port: process.env.PORT,
quiet: true,
disableHostCheck: true,
writeToDisk: true,
historyApiFallback: true,
proxy: {
'/api': {
target: process.env.PROXY_URL,
},
'/uploads': {
target: process.env.PROXY_URL,
},
},
},
optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
warnings: false,
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: true,
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
parallel: true,
cache: true,
sourceMap: true,
}),
],
splitChunks: {
chunks: 'all',
},
},
devtool: isEnvProduction ? 'source-map' : 'eval-cheap-module-source-map',
performance: false,
plugins: [
new DefinePlugin({
'process.env': JSON.stringify(dotEnv.parsed),
}),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html',
favicon: './src/assets/images/favicon.ico',
showErrors: true,
inject: true,
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[hash:10].css',
}),
],
};
Файловая структура для фронтенда-React на удаленном сервере
Файловая структура для бекенда-Nest на удаленном сервере
Сервис который делает запросы к бэкенду
Переменная на проде process.env.PROXY_URL приобретает значение PROXY_URL=
proto-dev.ru:4000 потому что Nest запущен на 4000 порту
import { store } from 'Store/index';
import { API_BASE } from 'Constants/httpRequest';
import * as userActions from 'Store/user/actions';
import * as appActions from 'Store/app/actions';
import getErrorNameFromResponse from 'Helpers/getErrorNameFromResponse';
import { SERVICE_MODAL_TYPES } from 'Constants/modalTypes';
const isEnvProduction = process.env.NODE_ENV === 'production';
const REQUEST_DEFAULT_OPTIONS = {
method: 'GET',
contentType: 'application/json',
body: false,
query: {},
};
const parseResponse = (response) => {
const contentType = response.headers.get('Content-Type') || '';
if (contentType.includes('json')) {return response.json();}
if (contentType.includes('text')) {return response.text();}
return response.blob();
};
const handleResponse = async (response) => {
if (response.ok) {
return await parseResponse(response);
}
if (response.status === 401) {
store.dispatch(userActions.signOut());
}
if (response.status >= 400 && response.status <= 500) {
const parsedResponse = await parseResponse(response);
return {
errorText: getErrorNameFromResponse(parsedResponse.error.message),
};
}
};
const handleRequestFail = (error) => {
console.error(error);
store.dispatch(appActions.showServiceModalAction({
type: SERVICE_MODAL_TYPES.infoModal,
contentProps: {
title: 'Что-то пошло не так...',
cancelButtonText: 'Закрыть',
onCancel: () => store.dispatch(appActions.hideServiceModalAction()),
},
}));
};
export default (route, options = {}) => {
const accessToken = localStorage.getItem('accessToken') || '';
const {
contentType,
method,
body,
query,
} = { ...REQUEST_DEFAULT_OPTIONS, ...options };
const fetchConfig = {
headers: {
'Authorization': `Bearer ${accessToken.replace(/"/g, '')}`,
},
method,
};
if (body && method !== 'GET') {
fetchConfig.body = body;
}
if (contentType) {
fetchConfig.headers['Content-Type'] = contentType;
}
const urlObject = new URL(`${API_BASE}${route}`, process.env.PROXY_URL);
for (const key in query) {
urlObject.searchParams.append(key, query[key]);
}
return fetch(urlObject, fetchConfig)
.then(handleResponse)
.catch(handleRequestFail);
};
Проблема в том что я делаю запрос с браузера на Nest и получаю ошибку. Как ее обойти, что это за ошибка ?