fix: retry 429 avec backoff + sérialisation uploadAll + 409 traité comme skipped
This commit is contained in:
@@ -249,8 +249,13 @@ export function useImages() {
|
|||||||
[items, update],
|
[items, update],
|
||||||
)
|
)
|
||||||
|
|
||||||
const uploadAll = useCallback(() => {
|
const uploadAll = useCallback(async () => {
|
||||||
items.filter((i) => i.status === 'ready').forEach((i) => uploadOne(i.id))
|
const ready = items.filter((i) => i.status === 'ready')
|
||||||
|
for (const item of ready) {
|
||||||
|
await uploadOne(item.id)
|
||||||
|
// Pause de 500ms entre chaque upload pour éviter le rate limit Shopify
|
||||||
|
await new Promise(r => setTimeout(r, 500))
|
||||||
|
}
|
||||||
}, [items, uploadOne])
|
}, [items, uploadOne])
|
||||||
|
|
||||||
const removeItem = useCallback((id: string) => {
|
const removeItem = useCallback((id: string) => {
|
||||||
|
|||||||
+34
-13
@@ -118,20 +118,41 @@ export async function uploadProductImage(
|
|||||||
const existingUrl = await productImageExists(shopifyProductId, filename)
|
const existingUrl = await productImageExists(shopifyProductId, filename)
|
||||||
if (existingUrl) return { url: existingUrl, skipped: true }
|
if (existingUrl) return { url: existingUrl, skipped: true }
|
||||||
|
|
||||||
const res = await fetch(`${baseUrl}/products/${shopifyProductId}/images.json`, {
|
// Retry avec backoff exponentiel (gère le 429 rate limit)
|
||||||
method: 'POST',
|
const MAX_RETRIES = 4
|
||||||
headers,
|
let delay = 1000
|
||||||
body: JSON.stringify({ image: { attachment: imageBase64, filename } }),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!res.ok) {
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
||||||
const body = await res.text()
|
const res = await fetch(`${baseUrl}/products/${shopifyProductId}/images.json`, {
|
||||||
throw new Error(`Shopify upload error: ${res.status} — ${body}`)
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({ image: { attachment: imageBase64, filename } }),
|
||||||
|
})
|
||||||
|
|
||||||
|
// 409 Conflict = image déjà présente → traiter comme skipped
|
||||||
|
if (res.status === 409) {
|
||||||
|
return { url: `https://placeholder/already-exists/${filename}`, skipped: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 429 Too Many Requests → attendre et réessayer
|
||||||
|
if (res.status === 429 && attempt < MAX_RETRIES) {
|
||||||
|
const retryAfter = parseInt(res.headers.get('Retry-After') ?? '0', 10)
|
||||||
|
await new Promise(r => setTimeout(r, retryAfter > 0 ? retryAfter * 1000 : delay))
|
||||||
|
delay *= 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const body = await res.text()
|
||||||
|
throw new Error(`Shopify upload error: ${res.status} — ${body}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json()
|
||||||
|
if (!data.image?.src) {
|
||||||
|
throw new Error('Shopify response invalide : champ image.src manquant')
|
||||||
|
}
|
||||||
|
return { url: data.image.src as string }
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json()
|
throw new Error('Shopify upload error: trop de tentatives (rate limit persistant)')
|
||||||
if (!data.image?.src) {
|
|
||||||
throw new Error('Shopify response invalide : champ image.src manquant')
|
|
||||||
}
|
|
||||||
return { url: data.image.src as string }
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user