Revert "Slack Oauth + verify sig"
This reverts commit ee95795e0f
.
monorepo/app-store-teams-integration
parent
ee95795e0f
commit
c9a3c5789e
|
@ -1,4 +1,4 @@
|
|||
const withTM = require("next-transpile-modules")([
|
||||
const withTM = require("@vercel/edge-functions-ui/transpile")([
|
||||
"@calcom/app-store",
|
||||
"@calcom/lib",
|
||||
"@calcom/prisma",
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
import { createHmac } from "crypto";
|
||||
import dayjs from "dayjs";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { stringify } from "querystring";
|
||||
|
||||
import appStore from "@calcom/app-store";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
|
||||
const signingSecret = process.env.SLACK_SIGNING_SECRET;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { args } = req.query;
|
||||
|
||||
const body = req.body;
|
||||
const timeStamp = req.headers["x-slack-request-timestamp"] as string; // Always returns a string and not a string[]
|
||||
const slackSignature = req.headers["x-slack-signature"] as string;
|
||||
const currentTime = dayjs().unix();
|
||||
|
||||
if (!timeStamp) {
|
||||
return res.status(400).json({ message: "Missing X-Slack-Request-Timestamp header" });
|
||||
}
|
||||
|
||||
if (!signingSecret) {
|
||||
return res.status(400).json({ message: "Missing process.env.SLACK_SIGNING_SECRET" });
|
||||
}
|
||||
|
||||
if (Math.abs(currentTime - parseInt(timeStamp)) > 60 * 5) {
|
||||
return res.status(400).json({ message: "Request is too old" });
|
||||
}
|
||||
|
||||
const signature_base = `v0:${timeStamp}:${stringify(body)}`;
|
||||
const signed_sig = "v0=" + createHmac("sha256", signingSecret).update(signature_base).digest("hex");
|
||||
|
||||
if (signed_sig !== slackSignature) {
|
||||
return res.status(400).json({ message: "Invalid signature" });
|
||||
}
|
||||
|
||||
if (!Array.isArray(args)) {
|
||||
return res.status(404).json({ message: `API route not found` });
|
||||
}
|
||||
|
||||
const [_appName, apiEndpoint] = args;
|
||||
const appName = _appName.split("_").join("");
|
||||
|
||||
try {
|
||||
const handler = appStore[appName].api[apiEndpoint];
|
||||
console.log(`API: ${appName}/${apiEndpoint}`);
|
||||
if (typeof handler !== "function")
|
||||
throw new HttpError({ statusCode: 404, message: `API handler not found` });
|
||||
|
||||
const response = await handler(req, res);
|
||||
console.log("response", response);
|
||||
|
||||
res.status(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof HttpError) {
|
||||
return res.status(error.statusCode).json({ message: error.message });
|
||||
}
|
||||
return res.status(404).json({ message: `API handler not found` });
|
||||
}
|
||||
|
||||
// Return a response to acknowledge receipt of the event
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 270 270" style="enable-background:new 0 0 270 270;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E01E5A;}
|
||||
.st1{fill:#36C5F0;}
|
||||
.st2{fill:#2EB67D;}
|
||||
.st3{fill:#ECB22E;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M99.4,151.2c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h12.9V151.2z"/>
|
||||
<path class="st0" d="M105.9,151.2c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v32.3c0,7.1-5.8,12.9-12.9,12.9
|
||||
s-12.9-5.8-12.9-12.9V151.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M118.8,99.4c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v12.9H118.8z"/>
|
||||
<path class="st1" d="M118.8,105.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9H86.5c-7.1,0-12.9-5.8-12.9-12.9
|
||||
s5.8-12.9,12.9-12.9H118.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M170.6,118.8c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9h-12.9V118.8z"/>
|
||||
<path class="st2" d="M164.1,118.8c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9V86.5c0-7.1,5.8-12.9,12.9-12.9
|
||||
c7.1,0,12.9,5.8,12.9,12.9V118.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M151.2,170.6c7.1,0,12.9,5.8,12.9,12.9c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9v-12.9H151.2z"/>
|
||||
<path class="st3" d="M151.2,164.1c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h32.3c7.1,0,12.9,5.8,12.9,12.9
|
||||
c0,7.1-5.8,12.9-12.9,12.9H151.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,13 +1,11 @@
|
|||
import * as example from "./_example";
|
||||
import * as dailyvideo from "./dailyvideo";
|
||||
import * as slackapp from "./slackapp";
|
||||
import * as zoomvideo from "./zoomvideo";
|
||||
|
||||
const appStore = {
|
||||
example,
|
||||
dailyvideo,
|
||||
zoomvideo,
|
||||
slackapp,
|
||||
};
|
||||
|
||||
export default appStore;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import { InstallProvider } from "@slack/oauth";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { stringify } from "querystring";
|
||||
|
||||
import { BASE_URL } from "@calcom/lib/constants";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
const client_id = process.env.SLACK_CLIENT_ID;
|
||||
const client_secret = process.env.SLACK_CLIENT_SECRET;
|
||||
const scopes = ["commands"];
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "GET") {
|
||||
// Get user
|
||||
await prisma.user.findFirst({
|
||||
rejectOnNotFound: true,
|
||||
where: {
|
||||
id: req.session?.user?.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
const params = {
|
||||
client_id,
|
||||
scope: scopes.join(","),
|
||||
};
|
||||
const query = stringify(params);
|
||||
const url = `https://slack.com/oauth/v2/authorize?${query}&user_`;
|
||||
// const url =
|
||||
// "https://slack.com/oauth/v2/authorize?client_id=3194129032064.3178385871204&scope=chat:write,commands&user_scope=";
|
||||
res.status(200).json({ url });
|
||||
}
|
||||
res.status(404).json({ error: "Not Found" });
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { stringify } from "querystring";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
const client_id = process.env.SLACK_CLIENT_ID;
|
||||
const client_secret = process.env.SLACK_CLIENT_SECRET;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "GET") {
|
||||
// Get user
|
||||
const { code } = req.query;
|
||||
console.log(req.query);
|
||||
|
||||
if (!code) {
|
||||
res.redirect("/apps/installed"); // Redirect to where the user was if they cancel the signup or if the oauth fails
|
||||
}
|
||||
|
||||
const query = {
|
||||
client_secret,
|
||||
client_id,
|
||||
code,
|
||||
};
|
||||
const params = stringify(query);
|
||||
console.log("params", params);
|
||||
|
||||
const url = `https://slack.com/api/oauth.v2.access?${params}`;
|
||||
const result = await fetch(url);
|
||||
const responseBody = await result.json();
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id: req.session?.user.id,
|
||||
},
|
||||
data: {
|
||||
credentials: {
|
||||
create: {
|
||||
type: "slack_app",
|
||||
key: responseBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
res.redirect("/apps/installed");
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { createEvent } from "../lib";
|
||||
|
||||
export enum SlackAppCommands {
|
||||
CREATE_EVENT = "create-event",
|
||||
}
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "POST") {
|
||||
const command = req.body.command.split("/").pop();
|
||||
|
||||
switch (command) {
|
||||
case SlackAppCommands.CREATE_EVENT:
|
||||
return await createEvent(req, res);
|
||||
default:
|
||||
return res.status(404).json({ message: `Command not found` });
|
||||
}
|
||||
}
|
||||
res.status(400).json({ message: "Invalid request" });
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export { default as add } from "./add";
|
||||
export { default as callback } from "./callback";
|
||||
export { default as commandHandler } from "./commandHandler";
|
|
@ -1,25 +0,0 @@
|
|||
import type { App } from "@calcom/types/App";
|
||||
|
||||
import _package from "./package.json";
|
||||
|
||||
export const metadata = {
|
||||
name: _package.name,
|
||||
description: _package.description,
|
||||
installed: !!(process.env.SLACK_CLIENT_ID && process.env.SLACK_CLIENT_SECRET),
|
||||
category: "video",
|
||||
imageSrc: "apps/slack.svg",
|
||||
label: "Slack App",
|
||||
logo: "/apps/slack.svg",
|
||||
publisher: "Cal.com",
|
||||
rating: 5,
|
||||
reviews: 69,
|
||||
slug: "slack_app",
|
||||
title: "Slack App",
|
||||
trending: true,
|
||||
type: "slack_app",
|
||||
url: "https://slack.com/",
|
||||
variant: "conferencing",
|
||||
verified: true,
|
||||
} as App;
|
||||
|
||||
export * as api from "./api";
|
|
@ -1,34 +0,0 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { CreateEventModal } from "./views";
|
||||
|
||||
export default async function createEvents(req: NextApiRequest, res: NextApiResponse) {
|
||||
const body = req.body;
|
||||
|
||||
const data = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: "slack_app",
|
||||
key: {
|
||||
path: ["authed_user", "id"],
|
||||
equals: body.user_id,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
username: true,
|
||||
eventTypes: {
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) res.status(200).json({ message: "No user found" });
|
||||
|
||||
res.status(200).json(CreateEventModal(data));
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export { default as locationOption } from "./locationOption";
|
||||
export { default as createEvent } from "./createEvent";
|
|
@ -1,9 +0,0 @@
|
|||
import { LocationType } from "@calcom/lib/location";
|
||||
|
||||
const locationOption = {
|
||||
value: LocationType.Slack,
|
||||
label: "Slack App",
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export default locationOption;
|
|
@ -1,49 +0,0 @@
|
|||
import { User } from "@prisma/client";
|
||||
import { Modal, Blocks, Elements, Bits } from "slack-block-builder";
|
||||
|
||||
const CreateEventModal = (
|
||||
data:
|
||||
| (Credential & {
|
||||
user: {
|
||||
username: string | null;
|
||||
eventTypes: {
|
||||
id: number;
|
||||
title: string;
|
||||
}[];
|
||||
} | null;
|
||||
})
|
||||
| null
|
||||
) => {
|
||||
return Modal({ title: "Cal.com", submit: "Create" })
|
||||
.blocks(
|
||||
Blocks.Section({ text: `Hey there, ${data?.user?.username}!` }),
|
||||
Blocks.Divider(),
|
||||
Blocks.Input({ label: "Which event would you like to create?" }).element(
|
||||
Elements.StaticSelect({ placeholder: "Which event would you like to create?" })
|
||||
.actionId("events_types")
|
||||
.options(
|
||||
data?.user?.eventTypes.map((item: any) =>
|
||||
Bits.Option({ text: item.title ?? "No Name", value: item.id.toString() })
|
||||
)
|
||||
)
|
||||
),
|
||||
Blocks.Input({ label: "Who would you like to invite to your event?" }).element(
|
||||
Elements.UserMultiSelect({ placeholder: "Who would you like to invite to your event?" }).actionId(
|
||||
"invite_users"
|
||||
)
|
||||
),
|
||||
Blocks.Input({ label: "When would this event be?" }).element(
|
||||
Elements.DatePicker({ placeholder: "Select Date" }).actionId("event_date")
|
||||
),
|
||||
Blocks.Input({ label: "What time would you like to start?" }).element(
|
||||
Elements.TimePicker({ placeholder: "Select Time" }).actionId("event_start_time")
|
||||
)
|
||||
)
|
||||
.buildToJSON();
|
||||
};
|
||||
|
||||
export default CreateEventModal;
|
||||
|
||||
// Elements.StaticSelect({ placeholder: "Which event would you like to create?" })
|
||||
// .actionId("events_types")
|
||||
// .options(data.events.map((item) => Bits.Option({ text: item.name, value: item.id })))
|
|
@ -1 +0,0 @@
|
|||
export { default as CreateEventModal } from "./CreateEventModal";
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"name": "@calcom/slackapp",
|
||||
"version": "0.0.0",
|
||||
"main": "./index.ts",
|
||||
"description": "This is a package for the intergration of slack into the app-store",
|
||||
"dependencies": {
|
||||
"@calcom/prisma": "*",
|
||||
"slack-block-builder": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@calcom/types": "*"
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 270 270" style="enable-background:new 0 0 270 270;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E01E5A;}
|
||||
.st1{fill:#36C5F0;}
|
||||
.st2{fill:#2EB67D;}
|
||||
.st3{fill:#ECB22E;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M99.4,151.2c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h12.9V151.2z"/>
|
||||
<path class="st0" d="M105.9,151.2c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v32.3c0,7.1-5.8,12.9-12.9,12.9
|
||||
s-12.9-5.8-12.9-12.9V151.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M118.8,99.4c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v12.9H118.8z"/>
|
||||
<path class="st1" d="M118.8,105.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9H86.5c-7.1,0-12.9-5.8-12.9-12.9
|
||||
s5.8-12.9,12.9-12.9H118.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M170.6,118.8c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9h-12.9V118.8z"/>
|
||||
<path class="st2" d="M164.1,118.8c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9V86.5c0-7.1,5.8-12.9,12.9-12.9
|
||||
c7.1,0,12.9,5.8,12.9,12.9V118.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M151.2,170.6c7.1,0,12.9,5.8,12.9,12.9c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9v-12.9H151.2z"/>
|
||||
<path class="st3" d="M151.2,164.1c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h32.3c7.1,0,12.9,5.8,12.9,12.9
|
||||
c0,7.1-5.8,12.9-12.9,12.9H151.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -8,5 +8,4 @@ export enum LocationType {
|
|||
Jitsi = "integrations:jitsi",
|
||||
Huddle01 = "integrations:huddle01",
|
||||
Tandem = "integrations:tandem",
|
||||
Slack = "integrations:slack",
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ datasource db {
|
|||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["filterJson"]
|
||||
}
|
||||
|
||||
generator zod {
|
||||
|
|
|
@ -9,7 +9,7 @@ export interface App {
|
|||
* */
|
||||
installed: boolean;
|
||||
/** The app type */
|
||||
type: `${string}_calendar` | `${string}_payment` | `${string}_video` | `${string}_web3` | `${string}_app`;
|
||||
type: `${string}_calendar` | `${string}_payment` | `${string}_video` | `${string}_web3`;
|
||||
/** The display name for the app, TODO settle between this or name */
|
||||
title: string;
|
||||
/** The display name for the app */
|
||||
|
@ -19,7 +19,7 @@ export interface App {
|
|||
/** The icon to display in /apps/installed */
|
||||
imageSrc: string;
|
||||
/** TODO determine if we should use this instead of category */
|
||||
variant: "calendar" | "payment" | "conferencing" | "app";
|
||||
variant: "calendar" | "payment" | "conferencing";
|
||||
label: string;
|
||||
/** The slug for the app store public page inside `/apps/[slug] */
|
||||
slug: string;
|
||||
|
|
Loading…
Reference in New Issue