feat: add Convex backend — private/public APIs, schema, HTTP actions,… - #11
Conversation
|
Warning Review limit reached
Next review available in: 55 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Free Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (21)
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
Pull request overview
This PR introduces a Convex-powered backend package, adding schema + generated API types and a set of Convex functions split across public/ and private/ modules, plus integration of the @convex-dev/agent component.
Changes:
- Add Convex schema tables for contact sessions, conversations, files, widget settings, plugins, and secrets.
- Add new Convex queries/mutations/actions for widget settings, secrets, plugins, conversations, contact sessions, files, and agent-backed messaging.
- Wire up Convex app configuration to include the
@convex-dev/agentcomponent and update generated API typings.
Reviewed changes
Copilot reviewed 18 out of 22 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/backend/package.json | Adds @convex-dev/agent dependency for agent-backed messaging. |
| packages/backend/convex/schema.ts | Defines new tables used by the Convex backend. |
| packages/backend/convex/public/widgetSettings.ts | Public query to fetch widget settings by organization. |
| packages/backend/convex/public/secrets.ts | Public secret upsert and Vapi secret retrieval endpoint. |
| packages/backend/convex/public/plugins.ts | Public plugin read/remove endpoints (org-scoped). |
| packages/backend/convex/public/organizations.ts | Public organization “validate” action. |
| packages/backend/convex/public/messages.ts | Public agent-backed message list/create endpoints. |
| packages/backend/convex/public/conversations.ts | Public query to fetch a conversation if it matches a contact session. |
| packages/backend/convex/public/contactSessions.ts | Public contact session create/get/validate endpoints. |
| packages/backend/convex/private/widgetSettings.ts | Private org-scoped widget settings read/upsert endpoints. |
| packages/backend/convex/private/vapi.ts | Private actions to call Vapi API using server API key. |
| packages/backend/convex/private/secrets.ts | Private secret upsert endpoint (org-scoped). |
| packages/backend/convex/private/plugins.ts | Private plugin read/remove endpoints (org-scoped). |
| packages/backend/convex/private/messages.ts | Private agent-backed message endpoints + OpenAI response enhancement action. |
| packages/backend/convex/private/files.ts | Private file list/add/delete endpoints backed by Convex storage. |
| packages/backend/convex/private/conversations.ts | Private conversation list/get/updateStatus endpoints (includes last-message lookup). |
| packages/backend/convex/private/contactSessions.ts | Private query to fetch contact session by conversation id. |
| packages/backend/convex/convex.config.ts | Registers the agent component with the Convex app. |
| packages/backend/convex/_generated/api.d.ts | Updates generated API module typings to include the new modules. |
| packages/backend/convex/playground.ts | Present in PR context (contents not provided in prompt). |
| packages/backend/convex/http.ts | Present in PR context (contents not provided in prompt). |
| packages/backend/convex/constants.ts | Present in PR context (contents not provided in prompt). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import { query, action } from "../_generated/server"; | ||
| import { v } from "convex/values"; | ||
| import { paginationOptsValidator } from "convex/server"; | ||
| import { listMessages, saveMessage } from "@convex-dev/agent"; |
| import { query, mutation, action } from "../_generated/server"; | ||
| import { v } from "convex/values"; | ||
| import { paginationOptsValidator } from "convex/server"; | ||
| import { listMessages, saveMessage } from "@convex-dev/agent"; |
| handler: async (_ctx, args) => { | ||
| const { prompt } = args; | ||
| const response = await fetch("https://api.openai.com/v1/chat/completions", { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, | ||
| }, | ||
| body: JSON.stringify({ |
| @@ -0,0 +1,63 @@ | |||
| import { action, mutation } from "../_generated/server"; | |||
| export const getVapiSecrets = action({ | ||
| args: { | ||
| organizationId: v.string(), | ||
| }, | ||
| handler: async (ctx, args) => { | ||
| const { organizationId } = args; | ||
|
|
||
| const secret = await ctx.db | ||
| .query("secrets") | ||
| .filter((q) => | ||
| q.and( | ||
| q.eq(q.field("service"), "vapi"), | ||
| q.eq(q.field("organizationId"), organizationId), | ||
| ), | ||
| ) | ||
| .first(); | ||
|
|
||
| if (!secret) { | ||
| return null; | ||
| } | ||
|
|
||
| const value = secret.value as { publicApiKey?: string }; | ||
| return value.publicApiKey ? { publicApiKey: value.publicApiKey } : null; | ||
| }, | ||
| }); |
| handler: async (ctx, args) => { | ||
| const result = await ctx.db | ||
| .query("files") | ||
| .order("desc") | ||
| .paginate(args.paginationOpts); | ||
|
|
| handler: async (ctx, args) => { | ||
| const { bytes, filename, mimeType, category } = args; | ||
|
|
||
| const storageId = await ctx.storage.store(bytes); | ||
|
|
||
| const fileId = await ctx.db.insert("files", { | ||
| name: filename, | ||
| type: mimeType, | ||
| size: bytes.byteLength, | ||
| storageId, | ||
| category, | ||
| organizationId: "", | ||
| }); |
| handler: async (ctx, args) => { | ||
| const { entryId } = args; | ||
| const file = await ctx.db.get("files", entryId); | ||
| if (!file) { | ||
| throw new Error("File not found"); | ||
| } | ||
|
|
||
| await ctx.storage.delete(file.storageId); | ||
| await ctx.db.delete("files", entryId); | ||
| }, |
| const lastMessage = await ctx.db | ||
| .query("messages") | ||
| .withIndex("by_threadId", (q) => q.eq("threadId", conversation.threadId)) | ||
| .order("desc") | ||
| .first(); |
| export const getAssistants = action({ | ||
| args: {}, | ||
| handler: async () => { | ||
| const apiKey = process.env.VAPI_API_KEY; | ||
| if (!apiKey) { |
… playground