feat: initial commit — tcb_devportal Next.js app
Some checks failed
homelab-k8s-services/tcb_devportal/pipeline/head There was a failure building this commit
Some checks failed
homelab-k8s-services/tcb_devportal/pipeline/head There was a failure building this commit
Includes Helm chart (tcb-devportal), Jenkinsfile with homelab CI pipeline, and Next.js app source.
This commit is contained in:
114
src/services/auth/auth.query-options.tsx
Normal file
114
src/services/auth/auth.query-options.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
// hooks/useAuth.ts
|
||||
|
||||
import {
|
||||
AUTH_INFO_KEY,
|
||||
AUTH_REFRESH_FAILED_EVENT,
|
||||
} from '@/constants/system'
|
||||
import { useRouter } from '@/i18n/navigation'
|
||||
import { BaseAPIResponse } from '@/models/api/common'
|
||||
import { useGetUrlLoginMutation } from '@/share/layout/end-user/header/hook'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { AxiosError } from 'axios'
|
||||
import { useCallback } from 'react'
|
||||
import {
|
||||
AuthInfoBase,
|
||||
AuthInfoBaseResponse,
|
||||
AuthInfoFullSchema,
|
||||
LogoutRequest,
|
||||
} from './auth.schema'
|
||||
import authApi from './auth.service'
|
||||
|
||||
export const authKeys = {
|
||||
all: ['auth'] as const,
|
||||
verifyToken: (token?: string) =>
|
||||
[...authKeys.all, token, 'verifyToken'] as const,
|
||||
}
|
||||
|
||||
export function useVerifyToken(token: string) {
|
||||
return useQuery({
|
||||
queryKey: authKeys.verifyToken(token),
|
||||
queryFn: () => authApi.verifyToken(token),
|
||||
retry: false,
|
||||
})
|
||||
}
|
||||
|
||||
const clearAuthAndLogout = () => {
|
||||
localStorage.removeItem(AUTH_INFO_KEY)
|
||||
}
|
||||
|
||||
export const useRefresh = (
|
||||
onSuccessCallback: (data: AuthInfoBase) => void,
|
||||
onErrorCallback: () => void
|
||||
) => {
|
||||
return useMutation<
|
||||
AuthInfoBaseResponse,
|
||||
AxiosError
|
||||
>({
|
||||
mutationFn: () => authApi.refresh(),
|
||||
onSuccess: (data) => onSuccessCallback(data.data),
|
||||
onError: () => {
|
||||
clearAuthAndLogout()
|
||||
onErrorCallback()
|
||||
},
|
||||
})
|
||||
}
|
||||
export const useLogout = (onSuccess: () => void) => {
|
||||
const mutate = useGetUrlLoginMutation()
|
||||
return useMutation<BaseAPIResponse, AxiosError, LogoutRequest>({
|
||||
mutationFn: (body) => authApi.logout(body),
|
||||
onSuccess: () => {
|
||||
onSuccess()
|
||||
mutate.mutate()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useTokenRefresh = () => {
|
||||
const router = useRouter()
|
||||
const refreshToken = useCallback(async (): Promise<AuthInfoBaseResponse> => {
|
||||
const savedAuthInfoLocal = localStorage.getItem(AUTH_INFO_KEY)
|
||||
if (!savedAuthInfoLocal) {
|
||||
throw new Error('Cannot find auth information to refresh')
|
||||
}
|
||||
const authInfo = JSON.parse(savedAuthInfoLocal)
|
||||
|
||||
try {
|
||||
const newAuthInfo = await authApi.refresh()
|
||||
const newAuthInfoFull = AuthInfoFullSchema.parse({
|
||||
...newAuthInfo.data,
|
||||
user_info: authInfo.user_info,
|
||||
})
|
||||
localStorage.setItem(AUTH_INFO_KEY, JSON.stringify(newAuthInfoFull))
|
||||
return newAuthInfo
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof AxiosError &&
|
||||
e.response?.status === 401 &&
|
||||
typeof window !== 'undefined'
|
||||
) {
|
||||
window.dispatchEvent(new CustomEvent(AUTH_REFRESH_FAILED_EVENT))
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}, [])
|
||||
|
||||
const refreshTokenWithRetry = useCallback(
|
||||
async (onSuccess?: () => void) => {
|
||||
try {
|
||||
await refreshToken()
|
||||
onSuccess?.()
|
||||
} catch (refreshError) {
|
||||
clearAuthAndLogout()
|
||||
throw refreshError
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[router]
|
||||
)
|
||||
|
||||
return {
|
||||
refreshToken,
|
||||
refreshTokenWithRetry,
|
||||
clearAuthAndLogout,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user