1.1.3
This commit is contained in:
@@ -13,6 +13,7 @@ const PERMISSION_KEYS = [
|
||||
"move_members",
|
||||
"mute_members",
|
||||
"manage_nicknames",
|
||||
"ban_members",
|
||||
] as const;
|
||||
|
||||
export async function getRolesForUser(
|
||||
@@ -30,6 +31,31 @@ export async function getRolesForUser(
|
||||
return roles.filter((r): r is Doc<"roles"> => r !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side permission check. Use before any mutation that's
|
||||
* supposed to be gated — the client-side `getMyPermissions` hides
|
||||
* UI, but a crafted client can still call the mutation.
|
||||
*
|
||||
* Treats `isAdmin` bootstrap flag and the "Owner" role as granting
|
||||
* every permission, including ones that don't yet exist on the role
|
||||
* row. That keeps future permission additions working for the
|
||||
* original server owner without requiring a migration pass.
|
||||
*/
|
||||
export async function hasPermission(
|
||||
ctx: GenericQueryCtx<DataModel>,
|
||||
userId: Id<"userProfiles">,
|
||||
key: (typeof PERMISSION_KEYS)[number],
|
||||
): Promise<boolean> {
|
||||
const user = await ctx.db.get(userId);
|
||||
if (!user) return false;
|
||||
if (user.isAdmin) return true;
|
||||
const roles = await getRolesForUser(ctx, userId);
|
||||
if (roles.some((r) => r.name === "Owner")) return true;
|
||||
return roles.some(
|
||||
(r) => (r.permissions as Record<string, boolean> | undefined)?.[key] === true,
|
||||
);
|
||||
}
|
||||
|
||||
// List all roles
|
||||
export const list = query({
|
||||
args: {},
|
||||
@@ -249,15 +275,22 @@ export const getMyPermissions = query({
|
||||
move_members: v.boolean(),
|
||||
mute_members: v.boolean(),
|
||||
manage_nicknames: v.boolean(),
|
||||
ban_members: v.boolean(),
|
||||
}),
|
||||
handler: async (ctx, args) => {
|
||||
const user = await ctx.db.get(args.userId);
|
||||
const roles = await getRolesForUser(ctx, args.userId);
|
||||
// isAdmin or Owner-role bearers get everything — same logic as
|
||||
// `hasPermission`. Keeps UI and server enforcement consistent.
|
||||
const isSuper = !!user?.isAdmin || roles.some((r) => r.name === "Owner");
|
||||
|
||||
const finalPerms: Record<string, boolean> = {};
|
||||
for (const key of PERMISSION_KEYS) {
|
||||
finalPerms[key] = roles.some(
|
||||
(role) => (role.permissions as Record<string, boolean>)?.[key]
|
||||
);
|
||||
finalPerms[key] =
|
||||
isSuper ||
|
||||
roles.some(
|
||||
(role) => (role.permissions as Record<string, boolean>)?.[key],
|
||||
);
|
||||
}
|
||||
|
||||
return finalPerms as {
|
||||
@@ -270,6 +303,7 @@ export const getMyPermissions = query({
|
||||
move_members: boolean;
|
||||
mute_members: boolean;
|
||||
manage_nicknames: boolean;
|
||||
ban_members: boolean;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user