From 05b347af6cd556733eddda3c5cf2b0c2a0060903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Thu, 21 Jul 2022 14:13:55 +0200 Subject: [PATCH] Add login page and basic nested router with nav guards #2 --- src/App.vue | 35 ++++++++++++++++++------- src/assets/images/ls-login.png | Bin 0 -> 8560 bytes src/assets/logo.svg | 1 - src/components/NavbarTop.vue | 35 +++++++++++++++++++------ src/components/SidebarLeft.vue | 23 ++++++++--------- src/router/index.ts | 32 ++++++++++++++++------- src/stores/auth.ts | 45 +++++++++++++++++++++++++++++++++ src/views/BucketsView.vue | 20 +-------------- src/views/DashboardView.vue | 10 ++++++++ src/views/LoginView.vue | 34 +++++++++++++++++++++++++ 10 files changed, 176 insertions(+), 59 deletions(-) create mode 100644 src/assets/images/ls-login.png delete mode 100644 src/assets/logo.svg create mode 100644 src/stores/auth.ts create mode 100644 src/views/DashboardView.vue create mode 100644 src/views/LoginView.vue diff --git a/src/App.vue b/src/App.vue index 420561b..e293b27 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,16 +1,33 @@ <script setup lang="ts"> import NavbarTop from "./components/NavbarTop.vue"; -import SidebarLeft from "./components/SidebarLeft.vue"; +import { onBeforeMount } from "vue"; +import { useCookies } from "vue3-cookies"; +import { useAuthStore } from "@/stores/auth"; +import { useRouter } from "vue-router"; + +const { cookies } = useCookies(); +const store = useAuthStore(); +const router = useRouter(); + +onBeforeMount(() => { + store.setToken(cookies.get("bearer")); + router.beforeEach(async (to) => { + // make sure the user is authenticated + if ( + !store.authenticated && + // â—ï¸ Avoid an infinite redirect + to.name !== "login" + ) { + // redirect the user to the login page + return { name: "login" }; + } + }); +}); </script> -<template style="height: 100%"> +<template> <NavbarTop /> - <SidebarLeft /> - <router-view class="cona"></router-view> + <router-view></router-view> </template> -<style scoped> -.cona { - min-height: 80vh; -} -</style> +<style scoped></style> diff --git a/src/assets/images/ls-login.png b/src/assets/images/ls-login.png new file mode 100644 index 0000000000000000000000000000000000000000..ab72fa293def5199720b4a00bac0d824aad6d26a GIT binary patch literal 8560 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 OPEJ}`sz%~l$o~OMm(fuG literal 0 HcmV?d00001 diff --git a/src/assets/logo.svg b/src/assets/logo.svg deleted file mode 100644 index bc826fe..0000000 --- a/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg> \ No newline at end of file diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue index edf18f4..07ad562 100644 --- a/src/components/NavbarTop.vue +++ b/src/components/NavbarTop.vue @@ -2,8 +2,15 @@ import BootstrapIcon from "./BootstrapIcon.vue"; import { reactive, onBeforeUnmount, onMounted } from "vue"; import { MiscellaneousService } from "@/client"; +import { useAuthStore } from "@/stores/auth"; +import { useRouter } from "vue-router"; +import { useCookies } from "vue3-cookies"; -const api_connection = reactive({ connected: false, timer: null }); +const router = useRouter(); +const store = useAuthStore(); +const { cookies } = useCookies(); + +const api_connection = reactive({ connected: true, timer: null }); let timer: ReturnType<typeof setInterval> | undefined = undefined; function checkApiHealth() { MiscellaneousService.miscellaneousHealthCheck() @@ -15,6 +22,12 @@ function checkApiHealth() { }); } +function logout() { + store.logout(); + cookies.remove("bearer"); + router.push({ name: "login" }); +} + onMounted(() => { checkApiHealth(); timer = setInterval(checkApiHealth, 10000); @@ -36,7 +49,10 @@ onBeforeUnmount(() => { > Backend not reachable </span> - <div class="dropdown d-flex me-3"> + <div + class="dropdown d-flex me-3" + v-if="store.authenticated && store.user != null" + > <a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle-split" @@ -44,7 +60,7 @@ onBeforeUnmount(() => { data-bs-toggle="dropdown" aria-expanded="false" > - <strong class="me-2">Bilbo Baggins</strong> + <strong class="me-2">{{ store.user.display_name }}</strong> <bootstrap-icon icon="person-circle" fill="white" @@ -56,15 +72,18 @@ onBeforeUnmount(() => { class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1" > - <li><a class="dropdown-item" href="#">New project...</a></li> - <li><a class="dropdown-item" href="#">Settings</a></li> - <li><a class="dropdown-item" href="#">Profile</a></li> <li><hr class="dropdown-divider" /></li> - <li><a class="dropdown-item" href="#">Sign out</a></li> + <li> + <a class="dropdown-item pseudo-link" @click="logout">Sign out</a> + </li> </ul> </div> </div> </nav> </template> -<style scoped></style> +<style scoped> +.pseudo-link { + cursor: pointer; +} +</style> diff --git a/src/components/SidebarLeft.vue b/src/components/SidebarLeft.vue index a5d1866..677c417 100644 --- a/src/components/SidebarLeft.vue +++ b/src/components/SidebarLeft.vue @@ -2,32 +2,28 @@ <template> <div - class="d-flex flex-column flex-shrink-0 p-3 text-white bg-dark" - style="width: 200px" + class="d-flex flex-column flex-shrink-0 p-3 text-white bg-dark position-fixed top-50 start-0 translate-middle-y" + style="width: 200px; min-height: 80vh" > <ul class="nav nav-pills flex-column mb-auto"> - <li class="nav-item"> + <li class="nav-item mb-1"> <button - class="btn btn-toggle align-items-center rounded collapsed text-white" + class="btn btn-lg btn-toggle align-items-center rounded collapsed text-white" data-bs-toggle="collapse" - data-bs-target="#home-collapse" + data-bs-target="#os-collapse" aria-expanded="true" > Object Storage </button> - <div class="collapse show" id="home-collapse"> + <div class="collapse show fs-5" id="os-collapse"> <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small"> <li> - <router-link - to="/object-storage/buckets" - class="link-light rounded" + <router-link :to="{ name: 'buckets' }" class="link-light rounded" >Buckets</router-link > </li> <li> - <router-link - to="/object-storage/s3-keys" - class="link-light rounded" + <router-link :to="{ name: 's3_keys' }" class="link-light rounded" >S3 Keys</router-link > </li> @@ -38,4 +34,5 @@ </div> </template> -<style scoped></style> +<style scoped> +</style> diff --git a/src/router/index.ts b/src/router/index.ts index 3cc545e..6034817 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,21 +1,35 @@ import { createRouter, createWebHistory } from "vue-router"; import BucketsView from "../views/BucketsView.vue"; +import DashboardView from "../views/DashboardView.vue"; +import LoginView from "../views/LoginView.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { - path: "/object-storage/buckets", - name: "buckets", - component: BucketsView, + path: "/dashboard", + name: "dashboard", + component: DashboardView, + children: [ + { + path: "object-storage/buckets", + name: "buckets", + component: BucketsView, + }, + { + path: "object-storage/s3-keys", + name: "s3_keys", + // route level code-splitting + // this generates a separate chunk (About.[hash].js) for this route + // which is lazy-loaded when the route is visited. + component: () => import("../views/S3KeysView.vue"), + }, + ], }, { - path: "/object-storage/s3-keys", - name: "about", - // route level code-splitting - // this generates a separate chunk (About.[hash].js) for this route - // which is lazy-loaded when the route is visited. - component: () => import("../views/S3KeysView.vue"), + path: "/login", + name: "login", + component: LoginView, }, { path: "/", diff --git a/src/stores/auth.ts b/src/stores/auth.ts new file mode 100644 index 0000000..93dd60e --- /dev/null +++ b/src/stores/auth.ts @@ -0,0 +1,45 @@ +import { defineStore } from "pinia"; +import type { User } from "@/client"; +import { UserService } from "@/client"; +import { OpenAPI } from "@/client"; + +export type RootState = { + token: string | null; + user: User | null; +}; + +export const useAuthStore = defineStore({ + id: "auth", + state: () => + ({ + token: null, + user: null, + } as RootState), + getters: { + authenticated: (state) => state.token != null, + }, + actions: { + setToken(token: string | null) { + if (token != null) { + OpenAPI.TOKEN = token; + this.token = token; + UserService.userGetLoggedInUser() + .then((user) => { + this.user = user; + }) + .catch(() => { + this.token = null; + }); + } else { + this.token = null; + this.user = null; + } + }, + updateUser() { + this.setToken(this.token); + }, + logout() { + this.setToken(null); + }, + }, +}); diff --git a/src/views/BucketsView.vue b/src/views/BucketsView.vue index 94874bb..3635de2 100644 --- a/src/views/BucketsView.vue +++ b/src/views/BucketsView.vue @@ -1,25 +1,7 @@ -<script setup lang="ts"> -import { onMounted, reactive } from "vue"; -import { useCookies } from "vue3-cookies"; - -const { cookies } = useCookies(); -const my_cookie = reactive({ - val: "defaults", -}); - -onMounted(() => { - if (cookies.isKey("bearer")) { - my_cookie.val = cookies.get("bearer"); - } -}); -</script> +<script setup lang="ts"></script> <template> <main> - <p v-if="my_cookie.val !== 'defaults'"> - Cookie Present: {{ my_cookie.val }} - </p> - <p v-else>No Cookie set</p> <div>This the is the Buckets Page</div> </main> </template> diff --git a/src/views/DashboardView.vue b/src/views/DashboardView.vue new file mode 100644 index 0000000..ed97b6c --- /dev/null +++ b/src/views/DashboardView.vue @@ -0,0 +1,10 @@ +<script setup lang="ts"> +import SidebarLeft from "../components/SidebarLeft.vue"; +</script> + +<template> + <SidebarLeft /> + <router-view></router-view> +</template> + +<style scoped></style> diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue new file mode 100644 index 0000000..5de83eb --- /dev/null +++ b/src/views/LoginView.vue @@ -0,0 +1,34 @@ +<script setup lang="ts"> +import { onBeforeMount } from "vue"; +import { useAuthStore } from "@/stores/auth"; +import { useRouter } from "vue-router"; +import { OpenAPI } from "@/client"; + +const router = useRouter(); + +const store = useAuthStore(); + +onBeforeMount(() => { + if (store.authenticated) { + // If user is authenticated redirect him to the dashboard + router.push({ name: "buckets" }); + } +}); +</script> + +<template> + <div + class="card text-center bg-dark ms-md-auto position-absolute top-50 left-50 translate-middle" + > + <div class="card-header text-dark bg-light">LoginView</div> + <div class="card-body p-5"> + <h5 class="card-title">Login</h5> + <p class="card-text">Login to this service with LifeScience</p> + <a :href="OpenAPI.BASE + '/auth/login'" class="m-2"> + <img src="/src/assets/images/ls-login.png" alt="[LS Login]" /> + </a> + </div> + </div> +</template> + +<style scoped></style> -- GitLab