Enable refresh_token when it's about to expire(5 minutes)
This commit is contained in:
parent
c1fb9cfa50
commit
8632070391
@ -19,6 +19,7 @@
|
||||
"echarts": "^5.5.1",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"pdfjs-dist": "^4.3.136",
|
||||
"pinia": "^2.1.6",
|
||||
"vue": "^3.3.4",
|
||||
@ -41,4 +42,4 @@
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"webpack": "^5.88.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,6 +78,14 @@ export default {
|
||||
|
||||
UserAuthApi.signinByEmail(this.email, this.password)
|
||||
.then((response) => {
|
||||
// Extract the necessary data from the response
|
||||
const { access_token, refresh_token, expires_in } = response.data;
|
||||
|
||||
// Save tokens and expiration time in localStorage
|
||||
localStorage.setItem('access_token', access_token);
|
||||
localStorage.setItem('refresh_token', refresh_token);
|
||||
localStorage.setItem('expires_in', expires_in);
|
||||
|
||||
let signinAction = response.data.signin_result
|
||||
switch (signinAction) {
|
||||
case signinActionEnum.EXISTING_USER_PASSWORD_REQUIRED:
|
||||
|
||||
@ -1,20 +1,39 @@
|
||||
<template>
|
||||
<empty-content :loading="loading" :empty="requestGroups?.length === 0" />
|
||||
<div class="request-hub">
|
||||
<div v-for="(group, index) in requestGroups" :key="index" class="request-invitations" :id="group.name">
|
||||
<div
|
||||
v-for="(group, index) in requestGroups"
|
||||
:key="index"
|
||||
class="request-invitations"
|
||||
:id="group.name"
|
||||
>
|
||||
<div v-if="group.data" id="request-invitation-container">
|
||||
<div class="accordion accordion-list" v-for="(request, index) in group.data" :key="index" :id="request.id">
|
||||
<div
|
||||
class="accordion accordion-list"
|
||||
v-for="(request, index) in group.data"
|
||||
:key="index"
|
||||
:id="request.id"
|
||||
>
|
||||
<div class="accordion-item my-3">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
:data-bs-target="'#collapse' + index" aria-expanded="false" :aria-controls="'collapse' + index">
|
||||
<button
|
||||
class="accordion-button collapsed"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
:data-bs-target="'#collapse' + index"
|
||||
aria-expanded="false"
|
||||
:aria-controls="'collapse' + index"
|
||||
>
|
||||
<div class="dashed-container request-content-container">
|
||||
<div class="request-content-issuer-container">
|
||||
<label class="request-content-label" for="request-content-issuer-box">{{ $t('Issuer') }}</label>
|
||||
<label class="request-content-label" for="request-content-issuer-box">{{
|
||||
$t('Issuer')
|
||||
}}</label>
|
||||
<div class="request-content-box" id="request-content-issuer-box">
|
||||
<span class="request-content-issuer-text">
|
||||
{{ request.issuer_profile.first_name }}
|
||||
{{ request.issuer_profile.last_name }}</span>
|
||||
{{ request.issuer_profile.last_name }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="request-content-title-container">
|
||||
@ -48,8 +67,11 @@
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div :id="'collapse' + index" class="accordion-collapse collapse"
|
||||
data-bs-parent="#request-invitation-container">
|
||||
<div
|
||||
:id="'collapse' + index"
|
||||
class="accordion-collapse collapse"
|
||||
data-bs-parent="#request-invitation-container"
|
||||
>
|
||||
<div class="accordion-body">
|
||||
<div class="request-description-container">
|
||||
<button class="make-proposal-button" type="button" @click="Propose(request)">
|
||||
@ -59,13 +81,23 @@
|
||||
{{ $t('Message') }}
|
||||
</button>
|
||||
<div class="request-description-content" v-html="request.content"></div>
|
||||
<div class="pdf-actions" v-for="(file, index) in request.attached_files" :key="index">
|
||||
<button class="btn btn-link" data-bs-toggle="modal" data-bs-target="#pdf-viewer"
|
||||
@click="previewAttachedFile(request.id, file.document_id, file.file_name)">
|
||||
<div
|
||||
class="pdf-actions"
|
||||
v-for="(file, index) in request.attached_files"
|
||||
:key="index"
|
||||
>
|
||||
<button
|
||||
class="btn btn-link"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#pdf-viewer"
|
||||
@click="previewAttachedFile(request.id, file.document_id, file.file_name)"
|
||||
>
|
||||
{{ $t('Preview') }}{{ file.file_name }}
|
||||
</button>
|
||||
<button class="btn btn-link"
|
||||
@click="downloadAttachedFile(request.id, file.document_id, file.file_name)">
|
||||
<button
|
||||
class="btn btn-link"
|
||||
@click="downloadAttachedFile(request.id, file.document_id, file.file_name)"
|
||||
>
|
||||
{{ $t('Download') }}{{ file.file_name }}
|
||||
</button>
|
||||
</div>
|
||||
@ -76,45 +108,82 @@
|
||||
> -->
|
||||
<div class="issuer-achievement-container" id="issuer-achievement-container">
|
||||
<div class="issuer-achievement-isssuer-container">
|
||||
<label class="issuer-achievement-label" for="issuer-achievement-isssuer-content-div">{{ $t('Name')
|
||||
}}</label>
|
||||
<div class="issuer-achievement-content-container" id="issuer-achievement-isssuer-content-div">
|
||||
<label
|
||||
class="issuer-achievement-label"
|
||||
for="issuer-achievement-isssuer-content-div"
|
||||
>{{ $t('Name') }}</label
|
||||
>
|
||||
<div
|
||||
class="issuer-achievement-content-container"
|
||||
id="issuer-achievement-isssuer-content-div"
|
||||
>
|
||||
<span class="issuer-achievement-issuer-text">
|
||||
{{ request.issuer_profile.first_name }}
|
||||
{{ request.issuer_profile.last_name }}</span>
|
||||
{{ request.issuer_profile.last_name }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="issuer-achievement-stay-container">
|
||||
<label class="issuer-achievement-label" for="issuer-achievement-stay-content-div">{{ $t('Stay on Freeleaps') }} </label>
|
||||
<div class="issuer-achievement-content-container" id="issuer-achievement-stay-content-div">
|
||||
<label
|
||||
class="issuer-achievement-label"
|
||||
for="issuer-achievement-stay-content-div"
|
||||
>{{ $t('Stay on Freeleaps') }}
|
||||
</label>
|
||||
<div
|
||||
class="issuer-achievement-content-container"
|
||||
id="issuer-achievement-stay-content-div"
|
||||
>
|
||||
<span class="issuer-achievement-stay-content-text">
|
||||
{{ request.issuer_achievement.activeness.days_of_staying_on }}
|
||||
{{ $t('day(s)') }}</span>
|
||||
{{ $t('day(s)') }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="issuer-achievement-paid-container">
|
||||
<label class="issuer-achievement-label" for="issuer-achievement-paid-content-div">{{ $t('Total payment') }}</label>
|
||||
<div class="issuer-achievement-content-container" id="issuer-achievement-stay-content-div">
|
||||
<label
|
||||
class="issuer-achievement-label"
|
||||
for="issuer-achievement-paid-content-div"
|
||||
>{{ $t('Total payment') }}</label
|
||||
>
|
||||
<div
|
||||
class="issuer-achievement-content-container"
|
||||
id="issuer-achievement-stay-content-div"
|
||||
>
|
||||
<span class="issuer-achievement-paid-content-text">
|
||||
{{ request.issuer_achievement.issuer.spending.total }}
|
||||
{{ request.issuer_achievement.issuer.spending.currency }}</span>
|
||||
{{ request.issuer_achievement.issuer.spending.currency }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="issuer-achievement-deposit-container">
|
||||
<label class="issuer-achievement-label" for="issuer-achievement-deposit-content-div">{{
|
||||
$t('Deposit') }}</label>
|
||||
<div class="issuer-achievement-content-container" id="issuer-achievement-deposit-content-div">
|
||||
<label
|
||||
class="issuer-achievement-label"
|
||||
for="issuer-achievement-deposit-content-div"
|
||||
>{{ $t('Deposit') }}</label
|
||||
>
|
||||
<div
|
||||
class="issuer-achievement-content-container"
|
||||
id="issuer-achievement-deposit-content-div"
|
||||
>
|
||||
<span class="issuer-achievement-deposit-content-text">
|
||||
{{ request.issuer_achievement.issuer.deposit.available }}
|
||||
{{ request.issuer_achievement.issuer.deposit.currency }}</span>
|
||||
{{ request.issuer_achievement.issuer.deposit.currency }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="issuer-achievement-credit-container">
|
||||
<label class="issuer-achievement-label" for="issuer-achievement-credit-content-div">{{
|
||||
$t('Credit') }}</label>
|
||||
<div class="issuer-achievement-content-container" id="issuer-achievement-credit-content-div">
|
||||
<label
|
||||
class="issuer-achievement-label"
|
||||
for="issuer-achievement-credit-content-div"
|
||||
>{{ $t('Credit') }}</label
|
||||
>
|
||||
<div
|
||||
class="issuer-achievement-content-container"
|
||||
id="issuer-achievement-credit-content-div"
|
||||
>
|
||||
<span class="issuer-achievement-credit-content-text">
|
||||
{{ request.issuer_achievement.activeness.credit }}</span>
|
||||
{{ request.issuer_achievement.activeness.credit }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -126,12 +195,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="pdf-viewer" tabindex="-1" aria-labelledby="pdf-viewer-label" aria-hidden="true">
|
||||
<div
|
||||
class="modal fade"
|
||||
id="pdf-viewer"
|
||||
tabindex="-1"
|
||||
aria-labelledby="pdf-viewer-label"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="pdf-viewer-label">{{ pdfDocument.title }}</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<PDFReader :doc="pdfDocument.doc" />
|
||||
@ -213,7 +293,7 @@ export default {
|
||||
.then((response) => {
|
||||
console.log('resposne', response)
|
||||
// create file link in browser's memory
|
||||
// const href = URL.createObjectURL(response.data)
|
||||
// const ahref = URL.createObjectURL(response.data.download_url)
|
||||
|
||||
// create "a" HTML element with href to file & click
|
||||
const link = document.createElement('a')
|
||||
|
||||
@ -1,7 +1,75 @@
|
||||
import axios from 'axios'
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
|
||||
const backendAxios = axios.create({
|
||||
baseURL: ''
|
||||
baseURL: '',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// Function to check if the token has expired
|
||||
function isTokenExpired(token) {
|
||||
// Ensure token is a non-empty string
|
||||
if (!token || typeof token !== 'string') {
|
||||
console.error('Invalid token: must be a non-empty string')
|
||||
return true // Treat it as expired
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedToken = jwtDecode(token)
|
||||
console.log('this is decoded token', decodedToken)
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
// Buffer as 5 minu = 300 seconds
|
||||
return decodedToken.exp - 300 < now
|
||||
} catch (error) {
|
||||
console.error('Error decoding token:', error)
|
||||
return true // Treat it as expired if decoding fails
|
||||
}
|
||||
}
|
||||
|
||||
// Interceptor for handling token expiration and refreshing
|
||||
backendAxios.interceptors.request.use(
|
||||
async (config) => {
|
||||
let accessToken = localStorage.getItem('access_token')
|
||||
const refreshToken = localStorage.getItem('refresh_token')
|
||||
|
||||
// Check if the access token is expired
|
||||
if (!accessToken || isTokenExpired(accessToken)) {
|
||||
if (!refreshToken) {
|
||||
return config
|
||||
}
|
||||
try {
|
||||
// If access token is expired, refresh it using the refresh token
|
||||
const response = await axios.post('/api/user/signin/refresh-token', {}, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${refreshToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
accessToken = response.data.access_token
|
||||
const new_refreshToken = response.data.refresh_token
|
||||
|
||||
// Save the new access token to localStorage
|
||||
localStorage.setItem('access_token', accessToken)
|
||||
localStorage.setItem('refresh_token', new_refreshToken)
|
||||
} catch (error) {
|
||||
console.error('Token refresh failed. Redirecting to login.')
|
||||
// Optionally, handle token refresh failure (e.g., redirect to login)
|
||||
window.location.href = '/front-door'
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
// Add the (new) access token to the request headers
|
||||
if (accessToken) {
|
||||
config.headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export { backendAxios }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user