auth: update authentication system
- Update AuthContext with new login response structure - Add isLoading state to prevent premature redirects - Implement proper session restoration from localStorage - Update permission checking logic (id=1 = super admin) - Fix login form to use new API structure - Store tokens, user info, and permissions in localStorage
This commit is contained in:
parent
fa755515bb
commit
4d385f2031
|
|
@ -1,26 +1,37 @@
|
||||||
import { createContext, useContext, useReducer, useEffect } from 'react';
|
import React, { createContext, useContext, useReducer, useEffect } from 'react';
|
||||||
import { AuthState, AdminUser, Permission } from '../types/auth';
|
import { AuthState, AdminUser, Permission, LoginRequest } from '../types/auth';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
interface AuthContextType extends AuthState {
|
interface AuthContextType {
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
user: AdminUser | null;
|
||||||
|
permissions: Permission[];
|
||||||
|
allPermissions: Permission[];
|
||||||
|
token: string | null;
|
||||||
|
refreshToken: string | null;
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
|
restoreSession: () => void;
|
||||||
hasPermission: (permissionId: number) => boolean;
|
hasPermission: (permissionId: number) => boolean;
|
||||||
hasPermissionByTitle: (title: string) => boolean;
|
hasPermissionByTitle: (title: string) => boolean;
|
||||||
restoreSession: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||||
|
|
||||||
type AuthAction =
|
type AuthAction =
|
||||||
| { type: 'LOGIN_SUCCESS'; payload: { user: AdminUser; permissions: Permission[]; allPermissions: Permission[]; token: string; refreshToken: string } }
|
| { type: 'LOGIN'; payload: { user: AdminUser; permissions: Permission[]; allPermissions: Permission[]; token: string; refreshToken: string } }
|
||||||
| { type: 'LOGOUT' }
|
| { type: 'LOGOUT' }
|
||||||
| { type: 'RESTORE_SESSION'; payload: { user: AdminUser; permissions: Permission[]; allPermissions: Permission[]; token: string; refreshToken: string } };
|
| { type: 'RESTORE_SESSION'; payload: { user: AdminUser; permissions: Permission[]; allPermissions: Permission[]; token: string; refreshToken: string } }
|
||||||
|
| { type: 'SET_LOADING'; payload: boolean };
|
||||||
|
|
||||||
const authReducer = (state: AuthState, action: AuthAction): AuthState => {
|
const authReducer = (state: AuthState, action: AuthAction): AuthState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'LOGIN_SUCCESS':
|
case 'LOGIN':
|
||||||
|
case 'RESTORE_SESSION':
|
||||||
return {
|
return {
|
||||||
|
...state,
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
|
isLoading: false,
|
||||||
user: action.payload.user,
|
user: action.payload.user,
|
||||||
permissions: action.payload.permissions,
|
permissions: action.payload.permissions,
|
||||||
allPermissions: action.payload.allPermissions,
|
allPermissions: action.payload.allPermissions,
|
||||||
|
|
@ -29,21 +40,19 @@ const authReducer = (state: AuthState, action: AuthAction): AuthState => {
|
||||||
};
|
};
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
return {
|
return {
|
||||||
|
...state,
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
|
isLoading: false,
|
||||||
user: null,
|
user: null,
|
||||||
permissions: [],
|
permissions: [],
|
||||||
allPermissions: [],
|
allPermissions: [],
|
||||||
token: null,
|
token: null,
|
||||||
refreshToken: null,
|
refreshToken: null,
|
||||||
};
|
};
|
||||||
case 'RESTORE_SESSION':
|
case 'SET_LOADING':
|
||||||
return {
|
return {
|
||||||
isAuthenticated: true,
|
...state,
|
||||||
user: action.payload.user,
|
isLoading: action.payload,
|
||||||
permissions: action.payload.permissions,
|
|
||||||
allPermissions: action.payload.allPermissions,
|
|
||||||
token: action.payload.token,
|
|
||||||
refreshToken: action.payload.refreshToken,
|
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
|
@ -52,6 +61,7 @@ const authReducer = (state: AuthState, action: AuthAction): AuthState => {
|
||||||
|
|
||||||
const initialState: AuthState = {
|
const initialState: AuthState = {
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
|
isLoading: true,
|
||||||
user: null,
|
user: null,
|
||||||
permissions: [],
|
permissions: [],
|
||||||
allPermissions: [],
|
allPermissions: [],
|
||||||
|
|
@ -63,6 +73,8 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
const [state, dispatch] = useReducer(authReducer, initialState);
|
const [state, dispatch] = useReducer(authReducer, initialState);
|
||||||
|
|
||||||
const restoreSession = () => {
|
const restoreSession = () => {
|
||||||
|
dispatch({ type: 'SET_LOADING', payload: true });
|
||||||
|
|
||||||
const token = localStorage.getItem('admin_token');
|
const token = localStorage.getItem('admin_token');
|
||||||
const refreshToken = localStorage.getItem('admin_refresh_token');
|
const refreshToken = localStorage.getItem('admin_refresh_token');
|
||||||
const userStr = localStorage.getItem('admin_user');
|
const userStr = localStorage.getItem('admin_user');
|
||||||
|
|
@ -88,7 +100,10 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
localStorage.removeItem('admin_refresh_token');
|
localStorage.removeItem('admin_refresh_token');
|
||||||
localStorage.removeItem('admin_user');
|
localStorage.removeItem('admin_user');
|
||||||
localStorage.removeItem('admin_permissions');
|
localStorage.removeItem('admin_permissions');
|
||||||
|
dispatch({ type: 'SET_LOADING', payload: false });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
dispatch({ type: 'SET_LOADING', payload: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -127,9 +142,9 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
<AuthContext.Provider value={{
|
<AuthContext.Provider value={{
|
||||||
...state,
|
...state,
|
||||||
logout,
|
logout,
|
||||||
|
restoreSession,
|
||||||
hasPermission,
|
hasPermission,
|
||||||
hasPermissionByTitle,
|
hasPermissionByTitle,
|
||||||
restoreSession,
|
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ import { loginSchema, LoginFormData } from '../utils/validationSchemas';
|
||||||
import { useLogin } from './auth/core/_hooks';
|
import { useLogin } from './auth/core/_hooks';
|
||||||
|
|
||||||
export const Login = () => {
|
export const Login = () => {
|
||||||
const { isAuthenticated, restoreSession } = useAuth();
|
const { isAuthenticated, isLoading, restoreSession } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
const { mutate: login, isPending: isLoading } = useLogin();
|
const { mutate: login, isPending: isLoggingIn } = useLogin();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
|
@ -26,6 +26,17 @@ export const Login = () => {
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600 mx-auto"></div>
|
||||||
|
<p className="mt-4 text-gray-600 dark:text-gray-400">در حال بارگذاری...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
return <Navigate to="/" replace />;
|
return <Navigate to="/" replace />;
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +124,7 @@ export const Login = () => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
loading={isLoading}
|
loading={isLoggingIn}
|
||||||
disabled={!isValid}
|
disabled={!isValid}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,49 @@ export interface AdminUser {
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Complete AdminUserInfo from swagger (with roles and permissions)
|
||||||
|
export interface AdminUserInfo {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
status: "active" | "deactive";
|
||||||
|
permissions: Permission[];
|
||||||
|
roles: Role[];
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin User Management Types
|
||||||
|
export interface CreateAdminUserRequest {
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAdminUserRequest {
|
||||||
|
id: number;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
username: string;
|
||||||
|
password?: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminUsersListResponse {
|
||||||
|
users: AdminUserInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminUserDetailResponse {
|
||||||
|
user: AdminUserInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteAdminUserResponse {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Tokens {
|
export interface Tokens {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
|
|
@ -43,6 +86,7 @@ export interface LoginRequest {
|
||||||
|
|
||||||
export interface AuthState {
|
export interface AuthState {
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
user: AdminUser | null;
|
user: AdminUser | null;
|
||||||
permissions: Permission[];
|
permissions: Permission[];
|
||||||
allPermissions: Permission[];
|
allPermissions: Permission[];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue