import { query, mutation } from "./_generated/server"; import { v } from "convex/values"; // Create invite with encrypted payload export const create = mutation({ args: { code: v.string(), encryptedPayload: v.string(), createdBy: v.id("userProfiles"), maxUses: v.optional(v.number()), expiresAt: v.optional(v.number()), keyVersion: v.number(), }, returns: v.object({ success: v.boolean() }), handler: async (ctx, args) => { await ctx.db.insert("invites", { code: args.code, encryptedPayload: args.encryptedPayload, createdBy: args.createdBy, maxUses: args.maxUses, uses: 0, expiresAt: args.expiresAt, keyVersion: args.keyVersion, }); return { success: true }; }, }); // Fetch and validate invite (returns encrypted payload) export const use = query({ args: { code: v.string() }, returns: v.union( v.object({ encryptedPayload: v.string(), keyVersion: v.number(), }), v.object({ error: v.string() }) ), handler: async (ctx, args) => { const invite = await ctx.db .query("invites") .withIndex("by_code", (q) => q.eq("code", args.code)) .unique(); if (!invite) { return { error: "Invite not found" }; } if (invite.expiresAt && Date.now() > invite.expiresAt) { return { error: "Invite expired" }; } if ( invite.maxUses !== undefined && invite.maxUses !== null && invite.uses >= invite.maxUses ) { return { error: "Invite max uses reached" }; } return { encryptedPayload: invite.encryptedPayload, keyVersion: invite.keyVersion, }; }, }); // Revoke invite export const revoke = mutation({ args: { code: v.string() }, returns: v.object({ success: v.boolean() }), handler: async (ctx, args) => { const invite = await ctx.db .query("invites") .withIndex("by_code", (q) => q.eq("code", args.code)) .unique(); if (invite) { await ctx.db.delete(invite._id); } return { success: true }; }, });