fix(ops-web): stop desktop release upload timing out at complete step
Frontend CI / Frontend (push) Successful in 2m30s
Frontend CI / Frontend (push) Successful in 2m30s
The chunked upload complete request used the global 30s axios timeout, but the server merges chunks, hashes the file and writes ~100MB to OSS in that step, so large installers failed with "timeout of 30000ms exceeded" after all chunks were uploaded. - give the complete request a dedicated 10min timeout - let http.post pass through an AxiosRequestConfig - normalize client-side timeout errors to "request_timeout" Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,8 @@ export interface DesktopClientReleaseDownloadURLResult {
|
||||
|
||||
const DESKTOP_CLIENT_RELEASE_UPLOAD_TIMEOUT_MS = 30 * 60 * 1000
|
||||
const DESKTOP_CLIENT_RELEASE_CHUNK_TIMEOUT_MS = 5 * 60 * 1000
|
||||
// complete 阶段服务端需要合并分片、计算 SHA256 并把整个安装包上传到 OSS,远超默认 30s
|
||||
const DESKTOP_CLIENT_RELEASE_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000
|
||||
const DESKTOP_CLIENT_RELEASE_CHUNK_SIZE_BYTES = 16 * 1024 * 1024
|
||||
const DESKTOP_CLIENT_RELEASE_CHUNK_RETRIES = 2
|
||||
|
||||
@@ -257,6 +259,8 @@ async function uploadOSSInChunks(
|
||||
})
|
||||
return await http.post<DesktopClientReleaseUploadResult>(
|
||||
`/desktop-client/release-uploads/${session.upload_id}/complete`,
|
||||
undefined,
|
||||
{ timeout: DESKTOP_CLIENT_RELEASE_COMPLETE_TIMEOUT_MS },
|
||||
)
|
||||
} catch (error) {
|
||||
void http.delete(`/desktop-client/release-uploads/${session.upload_id}`).catch(() => undefined)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import axios, { type AxiosError, type AxiosInstance } from 'axios'
|
||||
import axios, { type AxiosError, type AxiosInstance, type AxiosRequestConfig } from 'axios'
|
||||
|
||||
import { clearStoredSession, readStoredSession } from '@/lib/storage'
|
||||
|
||||
@@ -77,6 +77,8 @@ client.interceptors.response.use(
|
||||
const status = error.response?.status
|
||||
const payload = error.response?.data
|
||||
const handled = status === 401 && !isLoginRequest(error.config?.url, error.config?.method)
|
||||
const isTimeout =
|
||||
!error.response && (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT')
|
||||
|
||||
if (handled) {
|
||||
clearStoredSession()
|
||||
@@ -88,7 +90,7 @@ client.interceptors.response.use(
|
||||
|
||||
return Promise.reject(
|
||||
new OpsApiError({
|
||||
message: payload?.message ?? error.message ?? 'request_failed',
|
||||
message: payload?.message ?? (isTimeout ? 'request_timeout' : error.message) ?? 'request_failed',
|
||||
code: typeof payload?.code === 'number' ? payload.code : undefined,
|
||||
status,
|
||||
detail: typeof payload?.detail === 'string' ? payload.detail : undefined,
|
||||
@@ -114,7 +116,8 @@ async function unwrap<T>(promise: Promise<{ data: ApiEnvelope<T> }>): Promise<T>
|
||||
|
||||
export const http = {
|
||||
get: <T>(url: string, params?: Record<string, unknown>) => unwrap<T>(client.get(url, { params })),
|
||||
post: <T, B = unknown>(url: string, body?: B) => unwrap<T>(client.post(url, body)),
|
||||
post: <T, B = unknown>(url: string, body?: B, config?: AxiosRequestConfig) =>
|
||||
unwrap<T>(client.post(url, body, config)),
|
||||
put: <T, B = unknown>(url: string, body?: B) => unwrap<T>(client.put(url, body)),
|
||||
patch: <T, B = unknown>(url: string, body?: B) => unwrap<T>(client.patch(url, body)),
|
||||
delete: <T>(url: string) => unwrap<T>(client.delete(url)),
|
||||
|
||||
Reference in New Issue
Block a user