Added user sign up and login.
This commit is contained in:
116
assets/components/auth/login.vue
Normal file
116
assets/components/auth/login.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<form
|
||||
class="login"
|
||||
@submit.prevent="login"
|
||||
>
|
||||
<div
|
||||
v-if="error"
|
||||
class="feedback error"
|
||||
>{{ error }}</div>
|
||||
|
||||
<div
|
||||
v-if="success"
|
||||
class="feedback success"
|
||||
>Login successful, redirecting</div>
|
||||
|
||||
<template v-else>
|
||||
<input
|
||||
v-model="username"
|
||||
placeholder="Username or e-mail"
|
||||
type="text"
|
||||
class="input"
|
||||
required
|
||||
>
|
||||
|
||||
<input
|
||||
v-model="password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
class="input"
|
||||
required
|
||||
>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="button button-primary"
|
||||
>Log in</button>
|
||||
|
||||
<router-link
|
||||
to="/signup"
|
||||
class="link link-external signup"
|
||||
>Sign up</router-link>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
async function login() {
|
||||
this.error = null;
|
||||
this.success = false;
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('login', {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
});
|
||||
|
||||
this.success = true;
|
||||
|
||||
setTimeout(() => {
|
||||
this.$router.replace(this.$route.query.ref || { name: 'home' });
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
this.error = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
username: null,
|
||||
password: null,
|
||||
success: false,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
login,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login {
|
||||
width: 20rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.input {
|
||||
margin: 0 0 .5rem 0;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: 0 0 .25rem 0;
|
||||
}
|
||||
|
||||
.signup {
|
||||
padding: .5rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
126
assets/components/auth/signup.vue
Normal file
126
assets/components/auth/signup.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<form
|
||||
class="signup"
|
||||
@submit.prevent="signup"
|
||||
>
|
||||
<div
|
||||
v-if="error"
|
||||
class="feedback error"
|
||||
>{{ error }}</div>
|
||||
|
||||
<div
|
||||
v-if="success"
|
||||
class="feedback success"
|
||||
>Signup successful, redirecting</div>
|
||||
|
||||
<template v-else>
|
||||
<input
|
||||
v-model="username"
|
||||
placeholder="Username"
|
||||
type="text"
|
||||
class="input"
|
||||
required
|
||||
>
|
||||
|
||||
<input
|
||||
v-model="email"
|
||||
placeholder="E-mail"
|
||||
type="email"
|
||||
class="input"
|
||||
required
|
||||
>
|
||||
|
||||
<input
|
||||
v-model="password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
class="input"
|
||||
required
|
||||
>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="button button-primary"
|
||||
>Sign up</button>
|
||||
|
||||
<router-link
|
||||
to="/login"
|
||||
class="link link-external login"
|
||||
>Log in</router-link>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
async function signup() {
|
||||
this.error = null;
|
||||
this.success = false;
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('signup', {
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
});
|
||||
|
||||
this.success = true;
|
||||
|
||||
setTimeout(() => {
|
||||
this.$router.replace(this.$route.query.ref || { name: 'home' });
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
this.error = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
username: null,
|
||||
email: null,
|
||||
password: null,
|
||||
success: false,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
signup,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.signup {
|
||||
width: 20rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.input {
|
||||
margin: 0 0 .5rem 0;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: 0 0 .25rem 0;
|
||||
}
|
||||
|
||||
.login {
|
||||
padding: .5rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -95,11 +95,26 @@
|
||||
<template v-slot:tooltip>
|
||||
<div class="menu">
|
||||
<ul class="menu-items noselect">
|
||||
<li
|
||||
class="menu-item disabled"
|
||||
<router-link
|
||||
v-if="!me"
|
||||
to="/login"
|
||||
class="menu-item"
|
||||
@click.stop
|
||||
>
|
||||
<Icon icon="enter2" />Sign in
|
||||
<Icon icon="enter2" />Log in
|
||||
</router-link>
|
||||
|
||||
<li
|
||||
v-if="me"
|
||||
class="menu-username"
|
||||
>{{ me.username }}</li>
|
||||
|
||||
<li
|
||||
v-if="me"
|
||||
class="menu-item"
|
||||
@click.stop="$store.dispatch('logout')"
|
||||
>
|
||||
<Icon icon="enter2" />Log out
|
||||
</li>
|
||||
|
||||
<li
|
||||
@@ -200,6 +215,10 @@ function theme(state) {
|
||||
return state.ui.theme;
|
||||
}
|
||||
|
||||
function me(state) {
|
||||
return state.auth.user;
|
||||
}
|
||||
|
||||
function setTheme(newTheme) {
|
||||
this.$store.dispatch('setTheme', newTheme);
|
||||
}
|
||||
@@ -224,6 +243,7 @@ export default {
|
||||
...mapState({
|
||||
sfw,
|
||||
theme,
|
||||
me,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
@@ -403,6 +423,8 @@ export default {
|
||||
.menu-item {
|
||||
display: flex;
|
||||
padding: .75rem 1rem .75rem .75rem;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
.icon {
|
||||
fill: var(--darken);
|
||||
@@ -428,6 +450,15 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.menu-username {
|
||||
font-weight: bold;
|
||||
color: var(--shadow-strong);
|
||||
font-size: .9rem;
|
||||
padding: .75rem 1rem;
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search-compact {
|
||||
display: none;
|
||||
height: 100%;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
.input {
|
||||
box-sizing: border-box;
|
||||
padding: .5rem;
|
||||
border: solid 1px var(--shadow-weak);
|
||||
border: solid 1px var(--shadow-hint);
|
||||
color: var(--shadow-strong);
|
||||
background: var(--background);
|
||||
font-size: 1rem;
|
||||
|
||||
&:focus {
|
||||
border: solid 1px var(--primary);
|
||||
}
|
||||
|
||||
&::-webkit-calendar-picker-indicator {
|
||||
opacity: .5;
|
||||
}
|
||||
@@ -20,6 +24,35 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: .5rem;
|
||||
font-weight: bold;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
color: var(--text-light);
|
||||
background: var(--primary);
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-strong);
|
||||
}
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--highlight-strong);
|
||||
background: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.album-toggle {
|
||||
height: fit-content;
|
||||
display: inline-flex;
|
||||
|
||||
@@ -36,6 +36,7 @@ $breakpoint4: 1500px;
|
||||
--female: #f0a;
|
||||
|
||||
--alert: #f00;
|
||||
--error: #f00;
|
||||
--warn: #fa0;
|
||||
--success: #5c2;
|
||||
|
||||
@@ -58,6 +59,7 @@ $breakpoint4: 1500px;
|
||||
--tile: #2a2a2a;
|
||||
|
||||
--link: #dd6688;
|
||||
--link-external: #48f;
|
||||
--empty: #333;
|
||||
|
||||
--crease: #eaeaea;
|
||||
|
||||
@@ -44,3 +44,12 @@ body {
|
||||
fill: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link-external {
|
||||
color: var(--link-external);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,22 @@ async function post(endpoint, data) {
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
async function del(endpoint) {
|
||||
const res = await fetch(`${config.api.url}${endpoint}`, {
|
||||
method: 'DELETE',
|
||||
mode: 'cors',
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const errorMsg = await res.text();
|
||||
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
async function graphql(query, variables = null) {
|
||||
const res = await fetch('/graphql', {
|
||||
method: 'POST',
|
||||
@@ -67,5 +83,6 @@ async function graphql(query, variables = null) {
|
||||
export {
|
||||
get,
|
||||
post,
|
||||
del,
|
||||
graphql,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
function initAuthActions(_store, _router) {}
|
||||
import { get, post, del } from '../api';
|
||||
|
||||
function initAuthActions(_store, _router) {
|
||||
async function fetchMe({ commit }) {
|
||||
const user = await get('/session');
|
||||
|
||||
commit('setUser', user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async function login({ commit }, credentials) {
|
||||
const user = await post('/session', credentials);
|
||||
|
||||
commit('setUser', user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async function signup({ commit }, credentials) {
|
||||
const user = await post('/users', credentials);
|
||||
|
||||
commit('setUser', user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async function logout({ commit }) {
|
||||
await del('/session');
|
||||
|
||||
commit('setUser', null);
|
||||
}
|
||||
|
||||
return {
|
||||
fetchMe,
|
||||
login,
|
||||
logout,
|
||||
signup,
|
||||
};
|
||||
}
|
||||
|
||||
export default initAuthActions;
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
export default {};
|
||||
function setUser(state, user) {
|
||||
state.user = user;
|
||||
}
|
||||
|
||||
export default {
|
||||
setUser,
|
||||
};
|
||||
|
||||
5
assets/js/auth/observers.js
Normal file
5
assets/js/auth/observers.js
Normal file
@@ -0,0 +1,5 @@
|
||||
async function initAuthObserver(store, _router) {
|
||||
await store.dispatch('fetchMe');
|
||||
}
|
||||
|
||||
export default initAuthObserver;
|
||||
@@ -1,4 +1,3 @@
|
||||
export default {
|
||||
authenticated: false,
|
||||
user: null,
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import mitt from 'mitt';
|
||||
import router from './router';
|
||||
import initStore from './store';
|
||||
import initUiObservers from './ui/observers';
|
||||
import initAuthObservers from './auth/observers';
|
||||
|
||||
import { formatDate, formatDuration } from './format';
|
||||
|
||||
@@ -64,6 +65,7 @@ async function init() {
|
||||
}
|
||||
|
||||
initUiObservers(store, router);
|
||||
initAuthObservers(store, router);
|
||||
|
||||
if (window.env.sfw) {
|
||||
store.dispatch('setSfw', true);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
|
||||
import Home from '../components/home/home.vue';
|
||||
import Login from '../components/auth/login.vue';
|
||||
import Signup from '../components/auth/signup.vue';
|
||||
import Release from '../components/releases/release.vue';
|
||||
import Entity from '../components/entities/entity.vue';
|
||||
import Networks from '../components/networks/networks.vue';
|
||||
@@ -16,6 +18,7 @@ import NotFound from '../components/errors/404.vue';
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
redirect: {
|
||||
name: 'updates',
|
||||
params: {
|
||||
@@ -25,6 +28,16 @@ const routes = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login,
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
name: 'singup',
|
||||
component: Signup,
|
||||
},
|
||||
{
|
||||
path: '/updates',
|
||||
redirect: {
|
||||
|
||||
Reference in New Issue
Block a user