Merge branch 'main' of github.com:calendso/calendso

This commit is contained in:
Peer Richelsen 2021-08-06 14:05:26 +02:00
commit 906168c9b0
3 changed files with 100 additions and 37 deletions

View file

@ -37,7 +37,7 @@ export default class CalEventParser {
* Returns a footer section with links to change the event (as HTML). * Returns a footer section with links to change the event (as HTML).
*/ */
public getChangeEventFooterHtml(): string { public getChangeEventFooterHtml(): string {
return `<p style="color: #4b5563; margin-top: 20px;">Need to make a change? <a href="${this.getCancelLink()}" style="color: #161e2e;">Cancel</a> or <a href="${this.getRescheduleLink()}" style="color: #161e2e;">reschedule</a>.</p>`; return `<p style="color: #4b5563; margin-top: 20px;">Need to make a change? <a href="${this.getCancelLink()}" style="color: #161e2e;">Cancel</a> or <a href="${this.getRescheduleLink()}" style="color: #161e2e;">reschedule</a></p>`;
} }
/** /**

View file

@ -10,8 +10,14 @@ import CalEventParser from "./CalEventParser";
const { google } = require("googleapis"); const { google } = require("googleapis");
const googleAuth = (credential) => { const googleAuth = (credential) => {
const { client_secret, client_id, redirect_uris } = JSON.parse(process.env.GOOGLE_API_CREDENTIALS).web; const { client_secret, client_id, redirect_uris } = JSON.parse(
const myGoogleAuth = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); process.env.GOOGLE_API_CREDENTIALS
).web;
const myGoogleAuth = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
myGoogleAuth.setCredentials(credential.key); myGoogleAuth.setCredentials(credential.key);
const isExpired = () => myGoogleAuth.isTokenExpiring(); const isExpired = () => myGoogleAuth.isTokenExpiring();
@ -43,7 +49,8 @@ const googleAuth = (credential) => {
}); });
return { return {
getToken: () => (!isExpired() ? Promise.resolve(myGoogleAuth) : refreshAccessToken()), getToken: () =>
!isExpired() ? Promise.resolve(myGoogleAuth) : refreshAccessToken(),
}; };
}; };
@ -81,7 +88,9 @@ const o365Auth = (credential) => {
.then(handleErrorsJson) .then(handleErrorsJson)
.then((responseBody) => { .then((responseBody) => {
credential.key.access_token = responseBody.access_token; credential.key.access_token = responseBody.access_token;
credential.key.expiry_date = Math.round(+new Date() / 1000 + responseBody.expires_in); credential.key.expiry_date = Math.round(
+new Date() / 1000 + responseBody.expires_in
);
return prisma.credential return prisma.credential
.update({ .update({
where: { where: {
@ -139,7 +148,11 @@ export interface CalendarApiAdapter {
deleteEvent(uid: string); deleteEvent(uid: string);
getAvailability(dateFrom, dateTo, selectedCalendars: IntegrationCalendar[]): Promise<unknown>; getAvailability(
dateFrom,
dateTo,
selectedCalendars: IntegrationCalendar[]
): Promise<unknown>;
listCalendars(): Promise<IntegrationCalendar[]>; listCalendars(): Promise<IntegrationCalendar[]>;
} }
@ -206,7 +219,7 @@ const MicrosoftOffice365Calendar = (credential): CalendarApiAdapter => {
return { return {
getAvailability: (dateFrom, dateTo, selectedCalendars) => { getAvailability: (dateFrom, dateTo, selectedCalendars) => {
const filter = "?$filter=start/dateTime ge '" + dateFrom + "' and end/dateTime le '" + dateTo + "'"; const filter = "?startdatetime=" + dateFrom + "&enddatetime=" + dateTo;
return auth return auth
.getToken() .getToken()
.then((accessToken) => { .then((accessToken) => {
@ -229,7 +242,7 @@ const MicrosoftOffice365Calendar = (credential): CalendarApiAdapter => {
headers: { headers: {
Prefer: 'outlook.timezone="Etc/GMT"', Prefer: 'outlook.timezone="Etc/GMT"',
}, },
url: `/me/calendars/${calendarId}/events${filter}`, url: `/me/calendars/${calendarId}/calendarView${filter}`,
})); }));
return fetch("https://graph.microsoft.com/v1.0/$batch", { return fetch("https://graph.microsoft.com/v1.0/$batch", {
@ -309,7 +322,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
getAvailability: (dateFrom, dateTo, selectedCalendars) => getAvailability: (dateFrom, dateTo, selectedCalendars) =>
new Promise((resolve, reject) => new Promise((resolve, reject) =>
auth.getToken().then((myGoogleAuth) => { auth.getToken().then((myGoogleAuth) => {
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth }); const calendar = google.calendar({
version: "v3",
auth: myGoogleAuth,
});
const selectedCalendarIds = selectedCalendars const selectedCalendarIds = selectedCalendars
.filter((e) => e.integration === integrationType) .filter((e) => e.integration === integrationType)
.map((e) => e.externalId); .map((e) => e.externalId);
@ -320,7 +336,9 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
} }
(selectedCalendarIds.length == 0 (selectedCalendarIds.length == 0
? calendar.calendarList.list().then((cals) => cals.data.items.map((cal) => cal.id)) ? calendar.calendarList
.list()
.then((cals) => cals.data.items.map((cal) => cal.id))
: Promise.resolve(selectedCalendarIds) : Promise.resolve(selectedCalendarIds)
) )
.then((calsIds) => { .then((calsIds) => {
@ -336,12 +354,19 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
if (err) { if (err) {
reject(err); reject(err);
} }
resolve(Object.values(apires.data.calendars).flatMap((item) => item["busy"])); resolve(
Object.values(apires.data.calendars).flatMap(
(item) => item["busy"]
)
);
} }
); );
}) })
.catch((err) => { .catch((err) => {
console.error("There was an error contacting google calendar service: ", err); console.error(
"There was an error contacting google calendar service: ",
err
);
reject(err); reject(err);
}); });
}) })
@ -375,7 +400,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
payload["conferenceData"] = event.conferenceData; payload["conferenceData"] = event.conferenceData;
} }
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth }); const calendar = google.calendar({
version: "v3",
auth: myGoogleAuth,
});
calendar.events.insert( calendar.events.insert(
{ {
auth: myGoogleAuth, auth: myGoogleAuth,
@ -385,7 +413,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
}, },
function (err, event) { function (err, event) {
if (err) { if (err) {
console.error("There was an error contacting google calendar service: ", err); console.error(
"There was an error contacting google calendar service: ",
err
);
return reject(err); return reject(err);
} }
return resolve(event.data); return resolve(event.data);
@ -418,7 +449,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
payload["location"] = event.location; payload["location"] = event.location;
} }
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth }); const calendar = google.calendar({
version: "v3",
auth: myGoogleAuth,
});
calendar.events.update( calendar.events.update(
{ {
auth: myGoogleAuth, auth: myGoogleAuth,
@ -430,7 +464,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
}, },
function (err, event) { function (err, event) {
if (err) { if (err) {
console.error("There was an error contacting google calendar service: ", err); console.error(
"There was an error contacting google calendar service: ",
err
);
return reject(err); return reject(err);
} }
return resolve(event.data); return resolve(event.data);
@ -441,7 +478,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
deleteEvent: (uid: string) => deleteEvent: (uid: string) =>
new Promise((resolve, reject) => new Promise((resolve, reject) =>
auth.getToken().then((myGoogleAuth) => { auth.getToken().then((myGoogleAuth) => {
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth }); const calendar = google.calendar({
version: "v3",
auth: myGoogleAuth,
});
calendar.events.delete( calendar.events.delete(
{ {
auth: myGoogleAuth, auth: myGoogleAuth,
@ -452,7 +492,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
}, },
function (err, event) { function (err, event) {
if (err) { if (err) {
console.error("There was an error contacting google calendar service: ", err); console.error(
"There was an error contacting google calendar service: ",
err
);
return reject(err); return reject(err);
} }
return resolve(event.data); return resolve(event.data);
@ -463,7 +506,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
listCalendars: () => listCalendars: () =>
new Promise((resolve, reject) => new Promise((resolve, reject) =>
auth.getToken().then((myGoogleAuth) => { auth.getToken().then((myGoogleAuth) => {
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth }); const calendar = google.calendar({
version: "v3",
auth: myGoogleAuth,
});
calendar.calendarList calendar.calendarList
.list() .list()
.then((cals) => { .then((cals) => {
@ -480,7 +526,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
); );
}) })
.catch((err) => { .catch((err) => {
console.error("There was an error contacting google calendar service: ", err); console.error(
"There was an error contacting google calendar service: ",
err
);
reject(err); reject(err);
}); });
}) })
@ -503,19 +552,29 @@ const calendars = (withCredentials): CalendarApiAdapter[] =>
}) })
.filter(Boolean); .filter(Boolean);
const getBusyCalendarTimes = (withCredentials, dateFrom, dateTo, selectedCalendars) => const getBusyCalendarTimes = (
withCredentials,
dateFrom,
dateTo,
selectedCalendars
) =>
Promise.all( Promise.all(
calendars(withCredentials).map((c) => c.getAvailability(dateFrom, dateTo, selectedCalendars)) calendars(withCredentials).map((c) =>
c.getAvailability(dateFrom, dateTo, selectedCalendars)
)
).then((results) => { ).then((results) => {
return results.reduce((acc, availability) => acc.concat(availability), []); return results.reduce((acc, availability) => acc.concat(availability), []);
}); });
const listCalendars = (withCredentials) => const listCalendars = (withCredentials) =>
Promise.all(calendars(withCredentials).map((c) => c.listCalendars())).then((results) => Promise.all(calendars(withCredentials).map((c) => c.listCalendars())).then(
results.reduce((acc, calendars) => acc.concat(calendars), []) (results) => results.reduce((acc, calendars) => acc.concat(calendars), [])
); );
const createEvent = async (credential: Credential, calEvent: CalendarEvent): Promise<unknown> => { const createEvent = async (
credential: Credential,
calEvent: CalendarEvent
): Promise<unknown> => {
const parser: CalEventParser = new CalEventParser(calEvent); const parser: CalEventParser = new CalEventParser(calEvent);
const uid: string = parser.getUid(); const uid: string = parser.getUid();
/* /*
@ -525,7 +584,9 @@ const createEvent = async (credential: Credential, calEvent: CalendarEvent): Pro
*/ */
const richEvent: CalendarEvent = parser.asRichEventPlain(); const richEvent: CalendarEvent = parser.asRichEventPlain();
const creationResult = credential ? await calendars([credential])[0].createEvent(richEvent) : null; const creationResult = credential
? await calendars([credential])[0].createEvent(richEvent)
: null;
const maybeHangoutLink = creationResult?.hangoutLink; const maybeHangoutLink = creationResult?.hangoutLink;
const maybeEntryPoints = creationResult?.entryPoints; const maybeEntryPoints = creationResult?.entryPoints;

View file

@ -1,12 +1,6 @@
import Head from "next/head"; import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@components/Dialog";
import Link from "next/link"; import Loader from "@components/Loader";
import prisma from "../../lib/prisma";
import Shell from "../../components/Shell";
import { useRouter } from "next/router";
import { getSession, useSession } from "next-auth/client";
import React, { Fragment, useRef } from "react";
import { Menu, Transition } from "@headlessui/react"; import { Menu, Transition } from "@headlessui/react";
import { import {
ClockIcon, ClockIcon,
DotsHorizontalIcon, DotsHorizontalIcon,
@ -16,9 +10,14 @@ import {
PlusIcon, PlusIcon,
UserIcon, UserIcon,
} from "@heroicons/react/solid"; } from "@heroicons/react/solid";
import Loader from "@components/Loader";
import classNames from "@lib/classNames"; import classNames from "@lib/classNames";
import { Dialog, DialogContent, DialogTrigger, DialogClose } from "@components/Dialog"; import { getSession, useSession } from "next-auth/client";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import React, { Fragment, useRef } from "react";
import Shell from "../../components/Shell";
import prisma from "../../lib/prisma";
export default function Availability({ user, types }) { export default function Availability({ user, types }) {
const [session, loading] = useSession(); const [session, loading] = useSession();
@ -225,7 +224,10 @@ export default function Availability({ user, types }) {
<div className="hidden sm:flex mt-4 flex-shrink-0 sm:mt-0 sm:ml-5"> <div className="hidden sm:flex mt-4 flex-shrink-0 sm:mt-0 sm:ml-5">
<div className="flex overflow-hidden space-x-5"> <div className="flex overflow-hidden space-x-5">
<Link href={"/" + session.user.username + "/" + type.slug}> <Link href={"/" + session.user.username + "/" + type.slug}>
<a className="text-neutral-400 p-2 border border-transparent hover:border-gray-200"> <a
target="_blank"
rel="noreferrer"
className="text-neutral-400 p-2 border border-transparent hover:border-gray-200">
<ExternalLinkIcon className="w-5 h-5" /> <ExternalLinkIcon className="w-5 h-5" />
</a> </a>
</Link> </Link>