Skip to content

Commit

Permalink
filter based on if user is part of org or not
Browse files Browse the repository at this point in the history
  • Loading branch information
sudhir9297 committed Jul 19, 2024
1 parent f4896e5 commit 6b68d0e
Show file tree
Hide file tree
Showing 11 changed files with 1,005 additions and 157 deletions.
6 changes: 6 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import type {
FilterApi,
FunctionReference,
} from "convex/server";
import type * as clerk from "../clerk.js";
import type * as files from "../files.js";
import type * as http from "../http.js";
import type * as users from "../users.js";

/**
* A utility for referencing Convex functions in your app's API.
Expand All @@ -25,7 +28,10 @@ import type * as files from "../files.js";
* ```
*/
declare const fullApi: ApiFromModules<{
clerk: typeof clerk;
files: typeof files;
http: typeof http;
users: typeof users;
}>;
export declare const api: FilterApi<
typeof fullApi,
Expand Down
18 changes: 18 additions & 0 deletions convex/clerk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use node'

import type { WebhookEvent } from '@clerk/clerk-sdk-node'
import { v } from 'convex/values'
import { Webhook } from 'svix'

import { internalAction } from './_generated/server'

const webhookSecret = process.env.CLERK_WEBHOOK_SECRET || ``

export const fulfill = internalAction({
args: { headers: v.any(), payload: v.string() },
handler: async (ctx, args) => {
const wh = new Webhook(webhookSecret)
const payload = wh.verify(args.payload, args.headers) as WebhookEvent
return payload
},
})
54 changes: 46 additions & 8 deletions convex/files.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
import { ConvexError, v } from 'convex/values'
import { mutation, query } from './_generated/server'
import { MutationCtx, QueryCtx, mutation, query } from './_generated/server'
import { getUser } from './users'

async function hasAccessToOrg(
ctx: QueryCtx | MutationCtx,
tokenIdentifier: string,
orgId: string,
) {
const user = await getUser(ctx, tokenIdentifier)

const hasAccess =
user.orgIds.includes(orgId) || user.tokenIdentifier.includes(orgId)

return hasAccess
}

export const createFile = mutation({
args: {
name: v.string(),
orgId: v.optional(v.string()),
orgId: v.string(),
},
async handler(ctx, args) {
console.log('server identity', await ctx.auth.getUserIdentity())
if (!(await ctx.auth.getUserIdentity())) {
throw new ConvexError(' you must login to add data')
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new ConvexError('you must be logged in to upload a file')
}

const hasAccess = await hasAccessToOrg(
ctx,
identity.tokenIdentifier,
args.orgId,
)

if (!hasAccess) {
throw new ConvexError('you do not have access to this org')
}

await ctx.db.insert('files', {
name: args.name,
orgId: args.orgId,
Expand All @@ -20,13 +46,25 @@ export const createFile = mutation({

export const getFiles = query({
args: {
orgId: v.optional(v.string()),
orgId: v.string(),
},
async handler(ctx, args) {
console.log('server identity', await ctx.auth.getUserIdentity())
if (!(await ctx.auth.getUserIdentity())) {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
return []
}

const hasAccess = await hasAccessToOrg(
ctx,
identity.tokenIdentifier,
args.orgId,
)

if (!hasAccess) {
return []
}

return ctx.db
.query('files')
.withIndex('by_orgId', (q) => q.eq('orgId', args.orgId))
Expand Down
48 changes: 48 additions & 0 deletions convex/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { httpRouter } from 'convex/server'
import { internal } from './_generated/api'
import { httpAction } from './_generated/server'

const http = httpRouter()

http.route({
path: '/clerk',
method: 'POST',
handler: httpAction(async (ctx, request) => {
const payloadString = await request.text()
const headerPayload = request.headers
try {
const result = await ctx.runAction(internal.clerk.fulfill, {
payload: payloadString,
headers: {
'svix-id': headerPayload.get('svix-id')!,
'svix-timestamp': headerPayload.get('svix-timestamp')!,
'svix-signature': headerPayload.get('svix-signature')!,
},
})

switch (result.type) {
case 'user.created':
await ctx.runMutation(internal.users.createUser, {
tokenIdentifier: `${process.env.CLERK_HOSTNAME}|${result.data.id}`,
})
break
case 'organizationMembership.created':
await ctx.runMutation(internal.users.addOrgIdToUser, {
tokenIdentifier: `${process.env.CLERK_HOSTNAME}|${result.data.public_user_data.user_id}`,
orgId: result.data.organization.id,
})
break
}

return new Response(null, {
status: 200,
})
} catch (err) {
return new Response('Webhook Error', {
status: 400,
})
}
}),
})

export default http
8 changes: 5 additions & 3 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { defineSchema, defineTable } from 'convex/server'
import { v } from 'convex/values'

export default defineSchema({
// Other tables here...

files: defineTable({ name: v.string(), orgId: v.optional(v.string()) }).index(
files: defineTable({ name: v.string(), orgId: v.string() }).index(
'by_orgId',
['orgId'],
),
users: defineTable({
tokenIdentifier: v.string(),
orgIds: v.array(v.string()),
}).index('by_tokenIdentifier', ['tokenIdentifier']),
})
43 changes: 43 additions & 0 deletions convex/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ConvexError, v } from 'convex/values'
import { MutationCtx, QueryCtx, internalMutation } from './_generated/server'

export async function getUser(
ctx: QueryCtx | MutationCtx,
tokenIdentifier: string,
) {
const user = await ctx.db
.query('users')
.withIndex('by_tokenIdentifier', (q) =>
q.eq('tokenIdentifier', tokenIdentifier),
)
.first()

if (!user) {
throw new ConvexError('expected user to be defined')
}

return user
}

export const createUser = internalMutation({
args: { tokenIdentifier: v.string() },
async handler(ctx, args) {
await ctx.db.insert('users', {
tokenIdentifier: args.tokenIdentifier,
orgIds: [],
})
},
})

export const addOrgIdToUser = internalMutation({
args: { tokenIdentifier: v.string(), orgId: v.string() },
async handler(ctx, args) {
const user = await getUser(ctx, args.tokenIdentifier)

console.log(user)

await ctx.db.patch(user._id, {
orgIds: [...user.orgIds, args.orgId],
})
},
})
Loading

0 comments on commit 6b68d0e

Please sign in to comment.