minor fixes
This commit is contained in:
@@ -17,6 +17,19 @@ const phases = {
|
||||
error: 'error',
|
||||
}
|
||||
|
||||
const DEFAULT_CHUNK_REQUEST_TIMEOUT_MS = 45000
|
||||
const MIN_CHUNK_SIZE_BYTES = 256 * 1024
|
||||
|
||||
function formatChunkSize(bytes) {
|
||||
if (!Number.isFinite(bytes) || bytes <= 0) return '0 KB'
|
||||
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(bytes % (1024 * 1024) === 0 ? 0 : 1)} MB`
|
||||
return `${Math.max(1, Math.round(bytes / 1024))} KB`
|
||||
}
|
||||
|
||||
function isRequestTooLarge(error) {
|
||||
return Number(error?.response?.status || 0) === 413
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
phase: phases.idle,
|
||||
sessionId: null,
|
||||
@@ -163,9 +176,14 @@ function getTypeKey(ct) {
|
||||
return String(ct.name || '').toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_]/g, '')
|
||||
}
|
||||
|
||||
function useUploadMachine({ draftId, filesCdnUrl, chunkSize, userId }) {
|
||||
function useUploadMachine({ draftId, filesCdnUrl, chunkSize, chunkRequestTimeoutMs, userId }) {
|
||||
const [state, dispatch] = useReducer(reducer, { ...initialState, draftId })
|
||||
const pollRef = useRef(null)
|
||||
const adaptiveChunkSizeRef = useRef(Math.max(1, Number(chunkSize || 0)))
|
||||
const effectiveChunkRequestTimeoutMs = (() => {
|
||||
const parsed = Number(chunkRequestTimeoutMs)
|
||||
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : DEFAULT_CHUNK_REQUEST_TIMEOUT_MS
|
||||
})()
|
||||
|
||||
const extractErrorMessage = useCallback((error, fallback) => {
|
||||
const message = error?.response?.data?.message
|
||||
@@ -379,6 +397,7 @@ function useUploadMachine({ draftId, filesCdnUrl, chunkSize, userId }) {
|
||||
|
||||
try {
|
||||
const res = await window.axios.post('/api/uploads/chunk', payload, {
|
||||
timeout: effectiveChunkRequestTimeoutMs,
|
||||
headers: uploadToken ? { 'X-Upload-Token': uploadToken } : undefined,
|
||||
})
|
||||
|
||||
@@ -389,13 +408,16 @@ function useUploadMachine({ draftId, filesCdnUrl, chunkSize, userId }) {
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
if (isRequestTooLarge(error)) {
|
||||
throw error
|
||||
}
|
||||
if (attempt < MAX_CHUNK_RETRIES) {
|
||||
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS * (attempt + 1)))
|
||||
return uploadChunk(sessionId, uploadToken, blob, offset, totalSize, attempt + 1)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}, [])
|
||||
}, [effectiveChunkRequestTimeoutMs])
|
||||
|
||||
const uploadFile = useCallback(async (sessionId, uploadToken, file) => {
|
||||
dispatch({ type: 'UPLOAD_START' })
|
||||
@@ -415,7 +437,8 @@ function useUploadMachine({ draftId, filesCdnUrl, chunkSize, userId }) {
|
||||
if (offset > totalSize) offset = 0
|
||||
|
||||
while (offset < totalSize) {
|
||||
const nextOffset = Math.min(offset + chunkSize, totalSize)
|
||||
const activeChunkSize = Math.max(MIN_CHUNK_SIZE_BYTES, Number(adaptiveChunkSizeRef.current || chunkSize || MIN_CHUNK_SIZE_BYTES))
|
||||
const nextOffset = Math.min(offset + activeChunkSize, totalSize)
|
||||
const chunk = file.slice(offset, nextOffset)
|
||||
|
||||
try {
|
||||
@@ -425,6 +448,14 @@ function useUploadMachine({ draftId, filesCdnUrl, chunkSize, userId }) {
|
||||
offset = nextOffset
|
||||
}
|
||||
} catch (error) {
|
||||
if (isRequestTooLarge(error) && activeChunkSize > MIN_CHUNK_SIZE_BYTES) {
|
||||
const nextChunkSize = Math.max(MIN_CHUNK_SIZE_BYTES, Math.floor(activeChunkSize / 2))
|
||||
if (nextChunkSize < activeChunkSize) {
|
||||
adaptiveChunkSizeRef.current = nextChunkSize
|
||||
pushNotice('warning', `Server rejected ${formatChunkSize(activeChunkSize)} chunks. Retrying with ${formatChunkSize(nextChunkSize)} chunks.`)
|
||||
continue
|
||||
}
|
||||
}
|
||||
const notice = mapUploadErrorNotice(error, 'File upload failed. Please retry.')
|
||||
dispatch({ type: 'UPLOAD_ERROR', error: notice.message })
|
||||
pushMappedNotice(notice)
|
||||
@@ -587,7 +618,7 @@ function useUploadMachine({ draftId, filesCdnUrl, chunkSize, userId }) {
|
||||
}
|
||||
}
|
||||
|
||||
export default function UploadPage({ draftId, filesCdnUrl, chunkSize }) {
|
||||
export default function UploadPage({ draftId, filesCdnUrl, chunkSize, chunkRequestTimeoutMs }) {
|
||||
const { props } = usePage()
|
||||
|
||||
const windowFlags = window?.SKINBASE_FLAGS || {}
|
||||
@@ -618,6 +649,7 @@ export default function UploadPage({ draftId, filesCdnUrl, chunkSize }) {
|
||||
<UploadWizard
|
||||
initialDraftId={draftId ?? null}
|
||||
chunkSize={chunkSize}
|
||||
chunkRequestTimeoutMs={chunkRequestTimeoutMs}
|
||||
contentTypes={Array.isArray(props?.content_types) ? props.content_types : []}
|
||||
suggestedTags={Array.isArray(props?.suggested_tags) ? props.suggested_tags : []}
|
||||
groupOptions={Array.isArray(props?.group_options) ? props.group_options : []}
|
||||
@@ -689,7 +721,13 @@ export default function UploadPage({ draftId, filesCdnUrl, chunkSize }) {
|
||||
const userId = props?.auth?.user?.id ?? null
|
||||
const suggestedTags = Array.isArray(props?.suggested_tags) ? props.suggested_tags : []
|
||||
const safeChunkSize = Math.max(1, Number(chunkSize || 0))
|
||||
const { state, dispatch, previewUrl, startUpload, cancelUpload } = useUploadMachine({ draftId, filesCdnUrl, chunkSize: safeChunkSize, userId })
|
||||
const { state, dispatch, previewUrl, startUpload, cancelUpload } = useUploadMachine({
|
||||
draftId,
|
||||
filesCdnUrl,
|
||||
chunkSize: safeChunkSize,
|
||||
chunkRequestTimeoutMs,
|
||||
userId,
|
||||
})
|
||||
const fileInputRef = useRef(null)
|
||||
const [confirmCancel, setConfirmCancel] = useState(false)
|
||||
const [contentTypes, setContentTypes] = useState([])
|
||||
|
||||
Reference in New Issue
Block a user