diff --git a/package.json b/package.json index 744ed1c..c6d9788 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,9 @@ "license": "MIT", "packageManager": "pnpm@10.33.0", "scripts": { - "lint": "node scripts/vue-cli-service.js lint", - "serve": "node scripts/vue-cli-service.js serve", - "build": "node scripts/vue-cli-service.js build", + "lint": "vue-cli-service lint", + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", "test": "jest" }, "dependencies": { diff --git a/scripts/vue-cli-service.js b/scripts/vue-cli-service.js deleted file mode 100644 index f078962..0000000 --- a/scripts/vue-cli-service.js +++ /dev/null @@ -1,37 +0,0 @@ -const { spawn } = require('child_process'); -const path = require('path'); - -const args = process.argv.slice(2); -const env = { ...process.env }; -const opensslLegacyProvider = '--openssl-legacy-provider'; -const nodeMajor = Number(process.versions.node.split('.')[0]); -const cliArgs = []; - -if (nodeMajor >= 17 && !(env.NODE_OPTIONS || '').includes(opensslLegacyProvider)) { - env.NODE_OPTIONS = [env.NODE_OPTIONS, opensslLegacyProvider].filter(Boolean).join(' '); -} - -if (args[0] === 'serve') { - cliArgs.push('--require', path.join(__dirname, 'webpack-dev-server-compat.js')); -} - -const child = spawn( - process.execPath, - [...cliArgs, require.resolve('@vue/cli-service/bin/vue-cli-service.js'), ...args], - { - env, - stdio: 'inherit', - }, -); - -child.on('close', (code, signal) => { - if (signal === 'SIGINT') { - process.exit(130); - } - - if (signal === 'SIGTERM') { - process.exit(143); - } - - process.exit(code || 0); -}); diff --git a/scripts/webpack-dev-server-compat.js b/scripts/webpack-dev-server-compat.js deleted file mode 100644 index 1bc9cd5..0000000 --- a/scripts/webpack-dev-server-compat.js +++ /dev/null @@ -1,14 +0,0 @@ -const Module = require('module'); -const https = require('https'); - -const originalLoad = Module._load; - -Module._load = function load(request, parent, isMain) { - if (request === 'spdy') { - return { - createServer: https.createServer, - }; - } - - return originalLoad.apply(this, arguments); -}; diff --git a/src/components/layout/MainNavbar/MainNavbar.vue b/src/components/layout/MainNavbar/MainNavbar.vue index 23323cf..93c8b1d 100644 --- a/src/components/layout/MainNavbar/MainNavbar.vue +++ b/src/components/layout/MainNavbar/MainNavbar.vue @@ -40,9 +40,8 @@ v-if="userInfo.auth_type.length === 0" id="user-actions" class="dropdown-menu dropdown-menu-small"> - - - Logout + + Logout @@ -55,6 +54,7 @@ diff --git a/src/layouts/Login.vue b/src/layouts/Login.vue index 1ec76d1..19eb03b 100644 --- a/src/layouts/Login.vue +++ b/src/layouts/Login.vue @@ -5,13 +5,13 @@
Welcome to Gorse dashboard
- + - + - + diff --git a/src/main.js b/src/main.js index 9e33511..60628bd 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,5 @@ /* eslint-disable */ +import axios from 'axios'; import { createApp } from 'vue'; import ShardsVue from '@gorse/shards-vue'; @@ -19,6 +20,7 @@ import 'material-icons/iconfont/material-icons.css'; // Core import App from './App.vue'; import router from './router'; +import { clearLoginStatus } from './utils/auth'; import { applyTheme, getPreferredTheme } from './utils/theme'; // Layouts @@ -58,6 +60,23 @@ const app = createApp(App); applyTheme(getPreferredTheme()); +axios.interceptors.response.use( + (response) => response, + (error) => { + if (error.response && error.response.status === 401 && !(error.config && error.config.skipAuthRedirect)) { + clearLoginStatus(); + if (router.currentRoute.value.name !== 'login') { + router.push({ + name: 'login', + query: { redirect: router.currentRoute.value.fullPath }, + }).catch(() => {}); + } + return new Promise(() => {}); + } + return Promise.reject(error); + }, +); + app.use(router); app.use(ShardsVue); app.use(hljsVuePlugin); @@ -66,4 +85,6 @@ app.component('default-layout', Default); app.component('login-layout', Login); app.config.globalProperties.$eventHub = createEventHub(); -app.mount('#app'); +router.isReady().then(() => { + app.mount('#app'); +}); diff --git a/src/router.js b/src/router.js index 34fd2a2..a76e525 100644 --- a/src/router.js +++ b/src/router.js @@ -1,3 +1,4 @@ +import axios from 'axios'; import { createRouter, createWebHistory } from 'vue-router'; import Tasks from './views/Tasks.vue'; @@ -16,8 +17,25 @@ import ImportItems from './views/ImportItems.vue'; import ImportUsers from './views/ImportUsers.vue'; import ImportFeedback from './views/ImportFeedback.vue'; import RecFlow from './views/RecFlow.vue'; +import { getLoginStatus, setLoginStatus } from './utils/auth'; -export default createRouter({ +async function verifyLoginStatus() { + try { + await axios.get('/api/dashboard/userinfo', { + skipAuthRedirect: true, + }); + setLoginStatus(true); + return true; + } catch (error) { + if (error.response && error.response.status === 401) { + setLoginStatus(false); + return false; + } + return null; + } +} + +const router = createRouter({ history: createWebHistory(process.env.BASE_URL), linkActiveClass: 'active', linkExactActiveClass: 'exact-active', @@ -120,3 +138,42 @@ export default createRouter({ }, ], }); + +router.beforeEach(async (to, from, next) => { + if (to.name === 'login') { + if (getLoginStatus() === true) { + const isLoggedIn = await verifyLoginStatus(); + if (isLoggedIn === true) { + next(to.query.redirect || '/overview'); + return; + } + } + next(); + return; + } + + const loginStatus = getLoginStatus(); + if (loginStatus === true) { + const isLoggedIn = await verifyLoginStatus(); + if (isLoggedIn === false) { + next({ name: 'login', query: { redirect: to.fullPath } }); + return; + } + next(); + return; + } + + if (loginStatus === false) { + next({ name: 'login', query: { redirect: to.fullPath } }); + return; + } + + const isLoggedIn = await verifyLoginStatus(); + if (isLoggedIn === false) { + next({ name: 'login', query: { redirect: to.fullPath } }); + } else { + next(); + } +}); + +export default router; diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 0000000..02b3ee3 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,21 @@ +const LOGIN_STATUS_KEY = 'gorse_dashboard_login_status'; + +let loginStatus = sessionStorage.getItem(LOGIN_STATUS_KEY); +loginStatus = loginStatus === null ? null : loginStatus === 'true'; + +export function getLoginStatus() { + return loginStatus; +} + +export function setLoginStatus(status) { + loginStatus = status; + if (status === null) { + sessionStorage.removeItem(LOGIN_STATUS_KEY); + } else { + sessionStorage.setItem(LOGIN_STATUS_KEY, status ? 'true' : 'false'); + } +} + +export function clearLoginStatus() { + setLoginStatus(false); +}