diff --git a/apps/api/src/controllers/error.html b/apps/api/src/controllers/error.html
new file mode 100644
index 00000000..b174d613
--- /dev/null
+++ b/apps/api/src/controllers/error.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+ Error - OpenPanel
+
+
+
+
+
+
+

+
Oops! Something went wrong
+
We encountered an error while processing your request. Please try again later or contact support if the problem
+ persists.
+
+
+
+
\ No newline at end of file
diff --git a/apps/api/src/controllers/live.controller.ts b/apps/api/src/controllers/live.controller.ts
index 341652fe..61fd2c38 100644
--- a/apps/api/src/controllers/live.controller.ts
+++ b/apps/api/src/controllers/live.controller.ts
@@ -224,39 +224,3 @@ export async function wsProjectNotifications(
getRedisSub().off('message', message as any);
});
}
-
-export async function wsIntegrationsSlack(
- connection: {
- socket: WebSocket;
- },
- req: FastifyRequest<{
- Querystring: {
- organizationId?: string;
- };
- }>,
-) {
- const { organizationId } = req.query;
-
- if (!organizationId) {
- connection.socket.send('No organizationId provided');
- connection.socket.close();
- return;
- }
-
- const subscribeToEvent = 'integrations:slack';
-
- getRedisSub().subscribe(subscribeToEvent);
- const onMessage = (channel: string, message: string) => {
- if (channel === subscribeToEvent) {
- const parsed = getSuperJson<{ organizationId: string }>(message);
- if (parsed && parsed.organizationId === organizationId) {
- connection.socket.send(message);
- }
- }
- };
- getRedisSub().on('message', onMessage);
- connection.socket.on('close', () => {
- getRedisSub().unsubscribe(subscribeToEvent);
- getRedisSub().off('message', onMessage);
- });
-}
diff --git a/apps/api/src/controllers/webhook.controller.ts b/apps/api/src/controllers/webhook.controller.ts
index 440acbeb..65b0b14f 100644
--- a/apps/api/src/controllers/webhook.controller.ts
+++ b/apps/api/src/controllers/webhook.controller.ts
@@ -1,5 +1,6 @@
+import fs from 'node:fs';
+import path from 'node:path';
import type { WebhookEvent } from '@clerk/fastify';
-import { setSuperJson } from '@openpanel/common';
import { AccessLevel, db } from '@openpanel/db';
import {
sendSlackNotification,
@@ -167,6 +168,7 @@ const paramsSchema = z.object({
const metadataSchema = z.object({
organizationId: z.string(),
+ projectId: z.string(),
integrationId: z.string(),
});
@@ -179,7 +181,7 @@ export async function slackWebhook(
const parsedParams = paramsSchema.safeParse(request.query);
if (!parsedParams.success) {
- request.log.error('Invalid params', parsedParams);
+ request.log.error(parsedParams.error, 'Invalid params');
return reply.status(400).send({ error: 'Invalid params' });
}
@@ -192,7 +194,7 @@ export async function slackWebhook(
);
if (!parsedMetadata.success) {
- request.log.error('Invalid metadata', parsedMetadata.error.errors);
+ request.log.error(parsedMetadata.error, 'Invalid metadata');
return reply.status(400).send({ error: 'Invalid metadata' });
}
@@ -217,10 +219,8 @@ export async function slackWebhook(
},
'Failed to parse slack auth response',
);
- return reply
- .status(400)
- .header('Content-Type', 'text/html')
- .send('Failed to exchange code for token
');
+ const html = fs.readFileSync(path.join(__dirname, 'error.html'), 'utf8');
+ return reply.status(500).header('Content-Type', 'text/html').send(html);
}
// Send a notification first to confirm the connection
@@ -230,10 +230,12 @@ export async function slackWebhook(
'👋 Hello. You have successfully connected OpenPanel.dev to your Slack workspace.',
});
+ const { projectId, organizationId, integrationId } = parsedMetadata.data;
+
await db.integration.update({
where: {
- id: parsedMetadata.data.integrationId,
- organizationId: parsedMetadata.data.organizationId,
+ id: integrationId,
+ organizationId,
},
data: {
config: {
@@ -243,22 +245,12 @@ export async function slackWebhook(
},
});
- getRedisPub().publish(
- 'integrations:slack',
- setSuperJson({
- organizationId: parsedMetadata.data.organizationId,
- }),
+ return reply.redirect(
+ `${process.env.NEXT_PUBLIC_DASHBOARD_URL}/${organizationId}/${projectId}/settings/integrations?tab=installed`,
);
-
- return reply
- .status(200)
- .header('Content-Type', 'text/html')
- .send('Slack integration added. You can close this window now.
');
} catch (err) {
request.log.error(err);
- return reply
- .status(500)
- .header('Content-Type', 'text/html')
- .send('Failed to exchange code for token
');
+ const html = fs.readFileSync(path.join(__dirname, 'error.html'), 'utf8');
+ return reply.status(500).header('Content-Type', 'text/html').send(html);
}
}
diff --git a/apps/api/src/routes/live.router.ts b/apps/api/src/routes/live.router.ts
index 4cd2766c..9bca7a2d 100644
--- a/apps/api/src/routes/live.router.ts
+++ b/apps/api/src/routes/live.router.ts
@@ -32,11 +32,6 @@ const liveRouter: FastifyPluginCallback = (fastify, opts, done) => {
{ websocket: true },
controller.wsProjectNotifications,
);
- fastify.get(
- '/integrations/slack',
- { websocket: true },
- controller.wsIntegrationsSlack,
- );
done();
});
diff --git a/apps/dashboard/src/components/integrations/forms/slack-integration.tsx b/apps/dashboard/src/components/integrations/forms/slack-integration.tsx
index 77e10600..aef05480 100644
--- a/apps/dashboard/src/components/integrations/forms/slack-integration.tsx
+++ b/apps/dashboard/src/components/integrations/forms/slack-integration.tsx
@@ -19,49 +19,20 @@ export function SlackIntegrationForm({
defaultValues?: RouterOutputs['integration']['get'];
onSuccess: () => void;
}) {
- const { organizationId } = useAppParams();
- useWS(`/live/integrations/slack?organizationId=${organizationId}`, (res) => {
- // @ts-expect-error
- console.log('3. slack integration done', window.slackPopup);
- // @ts-expect-error
- if (window.slackPopup && typeof window.slackPopup.close === 'function') {
- console.log('4. close popup');
- // @ts-expect-error
- window.slackPopup.close();
- }
- onSuccess();
- });
+ const { organizationId, projectId } = useAppParams();
+
const form = useForm({
defaultValues: {
id: defaultValues?.id,
organizationId,
+ projectId,
name: defaultValues?.name ?? '',
},
resolver: zodResolver(zCreateSlackIntegration),
});
const mutation = api.integration.createOrUpdateSlack.useMutation({
async onSuccess(res) {
- console.log('1. onSuccess', res);
-
- const url = res.slackInstallUrl;
- const width = 600;
- const height = 800;
- const left = window.screenX + (window.outerWidth - width) / 2;
- const top = window.screenY + (window.outerHeight - height) / 2.5;
- console.log('2. open popup');
- // @ts-expect-error
- window.slackPopup = window.open(
- url,
- '',
- `toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=${width}, height=${height}, top=${top}, left=${left}`,
- );
-
- // The popup might have been blocked, so we redirect the user to the URL instead
- //
- // @ts-expect-error
- if (!window.slackPopup) {
- window.location.href = url;
- }
+ window.location.href = res.slackInstallUrl;
},
onError() {
toast.error('Failed to create integration');
diff --git a/packages/integrations/src/slack.ts b/packages/integrations/src/slack.ts
index 4b02f385..be8d98c8 100644
--- a/packages/integrations/src/slack.ts
+++ b/packages/integrations/src/slack.ts
@@ -18,7 +18,8 @@ export const slackInstaller = new InstallProvider({
export const getSlackInstallUrl = ({
integrationId,
organizationId,
-}: { integrationId: string; organizationId: string }) => {
+ projectId,
+}: { integrationId: string; organizationId: string; projectId: string }) => {
return slackInstaller.generateInstallUrl({
scopes: [
'incoming-webhook',
@@ -27,7 +28,7 @@ export const getSlackInstallUrl = ({
'team:read',
],
redirectUri: SLACK_OAUTH_REDIRECT_URL,
- metadata: JSON.stringify({ integrationId, organizationId }),
+ metadata: JSON.stringify({ integrationId, organizationId, projectId }),
});
};
diff --git a/packages/trpc/src/routers/integration.ts b/packages/trpc/src/routers/integration.ts
index f6270b02..cee6f285 100644
--- a/packages/trpc/src/routers/integration.ts
+++ b/packages/trpc/src/routers/integration.ts
@@ -66,6 +66,7 @@ export const integrationRouter = createTRPCRouter({
slackInstallUrl: await getSlackInstallUrl({
integrationId: res.id,
organizationId: input.organizationId,
+ projectId: input.projectId,
}),
};
}
@@ -84,6 +85,7 @@ export const integrationRouter = createTRPCRouter({
slackInstallUrl: await getSlackInstallUrl({
integrationId: res.id,
organizationId: input.organizationId,
+ projectId: input.projectId,
}),
};
}),
diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts
index b6085c4b..341e4d91 100644
--- a/packages/validation/src/index.ts
+++ b/packages/validation/src/index.ts
@@ -214,6 +214,7 @@ const zCreateIntegration = z.object({
id: z.string().optional(),
name: z.string().min(1),
organizationId: z.string().min(1),
+ projectId: z.string().min(1),
});
export const zCreateSlackIntegration = zCreateIntegration;