Skip to content

Commit 4aec858

Browse files
committed
merge: resolve conflict, add hasPrivateConnections to shared catalog
2 parents e3f8271 + 66b7010 commit 4aec858

File tree

7 files changed

+54
-15
lines changed

7 files changed

+54
-15
lines changed

apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { ArrowLeftIcon } from "@heroicons/react/24/solid";
1010
import { SlackIcon } from "@trigger.dev/companyicons";
1111
import { VercelLogo } from "~/components/integrations/VercelLogo";
12+
import { useFeatureFlags } from "~/hooks/useFeatureFlags";
1213
import { useFeatures } from "~/hooks/useFeatures";
1314
import { type MatchedOrganization } from "~/hooks/useOrganizations";
1415
import { cn } from "~/utils/cn";
@@ -48,7 +49,8 @@ export function OrganizationSettingsSideMenu({
4849
organization: MatchedOrganization;
4950
buildInfo: BuildInfo;
5051
}) {
51-
const { isManagedCloud, hasPrivateConnections } = useFeatures();
52+
const { isManagedCloud } = useFeatures();
53+
const featureFlags = useFeatureFlags();
5254
const currentPlan = useCurrentPlan();
5355
const isAdmin = useHasAdminAccess();
5456
const showBuildInfo = isAdmin || !isManagedCloud;
@@ -105,7 +107,7 @@ export function OrganizationSettingsSideMenu({
105107
/>
106108
</>
107109
)}
108-
{hasPrivateConnections && (
110+
{featureFlags.hasPrivateConnections && (
109111
<SideMenuItem
110112
name="Private Connections"
111113
icon={LockClosedIcon}

apps/webapp/app/presenters/OrganizationsPresenter.server.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from "./SelectBestEnvironmentPresenter.server";
1111
import { sortEnvironments } from "~/utils/environmentSort";
1212
import { defaultAvatar, parseAvatar } from "~/components/primitives/Avatar";
13+
import { env } from "~/env.server";
1314
import { flags } from "~/v3/featureFlags.server";
1415
import { validatePartialFeatureFlags } from "~/v3/featureFlags";
1516

@@ -155,8 +156,12 @@ export class OrganizationsPresenter {
155156
},
156157
});
157158

158-
// Get global feature flags (no overrides or defaults)
159-
const globalFlags = await flags();
159+
// Get global feature flags with env-var-based defaults
160+
const globalFlags = await flags({
161+
defaultValues: {
162+
hasPrivateConnections: env.PRIVATE_CONNECTIONS_ENABLED === "1",
163+
},
164+
});
160165

161166
return orgs.map((org) => {
162167
const orgFlagsResult = org.featureFlags

apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { Header2 } from "~/components/primitives/Headers";
1313
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
1414
import { Paragraph } from "~/components/primitives/Paragraph";
1515
import { prisma } from "~/db.server";
16-
import { featuresForRequest } from "~/features.server";
16+
import { canAccessPrivateConnections } from "~/v3/canAccessPrivateConnections.server";
1717
import { logger } from "~/services/logger.server";
1818
import { getPrivateLinks } from "~/services/platform.v3.server";
1919
import { requireUserId } from "~/services/session.server";
@@ -44,8 +44,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
4444
const userId = await requireUserId(request);
4545
const { organizationSlug } = OrganizationParamsSchema.parse(params);
4646

47-
const { hasPrivateConnections } = featuresForRequest(request);
48-
if (!hasPrivateConnections) {
47+
const canAccess = await canAccessPrivateConnections({ organizationSlug, userId });
48+
if (!canAccess) {
4949
return redirect(organizationPath({ slug: organizationSlug }));
5050
}
5151

apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { Paragraph } from "~/components/primitives/Paragraph";
2424
import { Select, SelectItem } from "~/components/primitives/Select";
2525
import { prisma } from "~/db.server";
2626
import { env } from "~/env.server";
27-
import { featuresForRequest } from "~/features.server";
27+
import { canAccessPrivateConnections } from "~/v3/canAccessPrivateConnections.server";
2828
import {
2929
redirectWithErrorMessage,
3030
redirectWithSuccessMessage,
@@ -56,8 +56,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
5656
const userId = await requireUserId(request);
5757
const { organizationSlug } = OrganizationParamsSchema.parse(params);
5858

59-
const { hasPrivateConnections } = featuresForRequest(request);
60-
if (!hasPrivateConnections) {
59+
const canAccess = await canAccessPrivateConnections({ organizationSlug, userId });
60+
if (!canAccess) {
6161
return redirect(organizationPath({ slug: organizationSlug }));
6262
}
6363

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { prisma } from "~/db.server";
2+
import { env } from "~/env.server";
3+
import { FEATURE_FLAG } from "~/v3/featureFlags";
4+
import { makeFlag } from "~/v3/featureFlags.server";
5+
6+
export async function canAccessPrivateConnections(options: {
7+
organizationSlug: string;
8+
userId: string;
9+
}): Promise<boolean> {
10+
const { organizationSlug, userId } = options;
11+
12+
const org = await prisma.organization.findFirst({
13+
where: {
14+
slug: organizationSlug,
15+
members: { some: { userId } },
16+
},
17+
select: {
18+
featureFlags: true,
19+
},
20+
});
21+
22+
const flag = makeFlag();
23+
return flag({
24+
key: FEATURE_FLAG.hasPrivateConnections,
25+
defaultValue: env.PRIVATE_CONNECTIONS_ENABLED === "1",
26+
overrides: (org?.featureFlags as Record<string, unknown>) ?? {},
27+
});
28+
}

apps/webapp/app/v3/featureFlags.server.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,23 @@ export function makeFlag(_prisma: PrismaClientOrTransaction = prisma) {
3030

3131
const flagSchema = FeatureFlagCatalog[opts.key];
3232

33-
if (opts.overrides?.[opts.key]) {
33+
if (opts.overrides?.[opts.key] !== undefined) {
3434
const parsed = flagSchema.safeParse(opts.overrides[opts.key]);
3535

3636
if (parsed.success) {
3737
return parsed.data;
3838
}
3939
}
4040

41-
const parsed = flagSchema.safeParse(value?.value);
41+
if (value !== null) {
42+
const parsed = flagSchema.safeParse(value.value);
4243

43-
if (!parsed.success) {
44-
return opts.defaultValue;
44+
if (parsed.success) {
45+
return parsed.data;
46+
}
4547
}
4648

47-
return parsed.data;
49+
return opts.defaultValue;
4850
}
4951

5052
return flag;

apps/webapp/app/v3/featureFlags.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const FEATURE_FLAG = {
99
hasAiAccess: "hasAiAccess",
1010
hasAiModelsAccess: "hasAiModelsAccess",
1111
hasComputeAccess: "hasComputeAccess",
12+
hasPrivateConnections: "hasPrivateConnections",
1213
} as const;
1314

1415
export const FeatureFlagCatalog = {
@@ -20,6 +21,7 @@ export const FeatureFlagCatalog = {
2021
[FEATURE_FLAG.hasAiAccess]: z.coerce.boolean(),
2122
[FEATURE_FLAG.hasAiModelsAccess]: z.coerce.boolean(),
2223
[FEATURE_FLAG.hasComputeAccess]: z.coerce.boolean(),
24+
[FEATURE_FLAG.hasPrivateConnections]: z.coerce.boolean(),
2325
};
2426

2527
export type FeatureFlagKey = keyof typeof FeatureFlagCatalog;

0 commit comments

Comments
 (0)