Skip to content

Commit b8271bd

Browse files
committed
fix(webapp): no-op the connect action when a channel already exists
An out-of-band POST would flip a LINKED row back to PROVISIONING and re-send the Slack invite. Redirects instead, and gates both the loader and the action on the feature flag.
1 parent 00bc0a6 commit b8271bd

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.support.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useOrganization } from "~/hooks/useOrganizations";
1616
import { useShowSelfServe } from "~/hooks/useShowSelfServe";
1717
import { logger } from "~/services/logger.server";
1818
import { getCurrentPlan } from "~/services/platform.v3.server";
19+
import { isSupportChannelEnabled } from "~/services/supportChannelFlag.server";
1920
import { dashboardAction, dashboardLoader } from "~/services/routeBuilders/dashboardBuilder";
2021
import { getUserId } from "~/services/session.server";
2122
import {
@@ -59,6 +60,12 @@ export const loader = dashboardLoader(
5960
throw new Response("Not Found", { status: 404 });
6061
}
6162

63+
// Flag off means the feature does not exist yet, so 404 rather than render
64+
// an upsell for something nobody can buy.
65+
if (!(await isSupportChannelEnabled(organizationId))) {
66+
throw new Response("Not Found", { status: 404 });
67+
}
68+
6269
const supportChannel = await prisma.organizationSupportChannel.findFirst({
6370
where: { organizationId },
6471
});
@@ -89,6 +96,10 @@ export const action = dashboardAction(
8996
throw new Response("Not Found", { status: 404 });
9097
}
9198

99+
if (!(await isSupportChannelEnabled(organizationId))) {
100+
throw new Response("Not Found", { status: 404 });
101+
}
102+
92103
const formData = await request.formData();
93104
const result = ActionSchema.safeParse({ intent: formData.get("intent") });
94105
if (!result.success) {
@@ -100,6 +111,16 @@ export const action = dashboardAction(
100111
return json({ error: "Upgrade required" }, { status: 403 });
101112
}
102113

114+
// A live channel already covers this org. Without this an out-of-band POST
115+
// would flip the row back to PROVISIONING and re-send the Slack invite.
116+
const existing = await prisma.organizationSupportChannel.findFirst({
117+
where: { organizationId },
118+
select: { status: true },
119+
});
120+
if (existing?.status === "INVITED" || existing?.status === "LINKED") {
121+
return redirect(organizationSupportPath({ slug: params.organizationSlug }));
122+
}
123+
103124
try {
104125
await enqueueProvisionSupportChannel({ organizationId });
105126
} catch (error) {

apps/webapp/test/supportChannelSettings.e2e.full.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ async function seedConfirmedOrgWithAdmin(prisma: PrismaClient) {
4444
title: `Free Org ${suffix}`,
4545
slug: `free-org-${suffix}`,
4646
isActivated: true,
47+
// Per-org opt-in: the feature flag is off globally, so without this the
48+
// route 404s and the upsell branch below is never reached.
49+
featureFlags: { supportChannelEnabled: true },
4750
},
4851
});
4952
await prisma.orgMember.create({
@@ -94,6 +97,22 @@ describe("Support channel settings page", () => {
9497
expect(body).toContain("Upgrade to unlock");
9598
});
9699

100+
it("404s when the feature flag is off", async () => {
101+
const server = getTestServer();
102+
const { user, organization } = await seedConfirmedOrgWithAdmin(server.prisma);
103+
await server.prisma.organization.update({
104+
where: { id: organization.id },
105+
data: { featureFlags: { supportChannelEnabled: false } },
106+
});
107+
const cookie = await seedTestSession({ userId: user.id });
108+
109+
const res = await server.webapp.fetch(`/orgs/${organization.slug}/settings/support`, {
110+
headers: { Cookie: cookie },
111+
});
112+
113+
expect(res.status).toBe(404);
114+
});
115+
97116
it("POST intent=connect is rejected for a free org", async () => {
98117
const server = getTestServer();
99118
const { user, organization } = await seedConfirmedOrgWithAdmin(server.prisma);

0 commit comments

Comments
 (0)