import type { Env } from "../types"; const PAYLOAD_PREFIX = "webhooks/"; const DEFAULT_TTL_SECONDS = 3600; export interface PayloadStore { put(payload: string, ttl?: number): Promise; get(key: string): Promise; delete(key: string): Promise; } function generateKey(): string { const bytes = new Uint8Array(16); crypto.getRandomValues(bytes); const hex = Array.from(bytes) .map((b) => b.toString(16).padStart(2, "0")) .join(""); const now = new Date(); const y = now.getUTCFullYear(); const m = String(now.getUTCMonth() + 1).padStart(2, "0"); const d = String(now.getUTCDate()).padStart(2, "0"); return `${PAYLOAD_PREFIX}${y}/${m}/${d}/${hex}.json`; } export function r2PayloadStore(env: Env): PayloadStore { const bucket = env.PAYLOAD; if (!bucket) { return { async put(_payload: string): Promise { throw new Error("R2 binding is not configured"); }, async get(): Promise { throw new Error("R2 binding is not configured"); }, async delete(): Promise { throw new Error("R2 binding is not configured"); }, }; } return { async put(payload: string, ttl?: number): Promise { const key = generateKey(); const expires = new Date( Date.now() + (ttl ?? DEFAULT_TTL_SECONDS) * 1000, ); await bucket.put(key, payload, { httpMetadata: { contentType: "application/json" }, customMetadata: { expires: expires.toISOString() }, }); return key; }, async get(key: string): Promise { const obj = await bucket.get(key); if (!obj) return null; return obj.text(); }, async delete(key: string): Promise { await bucket.delete(key); }, }; }