40 lines
991 B
TypeScript
40 lines
991 B
TypeScript
|
|
import { createHmac } from "crypto";
|
||
|
|
|
||
|
|
export async function sendWebhook(
|
||
|
|
url: string,
|
||
|
|
secret: string,
|
||
|
|
event: string,
|
||
|
|
card: Record<string, unknown>
|
||
|
|
): Promise<{ ok: boolean; status?: number; error?: string }> {
|
||
|
|
const payload = JSON.stringify({
|
||
|
|
event,
|
||
|
|
timestamp: new Date().toISOString(),
|
||
|
|
card,
|
||
|
|
});
|
||
|
|
|
||
|
|
const headers: Record<string, string> = {
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
};
|
||
|
|
|
||
|
|
if (secret) {
|
||
|
|
const sig = createHmac("sha256", secret).update(payload).digest("hex");
|
||
|
|
headers["X-Webhook-Signature"] = sig;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const res = await fetch(url, {
|
||
|
|
method: "POST",
|
||
|
|
headers,
|
||
|
|
body: payload,
|
||
|
|
signal: AbortSignal.timeout(10_000),
|
||
|
|
});
|
||
|
|
if (!res.ok) {
|
||
|
|
return { ok: false, status: res.status, error: `HTTP ${res.status}` };
|
||
|
|
}
|
||
|
|
return { ok: true, status: res.status };
|
||
|
|
} catch (err) {
|
||
|
|
const message = err instanceof Error ? err.message : "Request failed";
|
||
|
|
return { ok: false, error: message };
|
||
|
|
}
|
||
|
|
}
|