Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e74505a
fix:(FI-40): fix conflict
NHSon05 Jun 29, 2026
d15ff38
Merge branch 'dev' and resolve conflicts
NHSon05 Jul 2, 2026
4c0a3b7
Fix Bad Request on update profile and resolve all stash pop conflicts
NHSon05 Jul 2, 2026
93e3c1c
Fix Bad Request (400) by converting null values to empty strings in u…
NHSon05 Jul 2, 2026
479bab8
Sync updated profile data to authStore to prevent old data flash on r…
NHSon05 Jul 2, 2026
e89179e
Fix data unwrap and authStore synchronization in use-query-auth
NHSon05 Jul 2, 2026
475c031
Refactor authentication workflow to strictly keep AccessToken in-memo…
NHSon05 Jul 2, 2026
0a472bf
Fix reload logging out and navigation issues in in-memory auth workflow
NHSon05 Jul 2, 2026
60627fc
Fix ProtectedRoute block by reading AccessToken from in-memory Zustan…
NHSon05 Jul 2, 2026
37dbc60
Restore authentication workflow back to storing AccessToken and Refre…
NHSon05 Jul 2, 2026
ce181ba
Refactor auth: in-memory AT, rt_token key in LS, doubly nested respon…
NHSon05 Jul 2, 2026
42d25d0
Fix automatic logout by using doRefresh() in response interceptor to …
NHSon05 Jul 2, 2026
05bd216
Fix dashboard fullName sync issue by properly unwrapping response dat…
NHSon05 Jul 2, 2026
588a1f3
Commit user manual rollback changes to LocalStorage auth flow
NHSon05 Jul 2, 2026
db2cc20
Manage user profile strictly in-memory using Zustand and React Query,…
NHSon05 Jul 2, 2026
34d951f
Fix reload page blanks by fetching user info on app initialization
NHSon05 Jul 2, 2026
a83790d
Fix reload token loss by only logging out on explicit auth failures (…
NHSon05 Jul 2, 2026
816db88
Correctly unwrap res.data in updateProfile onSuccess mutation handler…
NHSon05 Jul 2, 2026
774642e
Safeguard status mapping in LawyerProfile by checking both lawyerStat…
NHSon05 Jul 2, 2026
c7b9993
Optimize Axios clients to prevent auto-logouts on network failures du…
NHSon05 Jul 2, 2026
97d1ee4
Refactor authentication flow and sync lawyer location field across co…
NHSon05 Jul 2, 2026
8dfaf32
Restore direct API call for user info initialization and sync status …
NHSon05 Jul 2, 2026
bd7d78a
Fix user roleName and status lost after /auth/me fetch in both backen…
NHSon05 Jul 2, 2026
71392a1
Restrict BANNED status interceptor checks to auth endpoints in axios-…
NHSon05 Jul 2, 2026
973be2e
Fix Admin unban refresh state and prevent Admin logout on user ban ac…
NHSon05 Jul 2, 2026
39884cd
Merge branch 'origin/dev' into feat/FI-40 with ours strategy
NHSon05 Jul 2, 2026
a19bf93
Fix queryClient declaration in user ban/unban mutations
NHSon05 Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/src/core/modules/auth/auth.repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const PROFILE_SELECT = {
banned_by: true,
is_email_confirmed: true,
created_at: true,
status: true,
roles: {
select: { name: true }
}
};


Expand Down
2 changes: 2 additions & 0 deletions backend/src/core/modules/auth/service/auth.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ class Service {
? { planName: user.subscriptions.plan_name ?? null }
: undefined,
createdAt: user.created_at ?? undefined,
status: user.status ?? 'ACTIVE',
roleName: user.roles?.name || (Array.isArray(user.roles) ? user.roles[0]?.name : null) || 'USER'
};
}

Expand Down
24 changes: 13 additions & 11 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

import { ThemeProvider } from '@/app/providers/theme-provider'
import AutoScrollToTop from '@/components/scroll/auto-scroll-to-top'
import { authApi } from '@/core/services/auth.service'
import { scheduleTokenRefresh } from '@/core/shared/auth-refresh'
import { getAccessTokenFromLS } from '@/core/shared/storage'
import { useAuthStore } from '@/core/store/features/auth/authStore'
import useRoutesElements from '@/hooks/routes/use-router-element'
import { type Account } from '@/models/interface/auth.interface'

import '@/styles/theme.css'
import { useUserInfo } from './hooks/tanstack-query/auth/use-query-auth'
import { type UserRole } from './models/user/types'

const AppContent = () => {
Expand All @@ -22,24 +23,25 @@
const [isAppLoading, setIsAppLoading] = useState(true)
const logout = useAuthStore((state) => state.logout)
const updateUser = useAuthStore((state) => state.updateUser)
const { data: userData } = useUserInfo()

useEffect(() => {
const initializeApp = async () => {
const accessToken = getAccessTokenFromLS()
if (accessToken) {
try {
const userData = await authApi.getUserInfo() as Account
const currentUser = useAuthStore.getState().user
updateUser({
userId: userData?.id || '',
fullName: userData?.fullName || '',
email: userData?.email || '',
phone: userData?.phone || '',
location: userData?.location || '',
avatarUrl: userData?.avatarUrl || '',
roleName: userData?.roleName as UserRole
userId: userData.id || '',
fullName: userData.fullName || '',
email: userData.email || '',
phone: userData.phone || '',
location: userData.location || '',
avatarUrl: userData.avatarUrl || '',
roleName: userData.roleName as UserRole || currentUser?.roleName || 'USER',
status: userData.status || currentUser?.status || 'ACTIVE'
})
scheduleTokenRefresh()
} catch (error: any) {

Check warning on line 44 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / frontend

Unexpected any. Specify a different type
console.error('Failed to fetch user info on app initialize:', error)
if (error.response && [401, 403].includes(error.response.status)) {
logout()
Expand All @@ -50,7 +52,7 @@
}

initializeApp()
}, [logout, updateUser, userData?.avatarUrl, userData?.email, userData?.fullName, userData?.id, userData?.location, userData?.phone, userData?.roleName])
}, [logout, updateUser])

if (isAppLoading) {
return (
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/core/services/axios-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@
controllers.delete(response.config.url)
}

// Check if response indicates user is banned
// Check if response indicates user is banned (only for auth/profile endpoints of the current user)
const dataObj = response.data as any

Check warning on line 87 in frontend/src/core/services/axios-client.ts

View workflow job for this annotation

GitHub Actions / frontend

Unexpected any. Specify a different type
const user = dataObj?.data?.user || dataObj?.user
const userStatus = user?.status || dataObj?.data?.status || dataObj?.status
if (userStatus === 'BANNED' || userStatus === 'banned') {
const isAuthRequestUrl = response.config.url && response.config.url.includes('/auth/')
if (isAuthRequestUrl && (userStatus === 'BANNED' || userStatus === 'banned')) {
handleBannedUser(
user?.banReason || dataObj?.data?.banReason || dataObj?.banReason,
user?.bannedAt || dataObj?.data?.bannedAt || dataObj?.bannedAt
Expand All @@ -100,17 +101,19 @@
async (error) => {
const originalRequest = error.config

// Check if error response indicates user is banned
// Check if error response indicates user is banned (only for auth/profile endpoints of the current user)
const errorStatus = error.response?.status
const errorData = error.response?.data as any

Check warning on line 106 in frontend/src/core/services/axios-client.ts

View workflow job for this annotation

GitHub Actions / frontend

Unexpected any. Specify a different type
const errorMessage = errorData?.message || ''
const isAuthRequestUrl = originalRequest?.url && originalRequest.url.includes('/auth/')

if (
errorStatus === 403 ||
isAuthRequestUrl &&
(errorStatus === 403 ||
errorStatus === 401 ||
errorMessage.toLowerCase().includes('banned') ||
errorData?.status === 'BANNED' ||
errorData?.status === 'banned'
errorData?.status === 'banned')
) {
if (
errorMessage.toLowerCase().includes('banned') ||
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/hooks/tanstack-query/auth/use-query-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const useLoginAuth = () => {
mutationFn: (data: Account) => authApi.login(data),
onSuccess: (response: LoginApiResponse) => {
const user = response.data?.user
console.log(user)
const userStatus = user?.status || 'ACTIVE'

if (userStatus === 'ACTIVE') {
Expand Down Expand Up @@ -145,14 +146,16 @@ export const useUserInfo = () => {
if (query.data) {
// Map Account to UserResponseType structure
const account = query.data
const currentUser = useAuthStore.getState().user
updateUser({
userId: account.id || '',
fullName: account.fullName || '',
email: account.email || '',
phone: account.phone,
location: account.location,
avatarUrl: account.avatarUrl,
roleName: account.roleName as UserRole
roleName: account.roleName as UserRole || currentUser?.roleName || 'USER',
status: account.status || currentUser?.status || 'ACTIVE'
})
}
}, [query.data, updateUser])
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/hooks/users/use-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ export const useDeleteUser = () => {
}

export const useBanUser = () => {
// const queryClient = useQueryClient()
const queryClient = useQueryClient()
return useMutation({
mutationKey: [MUTATION_KEYS.banUser],
mutationFn: ({ id, reason }: { id: string; reason: string }) => usersApi.banUser(id, reason),
onSuccess: () => {
toastifyCommon.success('Khóa tài khoản thành công')
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.users] })
},
onError: (error) => {
handleError(error, 'Khóa tài khoản thất bại')
Expand All @@ -86,11 +87,13 @@ export const useBanUser = () => {
}

export const useUnbanUser = () => {
const queryClient = useQueryClient()
return useMutation({
mutationKey: [MUTATION_KEYS.unbanUser],
mutationFn: ({ id, reason }: { id: string; reason: string }) => usersApi.unbanUser(id, reason),
onSuccess: () => {
toastifyCommon.success('Kích hoạt tài khoản thành công')
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.users] })
},
onError: (error) => {
handleError(error, 'Kích hoạt tài khoản thất bại')
Expand Down
Loading