diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts new file mode 100644 index 0000000..e6d0eb6 --- /dev/null +++ b/frontend/src/api/auth.ts @@ -0,0 +1,49 @@ +import { request } from './http' + +export interface AuthUserGroup { + id: number + name: string + description: string + is_system: boolean +} + +export interface AuthUser { + id: number + username: string + email: string + nickname: string + avatar: string + status: number + groups: AuthUserGroup[] +} + +export interface LoginPayload { + account: string + password: string +} + +export interface LoginResponse { + token: string + expires_at: string + user: AuthUser +} + +export interface RegisterPayload { + username: string + email: string + password: string +} + +export function login(payload: LoginPayload): Promise { + return request('/auth/login', { + method: 'POST', + body: JSON.stringify(payload), + }) +} + +export function register(payload: RegisterPayload): Promise { + return request('/auth/register', { + method: 'POST', + body: JSON.stringify(payload), + }) +} diff --git a/frontend/src/api/http.ts b/frontend/src/api/http.ts index bdc8ff0..4ff5207 100644 --- a/frontend/src/api/http.ts +++ b/frontend/src/api/http.ts @@ -8,17 +8,38 @@ export class ApiError extends Error { } } +let authToken: string | null = null +let unauthorizedHandler: (() => void) | null = null + +export function setAuthToken(token: string | null) { + authToken = token +} + +export function setUnauthorizedHandler(handler: (() => void) | null) { + unauthorizedHandler = handler +} + export async function request(path: string, init?: RequestInit): Promise { + const headers = new Headers(init?.headers) + if (init?.body && !headers.has('Content-Type')) { + headers.set('Content-Type', 'application/json') + } + if (authToken) { + headers.set('Authorization', `Bearer ${authToken}`) + } + let response: Response try { - response = await fetch(`/api${path}`, { - headers: { 'Content-Type': 'application/json' }, - ...init, - }) + response = await fetch(`/api${path}`, { ...init, headers }) } catch { throw new ApiError(0, 'network error') } + const isAuthPath = path.startsWith('/auth/') + if (response.status === 401 && !isAuthPath) { + unauthorizedHandler?.() + } + if (!response.ok) { let message = response.statusText try { diff --git a/frontend/src/api/users.ts b/frontend/src/api/users.ts deleted file mode 100644 index eb2f86f..0000000 --- a/frontend/src/api/users.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { request } from './http' - -export interface RegisterPayload { - username: string - email: string - password: string -} - -export interface User { - id: number - username: string - email: string - nickname: string - avatar: string - status: number -} - -export function registerUser(payload: RegisterPayload): Promise { - return request('/auth/register', { - method: 'POST', - body: JSON.stringify(payload), - }) -} diff --git a/frontend/src/components/layout/AppHeader.vue b/frontend/src/components/layout/AppHeader.vue index 0dabb58..4c3a83b 100644 --- a/frontend/src/components/layout/AppHeader.vue +++ b/frontend/src/components/layout/AppHeader.vue @@ -1,11 +1,34 @@