Init Commit
quality / commitlint (push) Skipped
CD / update-wiki (push) Failing after 7s
quality / changes (push) Successful in 4s
quality / docker-check (push) Skipped
quality / web (push) Failing after 38s
quality / api (push) Successful in 49s
CD / quality (push) Failing after 1m36s
CD / publish (push) Skipped

This commit is contained in:
Denozordec
2026-09-04 11:48:19 +07:00
commit cb8a79260e
300 changed files with 42404 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
import {
clearToken,
ensureAuthConfig,
getToken,
hasPortalHandoffFlag,
isAuthEnabled,
isPortalHandoffCoolingDown,
redirectToPortalLogin,
} from '@/lib/auth'
export class ApiError extends Error {
constructor(
public status: number,
public code: string,
message: string,
) {
super(message)
this.name = 'ApiError'
}
}
async function handoffOnUnauthorized(): Promise<void> {
clearToken()
const cfg = await ensureAuthConfig()
if (
(cfg.required || isAuthEnabled()) &&
!hasPortalHandoffFlag() &&
!isPortalHandoffCoolingDown()
) {
redirectToPortalLogin(`${window.location.origin}/auth/callback`)
return
}
if (!cfg.required && !isAuthEnabled()) {
window.location.href = '/login'
}
}
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const token = getToken()
const headers = new Headers(init?.headers)
if (init?.body != null && !headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json')
}
if (token) headers.set('Authorization', `Bearer ${token}`)
const res = await fetch(path, { ...init, headers })
if (res.status === 401 && !path.includes('/auth/login')) {
await handoffOnUnauthorized()
throw new ApiError(401, 'UNAUTHORIZED', 'Unauthorized')
}
if (!res.ok) {
const body = await res.json().catch(() => ({}))
const err = body?.error
throw new ApiError(
res.status,
err?.code ?? 'UNKNOWN',
err?.message ?? res.statusText,
)
}
if (res.status === 204) return undefined as T
return res.json() as Promise<T>
}
export const api = {
get: <T>(path: string) => request<T>(path),
post: <T>(path: string, body?: unknown) =>
request<T>(path, { method: 'POST', body: body ? JSON.stringify(body) : undefined }),
patch: <T>(path: string, body: unknown) =>
request<T>(path, { method: 'PATCH', body: JSON.stringify(body) }),
put: <T>(path: string, body: unknown) =>
request<T>(path, { method: 'PUT', body: JSON.stringify(body) }),
delete: <T>(path: string) => request<T>(path, { method: 'DELETE' }),
}