
All checks were successful
Deploy to Production, build images and push to Gitea Registry / build_and_push (pull_request) Successful in 1m30s
This commit introduces a detailed roadmap for implementing various features, focusing on backend and frontend improvements. Key additions include: - New database schema designs for financial audit logging, archiving lists, and categorizing items. - Backend logic for financial audit logging, archiving functionality, and chore subtasks. - Frontend UI updates for archiving lists, managing categories, and enhancing the chore interface. - Introduction of a guest user flow and integration of Redis for caching to improve performance. These changes aim to enhance the application's functionality, user experience, and maintainability.
113 lines
3.8 KiB
TypeScript
113 lines
3.8 KiB
TypeScript
import axios from 'axios'
|
|
import { API_BASE_URL, API_VERSION, API_ENDPOINTS } from '@/config/api-config' // api-config.ts can be moved to src/config/
|
|
import router from '@/router' // Import the router instance
|
|
import { useAuthStore } from '@/stores/auth' // Import the auth store
|
|
import type { SettlementActivityCreate, SettlementActivityPublic } from '@/types/expense' // Import the types for the payload and response
|
|
import { stringify } from 'qs';
|
|
|
|
// Create axios instance
|
|
const api = axios.create({
|
|
baseURL: `${API_BASE_URL}/api/${API_VERSION}`,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
withCredentials: true, // Enable sending cookies and authentication headers
|
|
paramsSerializer: {
|
|
serialize: (params) => stringify(params, { arrayFormat: 'repeat' }),
|
|
},
|
|
})
|
|
|
|
// Create apiClient with helper methods
|
|
const apiClient = {
|
|
get: (endpoint: string, config = {}) => api.get(endpoint, config),
|
|
post: (endpoint: string, data = {}, config = {}) => api.post(endpoint, data, config),
|
|
put: (endpoint: string, data = {}, config = {}) => api.put(endpoint, data, config),
|
|
patch: (endpoint: string, data = {}, config = {}) => api.patch(endpoint, data, config),
|
|
delete: (endpoint: string, config = {}) => api.delete(endpoint, config),
|
|
}
|
|
|
|
// Store for tracking refresh promise to prevent concurrent refresh attempts
|
|
let refreshPromise: Promise<any> | null = null
|
|
|
|
// Request interceptor
|
|
api.interceptors.request.use(
|
|
(config) => {
|
|
const token = localStorage.getItem('token')
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`
|
|
}
|
|
return config
|
|
},
|
|
(error) => {
|
|
console.error('Request error:', error)
|
|
return Promise.reject(error)
|
|
},
|
|
)
|
|
|
|
// Response interceptor
|
|
api.interceptors.response.use(
|
|
(response) => response,
|
|
async (error) => {
|
|
const originalRequest = error.config
|
|
const authStore = useAuthStore()
|
|
|
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
originalRequest._retry = true
|
|
|
|
// If we're already refreshing, wait for that to complete
|
|
if (refreshPromise) {
|
|
try {
|
|
await refreshPromise
|
|
// After refresh completes, retry with new token
|
|
const newToken = localStorage.getItem('token')
|
|
if (newToken) {
|
|
originalRequest.headers.Authorization = `Bearer ${newToken}`
|
|
return api(originalRequest)
|
|
}
|
|
} catch (refreshError) {
|
|
// Refresh failed, redirect to login
|
|
return Promise.reject(error)
|
|
}
|
|
}
|
|
|
|
const refreshTokenValue = authStore.refreshToken
|
|
if (!refreshTokenValue) {
|
|
authStore.clearTokens()
|
|
await router.push('/auth/login')
|
|
return Promise.reject(error)
|
|
}
|
|
|
|
// Set refreshing state and create refresh promise
|
|
authStore.isRefreshing = true
|
|
refreshPromise = api.post(API_ENDPOINTS.AUTH.REFRESH, { refresh_token: refreshTokenValue }, {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
|
|
try {
|
|
const response = await refreshPromise
|
|
const { access_token: newAccessToken, refresh_token: newRefreshToken } = response.data
|
|
authStore.setTokens({ access_token: newAccessToken, refresh_token: newRefreshToken })
|
|
|
|
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`
|
|
return api(originalRequest)
|
|
} catch (refreshError) {
|
|
console.error('Token refresh failed:', refreshError)
|
|
authStore.clearTokens()
|
|
await router.push('/auth/login')
|
|
return Promise.reject(refreshError)
|
|
} finally {
|
|
authStore.isRefreshing = false
|
|
refreshPromise = null
|
|
}
|
|
}
|
|
return Promise.reject(error)
|
|
},
|
|
)
|
|
|
|
// Export the original axios too if some parts of your app used it directly
|
|
const globalAxios = axios
|
|
|
|
export { api, globalAxios, API_ENDPOINTS, apiClient }
|