import { NextResponse } from "next/server" import { createHmac } from "crypto" import { createSupabaseServerClient } from "@/lib/supabase/server" import { TIER_LIMITS, type SubscriptionTier } from "@asa-news/shared" import { rateLimit } from "@/lib/rate-limit" import { validateWebhookUrl } from "@/lib/validate-webhook-url" import { checkBotId } from "botid/server" export async function POST() { const botVerification = await checkBotId() if (botVerification.isBot) { return NextResponse.json({ error: "access denied" }, { status: 403 }) } const supabaseClient = await createSupabaseServerClient() const { data: { user }, } = await supabaseClient.auth.getUser() if (!user) { return NextResponse.json({ error: "not authenticated" }, { status: 401 }) } const rateLimitResult = await rateLimit(`webhook-test:${user.id}`, 5, 60_000) if (!rateLimitResult.success) { return NextResponse.json({ error: "too many requests" }, { status: 429 }) } const { data: profile } = await supabaseClient .from("user_profiles") .select( "tier, webhook_url, webhook_secret, webhook_enabled" ) .eq("id", user.id) .single() if ( !profile || !TIER_LIMITS[profile.tier as SubscriptionTier]?.allowsWebhooks ) { return NextResponse.json( { error: "webhooks require the developer plan" }, { status: 403 } ) } if (!profile.webhook_url) { return NextResponse.json( { error: "no webhook url configured" }, { status: 400 } ) } const validationResult = await validateWebhookUrl(profile.webhook_url) if (!validationResult.valid) { return NextResponse.json( { error: validationResult.error }, { status: 400 } ) } const testPayload = { event: "test", timestamp: new Date().toISOString(), entries: [ { entryIdentifier: "test-entry-000", feedIdentifier: "test-feed-000", title: "test webhook delivery", url: "https://asa.news", author: "asa.news", summary: "This is a test webhook payload to verify your endpoint.", publishedAt: new Date().toISOString(), }, ], } const payloadString = JSON.stringify(testPayload) const headers: Record = { "Content-Type": "application/json", "User-Agent": "asa.news Webhook/1.0", } if (profile.webhook_secret) { const signature = createHmac("sha256", profile.webhook_secret) .update(payloadString) .digest("hex") headers["X-Asa-Signature-256"] = `sha256=${signature}` } try { const response = await fetch(profile.webhook_url, { method: "POST", headers, body: payloadString, signal: AbortSignal.timeout(10_000), }) return NextResponse.json({ delivered: true, statusCode: response.status, }) } catch (deliveryError) { const errorMessage = deliveryError instanceof Error ? deliveryError.message : "Unknown error" return NextResponse.json({ delivered: false, error: errorMessage, }) } }