Files
calendar/packages/app-store/basecamp3/api/callback.ts
T
f80e9b2558 feat: add basecamp integration to cal.com (#9195)
* feat: installing the app works

* Update yarn.lock

* feat: /api/callback now gets user auth info from basecamp

* feat: updated basecamp logo

* feat: added project dropdown on event apps page

* feat: basecamp event creation and deletion working

* feat: basecamp event rescheduling now works

* refactor(CalendarService): basecamp CaldendarService code clean up

* refactor: code cleanup for basecamp app API

* feat: updated event summary text sent to basecamp

* chore: updated basecamp images and contact info

* fix: fixed typescript errors and added logic to refresh tokens on event settings

* refactor(CaldendarService): used refreshAccessToken from helpers.ts instead

* chore: updated basecamp description

* fix: fixed incorrect import

* fix: accidentally deleted props to toggle app for event

* chore: updated .env.appStore.example and added README for app

* Update .env.appStore.example

Co-authored-by: Leo Giovanetti <hello@leog.me>

* feat: added basecamp userAgent in env instead of hardcoded value

* feat: updated README to include how to set basecamp user agent env

* fix: removed unused import

* feat: used URLSearchParams to construct url params

* fix: fixed typescript errors

* chore: updated README to include an example on how to set basecamp user-agent

* feat: using TRPC instead of REST

* chore: removed old projects REST code

---------

Co-authored-by: Leo Giovanetti <hello@leog.me>
2023-08-19 17:04:56 -03:00

91 lines
2.7 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from "next";
import { WEBAPP_URL } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
import appConfig from "../config.json";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { code } = req.query;
const { client_id, client_secret, user_agent } = await getAppKeysFromSlug("basecamp3");
const redirectUri = WEBAPP_URL + "/api/integrations/basecamp3/callback";
const params = new URLSearchParams({
type: "web_server",
client_id: client_id as string,
client_secret: client_secret as string,
redirect_uri: redirectUri,
code: code as string,
});
// gets access token
const accessTokenResponse = await fetch(
`https://launchpad.37signals.com/authorization/token?${params.toString()}`,
{
method: "POST",
}
);
if (accessTokenResponse.status !== 200) {
let errorMessage = "Error with Basecamp 3 API";
try {
const responseBody = await accessTokenResponse.json();
errorMessage = responseBody.error;
} catch (e) {}
res.status(400).json({ message: errorMessage });
return;
}
const tokenResponseBody = await accessTokenResponse.json();
if (tokenResponseBody.error) {
res.status(400).json({ message: tokenResponseBody.error });
return;
}
// expiry date of 2 weeks
tokenResponseBody["expires_at"] = Date.now() + 1000 * 3600 * 24 * 14;
// get user details such as projects and account info
const userAuthResponse = await fetch("https://launchpad.37signals.com/authorization.json", {
headers: {
"User-Agent": user_agent as string,
Authorization: `Bearer ${tokenResponseBody.access_token}`,
},
});
if (userAuthResponse.status !== 200) {
let errorMessage = "Error with Basecamp 3 API";
try {
const body = await userAuthResponse.json();
errorMessage = body.error;
} catch (e) {}
res.status(400).json({ message: errorMessage });
return;
}
const authResponseBody = await userAuthResponse.json();
const userId = req.session?.user.id;
if (!userId) {
return res.status(404).json({ message: "No user found" });
}
await prisma.user.update({
where: {
id: req.session?.user.id,
},
data: {
credentials: {
create: {
type: appConfig.type,
key: { ...tokenResponseBody, account: authResponseBody.accounts[0] },
appId: appConfig.slug,
},
},
},
});
res.redirect(getInstalledAppPath({ variant: appConfig.variant, slug: appConfig.slug }));
}