From 147b16ca344fd83c230e5d0ad918379b0fbb8d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Fri, 6 Mar 2026 12:44:33 +0800 Subject: [PATCH] Add centralized API client with axios interceptors, remove vue-axios Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 15 ----- package.json | 1 - src/api/auth.js | 33 ++++++++++ src/api/client.js | 78 ++++++++++++++++++++++++ src/main.ts | 9 --- src/module/apiError.js | 14 +---- src/stores/acctMgmt.ts | 19 +++--- src/stores/allMapData.js | 23 +++---- src/stores/compare.js | 7 ++- src/stores/conformance.js | 19 +++--- src/stores/files.js | 20 +++--- src/stores/login.ts | 10 ++- src/stores/performance.js | 3 +- src/types/shims-vue.d.ts | 8 --- src/types/store.d.ts | 7 ++- src/types/vue-axios.d.ts | 8 +-- src/types/vue.d.ts | 14 ----- src/views/Compare/MapCompare.vue | 6 -- src/views/Discover/Conformance/index.vue | 6 -- src/views/Discover/Map/Map.vue | 6 -- src/views/Discover/Performance/index.vue | 6 -- src/views/MainContainer.vue | 5 -- tests/stores/acctMgmt.test.js | 40 ++++++------ tests/stores/allMapData.test.js | 47 +++++++------- tests/stores/compare.test.js | 27 ++++---- tests/stores/conformance.test.js | 43 ++++++------- tests/stores/files.test.js | 57 ++++++++--------- tests/stores/login.test.js | 21 ++++--- tests/stores/performance.test.js | 19 +++--- 29 files changed, 301 insertions(+), 270 deletions(-) create mode 100644 src/api/auth.js create mode 100644 src/api/client.js diff --git a/package-lock.json b/package-lock.json index 93ed482..32f5450 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "primevue": "^4.5.4", "tippy.js": "^6.3.7", "vue": "^3.5.29", - "vue-axios": "^3.5.2", "vue-chartjs": "^5.3.3", "vue-router": "^5.0.3", "vue-sweetalert2": "^5.0.11", @@ -7997,15 +7996,6 @@ } } }, - "node_modules/vue-axios": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.5.2.tgz", - "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==", - "peerDependencies": { - "axios": "*", - "vue": "^3.0.0 || ^2.0.0" - } - }, "node_modules/vue-chartjs": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.3.tgz", @@ -13305,11 +13295,6 @@ "@vue/shared": "3.5.29" } }, - "vue-axios": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.5.2.tgz", - "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==" - }, "vue-chartjs": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.3.tgz", diff --git a/package.json b/package.json index 4a01f96..20f2746 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "primevue": "^4.5.4", "tippy.js": "^6.3.7", "vue": "^3.5.29", - "vue-axios": "^3.5.2", "vue-chartjs": "^5.3.3", "vue-router": "^5.0.3", "vue-sweetalert2": "^5.0.11", diff --git a/src/api/auth.js b/src/api/auth.js new file mode 100644 index 0000000..c14f55e --- /dev/null +++ b/src/api/auth.js @@ -0,0 +1,33 @@ +import axios from 'axios'; +import { getCookie, setCookie, setCookieWithoutExpiration } from '@/utils/cookieUtil.js'; + +/** + * Refresh the access token using the refresh token cookie. + * Uses plain axios (not apiClient) to avoid interceptor loops. + * @returns {Promise} The new access token. + */ +export async function refreshTokenAndGetNew() { + const api = '/api/oauth/token'; + const config = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }; + const data = { + grant_type: 'refresh_token', + refresh_token: getCookie('luciaRefreshToken'), + }; + + const response = await axios.post(api, data, config); + const newAccessToken = response.data.access_token; + const newRefreshToken = response.data.refresh_token; + + setCookieWithoutExpiration('luciaToken', newAccessToken); + // Expire in ~6 months + const expiredMs = new Date(); + expiredMs.setMonth(expiredMs.getMonth() + 6); + const days = Math.ceil((expiredMs.getTime() - Date.now()) / (24 * 60 * 60 * 1000)); + setCookie('luciaRefreshToken', newRefreshToken, days); + + return newAccessToken; +} diff --git a/src/api/client.js b/src/api/client.js new file mode 100644 index 0000000..24dd77a --- /dev/null +++ b/src/api/client.js @@ -0,0 +1,78 @@ +import axios from 'axios'; +import { getCookie, deleteCookie } from '@/utils/cookieUtil.js'; + +const apiClient = axios.create(); + +// Request interceptor: automatically attach Authorization header +apiClient.interceptors.request.use((config) => { + const token = getCookie('luciaToken'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +// Response interceptor: handle 401 by attempting token refresh +let isRefreshing = false; +let pendingRequests = []; + +function onRefreshSuccess(newToken) { + pendingRequests.forEach((cb) => cb(newToken)); + pendingRequests = []; +} + +function onRefreshFailure(error) { + pendingRequests.forEach((cb) => cb(null, error)); + pendingRequests = []; +} + +apiClient.interceptors.response.use( + (response) => response, + async (error) => { + const originalRequest = error.config; + + // Only attempt refresh on 401, and not for auth endpoints or already-retried requests + if ( + error.response?.status !== 401 || + originalRequest._retried || + originalRequest.url === '/api/oauth/token' + ) { + return Promise.reject(error); + } + + if (isRefreshing) { + // Queue this request until the refresh completes + return new Promise((resolve, reject) => { + pendingRequests.push((newToken, err) => { + if (err) return reject(err); + originalRequest.headers.Authorization = `Bearer ${newToken}`; + originalRequest._retried = true; + resolve(apiClient(originalRequest)); + }); + }); + } + + isRefreshing = true; + originalRequest._retried = true; + + try { + // Dynamic import to avoid circular dependency with login store + const { refreshTokenAndGetNew } = await import('@/api/auth.js'); + const newToken = await refreshTokenAndGetNew(); + isRefreshing = false; + onRefreshSuccess(newToken); + originalRequest.headers.Authorization = `Bearer ${newToken}`; + return apiClient(originalRequest); + } catch (refreshError) { + isRefreshing = false; + onRefreshFailure(refreshError); + + // Refresh failed: clear auth and redirect to login + deleteCookie('luciaToken'); + window.location.href = '/login'; + return Promise.reject(refreshError); + } + } +); + +export default apiClient; diff --git a/src/main.ts b/src/main.ts index 346d0a9..f6bd4f0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,8 +3,6 @@ import { createApp, markRaw } from "vue"; import App from "./App.vue"; import router from "./router"; import pinia from '@/stores/main.js'; -import axios from 'axios'; -import VueAxios from 'vue-axios'; import moment from 'moment'; import mitt from 'mitt'; import ToastPlugin from 'vue-toast-notification'; @@ -49,15 +47,9 @@ import ContextMenu from 'primevue/contextmenu'; const emitter = mitt(); const app = createApp(App); -// 將 Axios 實例添加到 Vue 的應用實例中 -app.config.globalProperties.$http = axios; -app.$http = axios; // 將 $http 添加到 Vue 實例中 // Pinia Set pinia.use(({ store }) => { store.$router = markRaw(router); - store.$axios = markRaw(axios); - store.$toast = markRaw(ToastPlugin); - store.$http = app.$http; }); // can use `this.$moment` in Vue.js @@ -72,7 +64,6 @@ cytoscape.use( popper((ref) => ref) ); app.use(pinia); app.use(router); -app.use(VueAxios, axios); app.use(VueSweetalert2); app.use(ToastPlugin, { // use `this.$toast` in Vue.js position: 'bottom', diff --git a/src/module/apiError.js b/src/module/apiError.js index 22da8eb..b846b02 100644 --- a/src/module/apiError.js +++ b/src/module/apiError.js @@ -1,25 +1,17 @@ -import router from "@/router/index"; import loadingStore from '@/stores/loading.js'; import {useToast} from 'vue-toast-notification'; import 'vue-toast-notification/dist/theme-sugar.css'; -import axios from "axios"; -import { deleteCookie } from "@/utils/cookieUtil.js"; // Delay loading and toast const delay = (s = 0) => new Promise((resolve, reject) => setTimeout(resolve, s)); /** - * API catch error function - * @param {object} Error 後端 ERROR + * API catch error function. + * 401 errors are handled by the axios response interceptor in api/client.js. + * @param {object} error 後端 ERROR * @param {string} toastMessage Toast 的提示文字 - * @returns {string} Error HTTP Status */ export default async function apiError(error, toastMessage) { - if(error.request?.status === 401) { - delete axios.defaults.headers.common["Authorization"]; - deleteCookie("luciaToken"); - return router.push('/login'); - } const loading = loadingStore(); const $toast = useToast(); await delay(); diff --git a/src/stores/acctMgmt.ts b/src/stores/acctMgmt.ts index d74f9b8..aa6a774 100644 --- a/src/stores/acctMgmt.ts +++ b/src/stores/acctMgmt.ts @@ -1,4 +1,5 @@ import { defineStore } from 'pinia'; +import apiClient from '@/api/client.js'; import apiError from '@/module/apiError'; import piniaLoginStore from '@/stores/login'; import { JUST_CREATE_ACCOUNT_HOT_DURATION_MINS } from '@/constants/constants'; @@ -87,7 +88,7 @@ export default defineStore('acctMgmtStore', { async getAllUserAccounts() { const apiGetUserList = `/api/users`; try { - const response = await this.$axios.get(apiGetUserList); + const response = await apiClient.get(apiGetUserList); const customizedResponseData = await this.customizeAllUserList(response.data); this.allUserAccoutList = await this.moveCurrentLoginUserToFirstRow(customizedResponseData); } catch (error) { @@ -135,7 +136,7 @@ export default defineStore('acctMgmtStore', { const apiCreateAccount = `/api/users`; try { - const response = await this.$axios.post(apiCreateAccount, userToCreate); + const response = await apiClient.post(apiCreateAccount, userToCreate); if (response.status === 200) { this.isOneAccountJustCreate = true; this.justCreateUsername = userToCreate.username; @@ -154,7 +155,7 @@ export default defineStore('acctMgmtStore', { const apiDelete = `/api/users/${userToDelete}`; try { - const response = await this.$axios.delete(apiDelete); + const response = await apiClient.delete(apiDelete); return response.status === 200; } catch (error) { apiError(error, 'Failed to delete the account.'); @@ -170,7 +171,7 @@ export default defineStore('acctMgmtStore', { const apiEdit = `/api/users/${userToEdit}`; try { - const response = await this.$axios.put(apiEdit, { + const response = await apiClient.put(apiEdit, { username: editDetail.newUsername ? editDetail.newUsername : editDetail.username, password: editDetail.password, name: editDetail.name, @@ -186,7 +187,7 @@ export default defineStore('acctMgmtStore', { const apiEdit = `/api/users/${userToEdit}`; try { - const response = await this.$axios.put(apiEdit, { + const response = await apiClient.put(apiEdit, { username: userToEdit, name: newName, is_active: this.currentViewingUser.is_active, @@ -202,7 +203,7 @@ export default defineStore('acctMgmtStore', { const apiEdit = `/api/users/${userToEdit}`; try { - const response = await this.$axios.put(apiEdit, { + const response = await apiClient.put(apiEdit, { username: userToEdit, name: this.currentViewingUser.name, password: newPwd, @@ -223,7 +224,7 @@ export default defineStore('acctMgmtStore', { const apiAddRole = `/api/users/${usernameToEdit}/roles/${roleCode}`; try { - const response = await this.$axios.put(apiAddRole); + const response = await apiClient.put(apiAddRole); return response.status === 200; } catch (error) { apiError(error, 'Failed to add role to the account.'); @@ -238,7 +239,7 @@ export default defineStore('acctMgmtStore', { const apiDeleteRole = `/api/users/${usernameToEdit}/roles/${roleCode}`; try { - const response = await this.$axios.delete(apiDeleteRole); + const response = await apiClient.delete(apiDeleteRole); return response.status === 200; } catch (error) { apiError(error, 'Failed to delete a role frome the account.'); @@ -252,7 +253,7 @@ export default defineStore('acctMgmtStore', { async getUserDetail(uniqueUsername: string): Promise { const apiUserDetail = `/api/users/${uniqueUsername}`; try { - const response = await this.$axios.get(apiUserDetail); + const response = await apiClient.get(apiUserDetail); this.currentViewingUser = response.data; this.currentViewingUser.is_admin = response.data.roles.some(role => role.code === 'admin'); return response.status === 200; diff --git a/src/stores/allMapData.js b/src/stores/allMapData.js index 97211ec..f30e389 100644 --- a/src/stores/allMapData.js +++ b/src/stores/allMapData.js @@ -1,5 +1,6 @@ import { defineStore } from "pinia"; import moment from "moment"; +import apiClient from "@/api/client.js"; import apiError from '@/module/apiError.js'; import { Decimal } from 'decimal.js'; @@ -136,7 +137,7 @@ export default defineStore('allMapDataStore', { else api = `/api/logs/${logId}/discover`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allProcessMap = response.data.process_map; this.allBpmn = response.data.bpmn; this.allStats = response.data.stats; @@ -163,10 +164,10 @@ export default defineStore('allMapDataStore', { try { let baseResponse; - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allTrace = response.data; if(baseLogId) { - baseResponse = await this.$axios.get(baseApi); + baseResponse = await apiClient.get(baseApi); this.allBaseTrace = baseResponse.data; } } catch(error) { @@ -190,7 +191,7 @@ export default defineStore('allMapDataStore', { else api = `/api/logs/${logId}/traces/${traceId}?start=${start}&page_size=20`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allTraceTaskSeq = response.data.task_seq; this.allCase = response.data.cases; this.allCase.forEach(c => { @@ -229,7 +230,7 @@ export default defineStore('allMapDataStore', { const api = `/api/logs/${logId}/traces/${traceId}?start=${start}&page_size=20`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allBaseTraceTaskSeq = response.data.task_seq; this.allBaseCase = response.data.cases; this.allBaseCase.forEach(c => { @@ -266,7 +267,7 @@ export default defineStore('allMapDataStore', { const api = `/api/filters/params?log_id=${logId}`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allFilterTask = response.data.tasks; this.allFilterStartToEnd = response.data.sources; this.allFilterEndToStart = response.data.sinks; @@ -294,7 +295,7 @@ export default defineStore('allMapDataStore', { const api = `/api/filters/has-result?log_id=${logId}`; try { - const response = await this.$axios.post(api, this.postRuleData) + const response = await apiClient.post(api, this.postRuleData) this.hasResultRule = response.data.result; } catch(error) { apiError(error, 'Failed to load the Has Result.'); @@ -308,7 +309,7 @@ export default defineStore('allMapDataStore', { const api = `/api/temp-filters?log_id=${logId}`; try { - const response = await this.$axios.post(api, this.postRuleData) + const response = await apiClient.post(api, this.postRuleData) this.tempFilterId = response.data.id; } catch(error) { apiError(error, 'Failed to add the Temporary Filters.'); @@ -327,7 +328,7 @@ export default defineStore('allMapDataStore', { }; try { - const response = await this.$axios.post(api, createFilterObj); + const response = await apiClient.post(api, createFilterObj); this.createFilterId = response.data.id; this.tempFilterId = null; }catch(error) { @@ -343,7 +344,7 @@ export default defineStore('allMapDataStore', { if(createFilterId){ try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.temporaryData = response.data.rules; this.logId = response.data.log.id; this.filterName = response.data.name; @@ -362,7 +363,7 @@ export default defineStore('allMapDataStore', { const data = this.postRuleData; try { - const response = await this.$axios.put(api, data); + const response = await apiClient.put(api, data); this.isUpdateFilter = response.status === 200; this.tempFilterId = null; }catch(error) { diff --git a/src/stores/compare.js b/src/stores/compare.js index 087d8c8..b1383c8 100644 --- a/src/stores/compare.js +++ b/src/stores/compare.js @@ -1,4 +1,5 @@ import { defineStore } from "pinia"; +import apiClient from "@/api/client.js"; import apiError from '@/module/apiError.js'; export default defineStore('compareStore', { @@ -21,7 +22,7 @@ export default defineStore('compareStore', { const api = `/api/compare?datasets=${encodeURIComponent(queryString)}`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allCompareDashboardData = response.data; } catch(error) { apiError(error, 'Failed to load the Compare.'); @@ -44,7 +45,7 @@ export default defineStore('compareStore', { break; } try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); return response.data.stats; } catch(error) { @@ -58,7 +59,7 @@ export default defineStore('compareStore', { async getFileName(id) { id = Number(id) try { - const response = await this.$axios.get('/api/files'); + const response = await apiClient.get('/api/files'); const file = response.data.find(i => i.id === id); if(file) return file.name; diff --git a/src/stores/conformance.js b/src/stores/conformance.js index ddd6f96..196ab73 100644 --- a/src/stores/conformance.js +++ b/src/stores/conformance.js @@ -2,6 +2,7 @@ import { defineStore } from "pinia"; import moment from "moment"; import { Decimal } from 'decimal.js'; import abbreviateNumber from '@/module/abbreviateNumber.js'; +import apiClient from "@/api/client.js"; import apiError from '@/module/apiError.js'; export default defineStore('conformanceStore', { @@ -201,7 +202,7 @@ export default defineStore('conformanceStore', { api = `/api/log-checks/params?log_id=${logId}`; } try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allConformanceTask = response.data.tasks; this.allCfmSeqStart = response.data.sources; this.allCfmSeqEnd = response.data.sinks; @@ -230,7 +231,7 @@ export default defineStore('conformanceStore', { } try { - const response = await this.$axios.post(api, data); + const response = await apiClient.post(api, data); if(filterId !== null) { this.conformanceFilterTempCheckId = response.data.id; } @@ -266,7 +267,7 @@ export default defineStore('conformanceStore', { api = `/api/log-checks/${logCreateCheckId}`; } try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); if(!getRouteFile) { this.allConformanceTempReportData = response.data } else { @@ -302,7 +303,7 @@ export default defineStore('conformanceStore', { } try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allIssueTraces = response.data.traces; } catch(error) { apiError(error, 'Failed to Get the detail of a temporary log conformance issue.'); @@ -336,7 +337,7 @@ export default defineStore('conformanceStore', { } try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allTaskSeq = response.data.task_seq; this.allCases = response.data.cases; return response.data.cases; @@ -366,7 +367,7 @@ export default defineStore('conformanceStore', { else if(logCreateCheckId !== null) api = `/api/log-checks/${logCreateCheckId}/loops/${loopNo}`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allLoopTraces = response.data.traces; } catch(error) { apiError(error, 'Failed to Get the detail of a temporary log conformance loop.'); @@ -392,7 +393,7 @@ export default defineStore('conformanceStore', { else if(logCreateCheckId !== null) api = `/api/log-checks/${logCreateCheckId}/loops/${loopNo}/traces/${traceId}?start=${start}&page_size=20`; try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allLoopTaskSeq = response.data.task_seq; this.allLoopCases = response.data.cases; return response.data.cases; @@ -422,7 +423,7 @@ export default defineStore('conformanceStore', { else api = `/api/log-checks?log_id=${logId}`; try { - const response = await this.$axios.post(api, data); + const response = await apiClient.post(api, data); if(filterId !== null) { this.conformanceFilterCreateCheckId = response.data.id; this.conformanceFilterTempCheckId = null; @@ -449,7 +450,7 @@ export default defineStore('conformanceStore', { else if(logCreateCheckId !== null) api = `/api/log-checks/${logCreateCheckId}`; try { - const response = await this.$axios.put(api, data); + const response = await apiClient.put(api, data); this.isUpdateConformance = response.status === 200; this.conformanceLogTempCheckId = null; this.conformanceFilterTempCheckId = null; diff --git a/src/stores/files.js b/src/stores/files.js index a0e3341..18a8cfd 100644 --- a/src/stores/files.js +++ b/src/stores/files.js @@ -1,5 +1,5 @@ import { defineStore } from "pinia"; -import axios from "axios"; +import apiClient from "@/api/client.js"; import moment from 'moment'; import apiError from '@/module/apiError.js'; import Swal from 'sweetalert2'; @@ -66,7 +66,7 @@ export default defineStore('filesStore', { let parentLog = ''; try { - const response = await axios.get(api); + const response = await apiClient.get(api); this.allEventFiles = response.data; this.allEventFiles.forEach(o => { @@ -119,7 +119,7 @@ export default defineStore('filesStore', { uploadloader(); // 進度條 try { - const response = await axios.post(api, fromData, config); + const response = await apiClient.post(api, fromData, config); this.uploadId = response.data.id; this.$router.push({name: 'Upload'}); @@ -145,7 +145,7 @@ export default defineStore('filesStore', { const api = `/api/logs/csv-uploads/${uploadId}`; try { - const response = await axios.get(api); + const response = await apiClient.get(api); this.allUploadDetail = response.data.preview; } catch(error) { apiError(error, 'Failed to get upload detail.'); @@ -161,7 +161,7 @@ export default defineStore('filesStore', { uploadloader(); // 進度條 try { - const response = await axios.post(api, data); + const response = await apiClient.post(api, data); this.uploadLogId = await response.data.id; await Swal.close(); // 關閉進度條 @@ -211,7 +211,7 @@ export default defineStore('filesStore', { break; } try { - await axios.put(api, data); + await apiClient.put(api, data); this.uploadFileName = null; await this.fetchAllFiles(); } catch(error) { @@ -241,7 +241,7 @@ export default defineStore('filesStore', { break; } try { - const response = await axios.get(api); + const response = await apiClient.get(api); this.allDependentsData = response.data; } catch(error) { apiError(error, 'Failed to get Dependents of the files.'); @@ -276,7 +276,7 @@ export default defineStore('filesStore', { break; } try { - await axios.delete(api); + await apiClient.delete(api); await this.fetchAllFiles(); await deleteSuccess(); } catch(error) { @@ -296,7 +296,7 @@ export default defineStore('filesStore', { loading.isLoading = true; api = `/api/deletion/${id}`; try { - await axios.delete(api); + await apiClient.delete(api); } catch(error) { apiError(error, 'Failed to Remove a Deletion Record.') } finally { @@ -329,7 +329,7 @@ export default defineStore('filesStore', { return; } try { - const response = await axios.get(api); + const response = await apiClient.get(api); const csvData = response.data; const blob = new Blob([csvData], { type: 'text/csv' }); const url = window.URL.createObjectURL(blob); diff --git a/src/stores/login.ts b/src/stores/login.ts index cc5e12a..6faae30 100644 --- a/src/stores/login.ts +++ b/src/stores/login.ts @@ -1,5 +1,6 @@ import { defineStore } from "pinia"; import axios from 'axios'; +import apiClient from '@/api/client.js'; import apiError from '@/module/apiError.js'; import { deleteCookie, setCookie, setCookieWithoutExpiration, getCookie } from "../utils/cookieUtil"; @@ -51,7 +52,7 @@ export default defineStore('loginStore', { window.location.href = decodedUrl; } else { this.$router.push('/files'); - } + } } catch(error) { this.isInvalid = true; }; @@ -78,8 +79,6 @@ export default defineStore('loginStore', { setCookieWithoutExpiration("luciaToken", newAccessToken); setCookie("luciaRefreshToken", newRefreshToken, Math.ceil((this.expired - Date.now()) / (24 * 60 * 60 * 1000))); - - axios.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; } } catch(error) { // 若refresh token 失敗則導向至登入頁面 @@ -91,7 +90,6 @@ export default defineStore('loginStore', { * Logout, token expired */ logOut() { - delete axios.defaults.headers.common["Authorization"]; deleteCookie("luciaToken"); this.isLoggedIn = false; @@ -106,7 +104,7 @@ export default defineStore('loginStore', { const api = '/api/my-account'; try { - const response = await axios.get(api); + const response = await apiClient.get(api); this.userData = response.data; } catch(error) { @@ -120,7 +118,7 @@ export default defineStore('loginStore', { const api = '/api/my-account'; try { - await axios.get(api); + await apiClient.get(api); } catch(error) { this.$router.push('/login'); }; diff --git a/src/stores/performance.js b/src/stores/performance.js index 2c4a880..60c0710 100644 --- a/src/stores/performance.js +++ b/src/stores/performance.js @@ -1,4 +1,5 @@ import { defineStore } from "pinia"; +import apiClient from "@/api/client.js"; import apiError from '@/module/apiError.js'; export default defineStore('performanceStore', { @@ -36,7 +37,7 @@ export default defineStore('performanceStore', { break; } try { - const response = await this.$axios.get(api); + const response = await apiClient.get(api); this.allPerformanceData = response.data; } catch(error) { apiError(error, 'Failed to load the Performance.'); diff --git a/src/types/shims-vue.d.ts b/src/types/shims-vue.d.ts index 4cd881d..a0e6f90 100644 --- a/src/types/shims-vue.d.ts +++ b/src/types/shims-vue.d.ts @@ -4,11 +4,3 @@ declare module '*.vue' { const component: DefineComponent<{}, {}, any>; export default component; } - - declare module 'vue/types/vue' { - import { AxiosInstance } from 'axios'; - interface Vue { - $axios: AxiosInstance; - } - } - \ No newline at end of file diff --git a/src/types/store.d.ts b/src/types/store.d.ts index 795cf24..7970cec 100644 --- a/src/types/store.d.ts +++ b/src/types/store.d.ts @@ -1,7 +1,8 @@ -import { AxiosInstance } from 'axios'; +import 'pinia'; +import { Router } from 'vue-router'; declare module 'pinia' { export interface PiniaCustomProperties { - $axios: AxiosInstance; + $router: Router; } -} \ No newline at end of file +} diff --git a/src/types/vue-axios.d.ts b/src/types/vue-axios.d.ts index 7306897..e81f724 100644 --- a/src/types/vue-axios.d.ts +++ b/src/types/vue-axios.d.ts @@ -1,7 +1 @@ -import { AxiosInstance } from 'axios'; - -declare module 'vue/types/vue' { - interface Vue { - $axios: AxiosInstance; - } -} \ No newline at end of file +// vue-axios type declarations removed — API client is used directly diff --git a/src/types/vue.d.ts b/src/types/vue.d.ts index 9a079b0..fc38937 100644 --- a/src/types/vue.d.ts +++ b/src/types/vue.d.ts @@ -1,15 +1 @@ -import { AxiosInstance } from 'axios'; import { App } from 'vue'; - -declare module '@vue/runtime-core' { - interface ComponentCustomProperties { - $http: AxiosInstance; - } -} - -// 將類型擴展到 Vue 應用實例 -declare module 'vue' { - interface App { - $http: AxiosInstance; - } -} diff --git a/src/views/Compare/MapCompare.vue b/src/views/Compare/MapCompare.vue index 7dfe803..9192dee 100644 --- a/src/views/Compare/MapCompare.vue +++ b/src/views/Compare/MapCompare.vue @@ -56,8 +56,6 @@ import { onBeforeMount, computed, } from 'vue'; import { storeToRefs } from 'pinia'; import { useRoute } from 'vue-router'; - import axios from 'axios'; - import { getCookie } from "@/utils/cookieUtil.js"; import LoadingStore from '@/stores/loading.js'; import AllMapDataStore from '@/stores/allMapData.js'; import ConformanceStore from '@/stores/conformance.js'; @@ -519,10 +517,6 @@ if (isCheckPage) { const conformanceStore = ConformanceStore(); - // Save token in Headers. - const token = getCookie('luciaToken'); - - axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; switch (to.params.type) { case 'log': conformanceStore.conformanceLogCreateCheckId = to.params.fileId; diff --git a/src/views/Discover/Conformance/index.vue b/src/views/Discover/Conformance/index.vue index 932f55d..da27e61 100644 --- a/src/views/Discover/Conformance/index.vue +++ b/src/views/Discover/Conformance/index.vue @@ -9,13 +9,11 @@