From 1582cfd43f21a88cd2eec9e387d8e7e971bb90a4 Mon Sep 17 00:00:00 2001 From: Bailey Pumfleet Date: Fri, 26 Mar 2021 15:51:19 +0000 Subject: [PATCH] Add self-authentication with Google Calendar API --- components/Shell.tsx | 21 ++- pages/api/integrations.ts | 15 ++- pages/api/integrations/googlecalendar/add.ts | 37 +++++ .../integrations/googlecalendar/callback.ts | 45 +++++++ pages/integrations.tsx | 126 ++++++++++++++++-- 5 files changed, 225 insertions(+), 19 deletions(-) create mode 100644 pages/api/integrations/googlecalendar/add.ts create mode 100644 pages/api/integrations/googlecalendar/callback.ts diff --git a/components/Shell.tsx b/components/Shell.tsx index 07cad0bae7..0d32940b5e 100644 --- a/components/Shell.tsx +++ b/components/Shell.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { useState } from "react"; import { signOut, useSession } from 'next-auth/client'; @@ -22,11 +23,21 @@ export default function Shell(props) {
- Dashboard - Bookings - Availability - Integrations - Team + + Dashboard + + + Bookings + + + Availability + + + Integrations + + + Team +
diff --git a/pages/api/integrations.ts b/pages/api/integrations.ts index a5e406341e..7401a98de5 100644 --- a/pages/api/integrations.ts +++ b/pages/api/integrations.ts @@ -8,14 +8,23 @@ export default async function handler(req, res) { if (!session) { res.status(401).json({message: 'You must be logged in to do this'}); return; } - // TODO: Add user ID so filtering works properly. User object does not include ID currently. + // TODO: Add user ID to user session object + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true + } + }); const credentials = await prisma.credential.findMany({ where: { - userId: session.user.id, + userId: user.id, }, select: { - type: true + type: true, + key: true } }); diff --git a/pages/api/integrations/googlecalendar/add.ts b/pages/api/integrations/googlecalendar/add.ts new file mode 100644 index 0000000000..18520ae0af --- /dev/null +++ b/pages/api/integrations/googlecalendar/add.ts @@ -0,0 +1,37 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { getSession } from 'next-auth/client'; +import prisma from '../../../../lib/prisma'; +const {google} = require('googleapis'); + +const credentials = process.env.GOOGLE_API_CREDENTIALS; +const scopes = ['https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/calendar.events']; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === 'GET') { + // Check that user is authenticated + const session = await getSession({req: req}); + + if (!session) { res.status(401).json({message: 'You must be logged in to do this'}); return; } + + // TODO: Add user ID to user session object + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true + } + }); + + // Get token from Google Calendar API + const {client_secret, client_id, redirect_uris} = JSON.parse(credentials).web; + const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); + + const authUrl = oAuth2Client.generateAuthUrl({ + access_type: 'offline', + scope: scopes, + }); + + res.status(200).json({url: authUrl}); + } +} \ No newline at end of file diff --git a/pages/api/integrations/googlecalendar/callback.ts b/pages/api/integrations/googlecalendar/callback.ts new file mode 100644 index 0000000000..6e0c2f220b --- /dev/null +++ b/pages/api/integrations/googlecalendar/callback.ts @@ -0,0 +1,45 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { getSession } from 'next-auth/client'; +import prisma from '../../../../lib/prisma'; +const {google} = require('googleapis'); + +const credentials = process.env.GOOGLE_API_CREDENTIALS; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { code } = req.query + + // Check that user is authenticated + const session = await getSession({req: req}); + + if (!session) { res.status(401).json({message: 'You must be logged in to do this'}); return; } + + // TODO: Add user ID to user session object + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true + } + }); + + const {client_secret, client_id, redirect_uris} = JSON.parse(credentials).web; + const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); + + // Convert to token + oAuth2Client.getToken(code, async (err, token) => { + if (err) return console.error('Error retrieving access token', err); + + const credential = await prisma.credential.create({ + data: { + type: 'google_calendar', + key: token, + userId: user.id + } + }); + }); + + // Add the credential + + res.redirect('/integrations'); +} \ No newline at end of file diff --git a/pages/integrations.tsx b/pages/integrations.tsx index 4b2e8d0a66..445845fa7f 100644 --- a/pages/integrations.tsx +++ b/pages/integrations.tsx @@ -4,14 +4,15 @@ import { useState } from 'react'; import { useSession, getSession } from 'next-auth/client'; export default function Home() { - const [ session, loading ] = useSession(); - const [ integrations, setIntegrations ] = useState([]); + const [session, loading] = useSession(); + const [integrations, setIntegrations] = useState([]); + const [showAddModal, setShowAddModal] = useState(false); if (loading) { return

Loading...

; } else { if (!session) { - window.location.href="/"; + window.location.href = "/"; } } @@ -21,8 +22,19 @@ export default function Home() { .then((data) => setIntegrations(data)); } + // TODO: Stop this function from running repeatedly getIntegrations() + function toggleAddModal() { + setShowAddModal(!showAddModal); + } + + function googleCalendarHandler() { + fetch('/api/integrations/googlecalendar/add') + .then((response) => response.json()) + .then((data) => window.location.href = data.url); + } + return (
@@ -39,7 +51,7 @@ export default function Home() {
- {integration.type == 'google_calendar' && Google Calendar} + {integration.type == 'google_calendar' && Google Calendar}
@@ -59,10 +71,10 @@ export default function Home() {

} {!integration.key && -

- - - +

+ + + Not connected

} @@ -72,10 +84,10 @@ export default function Home() {
@@ -83,7 +95,99 @@ export default function Home() { )} + {integrations.length == 0 && +
+
+
+ + + +
+
+

+ You don't have any integrations added. +

+
+

+ You currently do not have any integrations set up. Add your first integration to get started. +

+
+
+ +
+
+
+
+ }
+ {showAddModal && +
+
+ {/* */} + + + {/* */} +
+
+
+ + + +
+
+ +
+

+ Link a new integration to your account. +

+
+
+
+
+
    +
  • +
    + Google Calendar +
    +
    +

    Google Calendar

    +

    For personal and business accounts

    +
    +
    + +
    +
  • +
+
+
+ +
+
+
+
+ }
);