Improve auth, checkout, and API client for real backend.
Wire OTP and cart/catalog to the API with Vite proxy, fix resend/register name persistence, harden order submit errors, and polish OTP and cart footer UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+53
-13
@@ -31,7 +31,14 @@ type RequestOptions = {
|
||||
|
||||
function buildUrl(path: string, query?: RequestOptions['query']): string {
|
||||
const base = getApiBaseUrl()
|
||||
const url = new URL(path.startsWith('http') ? path : `${base}${path.startsWith('/') ? '' : '/'}${path}`)
|
||||
const joined = path.startsWith('http')
|
||||
? path
|
||||
: `${base.replace(/\/$/, '')}/${path.replace(/^\//, '')}`
|
||||
|
||||
const url = joined.startsWith('http')
|
||||
? new URL(joined)
|
||||
: new URL(joined, typeof window !== 'undefined' ? window.location.origin : 'http://127.0.0.1')
|
||||
|
||||
if (query) {
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
if (value === undefined || value === null || value === '') continue
|
||||
@@ -41,6 +48,31 @@ function buildUrl(path: string, query?: RequestOptions['query']): string {
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
function errorMessageFromBody(data: unknown, status: number): string {
|
||||
if (typeof data === 'object' && data) {
|
||||
if ('message' in data) {
|
||||
const message = (data as { message: unknown }).message
|
||||
if (Array.isArray(message)) return message.join('، ')
|
||||
if (message != null && String(message).trim()) return String(message)
|
||||
}
|
||||
if ('error' in data) {
|
||||
const error = (data as { error: unknown }).error
|
||||
if (typeof error === 'string' && error.trim()) return `${error} (${status})`
|
||||
}
|
||||
}
|
||||
if (typeof data === 'string') {
|
||||
if (/upstream error/i.test(data)) {
|
||||
return 'سرور API در دسترس نیست. کمی بعد دوباره تلاش کنید.'
|
||||
}
|
||||
if (data.trim() && !data.trimStart().startsWith('<')) {
|
||||
return data.trim().slice(0, 200)
|
||||
}
|
||||
}
|
||||
if (status === 400) return 'درخواست نامعتبر است. اطلاعات سفارش را بررسی کنید.'
|
||||
if (status === 401) return 'نشست شما منقضی شده است. دوباره وارد شوید.'
|
||||
return `خطای سرور (${status})`
|
||||
}
|
||||
|
||||
export async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
Accept: 'application/json',
|
||||
@@ -53,11 +85,25 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
|
||||
if (token) headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
const res = await fetch(buildUrl(path, options.query), {
|
||||
method: options.method ?? (options.body !== undefined ? 'POST' : 'GET'),
|
||||
headers,
|
||||
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
||||
})
|
||||
let res: Response
|
||||
try {
|
||||
res = await fetch(buildUrl(path, options.query), {
|
||||
method: options.method ?? (options.body !== undefined ? 'POST' : 'GET'),
|
||||
headers,
|
||||
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
||||
signal: AbortSignal.timeout(20_000),
|
||||
})
|
||||
} catch (err) {
|
||||
const timedOut =
|
||||
(err instanceof DOMException && err.name === 'TimeoutError') ||
|
||||
(err instanceof Error && err.name === 'TimeoutError')
|
||||
throw new ApiError(
|
||||
0,
|
||||
timedOut
|
||||
? 'پاسخ سرور طول کشید. دوباره تلاش کنید.'
|
||||
: 'اتصال به سرور برقرار نشد. اینترنت یا آدرس API را بررسی کنید.',
|
||||
)
|
||||
}
|
||||
|
||||
const text = await res.text()
|
||||
let data: unknown = null
|
||||
@@ -70,13 +116,7 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const message =
|
||||
typeof data === 'object' && data && 'message' in data
|
||||
? Array.isArray((data as { message: unknown }).message)
|
||||
? ((data as { message: string[] }).message).join('، ')
|
||||
: String((data as { message: unknown }).message)
|
||||
: `خطای سرور (${res.status})`
|
||||
throw new ApiError(res.status, message)
|
||||
throw new ApiError(res.status, errorMessageFromBody(data, res.status))
|
||||
}
|
||||
|
||||
return data as T
|
||||
|
||||
Reference in New Issue
Block a user