Merge branch 'dev/front-methapon' into dev/front
This commit is contained in:
commit
2dd8b2b92d
8 changed files with 72 additions and 172 deletions
|
|
@ -1,7 +1,5 @@
|
||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="azay-admin-app">
|
<div>
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<transition>
|
<transition>
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
|
|
@ -9,5 +7,3 @@
|
||||||
</router-view>
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { getUsername, logout } from '@/services/KeyCloakService'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import KeyCloakService from '@/services/KeyCloakService'
|
|
||||||
const dropdownOpen = ref<boolean>(false)
|
const dropdownOpen = ref<boolean>(false)
|
||||||
const accountName = ref<string>()
|
const accountName = ref<string>(getUsername())
|
||||||
|
|
||||||
accountName.value = KeyCloakService.GetUserName()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -32,16 +30,7 @@ accountName.value = KeyCloakService.GetUserName()
|
||||||
</div>
|
</div>
|
||||||
<q-btn-dropdown stretch flat v-model="dropdownOpen">
|
<q-btn-dropdown stretch flat v-model="dropdownOpen">
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item
|
<q-item clickable v-close-popup tabindex="0" @click="() => logout()">
|
||||||
clickable
|
|
||||||
v-close-popup
|
|
||||||
tabindex="0"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
KeyCloakService.CallLogOut()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-avatar icon="logout" color="primary" text-color="white" caption>
|
<q-avatar icon="logout" color="primary" text-color="white" caption>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
|
|
@ -50,7 +39,6 @@ accountName.value = KeyCloakService.GetUserName()
|
||||||
<q-item-label>Logout</q-item-label>
|
<q-item-label>Logout</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-separator inset spaced></q-separator>
|
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,18 @@ import th from 'quasar/lang/th'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
import HttpService from '@/services/HttpService'
|
|
||||||
import quasarUserOptions from './quasar-user-options'
|
import quasarUserOptions from './quasar-user-options'
|
||||||
import router from './router'
|
|
||||||
|
|
||||||
import 'quasar/src/css/index.sass'
|
import 'quasar/src/css/index.sass'
|
||||||
|
import { login } from './services/KeyCloakService'
|
||||||
|
|
||||||
|
await login()
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
app.use(router)
|
app.use((await import('./router')).default)
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
|
|
||||||
app.use(Quasar, {
|
app.use(Quasar, {
|
||||||
...quasarUserOptions,
|
...quasarUserOptions,
|
||||||
plugins: {
|
plugins: {
|
||||||
|
|
@ -28,9 +28,7 @@ app.use(Quasar, {
|
||||||
|
|
||||||
app.component(
|
app.component(
|
||||||
'full-loader',
|
'full-loader',
|
||||||
defineAsyncComponent(() => import('@/components/FullLoader.vue'))
|
defineAsyncComponent(() => import('@/components/FullLoader.vue')),
|
||||||
)
|
)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
HttpService.configureAxiosKeycloak()
|
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,52 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import UserModule from '@/modules/01_user/router'
|
import UserModule from '@/modules/01_user/router'
|
||||||
import AdminModule from '@/modules/02_admin/router'
|
import AdminModule from '@/modules/02_admin/router'
|
||||||
import KeyCloakService from '@/services/KeyCloakService'
|
import { getRole, getToken, login } from '@/services/KeyCloakService'
|
||||||
|
|
||||||
|
const history = createWebHistory(import.meta.env.BASE_URL)
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history,
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'UserModule',
|
name: 'UserModule',
|
||||||
component: () => import('@/views/MainLayout.vue'),
|
component: () => import('@/views/MainLayout.vue'),
|
||||||
beforeEnter: (_to, _from, next) => {
|
beforeEnter: async (_to, _from, next) => {
|
||||||
const token = KeyCloakService.GetAccesToken()
|
const token = await getToken()
|
||||||
if (token) {
|
|
||||||
next()
|
|
||||||
} else {
|
|
||||||
KeyCloakService.CallLogin(() => {
|
|
||||||
const tokenAfterLogin = KeyCloakService.GetAccesToken()
|
|
||||||
const roles = KeyCloakService.GetUserRoles()
|
|
||||||
|
|
||||||
if (
|
if (token) return next()
|
||||||
tokenAfterLogin &&
|
|
||||||
(roles.includes('user') || roles.includes('admin'))
|
await login(async () => {
|
||||||
) {
|
if (await getToken()) return next()
|
||||||
next()
|
return next('/')
|
||||||
} else {
|
})
|
||||||
console.error('ไม่สามารถดึง Token หลังจากล็อกอินได้')
|
|
||||||
next('/')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
children: [...UserModule],
|
children: [...UserModule],
|
||||||
meta: {
|
meta: { statusAccount: false },
|
||||||
statusAccount: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/admin',
|
path: '/admin',
|
||||||
name: 'AdminModule',
|
name: 'AdminModule',
|
||||||
component: () => import('@/views/MainLayout.vue'),
|
component: () => import('@/views/MainLayout.vue'),
|
||||||
beforeEnter: (_to, _from, next) => {
|
beforeEnter: async (_to, _from, next) => {
|
||||||
const token = KeyCloakService.GetAccesToken()
|
const token = await getToken()
|
||||||
if (token) {
|
|
||||||
next()
|
|
||||||
} else {
|
|
||||||
KeyCloakService.CallLogin(() => {
|
|
||||||
const tokenAfterLogin = KeyCloakService.GetAccesToken()
|
|
||||||
const roles = KeyCloakService.GetUserRoles()
|
|
||||||
console.log(roles)
|
|
||||||
|
|
||||||
if (tokenAfterLogin && roles.includes('admin')) {
|
if (token) return next()
|
||||||
next()
|
|
||||||
} else {
|
await login(async () => {
|
||||||
console.error('ไม่สามารถดึง Token หลังจากล็อกอินได้')
|
const token = await getToken()
|
||||||
next('/')
|
const roles = getRole()
|
||||||
}
|
|
||||||
})
|
if (token && roles.includes('admin')) {
|
||||||
}
|
return next()
|
||||||
},
|
}
|
||||||
meta: {
|
|
||||||
statusAccount: true,
|
return next('/')
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
meta: { statusAccount: true },
|
||||||
children: [...AdminModule],
|
children: [...AdminModule],
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 404 Not Found
|
|
||||||
* ref: https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route
|
|
||||||
*/
|
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
name: 'NotFound',
|
name: 'NotFound',
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,11 @@
|
||||||
import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import KeyCloakService from '@/services/KeyCloakService'
|
import { getToken } from './KeyCloakService'
|
||||||
|
|
||||||
const HttpMethods = {
|
const instance = axios.create()
|
||||||
GET: 'GET',
|
|
||||||
POST: 'POST',
|
|
||||||
DELETE: 'DELETE',
|
|
||||||
}
|
|
||||||
|
|
||||||
const _axios = axios.create()
|
instance.interceptors.request.use(async (config) => {
|
||||||
const cb = (config: InternalAxiosRequestConfig) => {
|
config.headers.Authorization = `Bearer ${await getToken()}`
|
||||||
config.headers.Authorization = `Bearer ${KeyCloakService.GetAccesToken()}`
|
|
||||||
return config
|
return config
|
||||||
}
|
})
|
||||||
|
|
||||||
const configureAxiosKeycloak = (): void => {
|
export default instance
|
||||||
_axios.interceptors.request.use(
|
|
||||||
(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
|
|
||||||
if (KeyCloakService.IsLoggedIn()) {
|
|
||||||
KeyCloakService.UpdateToken(cb(config))
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAxiosClient = (): AxiosInstance => _axios
|
|
||||||
|
|
||||||
const HttpService = {
|
|
||||||
HttpMethods,
|
|
||||||
configureAxiosKeycloak,
|
|
||||||
getAxiosClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HttpService
|
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,42 @@
|
||||||
import Keycloak from 'keycloak-js'
|
import Keycloak from 'keycloak-js'
|
||||||
|
|
||||||
const keycloakInstance = new Keycloak()
|
const keycloak = new Keycloak()
|
||||||
|
|
||||||
interface CallbackOneParam<T1 = void, T2 = void> {
|
export async function login(cb?: (...args: any[]) => void) {
|
||||||
(param1: T1): T2
|
const auth = await keycloak
|
||||||
}
|
.init({
|
||||||
/**
|
onLoad: 'login-required',
|
||||||
* Initializes Keycloak instance and calls the provided callback function if successfully authenticated.
|
responseMode: 'query',
|
||||||
*
|
checkLoginIframe: false,
|
||||||
* @param onAuthenticatedCallback
|
|
||||||
*/
|
|
||||||
const Login = (onAuthenticatedCallback: CallbackOneParam): void => {
|
|
||||||
keycloakInstance
|
|
||||||
.init({ onLoad: 'login-required' })
|
|
||||||
.then(function (authenticated) {
|
|
||||||
authenticated ? onAuthenticatedCallback() : alert('non authenticated')
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.dir(e)
|
|
||||||
console.log(`keycloak init exception: ${e}`)
|
|
||||||
})
|
})
|
||||||
|
.catch((e) => console.dir(e))
|
||||||
|
|
||||||
|
if (auth && cb) cb()
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserName = (): string | undefined =>
|
export async function logout() {
|
||||||
keycloakInstance?.tokenParsed?.preferred_username
|
await keycloak.logout()
|
||||||
|
}
|
||||||
|
|
||||||
const Token = (): string | undefined => keycloakInstance?.token
|
export async function getToken() {
|
||||||
const IdToken = (): string | undefined => keycloakInstance?.idToken
|
await keycloak.updateToken(60).catch(() => login())
|
||||||
|
return keycloak.token
|
||||||
|
}
|
||||||
|
|
||||||
const LogOut = () => keycloakInstance.logout()
|
export function getUsername(): string {
|
||||||
|
return keycloak.tokenParsed?.preferred_username
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
export function getRole(): string[] {
|
||||||
const UserRoles = (): string[] | undefined => {
|
const decoded = keycloak.tokenParsed
|
||||||
if (keycloakInstance.resourceAccess === undefined) return undefined;
|
|
||||||
if (keycloakInstance.resourceAccess["express-client"] === undefined) return undefined;
|
|
||||||
|
|
||||||
return keycloakInstance.resourceAccess["express-client"].roles;
|
if (decoded && decoded.resource_access && decoded.azp) {
|
||||||
};
|
return decoded.resource_access[decoded.azp].roles
|
||||||
*/
|
|
||||||
const UserRoles = () => {
|
|
||||||
const decoded = DecodeToken()
|
|
||||||
|
|
||||||
if (decoded && decoded.resource_access) {
|
|
||||||
return decoded.resource_access[decoded.azp ?? ''].roles
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateToken = (successCallback: any) =>
|
export function isLoggedIn() {
|
||||||
keycloakInstance.updateToken(5).then(successCallback).catch(doLogin)
|
return !!keycloak.token
|
||||||
|
|
||||||
const doLogin = keycloakInstance.login
|
|
||||||
|
|
||||||
const isLoggedIn = () => !!keycloakInstance.token
|
|
||||||
|
|
||||||
const DecodeToken = () => {
|
|
||||||
return keycloakInstance.tokenParsed
|
|
||||||
}
|
}
|
||||||
const DecodeIdToken = () => {
|
|
||||||
return keycloakInstance.idTokenParsed
|
|
||||||
}
|
|
||||||
|
|
||||||
const KeycloakService = {
|
|
||||||
CallLogin: Login,
|
|
||||||
GetUserName: UserName,
|
|
||||||
GetAccesToken: Token,
|
|
||||||
GetIdToken: IdToken,
|
|
||||||
CallLogOut: LogOut,
|
|
||||||
GetUserRoles: UserRoles,
|
|
||||||
UpdateToken: updateToken,
|
|
||||||
IsLoggedIn: isLoggedIn,
|
|
||||||
GetDecodeToken: DecodeToken,
|
|
||||||
GetDecodeIdToken: DecodeIdToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default KeycloakService
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useLoader } from '@/stores/loader'
|
import { useLoader } from '@/stores/loader'
|
||||||
import HttpService from '@/services/HttpService'
|
import axiosClient from '@/services/HttpService'
|
||||||
|
|
||||||
const axiosClient = HttpService.getAxiosClient()
|
|
||||||
const apiEndpoint: string = import.meta.env.VITE_API_ENDPOINT
|
const apiEndpoint: string = import.meta.env.VITE_API_ENDPOINT
|
||||||
|
|
||||||
export interface EhrFolder {
|
export interface EhrFolder {
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ const { loader } = storeToRefs(loaderStore)
|
||||||
</q-header>
|
</q-header>
|
||||||
|
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<QPage>
|
<q-page>
|
||||||
<router-view :key="$route.fullPath" />
|
<router-view :key="$route.fullPath" />
|
||||||
</QPage>
|
</q-page>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
<full-loader :visibility="loader" />
|
<full-loader :visibility="loader" />
|
||||||
</q-layout>
|
</q-layout>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue