feat: добавить новый backend и cabinet
- добавлен новый Nest backend для auth, projects и project access tokens - добавлена control-plane схема БД и миграция Drizzle - перенесён старый backend в old-backend - добавлен React/Vite cabinet с auth-only входом и Mantine layout - обновлены workspace scripts и lockfile
This commit is contained in:
77
apps/cabinet/src/infra/backend-api/client.ts
Normal file
77
apps/cabinet/src/infra/backend-api/client.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { getAdminToken } from './token-storage'
|
||||
import type { AdminSessionResponseDto, LoginRequestDto, LoginResponseDto } from './types/backend-api.type'
|
||||
|
||||
type RequestOptions = {
|
||||
body?: unknown
|
||||
isAuthorized?: boolean
|
||||
method?: 'GET' | 'POST'
|
||||
}
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_BACKEND_API_BASE_URL ?? '/api'
|
||||
|
||||
export const backendApi = {
|
||||
auth: {
|
||||
login: (body: LoginRequestDto) => {
|
||||
return request<LoginResponseDto>('/auth/login', { body, method: 'POST' })
|
||||
},
|
||||
me: () => {
|
||||
return request<AdminSessionResponseDto>('/auth/me', { isAuthorized: true })
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
body: options.body ? JSON.stringify(options.body) : undefined,
|
||||
headers: buildHeaders(options),
|
||||
method: options.method ?? 'GET',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await readErrorMessage(response))
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>
|
||||
}
|
||||
|
||||
function buildHeaders(options: RequestOptions) {
|
||||
const headers: Record<string, string> = {
|
||||
Accept: 'application/json',
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
|
||||
if (options.isAuthorized) {
|
||||
const token = getAdminToken()
|
||||
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
async function readErrorMessage(response: Response) {
|
||||
try {
|
||||
const value = (await response.json()) as unknown
|
||||
|
||||
if (isRecord(value) && typeof value.message === 'string') {
|
||||
return value.message
|
||||
}
|
||||
|
||||
if (isRecord(value) && Array.isArray(value.message)) {
|
||||
return value.message.join(', ')
|
||||
}
|
||||
} catch {
|
||||
return `request failed with status ${response.status}`
|
||||
}
|
||||
|
||||
return `request failed with status ${response.status}`
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
Reference in New Issue
Block a user