optimize ui loading and dashboard performance
This commit is contained in:
+46
-15
@@ -22,6 +22,36 @@ export class ApiError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 10_000;
|
||||
|
||||
async function fetchWithTimeout(path, options = {}) {
|
||||
const { timeoutMs = DEFAULT_TIMEOUT_MS, signal: externalSignal, ...fetchOptions } = options;
|
||||
const controller = new AbortController();
|
||||
const abortFromExternal = () => controller.abort(externalSignal?.reason);
|
||||
if (externalSignal) {
|
||||
if (externalSignal.aborted) {
|
||||
abortFromExternal();
|
||||
} else {
|
||||
externalSignal.addEventListener('abort', abortFromExternal, { once: true });
|
||||
}
|
||||
}
|
||||
const timeout = window.setTimeout(() => controller.abort('REQUEST_TIMEOUT'), timeoutMs);
|
||||
try {
|
||||
return await fetch(`${API_BASE}${path}`, {
|
||||
...fetchOptions,
|
||||
signal: controller.signal,
|
||||
});
|
||||
} catch (error) {
|
||||
if (controller.signal.aborted && !externalSignal?.aborted) {
|
||||
throw new ApiError('请求超过 10 秒未响应,请稍后重试。', { status: 408, code: 'REQUEST_TIMEOUT' });
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
window.clearTimeout(timeout);
|
||||
externalSignal?.removeEventListener('abort', abortFromExternal);
|
||||
}
|
||||
}
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const token = window.localStorage.getItem(ACCESS_TOKEN_KEY);
|
||||
const headers = new Headers(options.headers || {});
|
||||
@@ -33,7 +63,7 @@ async function request(path, options = {}) {
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
const response = await fetchWithTimeout(path, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: 'include',
|
||||
@@ -58,7 +88,7 @@ async function requestBlob(path, options = {}) {
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
const response = await fetchWithTimeout(path, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: 'include',
|
||||
@@ -116,11 +146,12 @@ export const api = {
|
||||
setAccessToken('');
|
||||
}
|
||||
},
|
||||
dashboardSummary: () => request('/dashboard/summary'),
|
||||
dashboardTrends: (params = { hours: 24, bucketMinutes: 60 }) => request(`/dashboard/trends?${new URLSearchParams(params)}`),
|
||||
activeCalls: () => request('/active-calls'),
|
||||
dashboardSummary: (options) => request('/dashboard/summary', options),
|
||||
dashboardTrends: (params = { hours: 24, bucketMinutes: 60 }, options) => request(`/dashboard/trends?${new URLSearchParams(params)}`, options),
|
||||
dashboardOverview: (params = { hours: 24, bucketMinutes: 60 }, options) => request(`/dashboard/overview?${new URLSearchParams(params)}`, options),
|
||||
activeCalls: (options) => request('/active-calls', options),
|
||||
hangupActiveCall: (id) => request(`/active-calls/${encodeURIComponent(id)}/hangup`, { method: 'POST' }),
|
||||
cdrs: (params = {}) => request(`/cdrs${queryString({ take: 100, ...params })}`),
|
||||
cdrs: (params = {}) => request(`/cdrs${queryString({ take: 25, ...params })}`),
|
||||
cdrDetail: (id) => request(`/cdrs/${encodeURIComponent(id)}`),
|
||||
recordings: (params = {}) => request(`/recordings${queryString({ status: 'READY', limit: 100, ...params })}`),
|
||||
recordingDetail: (id) => request(`/recordings/${encodeURIComponent(id)}`),
|
||||
@@ -132,7 +163,7 @@ export const api = {
|
||||
enableQualityRule: (id) => request(`/quality/rules/${encodeURIComponent(id)}/enable`, { method: 'POST' }),
|
||||
disableQualityRule: (id) => request(`/quality/rules/${encodeURIComponent(id)}/disable`, { method: 'POST' }),
|
||||
deleteQualityRule: (id) => request(`/quality/rules/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
customers: () => request('/customers'),
|
||||
customers: (options) => request('/customers', options),
|
||||
createCustomer: (body) => request('/customers', { method: 'POST', body: jsonBody(body) }),
|
||||
updateCustomer: (id, body) => request(`/customers/${encodeURIComponent(id)}`, { method: 'PATCH', body: jsonBody(body) }),
|
||||
enableCustomer: (id) => request(`/customers/${encodeURIComponent(id)}/enable`, { method: 'POST' }),
|
||||
@@ -143,7 +174,7 @@ export const api = {
|
||||
method: 'POST',
|
||||
body: jsonBody({ ...body, idempotencyKey: idempotencyKey('customer-recharge') }),
|
||||
}),
|
||||
vendors: () => request('/vendors'),
|
||||
vendors: (options) => request('/vendors', options),
|
||||
createVendor: (body) => request('/vendors', { method: 'POST', body: jsonBody(body) }),
|
||||
updateVendor: (id, body) => request(`/vendors/${encodeURIComponent(id)}`, { method: 'PATCH', body: jsonBody(body) }),
|
||||
deleteVendor: (id) => request(`/vendors/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
@@ -152,26 +183,26 @@ export const api = {
|
||||
method: 'POST',
|
||||
body: jsonBody({ ...body, idempotencyKey: idempotencyKey('vendor-recharge') }),
|
||||
}),
|
||||
customerGateways: () => request('/customer-gateways'),
|
||||
customerGateways: (options) => request('/customer-gateways', options),
|
||||
createCustomerGateway: (body) => request('/customer-gateways', { method: 'POST', body: jsonBody(body) }),
|
||||
updateCustomerGateway: (id, body) => request(`/customer-gateways/${encodeURIComponent(id)}`, { method: 'PATCH', body: jsonBody(body) }),
|
||||
enableCustomerGateway: (id) => request(`/customer-gateways/${encodeURIComponent(id)}/enable`, { method: 'POST' }),
|
||||
disableCustomerGateway: (id) => request(`/customer-gateways/${encodeURIComponent(id)}/disable`, { method: 'POST' }),
|
||||
deleteCustomerGateway: (id) => request(`/customer-gateways/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
vendorGateways: () => request('/vendor-gateways'),
|
||||
vendorGateways: (options) => request('/vendor-gateways', options),
|
||||
createVendorGateway: (body) => request('/vendor-gateways', { method: 'POST', body: jsonBody(body) }),
|
||||
updateVendorGateway: (id, body) => request(`/vendor-gateways/${encodeURIComponent(id)}`, { method: 'PATCH', body: jsonBody(body) }),
|
||||
enableVendorGateway: (id) => request(`/vendor-gateways/${encodeURIComponent(id)}/enable`, { method: 'POST' }),
|
||||
disableVendorGateway: (id) => request(`/vendor-gateways/${encodeURIComponent(id)}/disable`, { method: 'POST' }),
|
||||
deleteVendorGateway: (id) => request(`/vendor-gateways/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
landingLineGroups: () => request('/landing-line-groups'),
|
||||
landingLineGroups: (options) => request('/landing-line-groups', options),
|
||||
deleteLandingLineGroup: (id) => request(`/landing-line-groups/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
recharges: () => request('/recharges?take=100'),
|
||||
users: () => request('/users'),
|
||||
recharges: (options) => request('/recharges?take=100', options),
|
||||
users: (options) => request('/users', options),
|
||||
deleteUser: (id) => request(`/users/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
roles: () => request('/roles'),
|
||||
roles: (options) => request('/roles', options),
|
||||
deleteRole: (id) => request(`/roles/${encodeURIComponent(id)}`, { method: 'DELETE' }),
|
||||
auditLogs: () => request('/audit-logs?take=100'),
|
||||
auditLogs: (options) => request('/audit-logs?take=100', options),
|
||||
businessPrefixes: (params = {}) => request(`/business-prefixes${queryString(params)}`),
|
||||
createBusinessPrefix: (body) => request('/business-prefixes', { method: 'POST', body: jsonBody(body) }),
|
||||
updateBusinessPrefix: (id, body) => request(`/business-prefixes/${encodeURIComponent(id)}`, { method: 'PATCH', body: jsonBody(body) }),
|
||||
|
||||
Reference in New Issue
Block a user