Files
DiscordClone/convex/channelKeys.ts

73 lines
2.0 KiB
TypeScript

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,
}));
},
});