feat(webhook): retry delivery — POST /deliveries/{id}/retry + UI кнопка
Раньше для replay'а failed/DLQ delivery нужно было либо ждать
backoff schedule (до 24h), либо лезть в БД и руками сбрасывать
attempt_count + dlq_at. Теперь admin замечает failed delivery в
UI, фиксит receiver (cert renewal, URL update), кликает 'повторить'
— dispatcher подхватит на следующем sendTick.
Backend:
- POST /api/v1/admin/webhooks/deliveries/{id}/retry
- RBAC: RESTRICTED (можно spam'ить receiver = destructive)
- Использует existing WebhookDelivery.resetDlq() — атомарно
status=retrying, attemptCount=0, dlqAt=null, nextAttemptAt=now
UI:
- Колонка 'Действия' в delivery history таблице (на webhook detail page)
- IconButton 'Повторить' показывается только для status=dlq|retrying
(delivered/pending не имеет смысла retry'ить)
- Confirm dialog объясняет что receiver получит payload снова
- onSuccess invalidate ['webhooks','deliveries'] + ['webhooks','dlq']
Tests: 75 admin-ui passed, rest-api compiles clean. Логика
resetDlq() уже покрыта unit tests в WebhookDeliveryTest.
This commit is contained in:
+22
@@ -142,6 +142,28 @@ public class WebhookSubscriptionController {
|
||||
return deliveries.map(WebhookDeliveryResponse::from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replay delivery из DLQ или retrying state — сбрасывает attemptCount=0,
|
||||
* dlqAt=null, status=retrying, nextAttemptAt=now. Dispatcher подхватит на
|
||||
* следующем sendTick.
|
||||
*
|
||||
* <p>Use case: оператор fixed receiver URL после deploy/cert renewal,
|
||||
* хочет replay'нуть failed delivery без ожидания TTL bypass'а.
|
||||
*
|
||||
* <p>RBAC: RESTRICTED — это destructive op (можно spam'ить receiver).
|
||||
*/
|
||||
@PostMapping("/deliveries/{id}/retry")
|
||||
public WebhookDeliveryResponse retryDelivery(@PathVariable Long id) {
|
||||
requireAdmin();
|
||||
WebhookDelivery delivery = deliveryRepository.findById(id)
|
||||
.orElseThrow(() -> OrdinisException.notFound(
|
||||
"webhook_delivery_not_found",
|
||||
"Webhook delivery " + id + " not found."));
|
||||
delivery.resetDlq();
|
||||
deliveryRepository.save(delivery);
|
||||
return WebhookDeliveryResponse.from(delivery);
|
||||
}
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
/** RESTRICTED scope обязателен для mutating endpoints. */
|
||||
|
||||
Reference in New Issue
Block a user