added jwt login and authentication plus some toast

This commit is contained in:
Grzegorz Leoniec 2018-10-17 19:57:24 +02:00
parent 2b11d14bde
commit 1735767cce
17 changed files with 523 additions and 38 deletions

View File

@ -0,0 +1,32 @@
.iziToast-target, .iziToast {
&,
&:after,
&.iziToast-color-dark:after {
box-shadow: none !important;
}
}
.iziToast .iziToast-message {
font-weight: 400 !important;
}
.iziToast.iziToast-color-red {
background: $color-danger !important;
border-color: $color-danger !important;
}
.iziToast.iziToast-color-orange {
background: $color-warning !important;
border-color: $color-warning !important;
}
.iziToast.iziToast-color-yellow {
background: $color-yellow !important;
border-color: $color-yellow !important;
}
.iziToast.iziToast-color-blue {
background: $color-secondary !important;
border-color: $color-secondary !important;
}
.iziToast.iziToast-color-green {
background: $color-success !important;
border-color: $color-success !important;
}

View File

@ -1,5 +1,6 @@
@import 'vue-cion-design-system/src/system/tokens/generated/tokens.scss';
@import './imports/_tooltip.scss';
@import './imports/_toast.scss';
// Transition Easing
$easeOut: cubic-bezier(0.19, 1, 0.22, 1);

View File

@ -21,7 +21,22 @@
style="min-width: 250px;"
@mouseover="popoverMouseEnter"
@mouseleave="popoveMouseLeave">
<!--<ds-avatar
:image="author.avatar"
:name="author.name || 'Anonymus'"
class="profile-avatar"
size="90px" />-->
<ds-flex>
<ds-flex-item class="ds-tab-nav-item">
<ds-space margin="small">
<ds-text
size="x-large"
style="margin-bottom: 0; text-align: center">{{ fanCount }}</ds-text>
<ds-text
size="small"
style="text-align: center">Fans</ds-text>
</ds-space>
</ds-flex-item>
<ds-flex-item class="ds-tab-nav-item ds-tab-nav-item-active">
<ds-space margin="small">
<ds-text
@ -42,16 +57,6 @@
style="text-align: center">Kommentare</ds-text>
</ds-space>
</ds-flex-item>
<ds-flex-item class="ds-tab-nav-item">
<ds-space margin="small">
<ds-text
size="x-large"
style="margin-bottom: 0; text-align: center">{{ fanCount }}</ds-text>
<ds-text
size="small"
style="text-align: center">Fans</ds-text>
</ds-space>
</ds-flex-item>
</ds-flex>
<!--<ds-text
color="soft"
@ -59,6 +64,7 @@
<ds-icon name="map-marker" /> Hamburg, Deutschland
</ds-text>-->
<ds-flex
v-if="!itsMe"
gutter="x-small"
style="margin-bottom: 0;">
<ds-flex-item :width="{base: 3}">
@ -100,6 +106,9 @@ export default {
}
},
computed: {
itsMe() {
return this.author.slug === this.$store.getters['auth/user'].slug
},
fanCount() {
let count = Number(this.author.followedByCount) || 0
if (this.voted) {
@ -145,3 +154,12 @@ export default {
}
}
</script>
<style lang="scss">
.profile-avatar {
display: block;
margin: auto;
margin-top: -45px;
border: #fff 5px solid;
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<ds-button
:disabled="disabled || !followId"
:loading="loading"
icon="plus"
primary
full-width
@ -18,11 +19,13 @@ export default {
},
data() {
return {
disabled: false
disabled: false,
loading: false
}
},
methods: {
follow() {
this.loading = true
this.$apollo
.mutate({
mutation: gql`
@ -35,11 +38,12 @@ export default {
}
`,
variables: {
myId: 'u1',
myId: this.$store.getters['auth/user'].id,
followId: this.followId
}
})
.then(() => {
this.loading = false
this.disabled = true
this.$emit('update')
})

View File

@ -7,6 +7,15 @@
href="/">
<ds-logo />
</a>
<a
v-router-link
:href="$router.resolve({name: 'profile-slug', params: {slug: user.slug}}).href">
<ds-avatar
:image="user.avatar"
:name="user.name"
style="float: right"
size="42" />
</a>
</ds-container>
</div>
<ds-container>
@ -16,3 +25,13 @@
</ds-container>
</div>
</template>
<script>
export default {
computed: {
user() {
return this.$store.getters['auth/user']
}
}
}
</script>

View File

@ -0,0 +1,26 @@
import { isEmpty } from 'lodash'
export default async ({ store, env, route, redirect }) => {
let publicPages = env.publicPages
// only affect non public pages
if (publicPages.indexOf(route.name) >= 0) {
return true
}
// await store.dispatch('auth/refreshJWT', 'authenticated middleware')
const isAuthenticated = await store.dispatch('auth/check')
if (isAuthenticated === true) {
return true
}
// try to logout user
// await store.dispatch('auth/logout', null, { root: true })
// set the redirect path for after the login
let params = {}
if (!isEmpty(route.path) && route.path !== '/') {
params.path = route.path
}
return redirect('/login', params)
}

View File

@ -1,4 +1,17 @@
const pkg = require('./package')
const envWhitelist = [
'BUILD_COMMIT',
'NODE_ENV',
'WEBAPP_HOST',
'WEBAPP_PORT',
'WEBAPP_BASE_URL',
'API_HOST',
'API_PORT',
'EMBED_API_URL',
'SENTRY_DNS_PUBLIC',
'MAPBOX_TOKEN',
'MAINTENANCE'
]
module.exports = {
mode: 'universal',
@ -7,6 +20,19 @@ module.exports = {
name: 'slide-up',
mode: 'out-in'
},
env: {
// pages which do NOT require a login
publicPages: [
'login',
'logout',
'register',
'signup',
'reset',
'reset-token',
'pages-slug'
]
},
/*
** Headers of the page
*/
@ -42,15 +68,12 @@ module.exports = {
{ src: '~/plugins/design-system.js', ssr: true },
{ src: '~/plugins/vue-directives.js', ssr: false },
{ src: '~/plugins/v-tooltip.js', ssr: false },
{ src: '~/plugins/izi-toast.js', ssr: false },
{ src: '~/plugins/vue-filters.js' }
],
router: {
// middleware: [
// 'maintenance',
// 'check-auth',
// 'authenticated'
// ],
middleware: ['authenticated'],
linkActiveClass: 'router-active-link'
},
/* router: {
@ -81,14 +104,17 @@ module.exports = {
/*
** Nuxt.js modules
*/
modules: ['@nuxtjs/apollo'],
modules: [
'@nuxtjs/apollo',
['@nuxtjs/dotenv', { only: envWhitelist }],
['nuxt-env', { keys: envWhitelist }]
],
// Give apollo module options
apollo: {
tokenName: 'yourApolloTokenName', // optional, default: apollo-token
tokenExpires: 10, // optional, default: 7 (days)
includeNodeModules: true, // optional, default: false (this includes graphql-tag for node_modules folder)
authenticationType: 'Basic', // optional, default: 'Bearer'
// tokenName: 'yourApolloTokenName', // optional, default: apollo-token
tokenExpires: 1, // optional, default: 7 (days)
// includeNodeModules: true, // optional, default: false (this includes graphql-tag for node_modules folder)
// optional
errorHandler(error) {
console.log(
@ -97,6 +123,14 @@ module.exports = {
error.message
)
},
// Watch loading state for all queries
// See 'Smart Query > options > watchLoading' for detail
// TODO: find a way to get this working
// watchLoading(isLoading) {
// console.log('Global loading', countModifier)
// this.$nuxt.$loading.start()
// },
// required
clientConfigs: {
default: {
@ -107,6 +141,8 @@ module.exports = {
httpLinkOptions: {
credentials: 'same-origin'
},
credentials: true,
// You can use `wss` for secure connection (recommended in production)
// Use `null` to disable subscriptions
// wsEndpoint: 'ws://localhost:4000', // optional

View File

@ -12,18 +12,22 @@
"generate": "nuxt generate",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"styleguide": "cd ./styleguide && yarn dev",
"styleguide:build": "cd ./styleguide && yarn build:lib && cd ../ && yarn upgrade file:./styleguide",
"styleguide:build": "cd ./styleguide && yarn build:lib && cd ../ && yarn upgrade --ignore-engines file:./styleguide",
"precommit": "yarn lint"
},
"dependencies": {
"@nuxtjs/apollo": "^4.0.0-rc3",
"@nuxtjs/dotenv": "^1.3.0",
"cross-env": "^5.2.0",
"date-fns": "^1.29.0",
"express": "^4.16.3",
"graphql-tag": "^2.10.0",
"jsonwebtoken": "^8.3.0",
"nuxt": "^2.0.0",
"nuxt-env": "^0.0.4",
"v-tooltip": "^2.0.0-rc.33",
"vue-cion-design-system": "file:./styleguide"
"vue-cion-design-system": "file:./styleguide",
"vue-izitoast": "^1.1.0"
},
"devDependencies": {
"babel-eslint": "^10.0.1",

View File

@ -71,12 +71,7 @@ export default {
shoutedCount
}
}
`),
variables() {
return {
id: this.$route.query.post || 'p1'
}
}
`)
}
}
}

View File

@ -11,7 +11,7 @@
<ds-card>
<ds-flex gutter="small">
<ds-flex-item
:width="{ base: '100%', sm: 1 }"
:width="{ base: '100%', sm: '50%' }"
center>
<ds-space
margin-top="small"
@ -24,21 +24,29 @@
</ds-space>
</ds-flex-item>
<ds-flex-item
:width="{ base: '100%', sm: 1 }"
:width="{ base: '100%', sm: '50%' }"
center>
<ds-space margin="small">
<ds-text size="small">Wenn Du ein Konto bei Human Connection hast, melde Dich bitte hier an.</ds-text>
</ds-space>
<form>
<form
:disabled="pending"
@submit.prevent="onSubmit">
<ds-input
:disabled="pending"
v-model="form.email"
placeholder="Deine E-Mail"
type="email"
icon="envelope"/>
<ds-input
:disabled="pending"
v-model="form.password"
placeholder="Dein Password"
icon="lock"
icon-right="question-circle"
type="password"/>
<ds-button
:loading="pending"
primary
full-width>
Anmelden
@ -57,8 +65,39 @@
</template>
<script>
import gql from 'graphql-tag'
export default {
layout: 'blank'
layout: 'blank',
data() {
return {
form: {
email: '',
password: ''
}
}
},
asyncData({ store, redirect }) {
if (store.getters['auth/isLoggedIn']) {
redirect('/')
}
},
computed: {
pending() {
return this.$store.getters['auth/pending']
}
},
methods: {
async onSubmit() {
try {
await this.$store.dispatch('auth/login', { ...this.form })
this.$toast.success('You are logged in!')
this.$router.replace('/')
} catch (err) {
this.$toast.error(err.message)
}
}
}
}
</script>

39
pages/logout.vue Normal file
View File

@ -0,0 +1,39 @@
<template>
<ds-container width="small">
<ds-flex>
<ds-flex-item
:width="{ base: '100%' }"
center>
<ds-space
style="text-align: center;"
margin-top="large"
margin-bottom="xxx-small"
center>
<img
style="width: 200px;"
src="/img/sign-up/onourjourney.png"
alt="Human Connection">
</ds-space>
<ds-space
style="text-align: center;"
margin-top="small"
margin-bottom="xxx-small"
center>
<ds-heading
tag="h3"
soft>Logging out...</ds-heading>
</ds-space>
</ds-flex-item>
</ds-flex>
</ds-container>
</template>
<script>
export default {
layout: 'blank',
async beforeCreate() {
await this.$store.dispatch('auth/logout')
this.$router.replace('/')
}
}
</script>

View File

@ -35,8 +35,9 @@
<ds-space
margin="small">
<hc-follow-button
v-if="!myProfile"
:follow-id="user.id"
@update="voted = true && fetchUser" />
@update="voted = true && fetchUser()" />
</ds-space>
</ds-card>
<ds-space/>
@ -49,7 +50,7 @@
Wem folgt {{ user.name | truncate(15) }}?
</ds-text>
</ds-space>
<template v-if="user.following.length">
<template v-if="user.following">
<ds-space
v-for="follow in user.following"
:key="follow.id"
@ -72,7 +73,7 @@
Wer folgt {{ user.name | truncate(15) }}?
</ds-text>
</ds-space>
<template v-if="user.followedBy.length">
<template v-if="user.followedBy">
<ds-space
v-for="follow in user.followedBy"
:key="follow.id"
@ -158,7 +159,6 @@ export default {
name: 'slide-up',
mode: 'out-in'
},
data() {
return {
User: [],
@ -166,6 +166,9 @@ export default {
}
},
computed: {
myProfile() {
return this.$route.params.slug === this.$store.getters['auth/user'].slug
},
fanCount() {
let count = Number(this.user.followedByCount) || 0
if (this.voted) {

8
pages/profile/index.vue Normal file
View File

@ -0,0 +1,8 @@
<script>
export default {
layout: 'blank',
asyncData({ error }) {
error({ statusCode: 404, message: 'Profile slug missing' })
}
}
</script>

13
plugins/izi-toast.js Normal file
View File

@ -0,0 +1,13 @@
import Vue from 'vue'
import VueIziToast from 'vue-izitoast'
import 'izitoast/dist/css/iziToast.css'
export default ({ app }) => {
Vue.use(VueIziToast, {
position: 'bottomRight',
transitionIn: 'bounceInLeft',
layout: 2,
theme: 'dark'
})
}

152
store/auth.js Normal file
View File

@ -0,0 +1,152 @@
import gql from 'graphql-tag'
import jwt from 'jsonwebtoken'
export const state = () => {
return {
user: null,
token: null,
pending: false
}
}
export const mutations = {
SET_USER(state, user) {
state.user = user || null
},
SET_USER_SETTINGS(state, userSettings) {
// state.user = Object.assign(state.user, {
// userSettings: Object.assign(this.getters['auth/userSettings'], userSettings)
// })
},
SET_TOKEN(state, token) {
state.token = token || null
},
SET_PENDING(state, pending) {
state.pending = pending
}
}
export const getters = {
isLoggedIn(state) {
return !!(state.user && state.token)
},
pending(state) {
return !!state.pending
},
isVerified(state) {
return !!state.user && state.user.isVerified && !!state.user.name
},
isAdmin(state) {
return !!state.user && state.user.role === 'ADMIN'
},
isModerator(state) {
return (
!!state.user &&
(state.user.role === 'ADMIN' || state.user.role === 'MODERATOR')
)
},
user(state) {
return state.user || {}
},
token(state) {
return state.token
}
// userSettings(state, getters, rootState, rootGetters) {
// const userSettings = (state.user && state.user.userSettings) ? state.user.userSettings : {}
//
// const defaultLanguage = (state.user && state.user.language) ? state.user.language : rootGetters['i18n/locale']
// let contentLanguages = !isEmpty(userSettings.contentLanguages) ? userSettings.contentLanguages : []
// if (isEmpty(contentLanguages)) {
// contentLanguages = userSettings.uiLanguage ? [userSettings.uiLanguage] : [defaultLanguage]
// }
//
// return Object.assign({
// uiLanguage: defaultLanguage,
// contentLanguages: contentLanguages
// }, userSettings)
// }
}
export const actions = {
async init({ commit }) {
if (process.client) {
return
}
const token = this.app.$apolloHelpers.getToken()
if (!token) {
return
}
const user = await jwt.verify(token, 'b/&&7b78BF&fv/Vd')
if (user.id) {
commit('SET_USER', {
id: user.id,
name: user.name,
slug: user.slug,
email: user.email,
avatar: user.avatar,
role: user.role
})
commit('SET_TOKEN', token)
}
},
async check({ commit, dispatch, getters }) {
if (!this.app.$apolloHelpers.getToken()) {
await dispatch('logout')
}
return getters.isLoggedIn
},
async login({ commit }, { email, password }) {
try {
commit('SET_PENDING', true)
commit('SET_USER', null)
commit('SET_TOKEN', null)
const res = await this.app.apolloProvider.defaultClient
.mutate({
mutation: gql(`
mutation($email: String!, $password: String!) {
login(email: $email, password: $password) {
id
name
email
avatar
token
}
}
`),
variables: { email, password }
})
.then(({ data }) => data && data.login)
if (res && res.token) {
await this.app.$apolloHelpers.onLogin(res.token)
commit('SET_TOKEN', res.token)
delete res.token
commit('SET_USER', res)
commit('SET_PENDING', false)
return true
} else {
commit('SET_PENDING', false)
throw new Error('THERE IS AN ERROR')
}
} catch (err) {
commit('SET_USER', null)
commit('SET_TOKEN', null)
commit('SET_PENDING', false)
throw new Error(err)
}
},
async logout({ commit }) {
commit('SET_USER', null)
commit('SET_TOKEN', null)
return this.app.$apolloHelpers.onLogout()
},
register(
{ dispatch, commit },
{ email, password, inviteCode, invitedByUserId }
) {},
async patch({ state, commit, dispatch }, data) {},
resendVerifySignup({ state, dispatch }) {},
resetPassword({ state }, data) {},
setNewPassword({ state }, data) {}
}

9
store/index.js Normal file
View File

@ -0,0 +1,9 @@
export const state = () => ({})
export const mutations = {}
export const actions = {
async nuxtServerInit({ dispatch }) {
await dispatch('auth/init')
}
}

View File

@ -708,6 +708,12 @@
babel-helper-vue-jsx-merge-props "^2.0.3"
babel-plugin-transform-vue-jsx "^4.0.1"
"@nuxtjs/dotenv@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@nuxtjs/dotenv/-/dotenv-1.3.0.tgz#fcfd8fa9da767ce1a79c11104ab998d610e23731"
dependencies:
dotenv "^6.0.0"
"@nuxtjs/friendly-errors-webpack-plugin@^2.0.2":
version "2.0.2"
resolved "http://registry.npmjs.org/@nuxtjs/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-2.0.2.tgz#80fd4c4276e87c09a9157a6f433724235eaaaf6e"
@ -1696,6 +1702,10 @@ browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.1.2:
electron-to-chromium "^1.3.73"
node-releases "^1.0.0-alpha.12"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
buffer-from@^1.0.0, buffer-from@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@ -2676,6 +2686,10 @@ dot-prop@^4.1.0, dot-prop@^4.1.1:
dependencies:
is-obj "^1.0.0"
dotenv@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.1.0.tgz#9853b6ca98292acb7dec67a95018fa40bccff42c"
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@ -2700,6 +2714,12 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
ecdsa-sig-formatter@1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3"
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -4119,6 +4139,10 @@ iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7"
izitoast@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/izitoast/-/izitoast-1.4.0.tgz#1aa4e3408b7159fba743506af66d8be54fd929fb"
js-base64@^2.1.8:
version "2.4.9"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03"
@ -4196,6 +4220,20 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
jsonwebtoken@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz#056c90eee9a65ed6e6c72ddb0a1d325109aaf643"
dependencies:
jws "^3.1.5"
lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@ -4205,6 +4243,21 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
jwa@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.6.tgz#87240e76c9808dbde18783cf2264ef4929ee50e6"
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.10"
safe-buffer "^5.0.1"
jws@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f"
dependencies:
jwa "^1.1.5"
safe-buffer "^5.0.1"
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@ -4336,10 +4389,30 @@ lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@ -4352,6 +4425,10 @@ lodash.mergewith@^4.6.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
lodash.tail@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
@ -4912,6 +4989,10 @@ number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
nuxt-env@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/nuxt-env/-/nuxt-env-0.0.4.tgz#5cc13436d2745da1d180f02910b7a2c7171e540c"
nuxt@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-2.1.0.tgz#0875c4c1ce5dbd729d973a5aa0de0ee8aad35a8b"
@ -7487,6 +7568,12 @@ vue-hot-reload-api@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2"
vue-izitoast@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/vue-izitoast/-/vue-izitoast-1.1.0.tgz#4b2b81b91e688eedc95d8f500752ac9ec87d3a8d"
dependencies:
izitoast "^1.3.0"
vue-loader@^15.4.2:
version "15.4.2"
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.4.2.tgz#812bb26e447dd3b84c485eb634190d914ce125e2"