Compare commits

...

3 Commits

Author SHA1 Message Date
Matthias Nannt
7b849d325c chore: update formbricks version in package.json 2024-12-04 15:30:15 +01:00
Piyush Gupta
0728a373a5 fix: invite flow 2024-12-02 19:33:12 +05:30
Piyush Gupta
2191d62c70 fix: users delete fix 2024-12-02 18:15:40 +05:30
5 changed files with 73 additions and 33 deletions

View File

@@ -3,6 +3,7 @@
import {
deleteMembership,
getMembershipsByUserId,
getOrganizationOwnerCount,
} from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/lib/membership";
import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware";
@@ -119,9 +120,9 @@ export const deleteMembershipAction = authenticatedActionClient
throw new AuthenticationError("Not a member of this organization");
}
const memberships = await getMembershipsByUserId(ctx.user.id);
const isLastOwner = memberships?.filter((m) => m.role === "owner").length === 1;
if (membership.role === "owner" && isLastOwner) {
const ownerCount = await getOrganizationOwnerCount(parsedInput.organizationId);
if (ownerCount <= 1) {
throw new ValidationError("You cannot delete the last owner of the organization");
}

View File

@@ -12,6 +12,36 @@ import { ZOptionalNumber, ZString } from "@formbricks/types/common";
import { DatabaseError, UnknownError } from "@formbricks/types/errors";
import { TMember, TMembership } from "@formbricks/types/memberships";
export const getOrganizationOwnerCount = reactCache(
async (organizationId: string): Promise<number> =>
cache(
async () => {
validateInputs([organizationId, ZString]);
try {
const ownersCount = await prisma.membership.count({
where: {
organizationId,
role: "owner",
},
});
return ownersCount;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
}
throw error;
}
},
[`getOrganizationOwnerCount-${organizationId}`],
{
tags: [membershipCache.tag.byOrganizationId(organizationId)],
}
)()
);
export const getMembersByOrganizationId = reactCache(
async (organizationId: string, page?: number): Promise<TMember[]> =>
cache(

View File

@@ -1,6 +1,7 @@
import { sendInviteAcceptedEmail } from "@/modules/email";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { unstable_after as after } from "next/server";
import { authOptions } from "@formbricks/lib/authOptions";
import { DEFAULT_LOCALE, WEBAPP_URL } from "@formbricks/lib/constants";
import { deleteInvite, getInvite } from "@formbricks/lib/invite/service";
@@ -32,6 +33,37 @@ const Page = async (props) => {
const isInviteExpired = new Date(invite.expiresAt) < new Date();
const createMembershipAction = async () => {
"use server";
if (!session || !user) return;
await createMembership(invite.organizationId, session.user.id, {
accepted: true,
role: invite.role,
});
await deleteInvite(inviteId);
await sendInviteAcceptedEmail(
invite.creator.name ?? "",
user?.name ?? "",
invite.creator.email,
user?.locale ?? DEFAULT_LOCALE
);
await updateUser(session.user.id, {
notificationSettings: {
...user.notificationSettings,
alert: user.notificationSettings.alert ?? {},
weeklySummary: user.notificationSettings.weeklySummary ?? {},
unsubscribedOrganizationIds: Array.from(
new Set([
...(user.notificationSettings?.unsubscribedOrganizationIds || []),
invite.organizationId,
])
),
},
});
};
if (isInviteExpired) {
return (
<ContentLayout
@@ -68,30 +100,8 @@ const Page = async (props) => {
</ContentLayout>
);
} else {
await createMembership(invite.organizationId, session.user.id, {
accepted: true,
role: invite.role,
});
await deleteInvite(inviteId);
await sendInviteAcceptedEmail(
invite.creator.name ?? "",
user?.name ?? "",
invite.creator.email,
user?.locale ?? DEFAULT_LOCALE
);
await updateUser(session.user.id, {
notificationSettings: {
...user.notificationSettings,
alert: user.notificationSettings.alert ?? {},
weeklySummary: user.notificationSettings.weeklySummary ?? {},
unsubscribedOrganizationIds: Array.from(
new Set([
...(user.notificationSettings?.unsubscribedOrganizationIds || []),
invite.organizationId,
])
),
},
after(async () => {
await createMembershipAction();
});
return (

View File

@@ -20,12 +20,11 @@ const nextConfig = {
assetPrefix: process.env.ASSET_PREFIX_URL || undefined,
output: "standalone",
poweredByHeader: false,
outputFileTracingIncludes: {
"app/api/packages": ["../../packages/js-core/dist/*", "../../packages/surveys/dist/*"],
},
experimental: {
serverComponentsExternalPackages: ["@aws-sdk"],
instrumentationHook: true,
outputFileTracingIncludes: {
"app/api/packages": ["../../packages/js-core/dist/*", "../../packages/surveys/dist/*"],
},
after: true,
},
transpilePackages: ["@formbricks/database", "@formbricks/ee", "@formbricks/ui", "@formbricks/lib"],
images: {

View File

@@ -1,6 +1,6 @@
{
"name": "@formbricks/web",
"version": "2.7.1",
"version": "2.7.2",
"private": true,
"scripts": {
"clean": "rimraf .turbo node_modules .next",