import { NextResponse } from "next/server" 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 GET() { const supabaseClient = await createSupabaseServerClient() const { data: { user }, } = await supabaseClient.auth.getUser() if (!user) { return NextResponse.json({ error: "not authenticated" }, { status: 401 }) } const { data: profile, error } = await supabaseClient .from("user_profiles") .select( "tier, webhook_url, webhook_secret, webhook_enabled, webhook_consecutive_failures" ) .eq("id", user.id) .single() if (error || !profile) { return NextResponse.json( { error: "failed to load webhook config" }, { status: 500 } ) } const maskedSecret = profile.webhook_secret ? profile.webhook_secret.slice(0, 4) + "••••" + profile.webhook_secret.slice(-4) : null return NextResponse.json({ webhookUrl: profile.webhook_url, webhookSecret: maskedSecret, webhookSecretConfigured: !!profile.webhook_secret, webhookEnabled: profile.webhook_enabled, consecutiveFailures: profile.webhook_consecutive_failures, }) } export async function PUT(request: Request) { 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-config:${user.id}`, 10, 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") .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 } ) } const body = await request.json().catch(() => ({})) const updates: Record = {} if (typeof body.webhookUrl === "string") { const trimmedUrl = body.webhookUrl.trim() if (trimmedUrl) { const validationResult = await validateWebhookUrl(trimmedUrl) if (!validationResult.valid) { return NextResponse.json( { error: validationResult.error }, { status: 400 } ) } } updates.webhook_url = trimmedUrl || null } if (typeof body.webhookSecret === "string") { updates.webhook_secret = body.webhookSecret.trim() || null } if (typeof body.webhookEnabled === "boolean") { if (body.webhookEnabled) { const effectiveUrl = typeof body.webhookUrl === "string" ? body.webhookUrl.trim() : profile.webhook_url if (!effectiveUrl) { return NextResponse.json( { error: "cannot enable webhooks without a url" }, { status: 400 } ) } updates.webhook_consecutive_failures = 0 } updates.webhook_enabled = body.webhookEnabled } if (Object.keys(updates).length === 0) { return NextResponse.json({ error: "no updates provided" }, { status: 400 }) } const { error } = await supabaseClient .from("user_profiles") .update(updates) .eq("id", user.id) if (error) { return NextResponse.json( { error: "failed to update webhook config" }, { status: 500 } ) } return NextResponse.json({ updated: true }) }