import { query, mutation } from "./_generated/server"; import { v } from "convex/values"; // Batch upsert encrypted key bundles export const uploadKeys = mutation({ args: { keys: v.array( v.object({ channelId: v.id("channels"), userId: v.id("userProfiles"), encryptedKeyBundle: v.string(), keyVersion: v.number(), }) ), }, returns: v.object({ success: v.boolean(), count: v.number() }), handler: async (ctx, args) => { for (const keyData of args.keys) { if (!keyData.channelId || !keyData.userId || !keyData.encryptedKeyBundle) { continue; } // Check if exists (upsert) const existing = await ctx.db .query("channelKeys") .withIndex("by_channel_and_user", (q) => q.eq("channelId", keyData.channelId).eq("userId", keyData.userId) ) .unique(); if (existing) { await ctx.db.patch(existing._id, { encryptedKeyBundle: keyData.encryptedKeyBundle, keyVersion: keyData.keyVersion, }); } else { await ctx.db.insert("channelKeys", { channelId: keyData.channelId, userId: keyData.userId, encryptedKeyBundle: keyData.encryptedKeyBundle, keyVersion: keyData.keyVersion, }); } } return { success: true, count: args.keys.length }; }, }); // Get user's encrypted key bundles (reactive!) export const getKeysForUser = query({ args: { userId: v.id("userProfiles") }, returns: v.array( v.object({ channel_id: v.id("channels"), encrypted_key_bundle: v.string(), key_version: v.number(), }) ), handler: async (ctx, args) => { const keys = await ctx.db .query("channelKeys") .withIndex("by_user", (q) => q.eq("userId", args.userId)) .collect(); return keys.map((k) => ({ channel_id: k.channelId, encrypted_key_bundle: k.encryptedKeyBundle, key_version: k.keyVersion, })); }, });