Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | 1x 1x 1x 1x 1x 1x 1x 1x 599x 599x 599x 599x 599x 1x 599x 599x 599x 599x 599x 599x 5x 5x 599x 599x 594x 594x 594x 594x 594x 599x 1x 543x 543x 543x 543x 543x 543x 4x 4x 4x 4x 4x 543x 543x 3x 3x 3x 3x 3x 543x 543x 543x 1x 4x 4x 4x 4x 1x 1x 1x 1x 1x 4x 4x 4x 1x 4x 4x 4x 4x 2x 2x 2x 2x 2x 2x 2x 4x 1x 274x 274x 274x 274x 274x 274x 274x 273x 273x 273x 273x 273x 273x 273x 246x 246x 274x 274x 27x 27x 27x 27x 27x 221x 23x 23x 265x 265x 4x 4x 4x 4x 4x 4x 11x 2x 2x 204x 204x 274x 1x 277x 277x 277x 277x 277x 252x 251x 277x 277x 1x 25x 25x 25x 25x 25x 25x | import { ConvexError } from "convex/values"
import type { UserIdentity } from "convex/server"
import type { Doc } from "../_generated/dataModel"
import type { MutationCtx, QueryCtx } from "../_generated/server"
type AuthCtx = QueryCtx | MutationCtx
export async function getCurrentIdentity(
ctx: AuthCtx
): Promise<UserIdentity | null> {
return await ctx.auth.getUserIdentity()
}
export async function getCurrentUser(
ctx: AuthCtx
): Promise<Doc<"users"> | null> {
const identity = await getCurrentIdentity(ctx)
if (!identity) {
return null
}
return await ctx.db
.query("users")
.withIndex("by_clerk_user_id", (q) =>
q.eq("clerkUserId", identity.subject)
)
.unique()
}
export async function requireCurrentUser(
ctx: AuthCtx
): Promise<Doc<"users">> {
const user = await getCurrentUser(ctx)
if (!user) {
throw new ConvexError({
code: "UNAUTHORIZED",
message: "You must be signed in.",
})
}
if (user.status === "disabled") {
throw new ConvexError({
code: "USER_DISABLED",
message: "This account is disabled.",
})
}
return user
}
export async function requireStaff(ctx: AuthCtx): Promise<Doc<"users">> {
const user = await requireCurrentUser(ctx)
if (!["staff", "admin", "superadmin"].includes(user.role)) {
throw new ConvexError({
code: "FORBIDDEN",
message: "Staff access is required.",
})
}
return user
}
export async function requireAdmin(ctx: AuthCtx): Promise<Doc<"users">> {
const user = await requireCurrentUser(ctx)
if (!["admin", "superadmin"].includes(user.role)) {
throw new ConvexError({
code: "FORBIDDEN",
message: "Admin access is required.",
})
}
return user
}
export async function requireDiscordGuildManager(
ctx: AuthCtx,
guildId: Doc<"guilds">["_id"]
): Promise<Doc<"discordGuildMemberships">> {
const user = await requireCurrentUser(ctx)
const directMembership = await ctx.db
.query("discordGuildMemberships")
.withIndex("by_user_id_and_guild_id", (q) =>
q.eq("userId", user._id).eq("guildId", guildId)
)
.unique()
if (isVerifiedGuildManager(directMembership)) {
return directMembership
}
const discordAccount = await ctx.db
.query("linkedAccounts")
.withIndex("by_user_id", (q) => q.eq("userId", user._id))
.filter((q) => q.eq(q.field("provider"), "discord"))
.first()
if (!discordAccount) {
throwForbiddenGuildAccess()
}
const membership = await ctx.db
.query("discordGuildMemberships")
.withIndex("by_guild_id_and_discord_user_id", (q) =>
q.eq("guildId", guildId).eq("discordUserId", discordAccount.providerAccountId)
)
.unique()
if (!isVerifiedGuildManager(membership)) {
throwForbiddenGuildAccess()
}
return membership
}
function isVerifiedGuildManager(
membership: Doc<"discordGuildMemberships"> | null
): membership is Doc<"discordGuildMemberships"> {
return Boolean(
membership?.canManage &&
membership.managementVerifiedAt !== undefined &&
membership.revokedAt === undefined
)
}
function throwForbiddenGuildAccess(): never {
throw new ConvexError({
code: "FORBIDDEN",
message: "Verified Discord guild management access is required.",
})
}
|