feat(web): add login page, auth store, API client, and route guard
- API client with axios interceptors: JWT attach + 401 auto-refresh - Auth store (Zustand): login/logout/loadFromStorage with localStorage - Login page: gradient background, Ant Design form, error handling - Home page: dashboard with statistics cards - App.tsx: PrivateRoute guard, /login route, auth state restoration - MainLayout: dynamic user display, logout dropdown, menu navigation - Users API service: CRUD with pagination support
This commit is contained in:
52
apps/web/src/api/auth.ts
Normal file
52
apps/web/src/api/auth.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import client from './client';
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
id: string;
|
||||
username: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
display_name?: string;
|
||||
avatar_url?: string;
|
||||
status: string;
|
||||
roles: RoleInfo[];
|
||||
}
|
||||
|
||||
export interface RoleInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
description?: string;
|
||||
is_system: boolean;
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
expires_in: number;
|
||||
user: UserInfo;
|
||||
}
|
||||
|
||||
export async function login(req: LoginRequest): Promise<LoginResponse> {
|
||||
const { data } = await client.post<{ success: boolean; data: LoginResponse }>(
|
||||
'/auth/login',
|
||||
req
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function refresh(refreshToken: string): Promise<LoginResponse> {
|
||||
const { data } = await client.post<{ success: boolean; data: LoginResponse }>(
|
||||
'/auth/refresh',
|
||||
{ refresh_token: refreshToken }
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function logout(): Promise<void> {
|
||||
await client.post('/auth/logout');
|
||||
}
|
||||
Reference in New Issue
Block a user