Merge branch 'feature/auth-module'
This commit is contained in:
commit
5560298388
@ -2,8 +2,10 @@ package be.vandewalleh.routing
|
||||
|
||||
import be.vandewalleh.auth.SimpleJWT
|
||||
import be.vandewalleh.auth.UsernamePasswordCredential
|
||||
import be.vandewalleh.extensions.respondStatus
|
||||
import be.vandewalleh.services.UserService
|
||||
import io.ktor.application.*
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.request.*
|
||||
import io.ktor.response.*
|
||||
@ -18,8 +20,7 @@ fun Routing.login(kodein: Kodein) {
|
||||
|
||||
data class TokenResponse(val token: String)
|
||||
|
||||
route("/user/login"){
|
||||
post {
|
||||
post("/user/login") {
|
||||
val credential = call.receive<UsernamePasswordCredential>()
|
||||
|
||||
val (email, password) = userService.getEmailAndPasswordFromUsername(credential.username)
|
||||
@ -31,7 +32,15 @@ fun Routing.login(kodein: Kodein) {
|
||||
|
||||
return@post call.respond(TokenResponse(simpleJwt.sign(email)))
|
||||
}
|
||||
|
||||
authenticate {
|
||||
get("/user/me") {
|
||||
// retrieve email from token
|
||||
val email = call.principal<UserIdPrincipal>()!!.name
|
||||
val info = userService.getUserInfo(email)
|
||||
if (info != null) call.respond(mapOf("user" to info))
|
||||
else call.respondStatus(HttpStatusCode.Unauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -48,6 +48,15 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
||||
.firstOrNull() != null
|
||||
}
|
||||
|
||||
fun getUserInfo(email: String): UserInfoDto? {
|
||||
return db.from(Users)
|
||||
.select(Users.email, Users.username)
|
||||
.where { Users.email eq email }
|
||||
.limit(0, 1)
|
||||
.map { UserInfoDto(it[Users.username]!!, it[Users.email]!!) }
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new user
|
||||
* password should already be hashed
|
||||
@ -86,3 +95,4 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
||||
}
|
||||
|
||||
data class UserDto(val username: String, val email: String, val password: String)
|
||||
data class UserInfoDto(val username: String, val email: String)
|
||||
@ -1,28 +0,0 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import {mapState} from "vuex";
|
||||
|
||||
const state = mapState(['token'])
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: `http://localhost:5000`,
|
||||
withCredentials: false,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
const token = state.token;
|
||||
if (token) {
|
||||
config.headers['Authorization'] = 'Bearer ' + token;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
Promise.reject(error)
|
||||
});
|
||||
|
||||
export default apiClient
|
||||
@ -7,7 +7,7 @@
|
||||
lazy-validation
|
||||
>
|
||||
<v-text-field
|
||||
v-model="username"
|
||||
v-model="form.username"
|
||||
:rules="usernameRules"
|
||||
label="Username"
|
||||
required
|
||||
@ -15,7 +15,7 @@
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
v-model="form.password"
|
||||
:rules="passwordRules"
|
||||
label="Password"
|
||||
required
|
||||
@ -30,7 +30,7 @@
|
||||
<v-btn
|
||||
:disabled="!valid"
|
||||
color="success"
|
||||
@click="submit"
|
||||
@click="userLogin"
|
||||
>
|
||||
Login
|
||||
</v-btn>
|
||||
@ -42,17 +42,24 @@
|
||||
export default {
|
||||
name: "LoginForm",
|
||||
methods: {
|
||||
submit() {
|
||||
async userLogin() {
|
||||
try {
|
||||
const response = await this.$auth.loginWith('local', {data: this.form})
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
valid: false,
|
||||
form: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
usernameRules: [
|
||||
v => !!v || 'Name is required',
|
||||
],
|
||||
password: '',
|
||||
passwordRules: [
|
||||
v => !!v || 'Password is required',
|
||||
]
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
lazy-validation
|
||||
>
|
||||
<v-text-field
|
||||
v-model="username"
|
||||
v-model="form.username"
|
||||
:rules="usernameRules"
|
||||
label="Username"
|
||||
required
|
||||
@ -15,7 +15,7 @@
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="email"
|
||||
v-model="form.email"
|
||||
:rules="emailRules"
|
||||
label="Email"
|
||||
required
|
||||
@ -23,7 +23,7 @@
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
v-model="form.password"
|
||||
:rules="passwordRules"
|
||||
label="Password"
|
||||
required
|
||||
@ -48,7 +48,7 @@
|
||||
<v-btn
|
||||
:disabled="!valid"
|
||||
color="success"
|
||||
@click="submit"
|
||||
@click="registerUser"
|
||||
>
|
||||
Register
|
||||
</v-btn>
|
||||
@ -60,22 +60,25 @@
|
||||
export default {
|
||||
name: "RegisterForm",
|
||||
methods: {
|
||||
submit() {
|
||||
|
||||
async registerUser() {
|
||||
const data = await this.$axios.post('/user', this.form)
|
||||
console.log(data)
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
valid: false,
|
||||
form: {
|
||||
username: '',
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
usernameRules: [
|
||||
v => !!v || 'Name is required',
|
||||
],
|
||||
email: '',
|
||||
emailRules: [
|
||||
v => !!v || 'Email is required',
|
||||
v => /.+@.+/.test(v) || 'E-mail must be valid',
|
||||
],
|
||||
password: '',
|
||||
passwordRules: [
|
||||
v => !!v || 'Password is required',
|
||||
v => v.length >= 6 || 'Password must be longer than 6 characters',
|
||||
|
||||
@ -6,10 +6,11 @@
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-toolbar-title>Notes</v-toolbar-title>
|
||||
<v-btn to="/" text rounded>Simple Notes</v-btn>
|
||||
<v-spacer/>
|
||||
<v-btn to="/notes" text rounded>My notes</v-btn>
|
||||
<v-btn to="/account" text rounded>Account</v-btn>
|
||||
<v-btn outlined v-if="this.$store.state.auth.loggedIn">Welcome {{this.$store.state.auth.user.username}}</v-btn>
|
||||
<v-btn v-else to="/account" text rounded>Account</v-btn>
|
||||
</v-app-bar>
|
||||
<v-content>
|
||||
<v-container>
|
||||
@ -20,9 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
export default {}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar
|
||||
fixed
|
||||
app
|
||||
color="primary"
|
||||
dark
|
||||
prominent
|
||||
>
|
||||
<v-toolbar-title>Notes</v-toolbar-title>
|
||||
<v-spacer/>
|
||||
<v-btn to="/notes" text rounded>My notes</v-btn>
|
||||
<v-btn to="/account" text rounded>Account</v-btn>
|
||||
</v-app-bar>
|
||||
<v-content>
|
||||
<v-container>
|
||||
<nuxt/>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html {
|
||||
overflow-y: auto
|
||||
}
|
||||
</style>
|
||||
@ -28,7 +28,7 @@ export default {
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
plugins: [],
|
||||
plugins: ['~/plugins/axios'],
|
||||
/*
|
||||
** Nuxt.js dev-modules
|
||||
*/
|
||||
@ -43,13 +43,38 @@ export default {
|
||||
// Doc: https://axios.nuxtjs.org/usage
|
||||
'@nuxtjs/axios',
|
||||
// Doc: https://github.com/nuxt-community/dotenv-module
|
||||
'@nuxtjs/dotenv'
|
||||
'@nuxtjs/dotenv',
|
||||
'@nuxtjs/auth'
|
||||
],
|
||||
/*
|
||||
** Axios module configuration
|
||||
** See https://axios.nuxtjs.org/options
|
||||
*/
|
||||
axios: {},
|
||||
|
||||
auth: {
|
||||
redirect: {
|
||||
login: '/account',
|
||||
logout: '/',
|
||||
home: '/',
|
||||
},
|
||||
watchLoggedIn: true,
|
||||
//cookie: true,
|
||||
strategies: {
|
||||
local: {
|
||||
endpoints: {
|
||||
login: {url: '/user/login', method: 'post', propertyName: 'token'},
|
||||
user: {url: '/user/me', method: 'get', propertyName: 'user'},
|
||||
},
|
||||
autoFetchUser: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
router: {
|
||||
middleware: ['auth']
|
||||
},
|
||||
|
||||
/*
|
||||
** vuetify module configuration
|
||||
** https://github.com/nuxt-community/vuetify-module
|
||||
|
||||
@ -8,10 +8,10 @@
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"generate": "nuxt generate",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
|
||||
"generate": "nuxt generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/auth": "^4.9.1",
|
||||
"@nuxtjs/axios": "^5.3.6",
|
||||
"@nuxtjs/dotenv": "^1.4.0",
|
||||
"masonry-layout": "^4.2.2",
|
||||
|
||||
@ -44,6 +44,9 @@
|
||||
export default {
|
||||
name: "centered",
|
||||
layout: "centered",
|
||||
options: {
|
||||
auth: 'guest',
|
||||
},
|
||||
data: () => ({
|
||||
tab: 0,
|
||||
tabs: ["Login", "Register"]
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
|
||||
export default {
|
||||
title: 'Home',
|
||||
layout: 'home',
|
||||
data: () => ({
|
||||
|
||||
}),
|
||||
options: {
|
||||
auth: false
|
||||
},
|
||||
data: () => ({}),
|
||||
head: () => ({
|
||||
title: "Home"
|
||||
})
|
||||
|
||||
7
frontend/plugins/axios.js
Normal file
7
frontend/plugins/axios.js
Normal file
@ -0,0 +1,7 @@
|
||||
export default function ({ $axios }) {
|
||||
$axios.onRequest(config => {
|
||||
console.log('Making request to ' + config.url)
|
||||
})
|
||||
|
||||
$axios.setBaseURL('http://localhost:8081')
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
export function setToken(token) {
|
||||
if (process.browser && token) {
|
||||
localStorage.setItem('token', token)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearToken() {
|
||||
if (process.browser) {
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
}
|
||||
|
||||
export default {setToken, clearToken}
|
||||
@ -1,19 +0,0 @@
|
||||
import apiClient from '@/api'
|
||||
|
||||
export default {
|
||||
async login({username, password}) {
|
||||
try {
|
||||
const {data} = await apiClient.post('/user/signin', {
|
||||
username,
|
||||
password
|
||||
})
|
||||
return {token: data["access_token"]}
|
||||
} catch (e) {
|
||||
if (e.response && e.response.status === 401)
|
||||
return Promise.reject({invalid: true})
|
||||
else
|
||||
return Promise.reject({error: true})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
import apiClient from '@/api'
|
||||
|
||||
export default {
|
||||
async register({username, email, password}) {
|
||||
try {
|
||||
await apiClient.post('/user', {
|
||||
username,
|
||||
email,
|
||||
password
|
||||
})
|
||||
return {success: true}
|
||||
} catch (e) {
|
||||
if (e.response && e.response.status === 409)
|
||||
return Promise.reject({exists: true})
|
||||
else
|
||||
return Promise.reject({error: true})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,27 +1,7 @@
|
||||
import {clearToken, setToken} from "@/services/LocalStorageService";
|
||||
export const state = () => ({})
|
||||
|
||||
export const state = () => ({
|
||||
token: ''
|
||||
})
|
||||
|
||||
export const mutations = {
|
||||
setToken(state, token) {
|
||||
state.token = token
|
||||
setToken(token)
|
||||
},
|
||||
clearToken(state) {
|
||||
state.token = null
|
||||
clearToken()
|
||||
},
|
||||
initToken(state) {
|
||||
state.token = localStorage.getItem('token')
|
||||
}
|
||||
}
|
||||
export const mutations = {}
|
||||
|
||||
export const actions = {}
|
||||
|
||||
export const getters = {
|
||||
isLoggedIn(state) {
|
||||
return state.token !== null && state.token !== ''
|
||||
}
|
||||
}
|
||||
export const getters = {}
|
||||
|
||||
@ -1048,6 +1048,20 @@
|
||||
webpack-node-externals "^1.7.2"
|
||||
webpackbar "^4.0.0"
|
||||
|
||||
"@nuxtjs/auth@^4.9.1":
|
||||
version "4.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/auth/-/auth-4.9.1.tgz#8827e4d23bf901711423434ad4a7073a8fc51603"
|
||||
integrity sha512-h5VZanq2+P47jq3t0EnsZv800cg/ufOPC6JqvcyeDFJM99p58jHSODAjDuePo3PrZxd8hovMk7zusU5lOHgjvQ==
|
||||
dependencies:
|
||||
"@nuxtjs/axios" "^5.9.5"
|
||||
body-parser "^1.19.0"
|
||||
consola "^2.11.3"
|
||||
cookie "^0.4.0"
|
||||
is-https "^1.0.0"
|
||||
js-cookie "^2.2.1"
|
||||
lodash "^4.17.15"
|
||||
nanoid "^2.1.11"
|
||||
|
||||
"@nuxtjs/axios@^5.3.6":
|
||||
version "5.9.7"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.9.7.tgz#ec78b72dbcb70fceee7724b7f24e0cb4d924440c"
|
||||
@ -1059,6 +1073,17 @@
|
||||
consola "^2.11.3"
|
||||
defu "^1.0.0"
|
||||
|
||||
"@nuxtjs/axios@^5.9.5":
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.10.0.tgz#3232980d781a208c672cd09e774e25e77208f0cb"
|
||||
integrity sha512-6zAvjQ/37qMzyk0OmgFI2iLAOJ6ADdm29mfRlmOKR5iR1ip3Mxzhm02O8WLcET3UrE74WuIHdli/WK/5e35bXw==
|
||||
dependencies:
|
||||
"@nuxtjs/proxy" "^1.3.3"
|
||||
axios "^0.19.2"
|
||||
axios-retry "^3.1.6"
|
||||
consola "^2.11.3"
|
||||
defu "^2.0.2"
|
||||
|
||||
"@nuxtjs/dotenv@^1.4.0":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@nuxtjs/dotenv/-/dotenv-1.4.1.tgz#dd5abb98e22cc7ae27139d3aa606151034293128"
|
||||
@ -1581,6 +1606,13 @@ axios-retry@^3.1.2:
|
||||
dependencies:
|
||||
is-retry-allowed "^1.1.0"
|
||||
|
||||
axios-retry@^3.1.6:
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.6.tgz#566d591b4fbcbcf90728b639a7642eb5cc3785c9"
|
||||
integrity sha512-pqOgBcpDtKU2YIBmHaHM8XnvzuOyRBxcvnD8+25uT0JcUEF0M1jq7Rpd7dTP27P8hQTynr/GNRuhEXZBLBffOw==
|
||||
dependencies:
|
||||
is-retry-allowed "^1.1.0"
|
||||
|
||||
axios@^0.19.2:
|
||||
version "0.19.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
|
||||
@ -1676,7 +1708,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
||||
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
|
||||
|
||||
body-parser@1.19.0:
|
||||
body-parser@1.19.0, body-parser@^1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
|
||||
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
|
||||
@ -2285,6 +2317,11 @@ cookie@^0.3.1:
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
|
||||
integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
|
||||
|
||||
cookie@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
|
||||
copy-concurrently@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
|
||||
@ -2669,6 +2706,11 @@ defu@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/defu/-/defu-1.0.0.tgz#43acb09dfcf81866fa3b0fc047ece18e5c30df71"
|
||||
integrity sha512-1Y1KRFxiiq+LYsZ3iP7xYSR8bHfmHFOUpDunZCN1ld1fGfDJWJIvkUBtjl3apnBwPuJtL/H7cwwlLYX8xPkraQ==
|
||||
|
||||
defu@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/defu/-/defu-2.0.2.tgz#9a3d4c1330d60c0ed4812e51864b948c51f7ad45"
|
||||
integrity sha512-E5dO3ji0TmVcZaB/2G6Ovu5zNHbWkgCU7v+EoE/Jj1Lbwv1BB6hNNKLkio2ZLI3/e3avlO634QUhQl4iCpm3Bg==
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@ -3704,13 +3746,6 @@ ignore@^5.1.4:
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
|
||||
integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
|
||||
|
||||
imagesloaded@4.1.4:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imagesloaded/-/imagesloaded-4.1.4.tgz#1376efcd162bb768c34c3727ac89cc04051f3cc7"
|
||||
integrity sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==
|
||||
dependencies:
|
||||
ev-emitter "^1.0.0"
|
||||
|
||||
import-cwd@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||
@ -3951,6 +3986,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-https@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-https/-/is-https-1.0.0.tgz#9c1dde000dc7e7288edb983bef379e498e7cb1bf"
|
||||
integrity sha512-1adLLwZT9XEXjzhQhZxd75uxf0l+xI9uTSFaZeSESjL3E1eXSPpO+u5RcgqtzeZ1KCaNvtEwZSTO2P4U5erVqQ==
|
||||
|
||||
is-nan@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03"
|
||||
@ -4068,6 +4108,11 @@ jest-worker@^25.1.0:
|
||||
merge-stream "^2.0.0"
|
||||
supports-color "^7.0.0"
|
||||
|
||||
js-cookie@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
|
||||
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -4581,6 +4626,11 @@ nan@^2.12.1:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||
|
||||
nanoid@^2.1.11:
|
||||
version "2.1.11"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
||||
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||
@ -7093,15 +7143,6 @@ vue-loader@^15.9.1:
|
||||
vue-hot-reload-api "^2.3.0"
|
||||
vue-style-loader "^4.1.0"
|
||||
|
||||
vue-masonry@^0.11.8:
|
||||
version "0.11.8"
|
||||
resolved "https://registry.yarnpkg.com/vue-masonry/-/vue-masonry-0.11.8.tgz#fc2dd458d13b557eebc68d70506af76aa6d1428e"
|
||||
integrity sha512-O+T+3zUghbKpjc+5aubXr8Kg1h9P334+Or9euYyXsQYa3mtScUqZFI6A16BijR9v4hYdtKksuPzU0mQplUvhDA==
|
||||
dependencies:
|
||||
imagesloaded "4.1.4"
|
||||
masonry-layout "^4.2.2"
|
||||
vue "^2.0.0"
|
||||
|
||||
vue-meta@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-meta/-/vue-meta-2.3.3.tgz#2a097f62817204b0da78be4d62aee0cb566eaee0"
|
||||
@ -7154,7 +7195,7 @@ vue-template-es2015-compiler@^1.9.0:
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||
|
||||
vue@^2.0.0, vue@^2.6.11:
|
||||
vue@^2.6.11:
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
|
||||
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user