PrinceP
Convex Community2y ago
4 replies
Prince

Dashboard crashing, webhooks not receiving requests, and logs not showing on new project

@nipunn moving here to avoid cluttering #general

Event IDs:
417b3977e3774793bd7b334912920064
23087658685c40b599ac24e9e9a1173a

I think this might be happening after I try to hit my /clerk-users-webhook HTTP handler. It returns a 502 error on the Clerk dashboard and when I return to convex to check the logs, everything crashes until I refresh. This is a brand new project, webhook code:

import { httpRouter } from "convex/server"
import { httpAction } from "./_generated/server"
import { internal } from "./_generated/api"
import type { WebhookEvent } from "@clerk/backend"
import { Webhook } from "svix"

function ensureEnvironmentVariable() {
  const value = process.env.CLERK_WEBHOOK_SECRET
  if (value === undefined) {
    throw new Error(`missing environment variable CLERK_WEBHOOK_SECRET`)
  }
  return value
}

const webhookSecret = ensureEnvironmentVariable()

const handleClerkWebhook = httpAction(async (ctx, request) => {
  const event = await validateRequest(request)
  if (!event) {
    return new Response("Error occured", {
      status: 400,
    })
  }
  switch (event.type) {
    case "user.created": // intentional fallthrough
    case "user.updated": {
      const existingUser = await ctx.runQuery(internal.users.getUser, {
        subject: event.data.id,
      })
      if (existingUser && event.type === "user.created") {
        console.warn("Overwriting user", event.data.id, "with", event.data)
      }
      console.log("creating/updating user", event.data.id)
      await ctx.runMutation(internal.users.updateOrCreateUser, {
        clerkUser: event.data,
      })
      break
    }
    case "user.deleted": {
      // Clerk docs say this is required, but the types say optional?
      const id = event.data.id!
      await ctx.runMutation(internal.users.deleteUser, { id })
      break
    }
    default: {
      console.log("ignored Clerk webhook event", event.type)
    }
  }
  return new Response(null, {
    status: 200,
  })
})

const http = httpRouter()
http.route({
  path: "/clerk-users-webhook",
  method: "POST",
  handler: handleClerkWebhook,
})

async function validateRequest(
  req: Request
): Promise<WebhookEvent | undefined> {
  const payloadString = await req.text()

  const svixHeaders = {
    "svix-id": req.headers.get("svix-id")!,
    "svix-timestamp": req.headers.get("svix-timestamp")!,
    "svix-signature": req.headers.get("svix-signature")!,
  }
  const wh = new Webhook(webhookSecret)
  let evt: Event | null = null
  try {
    evt = wh.verify(payloadString, svixHeaders) as Event
  } catch (_) {
    console.log("error verifying request")
    return
  }

  return evt as unknown as WebhookEvent
}

export default http


schema:
import { defineSchema, defineTable } from "convex/server"
import { v } from "convex/values"

export default defineSchema({
  users: defineTable({
    // this is UserJSON from @clerk/backend
    clerkUser: v.any(),
    admin: v.optional(v.boolean()),
  }).index("by_clerk_id", ["clerkUser.id"]),
})
image.png
Was this page helpful?