import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; export const ensureUser = mutation({ args: { tenantId: v.string(), email: v.string(), name: v.string(), avatarUrl: v.optional(v.string()), role: v.optional(v.string()), teams: v.optional(v.array(v.string())), }, handler: async (ctx, args) => { const existing = await ctx.db .query("users") .withIndex("by_tenant_email", (q) => q.eq("tenantId", args.tenantId).eq("email", args.email)) .first(); const reconcile = async (record: typeof existing) => { if (!record) return null; const shouldPatch = record.tenantId !== args.tenantId || (args.role && record.role !== args.role) || (args.avatarUrl && record.avatarUrl !== args.avatarUrl) || record.name !== args.name || (args.teams && JSON.stringify(args.teams) !== JSON.stringify(record.teams ?? [])); if (shouldPatch) { await ctx.db.patch(record._id, { tenantId: args.tenantId, role: args.role ?? record.role, avatarUrl: args.avatarUrl ?? record.avatarUrl, name: args.name, teams: args.teams ?? record.teams, }); const updated = await ctx.db.get(record._id); if (updated) { return updated; } } return record; }; if (existing) { const reconciled = await reconcile(existing); if (reconciled) { return reconciled; } } else { const anyTenant = (await ctx.db.query("users").collect()).find((user) => user.email === args.email); if (anyTenant) { const reconciled = await reconcile(anyTenant); if (reconciled) { return reconciled; } } } const id = await ctx.db.insert("users", { tenantId: args.tenantId, email: args.email, name: args.name, avatarUrl: args.avatarUrl, role: args.role ?? "AGENT", teams: args.teams ?? [], }); return await ctx.db.get(id); }, }); export const listAgents = query({ args: { tenantId: v.string() }, handler: async (ctx, { tenantId }) => { const agents = await ctx.db .query("users") .withIndex("by_tenant_role", (q) => q.eq("tenantId", tenantId).eq("role", "AGENT")) .collect(); return agents; }, });