diff options
Diffstat (limited to 'apps/web/app/api/webhook-config/route.ts')
| -rw-r--r-- | apps/web/app/api/webhook-config/route.ts | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/apps/web/app/api/webhook-config/route.ts b/apps/web/app/api/webhook-config/route.ts new file mode 100644 index 0000000..1ce9a30 --- /dev/null +++ b/apps/web/app/api/webhook-config/route.ts @@ -0,0 +1,117 @@ +import { NextResponse } from "next/server" +import { createSupabaseServerClient } from "@/lib/supabase/server" +import { createSupabaseAdminClient } from "@/lib/supabase/admin" +import { TIER_LIMITS, type SubscriptionTier } from "@asa-news/shared" +import { rateLimit } from "@/lib/rate-limit" + +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 adminClient = createSupabaseAdminClient() + const { data: profile, error } = await adminClient + .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 } + ) + } + + return NextResponse.json({ + webhookUrl: profile.webhook_url, + webhookSecret: profile.webhook_secret, + webhookEnabled: profile.webhook_enabled, + consecutiveFailures: profile.webhook_consecutive_failures, + }) +} + +export async function PUT(request: Request) { + const supabaseClient = await createSupabaseServerClient() + const { + data: { user }, + } = await supabaseClient.auth.getUser() + + if (!user) { + return NextResponse.json({ error: "Not authenticated" }, { status: 401 }) + } + + const rateLimitResult = rateLimit(`webhook-config:${user.id}`, 10, 60_000) + if (!rateLimitResult.success) { + return NextResponse.json({ error: "Too many requests" }, { status: 429 }) + } + + const adminClient = createSupabaseAdminClient() + + const { data: profile } = await adminClient + .from("user_profiles") + .select("tier") + .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<string, unknown> = {} + + if (typeof body.webhookUrl === "string") { + const trimmedUrl = body.webhookUrl.trim() + if (trimmedUrl && !trimmedUrl.startsWith("https://")) { + return NextResponse.json( + { error: "Webhook URL must use HTTPS" }, + { status: 400 } + ) + } + updates.webhook_url = trimmedUrl || null + } + + if (typeof body.webhookSecret === "string") { + updates.webhook_secret = body.webhookSecret.trim() || null + } + + if (typeof body.webhookEnabled === "boolean") { + updates.webhook_enabled = body.webhookEnabled + if (body.webhookEnabled) { + updates.webhook_consecutive_failures = 0 + } + } + + if (Object.keys(updates).length === 0) { + return NextResponse.json({ error: "No updates provided" }, { status: 400 }) + } + + const { error } = await adminClient + .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 }) +} |