File
Blob: src/client/components/webhooks/rotate-secret-dialog.tsx
| 1 | import type { WebhookSummary } from "@/contracts"; |
| 2 | import { useState } from "react"; |
| 3 | import { RotateCw } from "lucide-react"; |
| 4 | import { ConfirmDialog } from "@/client/components/ui"; |
| 5 | import { SecretRevealDialog } from "@/client/components/webhooks/secret-reveal-dialog"; |
| 6 | import { formatApiError, getApiClient } from "@/client/lib"; |
| 7 | import { useToast } from "@/client/toast"; |
| 8 | import { webhookProviderCatalog } from "@/lib/webhooks"; |
| 9 | |
| 10 | interface RotateSecretDialogProps { |
| 11 | open: boolean; |
| 12 | onClose: () => void; |
| 13 | webhook: WebhookSummary; |
| 14 | webhookUrl: string; |
| 15 | onRotated: () => void; |
| 16 | } |
| 17 | |
| 18 | export const RotateSecretDialog = ({ open, onClose, webhook, webhookUrl, onRotated }: RotateSecretDialogProps) => { |
| 19 | const { pushToast } = useToast(); |
| 20 | const [rotating, setRotating] = useState(false); |
| 21 | const [newSecret, setNewSecret] = useState<string | null>(null); |
| 22 | |
| 23 | const catalog = webhookProviderCatalog[webhook.provider]; |
| 24 | |
| 25 | const handleConfirm = () => { |
| 26 | setRotating(true); |
| 27 | |
| 28 | void getApiClient() |
| 29 | .rotateWebhookSecret(webhook.projectId, webhook.provider) |
| 30 | .then((response) => { |
| 31 | setNewSecret(response.secret); |
| 32 | onRotated(); |
| 33 | pushToast({ |
| 34 | tone: "success", |
| 35 | title: "Secret rotated", |
| 36 | message: `Update your ${catalog.displayName} webhook configuration.`, |
| 37 | }); |
| 38 | }) |
| 39 | .catch((reason: unknown) => { |
| 40 | pushToast({ tone: "error", title: "Rotation failed", message: formatApiError(reason) }); |
| 41 | onClose(); |
| 42 | }) |
| 43 | .finally(() => { |
| 44 | setRotating(false); |
| 45 | }); |
| 46 | }; |
| 47 | |
| 48 | const handleClose = () => { |
| 49 | setNewSecret(null); |
| 50 | onClose(); |
| 51 | }; |
| 52 | |
| 53 | if (newSecret) { |
| 54 | return ( |
| 55 | <SecretRevealDialog |
| 56 | open |
| 57 | onClose={handleClose} |
| 58 | secret={newSecret} |
| 59 | provider={webhook.provider} |
| 60 | webhookUrl={webhookUrl} |
| 61 | /> |
| 62 | ); |
| 63 | } |
| 64 | |
| 65 | return ( |
| 66 | <ConfirmDialog |
| 67 | open={open} |
| 68 | onConfirm={handleConfirm} |
| 69 | onCancel={onClose} |
| 70 | title="Rotate webhook secret" |
| 71 | description={`This will generate a new secret and invalidate the current one. You will need to update your ${catalog.displayName} webhook configuration immediately. This action cannot be undone.`} |
| 72 | confirmLabel={rotating ? "Rotating..." : "Rotate Secret"} |
| 73 | variant="warning" |
| 74 | icon={<RotateCw className="h-5 w-5" />} |
| 75 | /> |
| 76 | ); |
| 77 | }; |