import fetch from 'node-fetch' import moment from 'moment' import { logger } from './logger' import { prismaClient as prisma } from '../prismaClient' const ZOOM_API_URL = 'https://api.zoom.us/v2' async function updateAccessTokenIfExpired(accessToken: string) { const managingCompany = ( await prisma.managingCompany.findMany({ take: 1, where: { zoomAccessToken: accessToken }, }) )[0] if (!managingCompany) { throw new Error(`Can't find managing company for accessToken provided: ${accessToken}`) } // If zoomAccessTokenExpiresIn is not defined it's because we manually added the access token. Ex with Sentinelle if ( !managingCompany.zoomAccessTokenExpiresIn || moment().isBefore(moment(managingCompany.zoomAccessTokenExpiresIn)) ) { return Promise.resolve(accessToken) } try { const { access_token: newAccessToken, refresh_token: refreshToken } = await getNewAccessToken({ refreshToken: managingCompany.zoomRefreshToken, }) await prisma.managingCompany.update({ where: { id: managingCompany.id, }, data: { zoomAccessToken: newAccessToken, zoomRefreshToken: refreshToken, zoomAccessTokenExpiresIn: moment().add(1, 'hours').format(), }, }) return Promise.resolve(newAccessToken) } catch (error) { logger.error(error) await prisma.managingCompany.update({ where: { id: managingCompany.id, }, data: { zoomAccessToken: null, zoomRefreshToken: null, zoomAccessTokenExpiresIn: null, }, }) throw new Error('Invalid token. Please reconnect with Zoom in your managing company settings') } } async function getSharedHeaders(accessToken: string) { // Maybe not good but for now it works... accessToken = await updateAccessTokenIfExpired(accessToken) return { authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', } } async function handleZoomResponse(response) { let jsonResponseOrError try { jsonResponseOrError = await response.json() } catch {} if (!response.ok) { throw new Error(JSON.stringify(jsonResponseOrError) || 'Zoom error!') } return jsonResponseOrError || response.statusText } export async function getUserWebinars({ userId, accessToken, }: { userId: string accessToken: string }) { return fetch(`${ZOOM_API_URL}/users/${userId}/webinars`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getUsers(accessToken: string) { return fetch(`${ZOOM_API_URL}/users`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getUserSettings({ accessToken, userId, }: { accessToken: string userId: string }) { return fetch(`${ZOOM_API_URL}/users/${userId}/settings`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getRoomUsers(accessToken: string) { return fetch(`${ZOOM_API_URL}/rooms`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } type CreateWebinarInputType = { accessToken: string userId: string topic: string agenda: string start: string password: string contactName: string contactEmail: string } export async function createWebinar({ accessToken, userId, topic, agenda, start, password, contactName, contactEmail, }: CreateWebinarInputType) { return fetch(`${ZOOM_API_URL}/users/${userId}/webinars`, { method: 'POST', headers: await getSharedHeaders(accessToken), body: JSON.stringify({ topic, type: 5, agenda, // Like descritpion start_time: start, // duration: durationMinutes, // in minute password, settings: { host_video: true, // start video when we host join approval_type: 1, registration_type: 1, auto_recording: 'none', show_share_button: false, allow_multiple_devices: true, contact_name: contactName, contact_email: contactEmail, registrants_confirmation_email: false, registrants_email_notification: false, // For now }, }), }).then(handleZoomResponse) } export async function updateWebinar({ accessToken, webinarId, data, }: { accessToken: string webinarId: string data: any }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}`, { method: 'PATCH', headers: await getSharedHeaders(accessToken), body: JSON.stringify({ data, }), }).then(handleZoomResponse) } export async function getRegistrant({ accessToken, webinarId, registrantId, }: { accessToken: string webinarId: string registrantId: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}/registrants/${registrantId}`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getRegistrantsForWebinar({ accessToken, webinarId, }: { accessToken: string webinarId: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}/registrants`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function createWebinarRegistrant({ accessToken, webinarId, firstName, lastName, email, }: { accessToken: string webinarId: string firstName: string lastName: string email: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}/registrants`, { method: 'POST', headers: await getSharedHeaders(accessToken), body: JSON.stringify({ email, first_name: firstName, last_name: lastName, }), }).then(handleZoomResponse) } export async function deleteWebinar({ accessToken, webinarId, }: { accessToken: string webinarId: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}`, { method: 'DELETE', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getWebinar({ accessToken, webinarId, }: { accessToken: string webinarId: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getWebinarRegistrants({ accessToken, webinarId, }: { accessToken: string webinarId: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}/registrants`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function approveRegistrant({ accessToken, webinarId, registrantId, registrantEmail, }: { accessToken: string webinarId: string registrantId: string registrantEmail: string }) { return fetch(`${ZOOM_API_URL}/webinars/${webinarId}/registrants/status`, { method: 'PUT', headers: await getSharedHeaders(accessToken), body: JSON.stringify({ action: 'approve', registrants: [{ id: registrantId, email: registrantEmail }], }), }).then(handleZoomResponse) } export async function getWebinarParticipantsReport({ accessToken, webinarId, }: { accessToken: string webinarId: string }) { return fetch(`${ZOOM_API_URL}/report/webinars/${webinarId}/participants`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } export async function getWebinarParticipants({ accessToken, webinarId, }: { accessToken: string webinarId: string }) { return fetch(`${ZOOM_API_URL}/metrics/webinars/${webinarId}/participants`, { method: 'GET', headers: await getSharedHeaders(accessToken), }).then(handleZoomResponse) } const AUTH = Buffer.from( `${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}` ).toString('base64') export function getAccessToken({ code, redirectUrl }: { code: string; redirectUrl: string }) { return fetch( `https://zoom.us/oauth/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirectUrl}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Basic ${AUTH}`, }, } ).then(handleZoomResponse) } export function getNewAccessToken({ refreshToken }: { refreshToken: string }) { return fetch( `https://zoom.us/oauth/token?grant_type=refresh_token&refresh_token=${refreshToken}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Basic ${AUTH}`, }, } ).then(handleZoomResponse) }