mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-22 06:00:51 -06:00
Compare commits
3 Commits
feat/custo
...
v2.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b849d325c | ||
|
|
0728a373a5 | ||
|
|
2191d62c70 |
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@formbricks/web",
|
||||
"version": "2.7.1",
|
||||
"version": "2.7.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rimraf .turbo node_modules .next",
|
||||
|
||||
Reference in New Issue
Block a user