fix: add membership checks in [environmentId] route (#5020)

Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
This commit is contained in:
victorvhs017
2025-03-20 06:07:19 -03:00
committed by GitHub
parent 452709dec7
commit ed886e1794
31 changed files with 234 additions and 667 deletions

View File

@@ -2,21 +2,14 @@ import { ActionClassesTable } from "@/app/(app)/environments/[environmentId]/act
import { ActionClassDataRow } from "@/app/(app)/environments/[environmentId]/actions/components/ActionRowData";
import { ActionTableHeading } from "@/app/(app)/environments/[environmentId]/actions/components/ActionTableHeading";
import { AddActionModal } from "@/app/(app)/environments/[environmentId]/actions/components/AddActionModal";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { Metadata } from "next";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
import { getEnvironments } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
export const metadata: Metadata = {
@@ -25,51 +18,24 @@ export const metadata: Metadata = {
const Page = async (props) => {
const params = await props.params;
const session = await getServerSession(authOptions);
const { isReadOnly, project, isBilling, environment } = await getEnvironmentAuth(params.environmentId);
const t = await getTranslate();
const [actionClasses, organization, project] = await Promise.all([
getActionClasses(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
getProjectByEnvironmentId(params.environmentId),
]);
const [actionClasses] = await Promise.all([getActionClasses(params.environmentId)]);
const locale = await findMatchingLocale();
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
if (!project) {
throw new Error(t("common.project_not_found"));
}
const environments = await getEnvironments(project.id);
const currentEnvironment = environments.find((env) => env.id === params.environmentId);
if (!currentEnvironment) {
throw new Error(t("common.environment_not_found"));
}
const otherEnvironment = environments.filter((env) => env.id !== params.environmentId)[0];
const otherEnvActionClasses = await getActionClasses(otherEnvironment.id);
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember, isBilling } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, project.id);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
if (isBilling) {
return redirect(`/environments/${params.environmentId}/settings/billing`);
}
const isReadOnly = isMember && hasReadAccess;
const renderAddActionButton = () => (
<AddActionModal
environmentId={params.environmentId}
@@ -82,7 +48,7 @@ const Page = async (props) => {
<PageContentWrapper>
<PageHeader pageTitle={t("common.actions")} cta={!isReadOnly ? renderAddActionButton() : undefined} />
<ActionClassesTable
environment={currentEnvironment}
environment={environment}
otherEnvironment={otherEnvironment}
otherEnvActionClasses={otherEnvActionClasses}
environmentId={params.environmentId}

View File

@@ -1,21 +1,14 @@
import { AirtableWrapper } from "@/app/(app)/environments/[environmentId]/integrations/airtable/components/AirtableWrapper";
import { getSurveys } from "@/app/(app)/environments/[environmentId]/integrations/lib/surveys";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { getAirtableTables } from "@formbricks/lib/airtable/service";
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrations } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationItem } from "@formbricks/types/integration";
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
@@ -24,48 +17,25 @@ const Page = async (props) => {
const params = await props.params;
const t = await getTranslate();
const isEnabled = !!AIRTABLE_CLIENT_ID;
const [session, surveys, integrations, environment] = await Promise.all([
getServerSession(authOptions),
const { isReadOnly, environment } = await getEnvironmentAuth(params.environmentId);
const [surveys, integrations] = await Promise.all([
getSurveys(params.environmentId),
getIntegrations(params.environmentId),
getEnvironment(params.environmentId),
]);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error(t("common.project_not_found"));
}
const airtableIntegration: TIntegrationAirtable | undefined = integrations?.find(
(integration): integration is TIntegrationAirtable => integration.type === "airtable"
);
let airtableArray: TIntegrationItem[] = [];
if (airtableIntegration && airtableIntegration.config.key) {
if (airtableIntegration?.config.key) {
airtableArray = await getAirtableTables(params.environmentId);
}
const locale = await findMatchingLocale();
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
if (isReadOnly) {
redirect("./");
}

View File

@@ -1,13 +1,10 @@
import { GoogleSheetWrapper } from "@/app/(app)/environments/[environmentId]/integrations/google-sheets/components/GoogleSheetWrapper";
import { getSurveys } from "@/app/(app)/environments/[environmentId]/integrations/lib/surveys";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import {
GOOGLE_SHEETS_CLIENT_ID,
@@ -15,11 +12,7 @@ import {
GOOGLE_SHEETS_REDIRECT_URL,
WEBAPP_URL,
} from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrations } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationGoogleSheets } from "@formbricks/types/integration/google-sheet";
@@ -27,43 +20,20 @@ const Page = async (props) => {
const params = await props.params;
const t = await getTranslate();
const isEnabled = !!(GOOGLE_SHEETS_CLIENT_ID && GOOGLE_SHEETS_CLIENT_SECRET && GOOGLE_SHEETS_REDIRECT_URL);
const [session, surveys, integrations, environment] = await Promise.all([
getServerSession(authOptions),
const { isReadOnly, environment } = await getEnvironmentAuth(params.environmentId);
const [surveys, integrations] = await Promise.all([
getSurveys(params.environmentId),
getIntegrations(params.environmentId),
getEnvironment(params.environmentId),
]);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error(t("common.project_not_found"));
}
const googleSheetIntegration: TIntegrationGoogleSheets | undefined = integrations?.find(
(integration): integration is TIntegrationGoogleSheets => integration.type === "googleSheets"
);
const locale = await findMatchingLocale();
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
if (isReadOnly) {
redirect("./");
}

View File

@@ -1,13 +1,10 @@
import { getSurveys } from "@/app/(app)/environments/[environmentId]/integrations/lib/surveys";
import { NotionWrapper } from "@/app/(app)/environments/[environmentId]/integrations/notion/components/NotionWrapper";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import {
NOTION_AUTH_URL,
@@ -16,12 +13,8 @@ import {
NOTION_REDIRECT_URI,
WEBAPP_URL,
} from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrationByType } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getNotionDatabases } from "@formbricks/lib/notion/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationNotion, TIntegrationNotionDatabase } from "@formbricks/types/integration/notion";
@@ -34,44 +27,20 @@ const Page = async (props) => {
NOTION_AUTH_URL &&
NOTION_REDIRECT_URI
);
const [session, surveys, notionIntegration, environment] = await Promise.all([
getServerSession(authOptions),
const { isReadOnly, environment } = await getEnvironmentAuth(params.environmentId);
const [surveys, notionIntegration] = await Promise.all([
getSurveys(params.environmentId),
getIntegrationByType(params.environmentId, "notion"),
getEnvironment(params.environmentId),
]);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error(t("common.project_not_found"));
}
let databasesArray: TIntegrationNotionDatabase[] = [];
if (notionIntegration && (notionIntegration as TIntegrationNotion).config.key?.bot_id) {
databasesArray = (await getNotionDatabases(environment.id)) ?? [];
}
const locale = await findMatchingLocale();
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
if (isReadOnly) {
redirect("./");
}

View File

@@ -9,71 +9,40 @@ import notionLogo from "@/images/notion.png";
import SlackLogo from "@/images/slacklogo.png";
import WebhookLogo from "@/images/webhook.png";
import ZapierLogo from "@/images/zapier-small.png";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { Card } from "@/modules/ui/components/integration-card";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import Image from "next/image";
import { redirect } from "next/navigation";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrations } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { TIntegrationType } from "@formbricks/types/integration";
const Page = async (props) => {
const params = await props.params;
const environmentId = params.environmentId;
const t = await getTranslate();
const { isReadOnly, environment, isBilling } = await getEnvironmentAuth(params.environmentId);
const [
environment,
integrations,
organization,
session,
userWebhookCount,
zapierWebhookCount,
makeWebhookCount,
n8nwebhookCount,
activePiecesWebhookCount,
] = await Promise.all([
getEnvironment(environmentId),
getIntegrations(environmentId),
getOrganizationByEnvironmentId(params.environmentId),
getServerSession(authOptions),
getWebhookCountBySource(environmentId, "user"),
getWebhookCountBySource(environmentId, "zapier"),
getWebhookCountBySource(environmentId, "make"),
getWebhookCountBySource(environmentId, "n8n"),
getWebhookCountBySource(environmentId, "activepieces"),
getIntegrations(params.environmentId),
getWebhookCountBySource(params.environmentId, "user"),
getWebhookCountBySource(params.environmentId, "zapier"),
getWebhookCountBySource(params.environmentId, "make"),
getWebhookCountBySource(params.environmentId, "n8n"),
getWebhookCountBySource(params.environmentId, "activepieces"),
]);
const isIntegrationConnected = (type: TIntegrationType) =>
integrations.some((integration) => integration.type === type);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember, isBilling } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
if (isBilling) {
return redirect(`/environments/${params.environmentId}/settings/billing`);
@@ -244,7 +213,7 @@ const Page = async (props) => {
docsHref: "https://formbricks.com/docs/app-surveys/quickstart",
docsText: t("common.docs"),
docsNewTab: true,
connectHref: `/environments/${environmentId}/project/app-connection`,
connectHref: `/environments/${params.environmentId}/project/app-connection`,
connectText: t("common.connect"),
connectNewTab: false,
label: "Javascript SDK",

View File

@@ -1,20 +1,13 @@
import { getSurveys } from "@/app/(app)/environments/[environmentId]/integrations/lib/surveys";
import { SlackWrapper } from "@/app/(app)/environments/[environmentId]/integrations/slack/components/SlackWrapper";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getIntegrationByType } from "@formbricks/lib/integration/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { TIntegrationSlack } from "@formbricks/types/integration/slack";
@@ -23,40 +16,16 @@ const Page = async (props) => {
const isEnabled = !!(SLACK_CLIENT_ID && SLACK_CLIENT_SECRET);
const t = await getTranslate();
const [session, surveys, slackIntegration, environment] = await Promise.all([
getServerSession(authOptions),
const { isReadOnly, environment } = await getEnvironmentAuth(params.environmentId);
const [surveys, slackIntegration] = await Promise.all([
getSurveys(params.environmentId),
getIntegrationByType(params.environmentId, "slack"),
getEnvironment(params.environmentId),
]);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error(t("common.project_not_found"));
}
const locale = await findMatchingLocale();
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
project.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
if (isReadOnly) {
redirect("./");
}

View File

@@ -4,7 +4,7 @@ import { authOptions } from "@/modules/auth/lib/authOptions";
import { ToasterClient } from "@/modules/ui/components/toaster-client";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { notFound, redirect } from "next/navigation";
import { redirect } from "next/navigation";
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
@@ -49,7 +49,10 @@ const EnvLayout = async (props: {
}
const membership = await getMembershipByUserIdOrganizationId(session.user.id, organization.id);
if (!membership) return notFound();
if (!membership) {
throw new Error(t("common.membership_not_found"));
}
return (
<>

View File

@@ -157,6 +157,10 @@ const Page = async (props) => {
throw new Error(t("common.user_not_found"));
}
if (!memberships) {
throw new Error(t("common.membership_not_found"));
}
if (user?.notificationSettings) {
user.notificationSettings = setCompleteNotificationSettings(user.notificationSettings, memberships);
}

View File

@@ -1,18 +1,14 @@
import { AccountSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(account)/components/AccountSettingsNavbar";
import { AccountSecurity } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/AccountSecurity";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getIsMultiOrgEnabled, getIsTwoFactorAuthEnabled } from "@/modules/ee/license-check/lib/utils";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { SettingsId } from "@/modules/ui/components/settings-id";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import {
getOrganizationByEnvironmentId,
getOrganizationsWhereUserIsSingleOwner,
} from "@formbricks/lib/organization/service";
import { getOrganizationsWhereUserIsSingleOwner } from "@formbricks/lib/organization/service";
import { getUser } from "@formbricks/lib/user/service";
import { SettingsCard } from "../../components/SettingsCard";
import { DeleteAccount } from "./components/DeleteAccount";
@@ -25,20 +21,16 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
const t = await getTranslate();
const { environmentId } = params;
const session = await getServerSession(authOptions);
if (!session) {
throw new Error(t("common.session_not_found"));
}
const organization = await getOrganizationByEnvironmentId(environmentId);
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const { session } = await getEnvironmentAuth(params.environmentId);
const organizationsWithSingleOwner = await getOrganizationsWhereUserIsSingleOwner(session.user.id);
const user = session && session.user ? await getUser(session.user.id) : null;
const user = session?.user ? await getUser(session.user.id) : null;
if (!user) {
throw new Error(t("common.user_not_found"));
}
return (
<PageContentWrapper>

View File

@@ -1,18 +1,14 @@
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/utils";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { Button } from "@/modules/ui/components/button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { CheckIcon } from "lucide-react";
import { getServerSession } from "next-auth";
import Link from "next/link";
import { notFound } from "next/navigation";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
const Page = async (props) => {
const params = await props.params;
@@ -21,20 +17,8 @@ const Page = async (props) => {
notFound();
}
const session = await getServerSession(authOptions);
const { isMember, currentUserMembership } = await getEnvironmentAuth(params.environmentId);
const organization = await getOrganizationByEnvironmentId(params.environmentId);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const isPricingDisabled = isMember;
if (isPricingDisabled) {

View File

@@ -1,18 +1,14 @@
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { notFound } from "next/navigation";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { PROJECT_FEATURE_KEYS, STRIPE_PRICE_LOOKUP_KEYS } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import {
getMonthlyActiveOrganizationPeopleCount,
getMonthlyOrganizationResponseCount,
getOrganizationByEnvironmentId,
} from "@formbricks/lib/organization/service";
import { getOrganizationProjectsCount } from "@formbricks/lib/project/service";
import { PricingTable } from "./components/pricing-table";
@@ -20,29 +16,19 @@ import { PricingTable } from "./components/pricing-table";
export const PricingPage = async (props) => {
const params = await props.params;
const t = await getTranslate();
const organization = await getOrganizationByEnvironmentId(params.environmentId);
const { organization, isMember, currentUserMembership } = await getEnvironmentAuth(params.environmentId);
if (!IS_FORMBRICKS_CLOUD) {
notFound();
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const session = await getServerSession(authOptions);
if (!session) {
throw new Error(t("common.not_authorized"));
}
const [peopleCount, responseCount, projectCount] = await Promise.all([
getMonthlyActiveOrganizationPeopleCount(organization.id),
getMonthlyOrganizationResponseCount(organization.id),
getOrganizationProjectsCount(organization.id),
]);
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const hasBillingRights = !isMember;
return (

View File

@@ -1,20 +1,12 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { AttributesSection } from "@/modules/ee/contacts/[contactId]/components/attributes-section";
import { DeleteContactButton } from "@/modules/ee/contacts/[contactId]/components/delete-contact-button";
import { getContactAttributes } from "@/modules/ee/contacts/lib/contact-attributes";
import { getContact } from "@/modules/ee/contacts/lib/contacts";
import { getContactIdentifier } from "@/modules/ee/contacts/lib/utils";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
import { ResponseSection } from "./components/response-section";
@@ -23,45 +15,19 @@ export const SingleContactPage = async (props: {
}) => {
const params = await props.params;
const t = await getTranslate();
const [environment, environmentTags, project, session, organization, contact, contactAttributes] =
await Promise.all([
getEnvironment(params.environmentId),
getTagsByEnvironmentId(params.environmentId),
getProjectByEnvironmentId(params.environmentId),
getServerSession(authOptions),
getOrganizationByEnvironmentId(params.environmentId),
getContact(params.contactId),
getContactAttributes(params.contactId),
]);
if (!project) {
throw new Error(t("common.project_not_found"));
}
const { environment, isReadOnly } = await getEnvironmentAuth(params.environmentId);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const [environmentTags, contact, contactAttributes] = await Promise.all([
getTagsByEnvironmentId(params.environmentId),
getContact(params.contactId),
getContactAttributes(params.contactId),
]);
if (!contact) {
throw new Error(t("environments.contacts.contact_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
const getDeletePersonButton = () => {
return (
<DeleteContactButton

View File

@@ -1,21 +1,14 @@
import { contactCache } from "@/lib/cache/contact";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { UploadContactsCSVButton } from "@/modules/ee/contacts/components/upload-contacts-button";
import { getContactAttributeKeys } from "@/modules/ee/contacts/lib/contact-attribute-keys";
import { getContacts } from "@/modules/ee/contacts/lib/contacts";
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { IS_FORMBRICKS_CLOUD, ITEMS_PER_PAGE } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { ContactDataView } from "./components/contact-data-view";
import { ContactsSecondaryNavigation } from "./components/contacts-secondary-navigation";
@@ -24,39 +17,14 @@ export const ContactsPage = async ({
}: {
params: Promise<{ environmentId: string }>;
}) => {
const t = await getTranslate();
const params = await paramsProps;
const session = await getServerSession(authOptions);
if (!session) {
throw new Error("Session not found");
}
const { environment, isReadOnly } = await getEnvironmentAuth(params.environmentId);
const t = await getTranslate();
const isContactsEnabled = await getIsContactsEnabled();
const [environment, product] = await Promise.all([
getEnvironment(params.environmentId),
getProjectByEnvironmentId(params.environmentId),
]);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!product) {
throw new Error(t("common.product_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
product.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const productPermission = await getProjectPermissionByUserId(session.user.id, product.id);
const { hasReadAccess } = getTeamPermissionFlags(productPermission);
const isReadOnly = isMember && hasReadAccess;
const contactAttributeKeys = await getContactAttributeKeys(params.environmentId);
const initialContacts = await getContacts(params.environmentId, 0);

View File

@@ -1,22 +1,14 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { ContactsSecondaryNavigation } from "@/modules/ee/contacts/components/contacts-secondary-navigation";
import { getContactAttributeKeys } from "@/modules/ee/contacts/lib/contact-attribute-keys";
import { SegmentTable } from "@/modules/ee/contacts/segments/components/segment-table";
import { getSegments } from "@/modules/ee/contacts/segments/lib/segments";
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { CreateSegmentModal } from "./components/create-segment-modal";
export const SegmentsPage = async ({
@@ -26,42 +18,14 @@ export const SegmentsPage = async ({
}) => {
const params = await paramsProps;
const t = await getTranslate();
const [session, environment, product, segments, contactAttributeKeys, organization] = await Promise.all([
getServerSession(authOptions),
getEnvironment(params.environmentId),
getProjectByEnvironmentId(params.environmentId),
const { isReadOnly } = await getEnvironmentAuth(params.environmentId);
const [segments, contactAttributeKeys] = await Promise.all([
getSegments(params.environmentId),
getContactAttributeKeys(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
]);
if (!session) {
throw new Error("Session not found");
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
if (!product) {
throw new Error(t("common.product_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(
session?.user.id,
product.organizationId
);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const productPermission = await getProjectPermissionByUserId(session.user.id, product.id);
const { hasReadAccess } = getTeamPermissionFlags(productPermission);
const isReadOnly = isMember && hasReadAccess;
const isContactsEnabled = await getIsContactsEnabled();
if (!segments) {

View File

@@ -1,58 +1,28 @@
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getMultiLanguagePermission } from "@/modules/ee/license-check/lib/utils";
import { EditLanguage } from "@/modules/ee/multi-language-surveys/components/edit-language";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganization } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getUser } from "@formbricks/lib/user/service";
export const LanguagesPage = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
const t = await getTranslate();
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error(t("common.project_not_found"));
}
const organization = await getOrganization(project?.organizationId);
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const { organization, session, project, isReadOnly } = await getEnvironmentAuth(params.environmentId);
const isMultiLanguageAllowed = await getMultiLanguagePermission(organization.billing.plan);
const session = await getServerSession(authOptions);
if (!session) {
throw new Error("Session not found");
}
const user = await getUser(session.user.id);
if (!user) {
throw new Error("User not found");
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && !hasManageAccess;
return (
<PageContentWrapper>
<PageHeader pageTitle={t("common.project_configuration")}>

View File

@@ -1,37 +1,16 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { AccessView } from "@/modules/ee/teams/project-teams/components/access-view";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getTeamsByProjectId } from "./lib/team";
export const ProjectTeams = async (props: { params: Promise<{ environmentId: string }> }) => {
const t = await getTranslate();
const params = await props.params;
const [project, session, organization] = await Promise.all([
getProjectByEnvironmentId(params.environmentId),
getServerSession(authOptions),
getOrganizationByEnvironmentId(params.environmentId),
]);
if (!project) {
throw new Error(t("common.project_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isOwner, isManager } = getAccessFlags(currentUserMembership?.role);
const { project, isOwner, isManager } = await getEnvironmentAuth(params.environmentId);
const teams = await getTeamsByProjectId(project.id);

View File

@@ -0,0 +1,76 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { cache } from "react";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { TEnvironmentAuth } from "../types/environment-auth";
/**
* Common utility to fetch environment data and perform authorization checks
*
* Usage:
* const { environment, project, isReadOnly } = await getEnvironmentAuth(params.environmentId);
*/
export const getEnvironmentAuth = cache(async (environmentId: string): Promise<TEnvironmentAuth> => {
const t = await getTranslate();
// Perform all fetches in parallel
const [environment, project, session, organization] = await Promise.all([
getEnvironment(environmentId),
getProjectByEnvironmentId(environmentId),
getServerSession(authOptions),
getOrganizationByEnvironmentId(environmentId),
]);
if (!project) {
throw new Error(t("common.project_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
if (!currentUserMembership) {
throw new Error(t("common.membership_not_found"));
}
const { isMember, isOwner, isManager, isBilling } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasReadAccess, hasReadWriteAccess, hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
return {
environment,
project,
organization,
session,
currentUserMembership,
projectPermission,
isMember,
isOwner,
isManager,
isBilling,
hasReadAccess,
hasReadWriteAccess,
hasManageAccess,
isReadOnly,
};
});

View File

@@ -0,0 +1,29 @@
import { ZTeamPermission } from "@/modules/ee/teams/project-teams/types/team";
import { z } from "zod";
import { ZEnvironment } from "@formbricks/types/environment";
import { ZMembership } from "@formbricks/types/memberships";
import { ZOrganization } from "@formbricks/types/organizations";
import { ZProject } from "@formbricks/types/project";
import { ZUser } from "@formbricks/types/user";
export const ZEnvironmentAuth = z.object({
environment: ZEnvironment,
project: ZProject,
organization: ZOrganization,
session: z.object({
user: ZUser.pick({ id: true }),
expires: z.string(),
}),
currentUserMembership: ZMembership,
projectPermission: ZTeamPermission.nullable(),
isMember: z.boolean(),
isOwner: z.boolean(),
isManager: z.boolean(),
isBilling: z.boolean(),
hasReadAccess: z.boolean(),
hasReadWriteAccess: z.boolean(),
hasManageAccess: z.boolean(),
isReadOnly: z.boolean(),
});
export type TEnvironmentAuth = z.infer<typeof ZEnvironmentAuth>;

View File

@@ -1,6 +1,4 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { AddWebhookButton } from "@/modules/integrations/webhooks/components/add-webhook-button";
import { WebhookRowData } from "@/modules/integrations/webhooks/components/webhook-row-data";
import { WebhookTable } from "@/modules/integrations/webhooks/components/webhook-table";
@@ -10,46 +8,20 @@ import { GoBackButton } from "@/modules/ui/components/go-back-button";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getSurveys } from "@formbricks/lib/survey/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
export const WebhooksPage = async (props) => {
const params = await props.params;
const t = await getTranslate();
const [session, organization, webhooks, surveys, environment] = await Promise.all([
getServerSession(authOptions),
getOrganizationByEnvironmentId(params.environmentId),
const { isReadOnly, environment } = await getEnvironmentAuth(params.environmentId);
const [webhooks, surveys] = await Promise.all([
getWebhooks(params.environmentId),
getSurveys(params.environmentId, 200), // HOTFIX: not getting all surveys for now since it's maxing out the prisma accelerate limit
getEnvironment(params.environmentId),
]);
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session?.user.id, environment?.projectId);
const { hasReadAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && hasReadAccess;
const renderAddWebhookButton = () => <AddWebhookButton environment={environment} surveys={surveys} />;
const locale = await findMatchingLocale();

View File

@@ -1,5 +1,6 @@
import { WidgetStatusIndicator } from "@/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator";
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { EnvironmentIdField } from "@/modules/projects/settings/(setup)/components/environment-id-field";
import { SetupInstructions } from "@/modules/projects/settings/(setup)/components/setup-instructions";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
@@ -8,24 +9,12 @@ import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { WEBAPP_URL } from "@formbricks/lib/constants";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
export const AppConnectionPage = async (props) => {
const params = await props.params;
const t = await getTranslate();
const [environment, organization] = await Promise.all([
getEnvironment(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
]);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const { environment } = await getEnvironmentAuth(params.environmentId);
return (
<PageContentWrapper>

View File

@@ -1,54 +1,22 @@
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { EnvironmentNotice } from "@/modules/ui/components/environment-notice";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { findMatchingLocale } from "@formbricks/lib/utils/locale";
import { ApiKeyList } from "./components/api-key-list";
export const APIKeysPage = async (props) => {
const params = await props.params;
const t = await getTranslate();
const [session, environment, organization, project] = await Promise.all([
getServerSession(authOptions),
getEnvironment(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
getProjectByEnvironmentId(params.environmentId),
]);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
// Use the new utility to get all required data with authorization checks
const { environment, isReadOnly } = await getEnvironmentAuth(params.environmentId);
const locale = await findMatchingLocale();
if (!project) {
throw new Error(t("common.project_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && !hasManageAccess;
return (
<PageContentWrapper>
<PageHeader pageTitle={t("common.project_configuration")}>

View File

@@ -1,19 +1,13 @@
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { SettingsId } from "@/modules/ui/components/settings-id";
import packageJson from "@/package.json";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId, getProjects } from "@formbricks/lib/project/service";
import { getProjects } from "@formbricks/lib/project/service";
import { DeleteProject } from "./components/delete-project";
import { EditProjectNameForm } from "./components/edit-project-name-form";
import { EditWaitingTimeForm } from "./components/edit-waiting-time-form";
@@ -21,32 +15,13 @@ import { EditWaitingTimeForm } from "./components/edit-waiting-time-form";
export const GeneralSettingsPage = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
const t = await getTranslate();
const [project, session, organization] = await Promise.all([
getProjectByEnvironmentId(params.environmentId),
getServerSession(authOptions),
getOrganizationByEnvironmentId(params.environmentId),
]);
if (!project) {
throw new Error(t("common.project_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
const { isReadOnly, isOwner, isManager, project, organization } = await getEnvironmentAuth(
params.environmentId
);
const organizationProjects = await getProjects(organization.id);
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { isMember, isOwner, isManager } = getAccessFlags(currentUserMembership?.role);
const { hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && !hasManageAccess;
const isOwnerOrManager = isOwner || isManager;
return (

View File

@@ -1,12 +1,6 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getTranslate } from "@/tolgee/server";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { Metadata } from "next";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
export const metadata: Metadata = {
title: "Configuration",
@@ -14,35 +8,20 @@ export const metadata: Metadata = {
export const ProjectSettingsLayout = async (props) => {
const params = await props.params;
const { children } = props;
const t = await getTranslate();
try {
// Use the new utility to get all required data with authorization checks
const { isBilling } = await getEnvironmentAuth(params.environmentId);
const [organization, session] = await Promise.all([
getOrganizationByEnvironmentId(params.environmentId),
getServerSession(authOptions),
]);
// Redirect billing users
if (isBilling) {
return redirect(`/environments/${params.environmentId}/settings/billing`);
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
return children;
} catch (error) {
// The error boundary will catch this
throw error;
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session.user.id, organization.id);
const { isBilling } = getAccessFlags(currentUserMembership?.role);
if (isBilling) {
return redirect(`/environments/${params.environmentId}/settings/billing`);
}
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error("Project not found");
}
return children;
};

View File

@@ -1,52 +1,32 @@
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getWhiteLabelPermission } from "@/modules/ee/license-check/lib/utils";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { BrandingSettingsCard } from "@/modules/ee/whitelabel/remove-branding/components/branding-settings-card";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { EditLogo } from "@/modules/projects/settings/look/components/edit-logo";
import { getProjectByEnvironmentId } from "@/modules/projects/settings/look/lib/project";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { cn } from "@formbricks/lib/cn";
import { SURVEY_BG_COLORS, UNSPLASH_ACCESS_KEY } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { EditPlacementForm } from "./components/edit-placement-form";
import { ThemeStyling } from "./components/theme-styling";
export const ProjectLookSettingsPage = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
const t = await getTranslate();
const [session, organization, project] = await Promise.all([
getServerSession(authOptions),
getOrganizationByEnvironmentId(params.environmentId),
getProjectByEnvironmentId(params.environmentId),
]);
const { isReadOnly, organization } = await getEnvironmentAuth(params.environmentId);
const project = await getProjectByEnvironmentId(params.environmentId);
if (!project) {
throw new Error(t("common.project_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
throw new Error("Project not found");
}
const canRemoveBranding = await getWhiteLabelPermission(organization.billing.plan);
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && !hasManageAccess;
return (
<PageContentWrapper>
<PageHeader pageTitle={t("common.project_configuration")}>
@@ -60,7 +40,7 @@ export const ProjectLookSettingsPage = async (props: { params: Promise<{ environ
environmentId={params.environmentId}
project={project}
colors={SURVEY_BG_COLORS}
isUnsplashConfigured={UNSPLASH_ACCESS_KEY ? true : false}
isUnsplashConfigured={!!UNSPLASH_ACCESS_KEY}
isReadOnly={isReadOnly}
/>
</SettingsCard>

View File

@@ -1,17 +1,9 @@
import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ProjectConfigNavigation } from "@/modules/projects/settings/components/project-config-navigation";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service";
import { getProjectByEnvironmentId } from "@formbricks/lib/project/service";
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
import { getTagsOnResponsesCount } from "@formbricks/lib/tagOnResponse/service";
import { EditTagsWrapper } from "./components/edit-tags-wrapper";
@@ -19,42 +11,14 @@ import { EditTagsWrapper } from "./components/edit-tags-wrapper";
export const TagsPage = async (props) => {
const params = await props.params;
const t = await getTranslate();
const environment = await getEnvironment(params.environmentId);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
const [tags, environmentTagsCount, organization, session, project] = await Promise.all([
const { isReadOnly, environment } = await getEnvironmentAuth(params.environmentId);
const [tags, environmentTagsCount] = await Promise.all([
getTagsByEnvironmentId(params.environmentId),
getTagsOnResponsesCount(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),
getServerSession(authOptions),
getProjectByEnvironmentId(params.environmentId),
]);
if (!environment) {
throw new Error(t("common.environment_not_found"));
}
if (!organization) {
throw new Error(t("common.organization_not_found"));
}
if (!session) {
throw new Error(t("common.session_not_found"));
}
if (!project) {
throw new Error(t("common.project_not_found"));
}
const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id);
const { isMember } = getAccessFlags(currentUserMembership?.role);
const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id);
const { hasManageAccess } = getTeamPermissionFlags(projectPermission);
const isReadOnly = isMember && !hasManageAccess;
return (
<PageContentWrapper>
<PageHeader pageTitle={t("common.project_configuration")}>

View File

@@ -237,6 +237,7 @@
"maximum": "Maximal",
"member": "Mitglied",
"members": "Mitglieder",
"membership_not_found": "Mitgliedschaft nicht gefunden",
"metadata": "Metadaten",
"minimum": "Minimum",
"mobile_overlay_text": "Formbricks ist für Geräte mit kleineren Auflösungen nicht verfügbar.",

View File

@@ -237,6 +237,7 @@
"maximum": "Maximum",
"member": "Member",
"members": "Members",
"membership_not_found": "Membership not found",
"metadata": "Metadata",
"minimum": "Minimum",
"mobile_overlay_text": "Formbricks is not available for devices with smaller resolutions.",

View File

@@ -237,6 +237,7 @@
"maximum": "Max",
"member": "Membre",
"members": "Membres",
"membership_not_found": "Abonnement non trouvé",
"metadata": "Métadonnées",
"minimum": "Min",
"mobile_overlay_text": "Formbricks n'est pas disponible pour les appareils avec des résolutions plus petites.",

View File

@@ -237,6 +237,7 @@
"maximum": "Máximo",
"member": "Membros",
"members": "Membros",
"membership_not_found": "Assinatura não encontrada",
"metadata": "metadados",
"minimum": "Mínimo",
"mobile_overlay_text": "O Formbricks não está disponível para dispositivos com resoluções menores.",

View File

@@ -237,6 +237,7 @@
"maximum": "Máximo",
"member": "Membro",
"members": "Membros",
"membership_not_found": "Associação não encontrada",
"metadata": "Metadados",
"minimum": "Mínimo",
"mobile_overlay_text": "O Formbricks não está disponível para dispositivos com resoluções menores.",

View File

@@ -237,6 +237,7 @@
"maximum": "最大值",
"member": "成員",
"members": "成員",
"membership_not_found": "找不到成員資格",
"metadata": "元數據",
"minimum": "最小值",
"mobile_overlay_text": "Formbricks 不適用於較小解析度的裝置。",