feat: Implement core Discord clone functionality including Convex backend, Electron frontend, and UI components for chat, voice, and settings.
This commit is contained in:
210
convex/roles.ts
Normal file
210
convex/roles.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
import { query, mutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
// List all roles
|
||||
export const list = query({
|
||||
args: {},
|
||||
returns: v.array(v.any()),
|
||||
handler: async (ctx) => {
|
||||
const roles = await ctx.db.query("roles").collect();
|
||||
return roles.sort((a, b) => (b.position || 0) - (a.position || 0));
|
||||
},
|
||||
});
|
||||
|
||||
// Create new role
|
||||
export const create = mutation({
|
||||
args: {
|
||||
name: v.optional(v.string()),
|
||||
color: v.optional(v.string()),
|
||||
permissions: v.optional(v.any()),
|
||||
position: v.optional(v.number()),
|
||||
isHoist: v.optional(v.boolean()),
|
||||
},
|
||||
returns: v.any(),
|
||||
handler: async (ctx, args) => {
|
||||
const id = await ctx.db.insert("roles", {
|
||||
name: args.name || "new role",
|
||||
color: args.color || "#99aab5",
|
||||
position: args.position || 0,
|
||||
permissions: args.permissions || {},
|
||||
isHoist: args.isHoist || false,
|
||||
});
|
||||
|
||||
return await ctx.db.get(id);
|
||||
},
|
||||
});
|
||||
|
||||
// Update role properties
|
||||
export const update = mutation({
|
||||
args: {
|
||||
id: v.id("roles"),
|
||||
name: v.optional(v.string()),
|
||||
color: v.optional(v.string()),
|
||||
permissions: v.optional(v.any()),
|
||||
position: v.optional(v.number()),
|
||||
isHoist: v.optional(v.boolean()),
|
||||
},
|
||||
returns: v.any(),
|
||||
handler: async (ctx, args) => {
|
||||
const role = await ctx.db.get(args.id);
|
||||
if (!role) throw new Error("Role not found");
|
||||
|
||||
const updates: Record<string, unknown> = {};
|
||||
if (args.name !== undefined) updates.name = args.name;
|
||||
if (args.color !== undefined) updates.color = args.color;
|
||||
if (args.permissions !== undefined) updates.permissions = args.permissions;
|
||||
if (args.position !== undefined) updates.position = args.position;
|
||||
if (args.isHoist !== undefined) updates.isHoist = args.isHoist;
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await ctx.db.patch(args.id, updates);
|
||||
}
|
||||
|
||||
return await ctx.db.get(args.id);
|
||||
},
|
||||
});
|
||||
|
||||
// Delete role
|
||||
export const remove = mutation({
|
||||
args: { id: v.id("roles") },
|
||||
returns: v.object({ success: v.boolean() }),
|
||||
handler: async (ctx, args) => {
|
||||
const role = await ctx.db.get(args.id);
|
||||
if (!role) throw new Error("Role not found");
|
||||
|
||||
// Delete user_role assignments
|
||||
const assignments = await ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_role", (q) => q.eq("roleId", args.id))
|
||||
.collect();
|
||||
for (const a of assignments) {
|
||||
await ctx.db.delete(a._id);
|
||||
}
|
||||
|
||||
await ctx.db.delete(args.id);
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
// List members with roles
|
||||
export const listMembers = query({
|
||||
args: {},
|
||||
returns: v.array(v.any()),
|
||||
handler: async (ctx) => {
|
||||
const users = await ctx.db.query("userProfiles").collect();
|
||||
|
||||
const result = await Promise.all(
|
||||
users.map(async (user) => {
|
||||
const userRoleAssignments = await ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user", (q) => q.eq("userId", user._id))
|
||||
.collect();
|
||||
|
||||
const roles = await Promise.all(
|
||||
userRoleAssignments.map(async (ur) => {
|
||||
const role = await ctx.db.get(ur.roleId);
|
||||
return role;
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
public_identity_key: user.publicIdentityKey,
|
||||
roles: roles.filter(Boolean),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
|
||||
// Assign role to user
|
||||
export const assign = mutation({
|
||||
args: {
|
||||
roleId: v.id("roles"),
|
||||
userId: v.id("userProfiles"),
|
||||
},
|
||||
returns: v.object({ success: v.boolean() }),
|
||||
handler: async (ctx, args) => {
|
||||
// Check if already assigned
|
||||
const existing = await ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user_and_role", (q) =>
|
||||
q.eq("userId", args.userId).eq("roleId", args.roleId)
|
||||
)
|
||||
.unique();
|
||||
|
||||
if (!existing) {
|
||||
await ctx.db.insert("userRoles", {
|
||||
userId: args.userId,
|
||||
roleId: args.roleId,
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
// Remove role from user
|
||||
export const unassign = mutation({
|
||||
args: {
|
||||
roleId: v.id("roles"),
|
||||
userId: v.id("userProfiles"),
|
||||
},
|
||||
returns: v.object({ success: v.boolean() }),
|
||||
handler: async (ctx, args) => {
|
||||
const existing = await ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user_and_role", (q) =>
|
||||
q.eq("userId", args.userId).eq("roleId", args.roleId)
|
||||
)
|
||||
.unique();
|
||||
|
||||
if (existing) {
|
||||
await ctx.db.delete(existing._id);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
// Get current user's aggregated permissions
|
||||
export const getMyPermissions = query({
|
||||
args: { userId: v.id("userProfiles") },
|
||||
returns: v.object({
|
||||
manage_channels: v.boolean(),
|
||||
manage_roles: v.boolean(),
|
||||
create_invite: v.boolean(),
|
||||
embed_links: v.boolean(),
|
||||
attach_files: v.boolean(),
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const userRoleAssignments = await ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user", (q) => q.eq("userId", args.userId))
|
||||
.collect();
|
||||
|
||||
const finalPerms = {
|
||||
manage_channels: false,
|
||||
manage_roles: false,
|
||||
create_invite: false,
|
||||
embed_links: false,
|
||||
attach_files: false,
|
||||
};
|
||||
|
||||
for (const ur of userRoleAssignments) {
|
||||
const role = await ctx.db.get(ur.roleId);
|
||||
if (!role) continue;
|
||||
const p = (role.permissions || {}) as Record<string, boolean>;
|
||||
if (p.manage_channels) finalPerms.manage_channels = true;
|
||||
if (p.manage_roles) finalPerms.manage_roles = true;
|
||||
if (p.create_invite) finalPerms.create_invite = true;
|
||||
if (p.embed_links) finalPerms.embed_links = true;
|
||||
if (p.attach_files) finalPerms.attach_files = true;
|
||||
}
|
||||
|
||||
return finalPerms;
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user