129 lines
3.5 KiB
TypeScript
129 lines
3.5 KiB
TypeScript
import { useAuthStore } from '@/stores/auth-store'
|
|
import { toQueryParams, type Page, type PageRequest } from './pagination'
|
|
|
|
export abstract class ApiError {
|
|
readonly message: string
|
|
|
|
constructor(message: string) {
|
|
this.message = message
|
|
}
|
|
}
|
|
|
|
export class NetworkError extends ApiError {}
|
|
|
|
export class StatusError extends ApiError {
|
|
readonly status: number
|
|
|
|
constructor(status: number, message: string) {
|
|
super(message)
|
|
this.status = status
|
|
}
|
|
}
|
|
|
|
export abstract class ApiClient {
|
|
private baseUrl: string = import.meta.env.VITE_API_BASE_URL
|
|
|
|
protected async getJson<T>(path: string): Promise<T> {
|
|
const r = await this.doRequest('GET', path)
|
|
return await r.json()
|
|
}
|
|
|
|
protected async getJsonPage<T>(
|
|
path: string,
|
|
paginationOptions: PageRequest | undefined = undefined,
|
|
): Promise<Page<T>> {
|
|
let p = path
|
|
if (paginationOptions !== undefined) {
|
|
p += '?' + toQueryParams(paginationOptions)
|
|
}
|
|
return this.getJson(p)
|
|
}
|
|
|
|
protected async getText(path: string): Promise<string> {
|
|
const r = await this.doRequest('GET', path)
|
|
return await r.text()
|
|
}
|
|
|
|
protected async postJson<T>(path: string, body: object | undefined = undefined): Promise<T> {
|
|
const r = await this.doRequest('POST', path, body)
|
|
return await r.json()
|
|
}
|
|
|
|
protected async postText(path: string, body: object | undefined = undefined): Promise<string> {
|
|
const r = await this.doRequest('POST', path, body)
|
|
return await r.text()
|
|
}
|
|
|
|
protected async postNoResponse(
|
|
path: string,
|
|
body: object | undefined = undefined,
|
|
): Promise<void> {
|
|
await this.doRequest('POST', path, body)
|
|
}
|
|
|
|
protected async delete(path: string): Promise<void> {
|
|
await this.doRequest('DELETE', path)
|
|
}
|
|
|
|
protected async putJson<T>(path: string, body: object | undefined = undefined): Promise<T> {
|
|
const r = await this.doRequest('PUT', path, body)
|
|
return await r.json()
|
|
}
|
|
|
|
async getApiStatus(): Promise<boolean> {
|
|
try {
|
|
await this.doRequest('GET', '/status')
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does a generic request, returning the response if successful, or throwing an ApiError if
|
|
* any sort of error or non-OK status is returned.
|
|
* @param method The HTTP method to use.
|
|
* @param path The API path to request.
|
|
* @param body The request body.
|
|
* @returns A promise that resolves to an OK response.
|
|
*/
|
|
private async doRequest(
|
|
method: string,
|
|
path: string,
|
|
body: object | undefined = undefined,
|
|
): Promise<Response> {
|
|
const settings: RequestInit = { method }
|
|
const headers: HeadersInit = {}
|
|
if (body !== undefined && typeof body === 'object') {
|
|
headers['Content-Type'] = 'application/json'
|
|
settings.body = JSON.stringify(body)
|
|
}
|
|
const authStore = useAuthStore()
|
|
if (authStore.state) {
|
|
headers['Authorization'] = 'Bearer ' + authStore.state.token
|
|
}
|
|
settings.headers = headers
|
|
try {
|
|
const response = await fetch(this.baseUrl + path, settings)
|
|
if (!response.ok) {
|
|
const message = await response.text()
|
|
throw new StatusError(response.status, message)
|
|
}
|
|
return response
|
|
} catch (error) {
|
|
if (error instanceof ApiError) throw error
|
|
let message = 'Request to ' + path + ' failed.'
|
|
if (
|
|
typeof error === 'object' &&
|
|
error !== null &&
|
|
'message' in error &&
|
|
typeof error.message === 'string'
|
|
) {
|
|
message = error.message
|
|
}
|
|
console.error(error)
|
|
throw new NetworkError(message)
|
|
}
|
|
}
|
|
}
|