mirror of
https://github.com/maciejpedzich/racemash.git
synced 2024-11-27 16:15:47 +01:00
Implement authentication module
This commit is contained in:
parent
9713b47b65
commit
84156909ab
16
src/App.vue
16
src/App.vue
@ -1,12 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuth } from './composables/useAuth';
|
||||
import NavMenu from './components/ui/NavMenu.vue';
|
||||
|
||||
const { userLoadingFinished } = useAuth();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<NavMenu />
|
||||
<v-main>
|
||||
<RouterView />
|
||||
<section
|
||||
v-if="!userLoadingFinished"
|
||||
class="w-100 h-100 pb-4 d-flex justify-center align-center"
|
||||
>
|
||||
<v-progress-circular
|
||||
:size="100"
|
||||
:width="7"
|
||||
color="indigo"
|
||||
indeterminate
|
||||
></v-progress-circular>
|
||||
</section>
|
||||
<RouterView v-show="userLoadingFinished" />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
@ -1,8 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
import ThemeSwitch from './ThemeSwitch.vue';
|
||||
|
||||
const { isLoggedIn, logOut } = useAuth();
|
||||
const router = useRouter();
|
||||
|
||||
const showDrawer = ref(false);
|
||||
|
||||
const logOutAndGoToLogIn = async () => {
|
||||
await logOut();
|
||||
await router.push('/log-in');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -16,25 +27,23 @@ const showDrawer = ref(false);
|
||||
</v-app-bar>
|
||||
<v-navigation-drawer v-model="showDrawer" temporary>
|
||||
<v-list density="compact" nav>
|
||||
<v-list-item title="Home" prepend-icon="mdi-home" link to="/" />
|
||||
<template v-if="isLoggedIn">
|
||||
<v-list-item title="Vote" prepend-icon="mdi-vote" link to="/vote" />
|
||||
<v-list-item
|
||||
title="Home"
|
||||
prepend-icon="mdi-home"
|
||||
link
|
||||
to="/"
|
||||
></v-list-item>
|
||||
<v-list-item
|
||||
title="Vote"
|
||||
prepend-icon="mdi-vote"
|
||||
link
|
||||
to="/vote"
|
||||
></v-list-item>
|
||||
title="Log out"
|
||||
prepend-icon="mdi-logout"
|
||||
@click="logOutAndGoToLogIn"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-list-item
|
||||
title="Log in"
|
||||
prepend-icon="mdi-login"
|
||||
link
|
||||
to="/log-in"
|
||||
></v-list-item>
|
||||
<v-list-item title="Log out" prepend-icon="mdi-logout"></v-list-item>
|
||||
/>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
</template>
|
||||
|
54
src/composables/useAuth.ts
Normal file
54
src/composables/useAuth.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import { Models } from 'appwrite';
|
||||
|
||||
import { account } from '@/appwrite';
|
||||
|
||||
const user = ref<Models.User<Models.Preferences> | null>(null);
|
||||
const userLoadingFinished = ref(false);
|
||||
const isLoggedIn = computed(() => !!user.value);
|
||||
|
||||
export function useAuth() {
|
||||
const logIn = (provider: 'github' | 'discord') => {
|
||||
const redirectPath = localStorage.getItem('redirectPath') || '/';
|
||||
const permissionScopes =
|
||||
provider === 'github'
|
||||
? ['read:user', 'user:email']
|
||||
: ['identify', 'email'];
|
||||
|
||||
account.createOAuth2Session(
|
||||
provider,
|
||||
`${location.origin}${redirectPath}`,
|
||||
`${location.origin}/log-in#oauth-error`,
|
||||
permissionScopes
|
||||
);
|
||||
};
|
||||
|
||||
const loadUser = async () => {
|
||||
try {
|
||||
if (userLoadingFinished.value) return;
|
||||
|
||||
const currentUser = await account.get();
|
||||
user.value = currentUser;
|
||||
} catch (error) {
|
||||
// TODO: Add to globaL Vue error logger and implement snackbar message bus
|
||||
console.error(error);
|
||||
user.value = null;
|
||||
} finally {
|
||||
userLoadingFinished.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const logOut = async () => {
|
||||
await account.deleteSession('current');
|
||||
user.value = null;
|
||||
};
|
||||
|
||||
return {
|
||||
user,
|
||||
userLoadingFinished,
|
||||
isLoggedIn,
|
||||
logIn,
|
||||
loadUser,
|
||||
logOut
|
||||
};
|
||||
}
|
19
src/guards/auth.ts
Normal file
19
src/guards/auth.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
|
||||
export async function authGuard(
|
||||
to: RouteLocationNormalized,
|
||||
from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
const { isLoggedIn, loadUser } = useAuth();
|
||||
|
||||
await loadUser();
|
||||
|
||||
if (!to.meta.authRequired || isLoggedIn.value) {
|
||||
return next();
|
||||
} else {
|
||||
localStorage.setItem('redirectPath', to.fullPath);
|
||||
return next('/log-in');
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
|
||||
import { authGuard } from '@/guards/auth';
|
||||
import Home from '@/views/Home.vue';
|
||||
|
||||
const routes = [
|
||||
@ -6,6 +8,17 @@ const routes = [
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/log-in',
|
||||
name: 'LogIn',
|
||||
component: () => import('../views/LogIn.vue')
|
||||
},
|
||||
{
|
||||
path: '/vote',
|
||||
name: 'Vote',
|
||||
meta: { authRequired: true },
|
||||
component: () => import('../views/Vote.vue')
|
||||
}
|
||||
];
|
||||
|
||||
@ -14,4 +27,6 @@ const router = createRouter({
|
||||
routes
|
||||
});
|
||||
|
||||
router.beforeEach(authGuard);
|
||||
|
||||
export default router;
|
||||
|
7
src/types.d.ts
vendored
Normal file
7
src/types.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import 'vue-router';
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
authRequired?: boolean;
|
||||
}
|
||||
}
|
21
src/views/LogIn.vue
Normal file
21
src/views/LogIn.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAuth } from '@/composables/useAuth';
|
||||
|
||||
const { logIn } = useAuth();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="w-100 h-100 d-flex flex-column justify-center align-center">
|
||||
<h1 class="text-h3 mb-3">Log in via:</h1>
|
||||
<v-container>
|
||||
<v-row align="center" justify="center">
|
||||
<v-col cols="auto">
|
||||
<v-btn size="large" @click="logIn('github')">GitHub</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="auto">
|
||||
<v-btn size="large" @click="logIn('discord')">Discord</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</section>
|
||||
</template>
|
3
src/views/Vote.vue
Normal file
3
src/views/Vote.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<h1>Vote</h1>
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user