App env fixes
This commit is contained in:
		
							parent
							
								
									4c8ff47ae7
								
							
						
					
					
						commit
						0213f66eb6
					
				
					 17 changed files with 169 additions and 138 deletions
				
			
		| 
						 | 
					@ -112,7 +112,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
 | 
				
			||||||
  credentialIds: number[];
 | 
					  credentialIds: number[];
 | 
				
			||||||
  type: App["type"];
 | 
					  type: App["type"];
 | 
				
			||||||
  isGlobal?: boolean;
 | 
					  isGlobal?: boolean;
 | 
				
			||||||
  installed: boolean;
 | 
					  installed?: boolean;
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
  const { t } = useLocale();
 | 
					  const { t } = useLocale();
 | 
				
			||||||
  const [credentialId] = props.credentialIds;
 | 
					  const [credentialId] = props.credentialIds;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2137,8 +2137,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
 | 
				
			||||||
  const t = await getTranslation(currentUser?.locale ?? "en", "common");
 | 
					  const t = await getTranslation(currentUser?.locale ?? "en", "common");
 | 
				
			||||||
  const integrations = getApps(credentials);
 | 
					  const integrations = getApps(credentials);
 | 
				
			||||||
  const locationOptions = getLocationOptions(integrations, t);
 | 
					  const locationOptions = getLocationOptions(integrations, t);
 | 
				
			||||||
 | 
					  const hasPaymentIntegration = !!credentials.find((credential) => credential.type === "stripe_payment");
 | 
				
			||||||
  const hasPaymentIntegration = hasIntegration(integrations, "stripe_payment");
 | 
					 | 
				
			||||||
  const currency =
 | 
					  const currency =
 | 
				
			||||||
    (credentials.find((integration) => integration.type === "stripe_payment")?.key as unknown as StripeData)
 | 
					    (credentials.find((integration) => integration.type === "stripe_payment")?.key as unknown as StripeData)
 | 
				
			||||||
      ?.default_currency || "usd";
 | 
					      ?.default_currency || "usd";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import _package from "./package.json";
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  name: "Giphy",
 | 
					  name: "Giphy",
 | 
				
			||||||
  description: _package.description,
 | 
					  description: _package.description,
 | 
				
			||||||
  installed: !!process.env.GIPHY_API_KEY,
 | 
					 | 
				
			||||||
  category: "other",
 | 
					  category: "other",
 | 
				
			||||||
  // If using static next public folder, can then be referenced from the base URL (/).
 | 
					  // If using static next public folder, can then be referenced from the base URL (/).
 | 
				
			||||||
  imageSrc: "/api/app-store/giphy/icon.svg",
 | 
					  imageSrc: "/api/app-store/giphy/icon.svg",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import _package from "./package.json";
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  name: "HubSpot CRM",
 | 
					  name: "HubSpot CRM",
 | 
				
			||||||
  description: _package.description,
 | 
					  description: _package.description,
 | 
				
			||||||
  installed: !!(process.env.HUBSPOT_CLIENT_ID && process.env.HUBSPOT_CLIENT_SECRET),
 | 
					 | 
				
			||||||
  type: "hubspot_other_calendar",
 | 
					  type: "hubspot_other_calendar",
 | 
				
			||||||
  imageSrc: "/api/app-store/hubspotothercalendar/icon.svg",
 | 
					  imageSrc: "/api/app-store/hubspotothercalendar/icon.svg",
 | 
				
			||||||
  variant: "other_calendar",
 | 
					  variant: "other_calendar",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,20 +3,21 @@ import type { NextApiRequest, NextApiResponse } from "next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
 | 
					import { WEBAPP_URL } from "@calcom/lib/constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scopes = ["crm.objects.contacts.read", "crm.objects.contacts.write"];
 | 
					const scopes = ["crm.objects.contacts.read", "crm.objects.contacts.write"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const client_id = process.env.HUBSPOT_CLIENT_ID;
 | 
					let client_id = "";
 | 
				
			||||||
const hubspotClient = new hubspot.Client();
 | 
					const hubspotClient = new hubspot.Client();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
					export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
				
			||||||
  if (!client_id) {
 | 
					  if (req.method !== "GET") return res.status(405).json({ message: "Method not allowed" });
 | 
				
			||||||
    res.status(400).json({ message: "HubSpot client id missing." });
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (req.method === "GET") {
 | 
					  const appKeys = await getAppKeysFromSlug("hubspot");
 | 
				
			||||||
    const redirectUri = WEBAPP_URL + "/api/integrations/hubspotothercalendar/callback";
 | 
					  if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
    const url = hubspotClient.oauth.getAuthorizationUrl(client_id, redirectUri, scopes.join(" "));
 | 
					  if (!client_id) return res.status(400).json({ message: "HubSpot client id missing." });
 | 
				
			||||||
    res.status(200).json({ url });
 | 
					
 | 
				
			||||||
  }
 | 
					  const redirectUri = WEBAPP_URL + "/api/integrations/hubspotothercalendar/callback";
 | 
				
			||||||
 | 
					  const url = hubspotClient.oauth.getAuthorizationUrl(client_id, redirectUri, scopes.join(" "));
 | 
				
			||||||
 | 
					  res.status(200).json({ url });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import _package from "./package.json";
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  name: "Office 365 / Outlook.com Calendar",
 | 
					  name: "Office 365 / Outlook.com Calendar",
 | 
				
			||||||
  description: _package.description,
 | 
					  description: _package.description,
 | 
				
			||||||
  installed: !!(process.env.MS_GRAPH_CLIENT_ID && process.env.MS_GRAPH_CLIENT_SECRET),
 | 
					 | 
				
			||||||
  type: "office365_calendar",
 | 
					  type: "office365_calendar",
 | 
				
			||||||
  title: "Office 365 / Outlook.com Calendar",
 | 
					  title: "Office 365 / Outlook.com Calendar",
 | 
				
			||||||
  imageSrc: "/api/app-store/office365calendar/icon.svg",
 | 
					  imageSrc: "/api/app-store/office365calendar/icon.svg",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,26 @@
 | 
				
			||||||
import type { NextApiRequest, NextApiResponse } from "next";
 | 
					import type { NextApiRequest, NextApiResponse } from "next";
 | 
				
			||||||
import { stringify } from "querystring";
 | 
					import { stringify } from "querystring";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { BASE_URL } from "@calcom/lib/constants";
 | 
					import { WEBAPP_URL } from "@calcom/lib/constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { encodeOAuthState } from "../../_utils/encodeOAuthState";
 | 
					import { encodeOAuthState } from "../../_utils/encodeOAuthState";
 | 
				
			||||||
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scopes = ["User.Read", "Calendars.Read", "Calendars.ReadWrite", "offline_access"];
 | 
					const scopes = ["User.Read", "Calendars.Read", "Calendars.ReadWrite", "offline_access"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let client_id = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
					export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
				
			||||||
  if (req.method === "GET") {
 | 
					  if (req.method === "GET") {
 | 
				
			||||||
 | 
					    const appKeys = await getAppKeysFromSlug("office365-calendar");
 | 
				
			||||||
 | 
					    if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					    if (!client_id) return res.status(400).json({ message: "Office 365 client_id missing." });
 | 
				
			||||||
    const state = encodeOAuthState(req);
 | 
					    const state = encodeOAuthState(req);
 | 
				
			||||||
    const params = {
 | 
					    const params = {
 | 
				
			||||||
      response_type: "code",
 | 
					      response_type: "code",
 | 
				
			||||||
      scope: scopes.join(" "),
 | 
					      scope: scopes.join(" "),
 | 
				
			||||||
      client_id: process.env.MS_GRAPH_CLIENT_ID,
 | 
					      client_id,
 | 
				
			||||||
      redirect_uri: BASE_URL + "/api/integrations/office365calendar/callback",
 | 
					      redirect_uri: WEBAPP_URL + "/api/integrations/office365calendar/callback",
 | 
				
			||||||
      state,
 | 
					      state,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    const query = stringify(params);
 | 
					    const query = stringify(params);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,39 +3,40 @@ import { Credential } from "@prisma/client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getLocation, getRichDescription } from "@calcom/lib/CalEventParser";
 | 
					import { getLocation, getRichDescription } from "@calcom/lib/CalEventParser";
 | 
				
			||||||
import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
 | 
					import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
 | 
				
			||||||
 | 
					import { HttpError } from "@calcom/lib/http-error";
 | 
				
			||||||
import logger from "@calcom/lib/logger";
 | 
					import logger from "@calcom/lib/logger";
 | 
				
			||||||
import prisma from "@calcom/prisma";
 | 
					import prisma from "@calcom/prisma";
 | 
				
			||||||
import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
 | 
					import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime";
 | 
				
			||||||
import type {
 | 
					import type {
 | 
				
			||||||
  Calendar,
 | 
					  Calendar,
 | 
				
			||||||
  CalendarEvent,
 | 
					  CalendarEvent,
 | 
				
			||||||
  IntegrationCalendar,
 | 
					 | 
				
			||||||
  BatchResponse,
 | 
					 | 
				
			||||||
  EventBusyDate,
 | 
					  EventBusyDate,
 | 
				
			||||||
 | 
					  IntegrationCalendar,
 | 
				
			||||||
  NewCalendarEventType,
 | 
					  NewCalendarEventType,
 | 
				
			||||||
} from "@calcom/types/Calendar";
 | 
					} from "@calcom/types/Calendar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
import { O365AuthCredentials } from "../types/Office365Calendar";
 | 
					import { O365AuthCredentials } from "../types/Office365Calendar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MS_GRAPH_CLIENT_ID = process.env.MS_GRAPH_CLIENT_ID || "";
 | 
					let client_id = "";
 | 
				
			||||||
const MS_GRAPH_CLIENT_SECRET = process.env.MS_GRAPH_CLIENT_SECRET || "";
 | 
					let client_secret = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Office365CalendarService implements Calendar {
 | 
					export default class Office365CalendarService implements Calendar {
 | 
				
			||||||
  private url = "";
 | 
					  private url = "";
 | 
				
			||||||
  private integrationName = "";
 | 
					  private integrationName = "";
 | 
				
			||||||
  private log: typeof logger;
 | 
					  private log: typeof logger;
 | 
				
			||||||
  auth: { getToken: () => Promise<string> };
 | 
					  auth: Promise<{ getToken: () => Promise<string> }>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(credential: Credential) {
 | 
					  constructor(credential: Credential) {
 | 
				
			||||||
    this.integrationName = "office365_calendar";
 | 
					    this.integrationName = "office365_calendar";
 | 
				
			||||||
    this.auth = this.o365Auth(credential);
 | 
					    this.auth = this.o365Auth(credential).then((t) => t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.log = logger.getChildLogger({ prefix: [`[[lib] ${this.integrationName}`] });
 | 
					    this.log = logger.getChildLogger({ prefix: [`[[lib] ${this.integrationName}`] });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async createEvent(event: CalendarEvent): Promise<NewCalendarEventType> {
 | 
					  async createEvent(event: CalendarEvent): Promise<NewCalendarEventType> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const accessToken = await this.auth.getToken();
 | 
					      const accessToken = await (await this.auth).getToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const calendarId = event.destinationCalendar?.externalId
 | 
					      const calendarId = event.destinationCalendar?.externalId
 | 
				
			||||||
        ? `${event.destinationCalendar.externalId}/`
 | 
					        ? `${event.destinationCalendar.externalId}/`
 | 
				
			||||||
| 
						 | 
					@ -60,7 +61,7 @@ export default class Office365CalendarService implements Calendar {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async updateEvent(uid: string, event: CalendarEvent): Promise<any> {
 | 
					  async updateEvent(uid: string, event: CalendarEvent): Promise<any> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const accessToken = await this.auth.getToken();
 | 
					      const accessToken = await (await this.auth).getToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const response = await fetch("https://graph.microsoft.com/v1.0/me/calendar/events/" + uid, {
 | 
					      const response = await fetch("https://graph.microsoft.com/v1.0/me/calendar/events/" + uid, {
 | 
				
			||||||
        method: "PATCH",
 | 
					        method: "PATCH",
 | 
				
			||||||
| 
						 | 
					@ -81,7 +82,7 @@ export default class Office365CalendarService implements Calendar {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async deleteEvent(uid: string): Promise<void> {
 | 
					  async deleteEvent(uid: string): Promise<void> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const accessToken = await this.auth.getToken();
 | 
					      const accessToken = await (await this.auth).getToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const response = await fetch("https://graph.microsoft.com/v1.0/me/calendar/events/" + uid, {
 | 
					      const response = await fetch("https://graph.microsoft.com/v1.0/me/calendar/events/" + uid, {
 | 
				
			||||||
        method: "DELETE",
 | 
					        method: "DELETE",
 | 
				
			||||||
| 
						 | 
					@ -109,9 +110,9 @@ export default class Office365CalendarService implements Calendar {
 | 
				
			||||||
    const filter = `?startdatetime=${encodeURIComponent(
 | 
					    const filter = `?startdatetime=${encodeURIComponent(
 | 
				
			||||||
      dateFromParsed.toISOString()
 | 
					      dateFromParsed.toISOString()
 | 
				
			||||||
    )}&enddatetime=${encodeURIComponent(dateToParsed.toISOString())}`;
 | 
					    )}&enddatetime=${encodeURIComponent(dateToParsed.toISOString())}`;
 | 
				
			||||||
    return this.auth
 | 
					    return (await this.auth)
 | 
				
			||||||
      .getToken()
 | 
					      .getToken()
 | 
				
			||||||
      .then((accessToken) => {
 | 
					      .then(async (accessToken) => {
 | 
				
			||||||
        const selectedCalendarIds = selectedCalendars
 | 
					        const selectedCalendarIds = selectedCalendars
 | 
				
			||||||
          .filter((e) => e.integration === this.integrationName)
 | 
					          .filter((e) => e.integration === this.integrationName)
 | 
				
			||||||
          .map((e) => e.externalId)
 | 
					          .map((e) => e.externalId)
 | 
				
			||||||
| 
						 | 
					@ -121,50 +122,44 @@ export default class Office365CalendarService implements Calendar {
 | 
				
			||||||
          return Promise.resolve([]);
 | 
					          return Promise.resolve([]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        const ids = await (selectedCalendarIds.length === 0
 | 
				
			||||||
          selectedCalendarIds.length === 0
 | 
					          ? this.listCalendars().then((cals) => cals.map((e_2) => e_2.externalId).filter(Boolean) || [])
 | 
				
			||||||
            ? this.listCalendars().then((cals) => cals.map((e) => e.externalId).filter(Boolean) || [])
 | 
					          : Promise.resolve(selectedCalendarIds));
 | 
				
			||||||
            : Promise.resolve(selectedCalendarIds)
 | 
					        const requests = ids.map((calendarId, id) => ({
 | 
				
			||||||
        ).then((ids) => {
 | 
					          id,
 | 
				
			||||||
          const requests = ids.map((calendarId, id) => ({
 | 
					          method: "GET",
 | 
				
			||||||
            id,
 | 
					          url: `/me/calendars/${calendarId}/calendarView${filter}`,
 | 
				
			||||||
            method: "GET",
 | 
					        }));
 | 
				
			||||||
            url: `/me/calendars/${calendarId}/calendarView${filter}`,
 | 
					        const response = await fetch("https://graph.microsoft.com/v1.0/$batch", {
 | 
				
			||||||
          }));
 | 
					          method: "POST",
 | 
				
			||||||
 | 
					          headers: {
 | 
				
			||||||
          return fetch("https://graph.microsoft.com/v1.0/$batch", {
 | 
					            Authorization: "Bearer " + accessToken,
 | 
				
			||||||
            method: "POST",
 | 
					            "Content-Type": "application/json",
 | 
				
			||||||
            headers: {
 | 
					          },
 | 
				
			||||||
              Authorization: "Bearer " + accessToken,
 | 
					          body: JSON.stringify({ requests }),
 | 
				
			||||||
              "Content-Type": "application/json",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            body: JSON.stringify({ requests }),
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
            .then(handleErrorsJson)
 | 
					 | 
				
			||||||
            .then((responseBody: BatchResponse) =>
 | 
					 | 
				
			||||||
              responseBody.responses.reduce(
 | 
					 | 
				
			||||||
                (acc: BufferedBusyTime[], subResponse) =>
 | 
					 | 
				
			||||||
                  acc.concat(
 | 
					 | 
				
			||||||
                    subResponse.body.value.map((evt) => {
 | 
					 | 
				
			||||||
                      return {
 | 
					 | 
				
			||||||
                        start: evt.start.dateTime + "Z",
 | 
					 | 
				
			||||||
                        end: evt.end.dateTime + "Z",
 | 
					 | 
				
			||||||
                      };
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                  ),
 | 
					 | 
				
			||||||
                []
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        const responseBody = await handleErrorsJson(response);
 | 
				
			||||||
 | 
					        return responseBody.responses.reduce(
 | 
				
			||||||
 | 
					          (acc: BufferedBusyTime[], subResponse: { body: { value: any[] } }) =>
 | 
				
			||||||
 | 
					            acc.concat(
 | 
				
			||||||
 | 
					              subResponse.body.value.map((evt) => {
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                  start: evt.start.dateTime + "Z",
 | 
				
			||||||
 | 
					                  end: evt.end.dateTime + "Z",
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          []
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      .catch((err) => {
 | 
					      .catch((err: unknown) => {
 | 
				
			||||||
        console.log(err);
 | 
					        console.log(err);
 | 
				
			||||||
        return Promise.reject([]);
 | 
					        return Promise.reject([]);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async listCalendars(): Promise<IntegrationCalendar[]> {
 | 
					  async listCalendars(): Promise<IntegrationCalendar[]> {
 | 
				
			||||||
    return this.auth.getToken().then((accessToken) =>
 | 
					    return (await this.auth).getToken().then((accessToken) =>
 | 
				
			||||||
      fetch("https://graph.microsoft.com/v1.0/me/calendars", {
 | 
					      fetch("https://graph.microsoft.com/v1.0/me/calendars", {
 | 
				
			||||||
        method: "get",
 | 
					        method: "get",
 | 
				
			||||||
        headers: {
 | 
					        headers: {
 | 
				
			||||||
| 
						 | 
					@ -187,38 +182,41 @@ export default class Office365CalendarService implements Calendar {
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private o365Auth = (credential: Credential) => {
 | 
					  private o365Auth = async (credential: Credential) => {
 | 
				
			||||||
 | 
					    const appKeys = await getAppKeysFromSlug("office365-calendar");
 | 
				
			||||||
 | 
					    if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					    if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret;
 | 
				
			||||||
 | 
					    if (!client_id) throw new HttpError({ statusCode: 400, message: "office365 client_id missing." });
 | 
				
			||||||
 | 
					    if (!client_secret) throw new HttpError({ statusCode: 400, message: "office365 client_secret missing." });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const isExpired = (expiryDate: number) => expiryDate < Math.round(+new Date() / 1000);
 | 
					    const isExpired = (expiryDate: number) => expiryDate < Math.round(+new Date() / 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const o365AuthCredentials = credential.key as O365AuthCredentials;
 | 
					    const o365AuthCredentials = credential.key as O365AuthCredentials;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const refreshAccessToken = (refreshToken: string) => {
 | 
					    const refreshAccessToken = async (refreshToken: string) => {
 | 
				
			||||||
      return fetch("https://login.microsoftonline.com/common/oauth2/v2.0/token", {
 | 
					      const response = await fetch("https://login.microsoftonline.com/common/oauth2/v2.0/token", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
 | 
					        headers: { "Content-Type": "application/x-www-form-urlencoded" },
 | 
				
			||||||
        body: new URLSearchParams({
 | 
					        body: new URLSearchParams({
 | 
				
			||||||
          scope: "User.Read Calendars.Read Calendars.ReadWrite",
 | 
					          scope: "User.Read Calendars.Read Calendars.ReadWrite",
 | 
				
			||||||
          client_id: MS_GRAPH_CLIENT_ID,
 | 
					          client_id,
 | 
				
			||||||
          refresh_token: refreshToken,
 | 
					          refresh_token: refreshToken,
 | 
				
			||||||
          grant_type: "refresh_token",
 | 
					          grant_type: "refresh_token",
 | 
				
			||||||
          client_secret: MS_GRAPH_CLIENT_SECRET,
 | 
					          client_secret,
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
      })
 | 
					      });
 | 
				
			||||||
        .then(handleErrorsJson)
 | 
					      const responseBody = await handleErrorsJson(response);
 | 
				
			||||||
        .then((responseBody) => {
 | 
					      o365AuthCredentials.access_token = responseBody.access_token;
 | 
				
			||||||
          o365AuthCredentials.access_token = responseBody.access_token;
 | 
					      o365AuthCredentials.expiry_date = Math.round(+new Date() / 1000 + responseBody.expires_in);
 | 
				
			||||||
          o365AuthCredentials.expiry_date = Math.round(+new Date() / 1000 + responseBody.expires_in);
 | 
					      await prisma.credential.update({
 | 
				
			||||||
          return prisma.credential
 | 
					        where: {
 | 
				
			||||||
            .update({
 | 
					          id: credential.id,
 | 
				
			||||||
              where: {
 | 
					        },
 | 
				
			||||||
                id: credential.id,
 | 
					        data: {
 | 
				
			||||||
              },
 | 
					          key: o365AuthCredentials,
 | 
				
			||||||
              data: {
 | 
					        },
 | 
				
			||||||
                key: o365AuthCredentials,
 | 
					      });
 | 
				
			||||||
              },
 | 
					      return o365AuthCredentials.access_token;
 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .then(() => o365AuthCredentials.access_token);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,6 @@ import _package from "./package.json";
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  name: "Microsoft 365/Teams",
 | 
					  name: "Microsoft 365/Teams",
 | 
				
			||||||
  description: _package.description,
 | 
					  description: _package.description,
 | 
				
			||||||
  installed: !!(process.env.MS_GRAPH_CLIENT_ID && process.env.MS_GRAPH_CLIENT_SECRET),
 | 
					 | 
				
			||||||
  type: "office365_video",
 | 
					  type: "office365_video",
 | 
				
			||||||
  imageSrc: "/api/app-store/office365video/icon.svg",
 | 
					  imageSrc: "/api/app-store/office365video/icon.svg",
 | 
				
			||||||
  variant: "conferencing",
 | 
					  variant: "conferencing",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,26 @@
 | 
				
			||||||
import type { NextApiRequest, NextApiResponse } from "next";
 | 
					import type { NextApiRequest, NextApiResponse } from "next";
 | 
				
			||||||
import { stringify } from "querystring";
 | 
					import { stringify } from "querystring";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { BASE_URL } from "@calcom/lib/constants";
 | 
					import { WEBAPP_URL } from "@calcom/lib/constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { encodeOAuthState } from "../../_utils/encodeOAuthState";
 | 
					import { encodeOAuthState } from "../../_utils/encodeOAuthState";
 | 
				
			||||||
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scopes = ["OnlineMeetings.ReadWrite"];
 | 
					const scopes = ["OnlineMeetings.ReadWrite"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let client_id = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
					export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
				
			||||||
  if (req.method === "GET") {
 | 
					  if (req.method === "GET") {
 | 
				
			||||||
 | 
					    const appKeys = await getAppKeysFromSlug("office365-calendar");
 | 
				
			||||||
 | 
					    if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					    if (!client_id) return res.status(400).json({ message: "Office 365 client_id missing." });
 | 
				
			||||||
    const state = encodeOAuthState(req);
 | 
					    const state = encodeOAuthState(req);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const params = {
 | 
					    const params = {
 | 
				
			||||||
      response_type: "code",
 | 
					      response_type: "code",
 | 
				
			||||||
      scope: scopes.join(" "),
 | 
					      scope: scopes.join(" "),
 | 
				
			||||||
      client_id: process.env.MS_GRAPH_CLIENT_ID,
 | 
					      client_id,
 | 
				
			||||||
      redirect_uri: BASE_URL + "/api/integrations/office365video/callback",
 | 
					      redirect_uri: WEBAPP_URL + "/api/integrations/office365video/callback",
 | 
				
			||||||
      state,
 | 
					      state,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    const query = stringify(params);
 | 
					    const query = stringify(params);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,16 @@
 | 
				
			||||||
import { Credential } from "@prisma/client";
 | 
					import { Credential } from "@prisma/client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
 | 
					import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
 | 
				
			||||||
 | 
					import { HttpError } from "@calcom/lib/http-error";
 | 
				
			||||||
import prisma from "@calcom/prisma";
 | 
					import prisma from "@calcom/prisma";
 | 
				
			||||||
import type { CalendarEvent } from "@calcom/types/Calendar";
 | 
					import type { CalendarEvent } from "@calcom/types/Calendar";
 | 
				
			||||||
import type { PartialReference } from "@calcom/types/EventManager";
 | 
					import type { PartialReference } from "@calcom/types/EventManager";
 | 
				
			||||||
import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";
 | 
					import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MS_GRAPH_CLIENT_ID = process.env.MS_GRAPH_CLIENT_ID || "";
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
const MS_GRAPH_CLIENT_SECRET = process.env.MS_GRAPH_CLIENT_SECRET || "";
 | 
					
 | 
				
			||||||
 | 
					let client_id = "";
 | 
				
			||||||
 | 
					let client_secret = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** @link https://docs.microsoft.com/en-us/graph/api/application-post-onlinemeetings?view=graph-rest-1.0&tabs=http#response */
 | 
					/** @link https://docs.microsoft.com/en-us/graph/api/application-post-onlinemeetings?view=graph-rest-1.0&tabs=http#response */
 | 
				
			||||||
export interface TeamsEventResult {
 | 
					export interface TeamsEventResult {
 | 
				
			||||||
| 
						 | 
					@ -30,41 +33,45 @@ interface O365AuthCredentials {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Checks to see if our O365 user token is valid or if we need to refresh
 | 
					// Checks to see if our O365 user token is valid or if we need to refresh
 | 
				
			||||||
const o365Auth = (credential: Credential) => {
 | 
					const o365Auth = async (credential: Credential) => {
 | 
				
			||||||
 | 
					  const appKeys = await getAppKeysFromSlug("msteams");
 | 
				
			||||||
 | 
					  if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					  if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret;
 | 
				
			||||||
 | 
					  if (!client_id) throw new HttpError({ statusCode: 400, message: "MS teams client_id missing." });
 | 
				
			||||||
 | 
					  if (!client_secret) throw new HttpError({ statusCode: 400, message: "MS teams client_secret missing." });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const isExpired = (expiryDate: number) => expiryDate < Math.round(+new Date() / 1000);
 | 
					  const isExpired = (expiryDate: number) => expiryDate < Math.round(+new Date() / 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const o365AuthCredentials = credential.key as unknown as O365AuthCredentials;
 | 
					  const o365AuthCredentials = credential.key as unknown as O365AuthCredentials;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const refreshAccessToken = (refreshToken: string) => {
 | 
					  const refreshAccessToken = async (refreshToken: string) => {
 | 
				
			||||||
    return fetch("https://login.microsoftonline.com/common/oauth2/v2.0/token", {
 | 
					    const response = await fetch("https://login.microsoftonline.com/common/oauth2/v2.0/token", {
 | 
				
			||||||
      method: "POST",
 | 
					      method: "POST",
 | 
				
			||||||
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
 | 
					      headers: { "Content-Type": "application/x-www-form-urlencoded" },
 | 
				
			||||||
      body: new URLSearchParams({
 | 
					      body: new URLSearchParams({
 | 
				
			||||||
        scope: "User.Read Calendars.Read Calendars.ReadWrite",
 | 
					        scope: "User.Read Calendars.Read Calendars.ReadWrite",
 | 
				
			||||||
        client_id: MS_GRAPH_CLIENT_ID,
 | 
					        client_id,
 | 
				
			||||||
        refresh_token: refreshToken,
 | 
					        refresh_token: refreshToken,
 | 
				
			||||||
        grant_type: "refresh_token",
 | 
					        grant_type: "refresh_token",
 | 
				
			||||||
        client_secret: MS_GRAPH_CLIENT_SECRET,
 | 
					        client_secret,
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
      .then(handleErrorsJson)
 | 
					    const responseBody = await handleErrorsJson(response);
 | 
				
			||||||
      .then(async (responseBody) => {
 | 
					    // set expiry date as offset from current time.
 | 
				
			||||||
        // set expiry date as offset from current time.
 | 
					    responseBody.expiry_date = Math.round(Date.now() + responseBody.expires_in * 1000);
 | 
				
			||||||
        responseBody.expiry_date = Math.round(Date.now() + responseBody.expires_in * 1000);
 | 
					    delete responseBody.expires_in;
 | 
				
			||||||
        delete responseBody.expires_in;
 | 
					    // Store new tokens in database.
 | 
				
			||||||
        // Store new tokens in database.
 | 
					    await prisma.credential.update({
 | 
				
			||||||
        await prisma.credential.update({
 | 
					      where: {
 | 
				
			||||||
          where: {
 | 
					        id: credential.id,
 | 
				
			||||||
            id: credential.id,
 | 
					      },
 | 
				
			||||||
          },
 | 
					      data: {
 | 
				
			||||||
          data: {
 | 
					        key: responseBody,
 | 
				
			||||||
            key: responseBody,
 | 
					      },
 | 
				
			||||||
          },
 | 
					    });
 | 
				
			||||||
        });
 | 
					    o365AuthCredentials.expiry_date = responseBody.expiry_date;
 | 
				
			||||||
        o365AuthCredentials.expiry_date = responseBody.expiry_date;
 | 
					    o365AuthCredentials.access_token = responseBody.access_token;
 | 
				
			||||||
        o365AuthCredentials.access_token = responseBody.access_token;
 | 
					    return o365AuthCredentials.access_token;
 | 
				
			||||||
        return o365AuthCredentials.access_token;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
| 
						 | 
					@ -92,7 +99,7 @@ const TeamsVideoApiAdapter = (credential: Credential): VideoApiAdapter => {
 | 
				
			||||||
      return Promise.resolve([]);
 | 
					      return Promise.resolve([]);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    updateMeeting: async (bookingRef: PartialReference, event: CalendarEvent) => {
 | 
					    updateMeeting: async (bookingRef: PartialReference, event: CalendarEvent) => {
 | 
				
			||||||
      const accessToken = await auth.getToken();
 | 
					      const accessToken = await (await auth).getToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resultString = await fetch("https://graph.microsoft.com/v1.0/me/onlineMeetings", {
 | 
					      const resultString = await fetch("https://graph.microsoft.com/v1.0/me/onlineMeetings", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
| 
						 | 
					@ -116,7 +123,7 @@ const TeamsVideoApiAdapter = (credential: Credential): VideoApiAdapter => {
 | 
				
			||||||
      return Promise.resolve([]);
 | 
					      return Promise.resolve([]);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    createMeeting: async (event: CalendarEvent): Promise<VideoCallData> => {
 | 
					    createMeeting: async (event: CalendarEvent): Promise<VideoCallData> => {
 | 
				
			||||||
      const accessToken = await auth.getToken();
 | 
					      const accessToken = await (await auth).getToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resultString = await fetch("https://graph.microsoft.com/v1.0/me/onlineMeetings", {
 | 
					      const resultString = await fetch("https://graph.microsoft.com/v1.0/me/onlineMeetings", {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,11 +97,6 @@ function getApps(userCredentials: CredentialData[]) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type AppMeta = ReturnType<typeof getApps>;
 | 
					export type AppMeta = ReturnType<typeof getApps>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** @deprecated use `getApps`  */
 | 
					 | 
				
			||||||
export function hasIntegration(apps: AppMeta, type: string): boolean {
 | 
					 | 
				
			||||||
  return !!apps.find((app) => app.type === type && !!app.installed && app.credentials.length > 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function hasIntegrationInstalled(type: App["type"]): boolean {
 | 
					export function hasIntegrationInstalled(type: App["type"]): boolean {
 | 
				
			||||||
  return ALL_APPS.some((app) => app.type === type && !!app.installed);
 | 
					  return ALL_APPS.some((app) => app.type === type && !!app.installed);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,6 @@ import _package from "./package.json";
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  name: "Zoom Video",
 | 
					  name: "Zoom Video",
 | 
				
			||||||
  description: _package.description,
 | 
					  description: _package.description,
 | 
				
			||||||
  installed: !!(process.env.ZOOM_CLIENT_ID && process.env.ZOOM_CLIENT_SECRET),
 | 
					 | 
				
			||||||
  type: "zoom_video",
 | 
					  type: "zoom_video",
 | 
				
			||||||
  imageSrc: "/api/app-store/zoomvideo/icon.svg",
 | 
					  imageSrc: "/api/app-store/zoomvideo/icon.svg",
 | 
				
			||||||
  variant: "conferencing",
 | 
					  variant: "conferencing",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,12 @@
 | 
				
			||||||
import type { NextApiRequest, NextApiResponse } from "next";
 | 
					import type { NextApiRequest, NextApiResponse } from "next";
 | 
				
			||||||
import { stringify } from "querystring";
 | 
					import { stringify } from "querystring";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { BASE_URL } from "@calcom/lib/constants";
 | 
					import { WEBAPP_URL } from "@calcom/lib/constants";
 | 
				
			||||||
import prisma from "@calcom/prisma";
 | 
					import prisma from "@calcom/prisma";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const client_id = process.env.ZOOM_CLIENT_ID;
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let client_id = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
					export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
				
			||||||
  if (req.method === "GET") {
 | 
					  if (req.method === "GET") {
 | 
				
			||||||
| 
						 | 
					@ -19,10 +21,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const appKeys = await getAppKeysFromSlug("zoom");
 | 
				
			||||||
 | 
					    if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					    if (!client_id) return res.status(400).json({ message: "Zoom client_id missing." });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const params = {
 | 
					    const params = {
 | 
				
			||||||
      response_type: "code",
 | 
					      response_type: "code",
 | 
				
			||||||
      client_id,
 | 
					      client_id,
 | 
				
			||||||
      redirect_uri: BASE_URL + "/api/integrations/zoomvideo/callback",
 | 
					      redirect_uri: WEBAPP_URL + "/api/integrations/zoomvideo/callback",
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    const query = stringify(params);
 | 
					    const query = stringify(params);
 | 
				
			||||||
    const url = `https://zoom.us/oauth/authorize?${query}`;
 | 
					    const url = `https://zoom.us/oauth/authorize?${query}`;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,23 @@
 | 
				
			||||||
import type { NextApiRequest, NextApiResponse } from "next";
 | 
					import type { NextApiRequest, NextApiResponse } from "next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { BASE_URL } from "@calcom/lib/constants";
 | 
					import { WEBAPP_URL } from "@calcom/lib/constants";
 | 
				
			||||||
import prisma from "@calcom/prisma";
 | 
					import prisma from "@calcom/prisma";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const client_id = process.env.ZOOM_CLIENT_ID;
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
const client_secret = process.env.ZOOM_CLIENT_SECRET;
 | 
					
 | 
				
			||||||
 | 
					let client_id = "";
 | 
				
			||||||
 | 
					let client_secret = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
					export default async function handler(req: NextApiRequest, res: NextApiResponse) {
 | 
				
			||||||
  const { code } = req.query;
 | 
					  const { code } = req.query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const redirectUri = encodeURI(BASE_URL + "/api/integrations/zoomvideo/callback");
 | 
					  const appKeys = await getAppKeysFromSlug("zoom");
 | 
				
			||||||
 | 
					  if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					  if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret;
 | 
				
			||||||
 | 
					  if (!client_id) return res.status(400).json({ message: "Zoom client_id missing." });
 | 
				
			||||||
 | 
					  if (!client_secret) return res.status(400).json({ message: "Zoom client_secret missing." });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const redirectUri = encodeURI(WEBAPP_URL + "/api/integrations/zoomvideo/callback");
 | 
				
			||||||
  const authHeader = "Basic " + Buffer.from(client_id + ":" + client_secret).toString("base64");
 | 
					  const authHeader = "Basic " + Buffer.from(client_id + ":" + client_secret).toString("base64");
 | 
				
			||||||
  const result = await fetch(
 | 
					  const result = await fetch(
 | 
				
			||||||
    "https://zoom.us/oauth/token?grant_type=authorization_code&code=" + code + "&redirect_uri=" + redirectUri,
 | 
					    "https://zoom.us/oauth/token?grant_type=authorization_code&code=" + code + "&redirect_uri=" + redirectUri,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,14 @@
 | 
				
			||||||
import { Credential } from "@prisma/client";
 | 
					import { Credential } from "@prisma/client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
 | 
					import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
 | 
				
			||||||
 | 
					import { HttpError } from "@calcom/lib/http-error";
 | 
				
			||||||
import prisma from "@calcom/prisma";
 | 
					import prisma from "@calcom/prisma";
 | 
				
			||||||
import type { CalendarEvent } from "@calcom/types/Calendar";
 | 
					import type { CalendarEvent } from "@calcom/types/Calendar";
 | 
				
			||||||
import type { PartialReference } from "@calcom/types/EventManager";
 | 
					import type { PartialReference } from "@calcom/types/EventManager";
 | 
				
			||||||
import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";
 | 
					import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** @link https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate */
 | 
					/** @link https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate */
 | 
				
			||||||
export interface ZoomEventResult {
 | 
					export interface ZoomEventResult {
 | 
				
			||||||
  password: string;
 | 
					  password: string;
 | 
				
			||||||
| 
						 | 
					@ -67,13 +70,20 @@ interface ZoomToken {
 | 
				
			||||||
  refresh_token: string;
 | 
					  refresh_token: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const zoomAuth = (credential: Credential) => {
 | 
					let client_id = "";
 | 
				
			||||||
 | 
					let client_secret = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const zoomAuth = async (credential: Credential) => {
 | 
				
			||||||
 | 
					  const appKeys = await getAppKeysFromSlug("zoom");
 | 
				
			||||||
 | 
					  if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
 | 
				
			||||||
 | 
					  if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret;
 | 
				
			||||||
 | 
					  if (!client_id) throw new HttpError({ statusCode: 400, message: "Zoom client_id missing." });
 | 
				
			||||||
 | 
					  if (!client_secret) throw new HttpError({ statusCode: 400, message: "Zoom client_secret missing." });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const credentialKey = credential.key as unknown as ZoomToken;
 | 
					  const credentialKey = credential.key as unknown as ZoomToken;
 | 
				
			||||||
  const isTokenValid = (token: ZoomToken) =>
 | 
					  const isTokenValid = (token: ZoomToken) =>
 | 
				
			||||||
    token && token.token_type && token.access_token && (token.expires_in || token.expiry_date) < Date.now();
 | 
					    token && token.token_type && token.access_token && (token.expires_in || token.expiry_date) < Date.now();
 | 
				
			||||||
  const authHeader =
 | 
					  const authHeader = "Basic " + Buffer.from(client_id + ":" + client_secret).toString("base64");
 | 
				
			||||||
    "Basic " +
 | 
					 | 
				
			||||||
    Buffer.from(process.env.ZOOM_CLIENT_ID + ":" + process.env.ZOOM_CLIENT_SECRET).toString("base64");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const refreshAccessToken = (refreshToken: string) =>
 | 
					  const refreshAccessToken = (refreshToken: string) =>
 | 
				
			||||||
    fetch("https://zoom.us/oauth/token", {
 | 
					    fetch("https://zoom.us/oauth/token", {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								packages/types/App.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								packages/types/App.d.ts
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -8,10 +8,11 @@ import type { LocationType } from "@calcom/app-store/locations";
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export interface App {
 | 
					export interface App {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 | 
					   * @deprecated
 | 
				
			||||||
   * Wheter if the app is installed or not. Usually we check for api keys in env
 | 
					   * Wheter if the app is installed or not. Usually we check for api keys in env
 | 
				
			||||||
   * variables to determine if this is true or not.
 | 
					   * variables to determine if this is true or not.
 | 
				
			||||||
   * */
 | 
					   * */
 | 
				
			||||||
  installed: boolean;
 | 
					  installed?: boolean;
 | 
				
			||||||
  /** The app type */
 | 
					  /** The app type */
 | 
				
			||||||
  type:
 | 
					  type:
 | 
				
			||||||
    | `${string}_calendar`
 | 
					    | `${string}_calendar`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue