Это довольно сложная проблема, которая включает в себя МНОЖЕСТВО кусочков, поэтому я отправил полный исходный код на GitHub для всех, кто хочет помочь.
Стек
Хорошо, поэтому я пытаюсь настроить аутентификацию с помощью клиентского приложения Nuxt.js (Vue Framework) и серверного API Rails. Вот мой стек проектов:
- Бэкэнд API Rails
- Devise & Devise-JWT рубиновые камни для обслуживания JWT.
- База данных Postgres.
- Внешнее приложение Nuxt.js.
- @ nuxtjs / axios пакет для подключения приложения Nuxt с Rails API.
- @ nuxtjs / auth пакет для обработки логики аутентификации на стороне клиента в Nuxt.
- прокси Nginx для маршрутизации запросов к соответствующему сервису (Rails API или Nuxt App)
- Docker и docker-compose для соединения всех четырех сервисов (Rails, Nginx, Nuxt, Postgres)
Как я уже сказал, много частей. И что еще хуже, я даже не знаю, какой сервис вызывает проблемы, хотя у меня есть идея. Я вернусь к этому позже, хотя.
Проблема
Приложение Vue прекрасно работает, пока я не попытаюсь войти как существующий пользователь. Как только я это сделаю, произойдет что-то интересное.
Вначале он работает и даже перенаправляет на нужную страницу при входе. Однако, как только я перезагружаю страницу или пытаюсь щелкнуть по любой ссылке, веб-сайт зависает, и панель загрузки зависает на неопределенное время.
После небольшой загрузки сайта я получаю эту ошибку:
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Наряду с этой ошибкой я получаю JS Stack Trace, однако трассировка стека не всегда одинакова. Вот три, которые я получил до сих пор ...
JS Stacktrace 1:
view_1 | <--- JS stacktrace --->
view_1 |
view_1 | ==== JS stack trace =========================================
view_1 |
view_1 | 0: ExitFrame [pc: 0x26fd9624fb5d]
view_1 | Security context: 0x140a9a59d921 <JSObject>
view_1 | 1: on [0x1c685bd61259] [_stream_readable.js:~825] [pc=0x26fd96bd1601](this=0x27476ca40f19 <Socket map = 0x2513dd2aaca1>,0x1c685bd160d9 <String[5]: close>,0x27476ca41619 <JSBoundFunction (BoundTargetFunction 0x118981190661)>)
view_1 | 2: connectionListenerInternal(aka connectionListenerInternal) [0x118981190561] [_http_server.js:~325] [pc=0x26fd9706ea91](this=...
view_1 |
view_1 | FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
JS Stacktrace 2:
view_1 | <--- JS stacktrace --->
view_1 |
view_1 | ==== JS stack trace =========================================
view_1 |
view_1 | 0: ExitFrame [pc: 0x3595a694fb5d]
view_1 | Security context: 0x016ed8d9d921 <JSObject>
view_1 | 1: parse(aka parse) [0x34dc366765a9] [/app/node_modules/cookie/index.js:~49] [pc=0x3595a77ee3a0](this=0x297ef7d825b1 <undefined>,0x15d707f39c31 <String[265]: auth._token.local=Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwic2NwIjoidXNlciIsImF1ZCI6bnVsbCwiaWF0IjoxNTQ3MDczMjQyLCJleHAiOjE1NDcwNzY4NDIsImp0aSI6ImUwMWRlNjFkLTRkYzItNDRlMi1iZDY1LWQwYmRmZTcwNGNhZS...
view_1 |
view_1 | FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
JS Stacktrace 3:
view_1 | <--- JS stacktrace --->
view_1 |
view_1 | ==== JS stack trace =========================================
view_1 |
view_1 | 0: ExitFrame [pc: 0x2bda0b2cfb5d]
view_1 | Security context: 0x1e6ec149d921 <JSObject>
view_1 | 1: setContext(aka setContext) [0x3ef9c763dc89] [server-bundle.js:~2584] [pc=0x2bda0bbaa2d1](this=0x0a10aa8025b1 <undefined>,0x28d2a207a4c1 <Object map = 0x10d1f0bf3091>,0x28d2a207a5b1 <Object map = 0x10d1f0bf2cd1>)
view_1 | 2: createApp(aka createApp) [0x27c44ed85199] [server-bundle.js:~1745] [pc=0x2bda0bb9d782](this=0x0a10aa8025b1 <undefined>,0x2bee19762ce9 ...
view_1 |
view_1 | FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Моя теория
Я думаю, что проблема связана с тем, как @ nuxtjs / axios или @ nuxtjs / auth работает с прокси Nginx.
Причина, по которой я верю, заключается в том, что когда я использую ту же самую настройку для Vue и Rails, но соединяю их вместе напрямую через axios без прокси Nginx между ними, все работает отлично.
Это означает, что я точно знаю, что бэкэнд-аутентификация и логика JWT работают и возвращают токен, как и ожидалось, и я знаю, что внешний интерфейс работает при непосредственном подключении к бэкенду. Это заставляет меня думать, что, как я уже говорил выше, проблема связана с тем, как я настроил приложение Vue для связи с прокси Nginx.
Более конкретно, я думаю, что проблема может быть связана с тем, как @ nuxtjs / axios или @ nuxtjs / auth работает с прокси-сервером Nginx при использовании файлов cookie.
Я думаю, что это может иметь место, потому что приложение зависает только тогда, когда установлены куки JWT и хранилище сеансов. Если я удаляю файлы cookie в браузере Safari и обновляю страницу, веб-сайт Vue снова работает нормально.
Предполагая, что моя теория верна, вот соответствующий код:
Конфигурация Nginx:
upstream view {
server view:3000;
}
upstream api {
server api:8080;
}
server {
listen 80;
location / {
proxy_pass http://view;
}
# FOR DEVELOPMENT ONLY
location /sockjs-node {
proxy_pass http://view;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://api;
}
}
Nuxt Auth & Axios Config:
axios: {
proxy: true,
prefix: "/api"
},
auth: {
strategies: {
local: {
endpoints: {
login: { url: '/auth/users/sign_in' },
logout: { url: '/auth/users/sign_out', method: 'delete' },
user: { url: '/auth/users/current' }
}
}
}
},
Рельсы Маршруты:
Rails.application.routes.draw do
resources :atoms
scope '/auth', defaults: {format: :json} do
devise_for :users, controllers: {sessions: 'sessions'}
devise_scope :user do
get 'users/current', to: 'sessions#show'
end
end
end
Rails SessionController:
class SessionsController < Devise::SessionsController
def create
super { @token = current_token }
end
def show
end
private
def current_token
request.env['warden-jwt_auth.token'] if current_user
end
end
Rails Session Views:
# app/views/devise/sessions/create.json.jbuilder
json.token @token
# app/views/devise/sessions/show.json.jbuilder
if user_signed_in?
json.user do
json.(current_user, :id, :email)
end
end
Страница входа Nuxt (использует Vuetify):
<template>
<v-layout>
<v-flex>
<v-card v-if="$auth.loggedIn">
<v-alert type="error" :value="error">{{error}}</v-alert>
<v-card-text>
Logged in as {{$auth.user.email}}
</v-card-text>
<v-card-actions>
<v-btn @click="logout">Log out</v-btn>
</v-card-actions>
</v-card>
<v-card v-else>
<v-alert type="error" :value="error">{{error}}</v-alert>
<v-card-text>
<v-form>
<v-text-field v-model="email" label="Email" />
<v-text-field v-model="password" label="Password" type="password" />
</v-form>
<v-card-actions>
<v-btn @click="login">Log in</v-btn>
</v-card-actions>
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</template>
<script>
export default {
data () {
return {
email: '',
password: '',
error: null
}
},
methods: {
login () {
this.$auth.login({
data: {
user: {
email: this.email,
password: this.password
}
}
}).catch(e => {this.error = e + ''})
},
logout () {
this.$auth.logout().catch(e => {this.error = e + ''})
}
}
}
</script>
Докер Состав:
version: '3'
services:
db:
image: postgres
volumes:
- ./.tmp/db:/var/lib/postgresql/data
ports:
- "5432"
proxy:
build: proxy
restart: always
ports:
- '5000:80'
api:
build: api
environment:
- JWT_SECRET=${JWT_SECRET}
volumes:
- ./api:/app
depends_on:
- db
view:
build: view
volumes:
- /app/node_modules
- ./view:/app
Посмотреть код
Так как это очень сложный проект и состоит из множества частей, я ожидаю, что кому-то будет довольно сложно помочь только с данным кодом. По этой причине я установил репозиторий GitHub со всем кодом в его текущем состоянии. Вы можете просмотреть его здесь .
Чтобы запустить репозиторий, просто клонируйте проект
$ git clone https://github.com/JoshHadik/stack_nuxt_authentication_question.git
Переместиться в каталог
$ cd stack_nuxt_authentication_question/
И запустите docker-compose up --build (при условии, что на вашем компьютере есть docker и docker-compose.
$ docker-compose up --build
Настройка базы данных в новой вкладке терминала
$ docker-compose run api rails db:create db:migrate db:seed
После этого вы сможете получить доступ к приложению Vue @ localhost: 5000. Кроме того, база данных должна содержать пользователя со следующими учетными данными:
электронная почта: test@example.com
пароль: пароль
Маршрут входа в систему по адресу localhost: 5000 / login
Нет прокси
Я упоминал выше, что приложение работает, когда API и представление подключены напрямую без промежуточного прокси. чтобы продемонстрировать, как это работает, я создал дополнительную ветку в проекте под названием no-proxy. Если вы хотите проверить это, просто откройте новый каталог (не тот, к которому вы ранее клонировали) и запустите
$ git clone -b no-proxy https://github.com/JoshHadik/stack_nuxt_authentication_question.git
перейти в каталог
$ cd stack_nuxt_authentication_question/
затем снова запустите docker-compose
$ docker-compose up --build
и настройте базу данных в новой вкладке терминала:
$ docker-compose run api rails db:create db:migrate db:seed
Вы можете просмотреть приложение Nuxt для ветки без прокси-сервера @ localhost: 3000 и маршрут входа в систему @ localhost: 3000 / login. Вы должны иметь возможность войти с теми же учетными данными, что и выше.
Спасибо
Заранее спасибо всем и каждому, кто пытается помочь мне с этим!