Version Bump 1.0.40
All checks were successful
Build and Release / build-and-release (push) Successful in 19m24s

This commit is contained in:
Bryan1029384756
2026-04-06 23:47:40 -05:00
parent 8d7a0912c5
commit 9ef839938e
9 changed files with 229 additions and 41 deletions

View File

@@ -367,6 +367,151 @@ export const setNickname = mutation({
},
});
// Delete a user and all their associated data (admin only)
export const deleteUser = mutation({
args: {
requestingUserId: v.id("userProfiles"),
targetUserId: v.id("userProfiles"),
},
returns: v.object({ success: v.boolean(), error: v.optional(v.string()) }),
handler: async (ctx, args) => {
// Verify requesting user is admin
const requester = await ctx.db.get(args.requestingUserId);
if (!requester || !requester.isAdmin) {
return { success: false, error: "Only admins can delete users" };
}
// Prevent self-deletion
if (args.requestingUserId === args.targetUserId) {
return { success: false, error: "Cannot delete your own account" };
}
const target = await ctx.db.get(args.targetUserId);
if (!target) {
return { success: false, error: "User not found" };
}
// Prevent deleting other admins
if (target.isAdmin) {
return { success: false, error: "Cannot delete another admin" };
}
// Delete reactions made by this user (before messages, using index)
const userReactions = await ctx.db
.query("messageReactions")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const r of userReactions) {
await ctx.db.delete(r._id);
}
// Delete all messages by this user (using index)
const messages = await ctx.db
.query("messages")
.withIndex("by_sender", (q) => q.eq("senderId", args.targetUserId))
.collect();
for (const msg of messages) {
// Delete reactions on this message
const reactions = await ctx.db
.query("messageReactions")
.withIndex("by_message", (q) => q.eq("messageId", msg._id))
.collect();
for (const r of reactions) {
await ctx.db.delete(r._id);
}
await ctx.db.delete(msg._id);
}
// Delete channel keys
const channelKeys = await ctx.db
.query("channelKeys")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const ck of channelKeys) {
await ctx.db.delete(ck._id);
}
// Delete role assignments
const userRoles = await ctx.db
.query("userRoles")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const ur of userRoles) {
await ctx.db.delete(ur._id);
}
// Delete DM participations
const dmParts = await ctx.db
.query("dmParticipants")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const dp of dmParts) {
await ctx.db.delete(dp._id);
}
// Delete typing indicators
const typingIndicators = await ctx.db
.query("typingIndicators")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const ti of typingIndicators) {
await ctx.db.delete(ti._id);
}
// Delete voice states
const voiceStates = await ctx.db
.query("voiceStates")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const vs of voiceStates) {
await ctx.db.delete(vs._id);
}
// Delete read states
const readStates = await ctx.db
.query("channelReadState")
.withIndex("by_user", (q) => q.eq("userId", args.targetUserId))
.collect();
for (const rs of readStates) {
await ctx.db.delete(rs._id);
}
// Delete invites created by this user
const invites = await ctx.db
.query("invites")
.withIndex("by_creator", (q) => q.eq("createdBy", args.targetUserId))
.collect();
for (const inv of invites) {
await ctx.db.delete(inv._id);
}
// Delete custom emojis uploaded by this user
const emojis = await ctx.db
.query("customEmojis")
.withIndex("by_uploader", (q) => q.eq("uploadedBy", args.targetUserId))
.collect();
for (const emoji of emojis) {
await ctx.storage.delete(emoji.storageId);
await ctx.db.delete(emoji._id);
}
// Delete avatar from storage if exists
if (target.avatarStorageId) {
await ctx.storage.delete(target.avatarStorageId);
}
// Delete join sound from storage if exists
if (target.joinSoundStorageId) {
await ctx.storage.delete(target.joinSoundStorageId);
}
// Delete the user profile
await ctx.db.delete(args.targetUserId);
return { success: true };
},
});
// Internal: update credentials after password reset
export const updateCredentials = internalMutation({
args: {