0020_webhook_endpoints.sql 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. -- 0020_webhook_endpoints — inbound webhook receivers.
  2. -- The public POST /webhooks/:projectId/:endpointId endpoint authenticates
  3. -- callers via HMAC-SHA256(`${timestamp}.${rawBody}`, signingSecret). The
  4. -- signing secret is stored AES-256-GCM-encrypted, same KEK + format as
  5. -- project_env_vars. Every inbound request (accepted OR rejected) inserts
  6. -- one row into webhook_deliveries — the audit log surface is the only way
  7. -- an operator can tell "did the signature fail" vs "did my function 500".
  8. CREATE TABLE IF NOT EXISTS "webhook_endpoints" (
  9. "id" text PRIMARY KEY NOT NULL,
  10. "project_id" text NOT NULL,
  11. "name" text NOT NULL,
  12. "function_name" text NOT NULL,
  13. "signing_secret_encrypted" text NOT NULL,
  14. "enabled" boolean DEFAULT true NOT NULL,
  15. "last_delivery_at" timestamp with time zone,
  16. "last_delivery_status" text,
  17. "created_by" text,
  18. "created_at" timestamp with time zone DEFAULT now() NOT NULL,
  19. "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
  20. "deleted_at" timestamp with time zone,
  21. CONSTRAINT "webhook_endpoints_project_id_fk"
  22. FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE,
  23. CONSTRAINT "webhook_endpoints_created_by_fk"
  24. FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE SET NULL
  25. );
  26. CREATE UNIQUE INDEX IF NOT EXISTS "webhook_endpoints_project_name_idx"
  27. ON "webhook_endpoints" USING btree ("project_id", "name")
  28. WHERE "deleted_at" IS NULL;
  29. CREATE INDEX IF NOT EXISTS "webhook_endpoints_project_idx"
  30. ON "webhook_endpoints" USING btree ("project_id")
  31. WHERE "deleted_at" IS NULL;
  32. CREATE TABLE IF NOT EXISTS "webhook_deliveries" (
  33. "id" text PRIMARY KEY NOT NULL,
  34. "endpoint_id" text NOT NULL,
  35. "project_id" text NOT NULL,
  36. "status" text NOT NULL,
  37. "source_ip_hash" text,
  38. "function_name" text,
  39. "duration_ms" text,
  40. "error_message" text,
  41. "created_at" timestamp with time zone DEFAULT now() NOT NULL,
  42. CONSTRAINT "webhook_deliveries_endpoint_id_fk"
  43. FOREIGN KEY ("endpoint_id") REFERENCES "webhook_endpoints"("id") ON DELETE CASCADE,
  44. CONSTRAINT "webhook_deliveries_project_id_fk"
  45. FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE
  46. );
  47. CREATE INDEX IF NOT EXISTS "webhook_deliveries_endpoint_idx"
  48. ON "webhook_deliveries" USING btree ("endpoint_id", "created_at");
  49. CREATE INDEX IF NOT EXISTS "webhook_deliveries_project_idx"
  50. ON "webhook_deliveries" USING btree ("project_id", "created_at");