diff --git a/apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts b/apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts
index 5bf83bf493..b7f665ff10 100644
--- a/apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts
+++ b/apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts
@@ -1,10 +1,11 @@
import {
- AuthenticatableIntegration,
+ type AuthenticatableIntegration,
OrgIntegrationRepository,
} from "~/models/orgIntegration.server";
-import { logger } from "~/services/logger.server";
import { BasePresenter } from "./basePresenter.server";
-import { WebClient } from "@slack/web-api";
+import { type WebClient } from "@slack/web-api";
+import { tryCatch } from "@trigger.dev/core";
+import { logger } from "~/services/logger.server";
export class NewAlertChannelPresenter extends BasePresenter {
public async call(projectId: string) {
@@ -30,42 +31,66 @@ export class NewAlertChannelPresenter extends BasePresenter {
// If there is a slack integration, then we need to get a list of Slack Channels
if (slackIntegration) {
- const channels = await getSlackChannelsForToken(slackIntegration);
+ const [error, channels] = await tryCatch(getSlackChannelsForToken(slackIntegration));
+
+ if (error) {
+ if (isSlackError(error) && error.data.error === "token_revoked") {
+ return {
+ slack: {
+ status: "TOKEN_REVOKED" as const,
+ },
+ };
+ }
+
+ if (isSlackError(error) && error.data.error === "token_expired") {
+ return {
+ slack: {
+ status: "TOKEN_EXPIRED" as const,
+ },
+ };
+ }
+
+ logger.error("Failed fetching Slack channels for creating alerts", {
+ error,
+ slackIntegrationId: slackIntegration.id,
+ });
+
+ return {
+ slack: {
+ status: "FAILED_FETCHING_CHANNELS" as const,
+ },
+ };
+ }
return {
slack: {
status: "READY" as const,
- channels,
+ channels: channels ?? [],
integrationId: slackIntegration.id,
},
};
- } else {
- if (OrgIntegrationRepository.isSlackSupported) {
- return {
- slack: {
- status: "NOT_CONFIGURED" as const,
- },
- };
- } else {
- return {
- slack: {
- status: "NOT_AVAILABLE" as const,
- },
- };
- }
}
+
+ if (OrgIntegrationRepository.isSlackSupported) {
+ return {
+ slack: {
+ status: "NOT_CONFIGURED" as const,
+ },
+ };
+ }
+
+ return {
+ slack: {
+ status: "NOT_AVAILABLE" as const,
+ },
+ };
}
}
async function getSlackChannelsForToken(integration: AuthenticatableIntegration) {
const client = await OrgIntegrationRepository.getAuthenticatedClientForIntegration(integration);
-
const channels = await getAllSlackConversations(client);
- logger.debug("Received a list of slack conversations", {
- channels,
- });
-
return (channels ?? [])
.filter((channel) => !channel.is_archived)
.filter((channel) => channel.is_channel)
@@ -100,3 +125,15 @@ async function getAllSlackConversations(client: WebClient) {
return channels;
}
+
+function isSlackError(obj: unknown): obj is { data: { error: string } } {
+ return Boolean(
+ typeof obj === "object" &&
+ obj !== null &&
+ "data" in obj &&
+ typeof obj.data === "object" &&
+ obj.data !== null &&
+ "error" in obj.data &&
+ typeof obj.data.error === "string"
+ );
+}
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts
index 99dc07f12a..6800ab2ed8 100644
--- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts
+++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts
@@ -1,13 +1,11 @@
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { prisma } from "~/db.server";
-import { env } from "~/env.server";
import { redirectWithSuccessMessage } from "~/models/message.server";
import { OrgIntegrationRepository } from "~/models/orgIntegration.server";
import { findProjectBySlug } from "~/models/project.server";
import { requireUserId } from "~/services/session.server";
import {
EnvironmentParamSchema,
- ProjectParamSchema,
v3NewProjectAlertPath,
v3NewProjectAlertPathConnectToSlackPath,
} from "~/utils/pathBuilder";
@@ -16,6 +14,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
const userId = await requireUserId(request);
const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);
+ const url = new URL(request.url);
+ const shouldReinstall = url.searchParams.get("reinstall") === "true";
+
const project = await findProjectBySlug(organizationSlug, projectParam, userId);
if (!project) {
@@ -30,7 +31,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
},
});
- if (integration) {
+ // If integration exists and we're not reinstalling, redirect back to alerts
+ if (integration && !shouldReinstall) {
return redirectWithSuccessMessage(
`${v3NewProjectAlertPath({ slug: organizationSlug }, project, {
slug: envParam,
@@ -38,15 +40,15 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
request,
"Successfully connected your Slack workspace"
);
- } else {
- // Redirect to Slack
- return await OrgIntegrationRepository.redirectToAuthService(
- "SLACK",
- project.organizationId,
- request,
- v3NewProjectAlertPathConnectToSlackPath({ slug: organizationSlug }, project, {
- slug: envParam,
- })
- );
}
+
+ // Redirect to Slack for new installation or reinstallation
+ return await OrgIntegrationRepository.redirectToAuthService(
+ "SLACK",
+ project.organizationId,
+ request,
+ v3NewProjectAlertPathConnectToSlackPath({ slug: organizationSlug }, project, {
+ slug: envParam,
+ })
+ );
}
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
index 526798cd78..18ef2e522e 100644
--- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
+++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
@@ -356,6 +356,31 @@ export default function Page() {