feature(auth): replace clerk.com with custom auth (#103)
* feature(auth): replace clerk.com with custom auth * minor fixes * remove notification preferences * decrease live events interval fix(api): cookies.. # Conflicts: # .gitignore # apps/api/src/index.ts # apps/dashboard/src/app/providers.tsx # packages/trpc/src/trpc.ts
This commit is contained in:
committed by
Carl-Gerhard Lindesvärd
parent
f28802b1c2
commit
d31d9924a5
@@ -1,12 +1,13 @@
|
||||
import { clerkClient } from '@clerk/fastify';
|
||||
import { pathOr } from 'ramda';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { db } from '@openpanel/db';
|
||||
import { connectUserToOrganization, db } from '@openpanel/db';
|
||||
import { zInviteUser } from '@openpanel/validation';
|
||||
|
||||
import { generateSecureId } from '@openpanel/common/server/id';
|
||||
import { sendEmail } from '@openpanel/email';
|
||||
import { addDays } from 'date-fns';
|
||||
import { getOrganizationAccess } from '../access';
|
||||
import { TRPCAccessError } from '../errors';
|
||||
import { TRPCAccessError, TRPCBadRequestError } from '../errors';
|
||||
import { createTRPCRouter, protectedProcedure } from '../trpc';
|
||||
|
||||
export const organizationRouter = createTRPCRouter({
|
||||
@@ -59,68 +60,101 @@ export const organizationRouter = createTRPCRouter({
|
||||
},
|
||||
});
|
||||
|
||||
let invitationId: string | undefined;
|
||||
const alreadyMember = await db.member.findFirst({
|
||||
where: {
|
||||
userId: userExists?.id,
|
||||
organizationId: input.organizationId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userExists) {
|
||||
const ticket = await clerkClient.invitations.createInvitation({
|
||||
emailAddress: email,
|
||||
notify: true,
|
||||
});
|
||||
invitationId = ticket.id;
|
||||
if (alreadyMember && userExists) {
|
||||
throw TRPCBadRequestError(
|
||||
'User is already a member of the organization',
|
||||
);
|
||||
}
|
||||
|
||||
return db.member.create({
|
||||
const alreadyInvited = await db.invite.findFirst({
|
||||
where: {
|
||||
email,
|
||||
organizationId: input.organizationId,
|
||||
},
|
||||
});
|
||||
|
||||
if (alreadyInvited) {
|
||||
throw TRPCBadRequestError(
|
||||
'User is already invited to the organization',
|
||||
);
|
||||
}
|
||||
|
||||
const invite = await db.invite.create({
|
||||
data: {
|
||||
id: generateSecureId('invite'),
|
||||
email,
|
||||
organizationId: input.organizationId,
|
||||
role: input.role,
|
||||
invitedById: ctx.session.userId,
|
||||
meta: {
|
||||
access: input.access,
|
||||
invitationId,
|
||||
createdById: ctx.session.userId,
|
||||
projectAccess: input.access || [],
|
||||
expiresAt: addDays(new Date(), 3),
|
||||
},
|
||||
include: {
|
||||
organization: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (userExists) {
|
||||
const member = await connectUserToOrganization({
|
||||
user: userExists,
|
||||
inviteId: invite.id,
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'is_member',
|
||||
member,
|
||||
};
|
||||
}
|
||||
|
||||
await sendEmail('invite', {
|
||||
to: email,
|
||||
data: {
|
||||
url: `${process.env.NEXT_PUBLIC_DASHBOARD_URL}/onboarding?inviteId=${invite.id}`,
|
||||
organizationName: invite.organization.name,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'is_invited',
|
||||
invite,
|
||||
};
|
||||
}),
|
||||
revokeInvite: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
memberId: z.string(),
|
||||
inviteId: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const member = await db.member.findUniqueOrThrow({
|
||||
const invite = await db.invite.findUniqueOrThrow({
|
||||
where: {
|
||||
id: input.memberId,
|
||||
id: input.inviteId,
|
||||
},
|
||||
});
|
||||
|
||||
const access = await getOrganizationAccess({
|
||||
userId: ctx.session.userId,
|
||||
organizationId: member.organizationId,
|
||||
organizationId: invite.organizationId,
|
||||
});
|
||||
|
||||
if (access?.role !== 'org:admin') {
|
||||
throw TRPCAccessError('You do not have access to this project');
|
||||
}
|
||||
|
||||
const invitationId = pathOr<string | undefined>(
|
||||
undefined,
|
||||
['meta', 'invitationId'],
|
||||
member,
|
||||
);
|
||||
|
||||
if (invitationId) {
|
||||
await clerkClient.invitations
|
||||
.revokeInvitation(invitationId)
|
||||
.catch(() => {
|
||||
// Ignore errors, this will throw if the invitation is already accepted
|
||||
});
|
||||
}
|
||||
|
||||
return db.member.delete({
|
||||
return db.invite.delete({
|
||||
where: {
|
||||
id: input.memberId,
|
||||
id: input.inviteId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user