From cfaad2e25bf78e742869c577bf5c0edbd2d5ac77 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 19:19:39 +0530 Subject: [PATCH 01/72] chore: remove comment --- server/src/models/User.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/models/User.ts b/server/src/models/User.ts index b50c1e0f..5a3d552c 100644 --- a/server/src/models/User.ts +++ b/server/src/models/User.ts @@ -12,7 +12,6 @@ interface UserAttributes { proxy_password?: string | null; } -// Optional fields for creating a new user interface UserCreationAttributes extends Optional { } class User extends Model implements UserAttributes { From cff8f86d8a5c85b90a17363890bca06400ee32da Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 19:22:34 +0530 Subject: [PATCH 02/72] wip: oauth for gsheet integration --- server/src/routes/auth.ts | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 170831a5..b79179a9 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -4,6 +4,7 @@ import jwt from 'jsonwebtoken'; import { hashPassword, comparePassword } from '../utils/auth'; import { requireSignIn } from '../middlewares/auth'; import { genAPIKey } from '../utils/api'; +import { OAuth2Client } from 'google-auth-library'; export const router = Router(); interface AuthenticatedRequest extends Request { @@ -163,3 +164,55 @@ router.delete('/delete-api-key', requireSignIn, async (req, res) => { return res.status(500).json({ message: 'Error deleting API key', error: error.message }); } }); + +const googleClient = new OAuth2Client( + process.env.GOOGLE_CLIENT_ID, +); + +router.post('/google', requireSignIn, async (req: AuthenticatedRequest, res) => { + const { token } = req.body; + + const ticket = await googleClient.verifyIdToken({ + idToken: token, + audience: `${process.env.GOOGLE_CLIENT_ID}`, + }); + + const { name, email, picture, email_verified } = ticket.getPayload(); + + let emailVerified: boolean; + let userExist: any; + let user: any; + + if (email_verified) { + try { + emailVerified = true; + let userExist = await User.findOne({ raw: true, where: { email } }); + if (userExist) return res.status(400).send('User already exists') + } catch (error) { + return res.status(500).send(`Could not verify the user - ${error.message}.`); + } + } + + if (!userExist) { + emailVerified = false; + const hashedPassword = await hashPassword(email + name + email) + try { + user = await User.create({ email, password: hashedPassword }); + } catch (err) { + return res.status(500).send(`Could not create user - ${err.message}.`); + } + } + const jwtToken = jwt.sign({ + _id: user._id + }, process.env.JWT_SECRET as string, { + expiresIn: '3d' + }) + // return user and token to client, exclude hashed password + user.password = undefined + // send token in cookie + res.cookie('token', jwtToken, { + httpOnly: true + }) + // send user and token as json response + res.json({ user, jwtToken }); +}) From f756f8e6c1d97ef5df1a572273e832b6d83c3835 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:00:54 +0530 Subject: [PATCH 03/72] feat: google sheets email, access token & refresh token --- server/src/models/User.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server/src/models/User.ts b/server/src/models/User.ts index 5a3d552c..e0870015 100644 --- a/server/src/models/User.ts +++ b/server/src/models/User.ts @@ -10,6 +10,9 @@ interface UserAttributes { proxy_url?: string | null; proxy_username?: string | null; proxy_password?: string | null; + google_sheets_email?: string | null; + google_access_token?: string | null; + google_refresh_token?: string | null; } interface UserCreationAttributes extends Optional { } @@ -23,6 +26,9 @@ class User extends Model implements User public proxy_url!: string | null; public proxy_username!: string | null; public proxy_password!: string | null; + public google_sheets_email!: string | null; + public google_access_token!: string | null; + public google_refresh_token!: string | null; } User.init( @@ -72,6 +78,18 @@ User.init( type: DataTypes.STRING, allowNull: true, }, + google_sheets_email: { + type: DataTypes.STRING, + allowNull: true, + }, + google_access_token: { + type: DataTypes.STRING, + allowNull: true, + }, + google_refresh_token: { + type: DataTypes.STRING, + allowNull: true, + }, }, { sequelize, From 0b92b0001e3da31d57e86db19eed7c1b35cd79c0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:09:11 +0530 Subject: [PATCH 04/72] feat: handle google oauth & sheet scopes --- server/src/routes/auth.ts | 129 +++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 44 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index b79179a9..eb9a4bbe 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -4,7 +4,7 @@ import jwt from 'jsonwebtoken'; import { hashPassword, comparePassword } from '../utils/auth'; import { requireSignIn } from '../middlewares/auth'; import { genAPIKey } from '../utils/api'; -import { OAuth2Client } from 'google-auth-library'; +import { google } from 'googleapis'; export const router = Router(); interface AuthenticatedRequest extends Request { @@ -165,54 +165,95 @@ router.delete('/delete-api-key', requireSignIn, async (req, res) => { } }); -const googleClient = new OAuth2Client( +const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI ); -router.post('/google', requireSignIn, async (req: AuthenticatedRequest, res) => { - const { token } = req.body; +// Step 1: Redirect to Google for authentication +router.get('/google', (req, res) => { + const scopes = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/userinfo.email']; + const url = oauth2Client.generateAuthUrl({ + access_type: 'offline', + prompt: 'consent', // Ensures you get a refresh token on first login + scope: scopes, + }); + res.redirect(url); +}); - const ticket = await googleClient.verifyIdToken({ - idToken: token, - audience: `${process.env.GOOGLE_CLIENT_ID}`, +// Step 2: Handle Google OAuth callback +router.get('/google/callback', async (req, res) => { + const { code } = req.query; + + try { + // Get access and refresh tokens + if (typeof code !== 'string') { + return res.status(400).json({ message: 'Invalid code' }); + } + const { tokens } = await oauth2Client.getToken(code); + oauth2Client.setCredentials(tokens); + + // Get user profile from Google + const oauth2 = google.oauth2({ version: 'v2', auth: oauth2Client }); + const { data: { email } } = await oauth2.userinfo.get(); + + if (!email) { + return res.status(400).json({ message: 'Email not found' }); + } + + // Check if user already exists + let user = await User.findOne({ where: { email } }); + if (!user) { + const hashedPassword = await hashPassword(email + process.env.JWT_SECRET); + user = await User.create({ + email, + password: hashedPassword, + google_sheets_email: email, // Gmail used for Sheets + google_access_token: tokens.access_token, + google_refresh_token: tokens.refresh_token, + }); + } else { + // Update user's Google tokens if they exist + await User.update({ + google_access_token: tokens.access_token, + google_refresh_token: tokens.refresh_token, + }, { where: { email } }); + } + + // Generate JWT token for session + const jwtToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' }); + res.cookie('token', jwtToken, { httpOnly: true }); + + res.json({ message: 'Google authentication successful', user, jwtToken }); + } catch (error) { + res.status(500).json({ message: `Google OAuth error: ${error.message}` }); + } +}); + +// Step 3: Get data from Google Sheets +router.get('/gsheets/data', async (req, res) => { + const user = await User.findOne({ where: { id: req.userId } }); + if (!user) { + return res.status(400).json({ message: 'User not found' }); + } + + // Set Google OAuth credentials + oauth2Client.setCredentials({ + access_token: user.google_access_token, + refresh_token: user.google_refresh_token, }); - const { name, email, picture, email_verified } = ticket.getPayload(); + // If the access token has expired, it will be automatically refreshed using the refresh token + const sheets = google.sheets({ version: 'v4', auth: oauth2Client }); - let emailVerified: boolean; - let userExist: any; - let user: any; - - if (email_verified) { - try { - emailVerified = true; - let userExist = await User.findOne({ raw: true, where: { email } }); - if (userExist) return res.status(400).send('User already exists') - } catch (error) { - return res.status(500).send(`Could not verify the user - ${error.message}.`); - } + try { + const sheetData = await sheets.spreadsheets.values.get({ + spreadsheetId: 'your-spreadsheet-id', + range: 'Sheet1!A1:D5', // Example range + }); + res.json(sheetData.data); + } catch (error) { + res.status(500).json({ message: `Error accessing Google Sheets: ${error.message}` }); } - - if (!userExist) { - emailVerified = false; - const hashedPassword = await hashPassword(email + name + email) - try { - user = await User.create({ email, password: hashedPassword }); - } catch (err) { - return res.status(500).send(`Could not create user - ${err.message}.`); - } - } - const jwtToken = jwt.sign({ - _id: user._id - }, process.env.JWT_SECRET as string, { - expiresIn: '3d' - }) - // return user and token to client, exclude hashed password - user.password = undefined - // send token in cookie - res.cookie('token', jwtToken, { - httpOnly: true - }) - // send user and token as json response - res.json({ user, jwtToken }); -}) +}); From 5264789bba7508212fa7187558a50e609724c402 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:11:53 +0530 Subject: [PATCH 05/72] feat: read only gdrive access --- server/src/routes/auth.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index eb9a4bbe..9b3c98a0 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -173,7 +173,11 @@ const oauth2Client = new google.auth.OAuth2( // Step 1: Redirect to Google for authentication router.get('/google', (req, res) => { - const scopes = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/userinfo.email']; + const scopes = [ + 'https://www.googleapis.com/auth/spreadsheets', + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/drive.readonly', + ]; const url = oauth2Client.generateAuthUrl({ access_type: 'offline', prompt: 'consent', // Ensures you get a refresh token on first login From 1126890afae9ef5c6b0d1cda0f28cf5f3d52ba27 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:13:44 +0530 Subject: [PATCH 06/72] feat: access all gsheet files from gdrive --- server/src/routes/auth.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 9b3c98a0..0c8034b3 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -225,11 +225,23 @@ router.get('/google/callback', async (req, res) => { }, { where: { email } }); } + // List user's Google Sheets from their Google Drive + const drive = google.drive({ version: 'v3', auth: oauth2Client }); + const response = await drive.files.list({ + q: "mimeType='application/vnd.google-apps.spreadsheet'", // List only Google Sheets files + fields: 'files(id, name)', // Retrieve the ID and name of each file + }); + + const files = response.data.files || []; + if (files.length === 0) { + return res.status(404).json({ message: 'No spreadsheets found.' }); + } + // Generate JWT token for session const jwtToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' }); res.cookie('token', jwtToken, { httpOnly: true }); - res.json({ message: 'Google authentication successful', user, jwtToken }); + res.json({ message: 'Google authentication successful', user, jwtToken, files }); } catch (error) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); } From fee31bff7104b37f3d9c85309beb127b7f6441fd Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:14:00 +0530 Subject: [PATCH 07/72] chore: lint --- server/src/routes/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 0c8034b3..31ec6394 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -189,7 +189,7 @@ router.get('/google', (req, res) => { // Step 2: Handle Google OAuth callback router.get('/google/callback', async (req, res) => { const { code } = req.query; - + try { // Get access and refresh tokens if (typeof code !== 'string') { From c4972488aeb8a4576b261d0689f0ed128d0eae00 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:15:10 +0530 Subject: [PATCH 08/72] feat: get gsheet data --- server/src/routes/auth.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 31ec6394..b41a6241 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -248,8 +248,10 @@ router.get('/google/callback', async (req, res) => { }); // Step 3: Get data from Google Sheets -router.get('/gsheets/data', async (req, res) => { - const user = await User.findOne({ where: { id: req.userId } }); +router.post('/gsheets/data', async (req, res) => { + const { spreadsheetId } = req.body; + const user = await User.findOne({ where: { id: req.user.id } }); + if (!user) { return res.status(400).json({ message: 'User not found' }); } @@ -260,13 +262,13 @@ router.get('/gsheets/data', async (req, res) => { refresh_token: user.google_refresh_token, }); - // If the access token has expired, it will be automatically refreshed using the refresh token const sheets = google.sheets({ version: 'v4', auth: oauth2Client }); try { + // Fetch data from the spreadsheet (you can let the user choose a specific range too) const sheetData = await sheets.spreadsheets.values.get({ - spreadsheetId: 'your-spreadsheet-id', - range: 'Sheet1!A1:D5', // Example range + spreadsheetId, + range: 'Sheet1!A1:D5', // Default range, could be dynamic based on user input }); res.json(sheetData.data); } catch (error) { From 04679373c1c6fc48bf512280830fafe35c1ca167 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:25:02 +0530 Subject: [PATCH 09/72] fix: set error type as any --- server/src/routes/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index b41a6241..caac1d81 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -242,7 +242,7 @@ router.get('/google/callback', async (req, res) => { res.cookie('token', jwtToken, { httpOnly: true }); res.json({ message: 'Google authentication successful', user, jwtToken, files }); - } catch (error) { + } catch (error: any) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); } }); @@ -271,7 +271,7 @@ router.post('/gsheets/data', async (req, res) => { range: 'Sheet1!A1:D5', // Default range, could be dynamic based on user input }); res.json(sheetData.data); - } catch (error) { + } catch (error: any) { res.status(500).json({ message: `Error accessing Google Sheets: ${error.message}` }); } }); From 7b8000101a7b86502a2804a4e234c43903300f19 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 20:47:14 +0530 Subject: [PATCH 10/72] wip: gsheet integration ui --- .../molecules/IntegrationSettings.tsx | 66 +++++++++++++++---- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index ed6bea50..485c762c 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -1,7 +1,8 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { GenericModal } from "../atoms/GenericModal"; -import { MenuItem, TextField, Typography } from "@mui/material"; +import { MenuItem, TextField, Typography, CircularProgress } from "@mui/material"; import Button from "@mui/material/Button"; +import axios from 'axios'; import { modalStyle } from "./AddWhereCondModal"; interface IntegrationProps { @@ -18,18 +19,40 @@ export interface IntegrationSettings { } export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: IntegrationProps) => { - const [settings, setSettings] = useState({ credentials: '', spreadsheetId: '', range: '', data: '', }); + const [spreadsheets, setSpreadsheets] = useState<{ id: string, name: string }[]>([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (isOpen) { + // Fetch Google Sheets from backend when modal is opened + setLoading(true); + axios.get('http://localhost:8080/auth/google/callback') + .then(response => { + setSpreadsheets(response.data.files); + setLoading(false); + }) + .catch(error => { + setError(`Error fetching spreadsheets: ${error}`); + setLoading(false); + }); + } + }, [isOpen]); const handleChange = (field: keyof IntegrationSettings) => (e: React.ChangeEvent) => { setSettings({ ...settings, [field]: e.target.value }); }; + const handleSpreadsheetSelect = (e: React.ChangeEvent) => { + setSettings({ ...settings, spreadsheetId: e.target.value }); + }; + return ( - + {loading ? ( + + ) : error ? ( + {error} + ) : ( + + {spreadsheets.map(sheet => ( + + {sheet.name} + + ))} + + )} - From 0b78e132fbf8716d2faf75405906ef43b2619fdb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 21:14:58 +0530 Subject: [PATCH 11/72] feat: send user data --- server/src/routes/auth.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index caac1d81..16e9a984 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -168,7 +168,7 @@ router.delete('/delete-api-key', requireSignIn, async (req, res) => { const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, - process.env.GOOGLE_REDIRECT_URI + // process.env.GOOGLE_REDIRECT_URI ); // Step 1: Redirect to Google for authentication @@ -241,12 +241,18 @@ router.get('/google/callback', async (req, res) => { const jwtToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' }); res.cookie('token', jwtToken, { httpOnly: true }); - res.json({ message: 'Google authentication successful', user, jwtToken, files }); + res.json({ + message: 'Google authentication successful', + email: user.email, + jwtToken, + files + }); } catch (error: any) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); } }); + // Step 3: Get data from Google Sheets router.post('/gsheets/data', async (req, res) => { const { spreadsheetId } = req.body; From 4a56cfbde9194b776f4a5efc088a1fb60d8a325b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 21:18:16 +0530 Subject: [PATCH 12/72] feat: pass requireSignIn --- server/src/routes/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 16e9a984..bce87fc0 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -187,7 +187,7 @@ router.get('/google', (req, res) => { }); // Step 2: Handle Google OAuth callback -router.get('/google/callback', async (req, res) => { +router.get('/google/callback', requireSignIn, async (req, res) => { const { code } = req.query; try { @@ -254,7 +254,7 @@ router.get('/google/callback', async (req, res) => { // Step 3: Get data from Google Sheets -router.post('/gsheets/data', async (req, res) => { +router.post('/gsheets/data', requireSignIn, async (req, res) => { const { spreadsheetId } = req.body; const user = await User.findOne({ where: { id: req.user.id } }); From 13266d64b57549109bcadc3252868cd9e1af3484 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 21:47:11 +0530 Subject: [PATCH 13/72] fix: update user/s google sheet email --- server/src/routes/auth.ts | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index bce87fc0..3a9ae351 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -168,7 +168,7 @@ router.delete('/delete-api-key', requireSignIn, async (req, res) => { const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, - // process.env.GOOGLE_REDIRECT_URI + process.env.GOOGLE_REDIRECT_URI ); // Step 1: Redirect to Google for authentication @@ -206,25 +206,20 @@ router.get('/google/callback', requireSignIn, async (req, res) => { return res.status(400).json({ message: 'Email not found' }); } - // Check if user already exists - let user = await User.findOne({ where: { email } }); + // Get the currently authenticated user (from `requireSignIn`) + let user = await User.findOne({ where: { id: req.user.id } }); + if (!user) { - const hashedPassword = await hashPassword(email + process.env.JWT_SECRET); - user = await User.create({ - email, - password: hashedPassword, - google_sheets_email: email, // Gmail used for Sheets - google_access_token: tokens.access_token, - google_refresh_token: tokens.refresh_token, - }); - } else { - // Update user's Google tokens if they exist - await User.update({ - google_access_token: tokens.access_token, - google_refresh_token: tokens.refresh_token, - }, { where: { email } }); + return res.status(400).json({ message: 'User not found' }); } + // Update the current user's Google Sheets email and tokens + user = await user.update({ + google_sheets_email: email, + google_access_token: tokens.access_token, + google_refresh_token: tokens.refresh_token, + }); + // List user's Google Sheets from their Google Drive const drive = google.drive({ version: 'v3', auth: oauth2Client }); const response = await drive.files.list({ @@ -243,7 +238,7 @@ router.get('/google/callback', requireSignIn, async (req, res) => { res.json({ message: 'Google authentication successful', - email: user.email, + google_sheet_email: user.google_sheets_email, jwtToken, files }); @@ -252,7 +247,6 @@ router.get('/google/callback', requireSignIn, async (req, res) => { } }); - // Step 3: Get data from Google Sheets router.post('/gsheets/data', requireSignIn, async (req, res) => { const { spreadsheetId } = req.body; From 35befca5585bc100daa78ac854f14401183cb413 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 21:47:25 +0530 Subject: [PATCH 14/72] chore: lint --- server/src/routes/auth.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 3a9ae351..95889ead 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -236,11 +236,11 @@ router.get('/google/callback', requireSignIn, async (req, res) => { const jwtToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' }); res.cookie('token', jwtToken, { httpOnly: true }); - res.json({ - message: 'Google authentication successful', - google_sheet_email: user.google_sheets_email, - jwtToken, - files + res.json({ + message: 'Google authentication successful', + google_sheet_email: user.google_sheets_email, + jwtToken, + files }); } catch (error: any) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); From e380904ad9832758e81da1eff9d1be683b6778c6 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 22:34:31 +0530 Subject: [PATCH 15/72] feat: redirect to localhost:3000 --- server/src/routes/auth.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 95889ead..42c463b6 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -242,6 +242,7 @@ router.get('/google/callback', requireSignIn, async (req, res) => { jwtToken, files }); + res.redirect('http://localhost:3000'); } catch (error: any) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); } From e4b5d922db1baa387366b88185fe166904a8cb6d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 22:37:53 +0530 Subject: [PATCH 16/72] feat: handle oauth --- .../molecules/IntegrationSettings.tsx | 162 ++++++++++-------- 1 file changed, 88 insertions(+), 74 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 485c762c..a7c68c96 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -1,9 +1,9 @@ import React, { useState, useEffect } from 'react'; import { GenericModal } from "../atoms/GenericModal"; -import { MenuItem, TextField, Typography, CircularProgress } from "@mui/material"; +import { MenuItem, Typography, CircularProgress } from "@mui/material"; import Button from "@mui/material/Button"; +import TextField from "@mui/material/TextField"; import axios from 'axios'; -import { modalStyle } from "./AddWhereCondModal"; interface IntegrationProps { isOpen: boolean; @@ -12,52 +12,56 @@ interface IntegrationProps { } export interface IntegrationSettings { - credentials: string; spreadsheetId: string; - range: string; data: string; } export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: IntegrationProps) => { const [settings, setSettings] = useState({ - credentials: '', spreadsheetId: '', - range: '', data: '', }); + const [spreadsheets, setSpreadsheets] = useState<{ id: string, name: string }[]>([]); + const [userInfo, setUserInfo] = useState<{ email: string } | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); + const [isAuthenticated, setIsAuthenticated] = useState(false); - useEffect(() => { - if (isOpen) { - // Fetch Google Sheets from backend when modal is opened - setLoading(true); - axios.get('http://localhost:8080/auth/google/callback') - .then(response => { - setSpreadsheets(response.data.files); - setLoading(false); - }) - .catch(error => { - setError(`Error fetching spreadsheets: ${error}`); - setLoading(false); - }); - } - }, [isOpen]); - - const handleChange = (field: keyof IntegrationSettings) => (e: React.ChangeEvent) => { - setSettings({ ...settings, [field]: e.target.value }); + // Function to trigger Google OAuth authentication + const authenticateWithGoogle = () => { + window.location.href = 'http://localhost:8080/auth/google'; // Redirect to backend Google OAuth route }; + // Function to handle Google OAuth callback and fetch spreadsheets + const handleOAuthCallback = async () => { + try { + const response = await axios.get('http://localhost:8080/auth/google/callback'); + const { email, files } = response.data; + setUserInfo({ email }); + setSpreadsheets(files); + setIsAuthenticated(true); + } catch (error) { + setError('Error authenticating with Google'); + } + }; + + // Handle spreadsheet selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { setSettings({ ...settings, spreadsheetId: e.target.value }); }; + useEffect(() => { + // Simulate handling OAuth callback here after redirect + if (window.location.pathname === 'http://localhost:8080/auth/google/callback') { + handleOAuthCallback(); + } + }, [isOpen]); + return (
Google Sheets Integration - - - {loading ? ( - - ) : error ? ( - {error} - ) : ( - - {spreadsheets.map(sheet => ( - - {sheet.name} - - ))} - + Authenticate with Google + + ) : ( + <> + {/* Show user info and allow spreadsheet selection once authenticated */} + {userInfo && ( + + Logged in as: {userInfo.email} + + )} + + {loading ? ( + + ) : error ? ( + {error} + ) : ( + <> + + {spreadsheets.map(sheet => ( + + {sheet.name} + + ))} + + + {/* Display selected spreadsheet name */} + {settings.spreadsheetId && ( + + Selected Spreadsheet ID: {settings.spreadsheetId} + + )} + + )} + + + )} - - - -
); From b7e7f36f7bc716d1ecaf8fcbaa644b933f88e490 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 22:53:02 +0530 Subject: [PATCH 17/72] feat: include googhle_sheet_id --- server/src/models/User.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/src/models/User.ts b/server/src/models/User.ts index e0870015..33aeaea8 100644 --- a/server/src/models/User.ts +++ b/server/src/models/User.ts @@ -11,6 +11,7 @@ interface UserAttributes { proxy_username?: string | null; proxy_password?: string | null; google_sheets_email?: string | null; + google_sheet_id?: string | null; google_access_token?: string | null; google_refresh_token?: string | null; } @@ -27,6 +28,7 @@ class User extends Model implements User public proxy_username!: string | null; public proxy_password!: string | null; public google_sheets_email!: string | null; + public google_sheet_id?: string | null; public google_access_token!: string | null; public google_refresh_token!: string | null; } @@ -82,6 +84,10 @@ User.init( type: DataTypes.STRING, allowNull: true, }, + google_sheet_id: { + type: DataTypes.STRING, + allowNull: true, + }, google_access_token: { type: DataTypes.STRING, allowNull: true, From b2266252c51761366d5b65cf6f60d7711cd5e73b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 23:38:18 +0530 Subject: [PATCH 18/72] feat: move gsheet metadata to robot --- server/src/models/Robot.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/server/src/models/Robot.ts b/server/src/models/Robot.ts index ed9d7780..ed751ca8 100644 --- a/server/src/models/Robot.ts +++ b/server/src/models/Robot.ts @@ -19,6 +19,10 @@ interface RobotAttributes { id: string; recording_meta: RobotMeta; recording: RobotWorkflow; + google_sheets_email?: string | null; + google_sheet_id?: string | null; + google_access_token?: string | null; + google_refresh_token?: string | null; } interface RobotCreationAttributes extends Optional { } @@ -27,6 +31,10 @@ class Robot extends Model implements R public id!: string; public recording_meta!: RobotMeta; public recording!: RobotWorkflow; + public google_sheets_email!: string | null; + public google_sheet_id?: string | null; + public google_access_token!: string | null; + public google_refresh_token!: string | null; } Robot.init( @@ -44,6 +52,22 @@ Robot.init( type: DataTypes.JSONB, allowNull: false, }, + google_sheets_email: { + type: DataTypes.STRING, + allowNull: true, + }, + google_sheet_id: { + type: DataTypes.STRING, + allowNull: true, + }, + google_access_token: { + type: DataTypes.STRING, + allowNull: true, + }, + google_refresh_token: { + type: DataTypes.STRING, + allowNull: true, + }, }, { sequelize, From 09a4b59d4603df59a6d0030985d84273c06c253f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 23:38:34 +0530 Subject: [PATCH 19/72] chore: lint --- server/src/models/Robot.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/src/models/Robot.ts b/server/src/models/Robot.ts index ed751ca8..70e3db8d 100644 --- a/server/src/models/Robot.ts +++ b/server/src/models/Robot.ts @@ -55,19 +55,19 @@ Robot.init( google_sheets_email: { type: DataTypes.STRING, allowNull: true, - }, - google_sheet_id: { + }, + google_sheet_id: { type: DataTypes.STRING, allowNull: true, - }, - google_access_token: { + }, + google_access_token: { type: DataTypes.STRING, allowNull: true, - }, - google_refresh_token: { + }, + google_refresh_token: { type: DataTypes.STRING, allowNull: true, - }, + }, }, { sequelize, From 78912dedb5831b5972bb3e0a785cbacbdf06cc63 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 23:41:08 +0530 Subject: [PATCH 20/72] fix: move gsheet logic --- server/src/models/User.ts | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/server/src/models/User.ts b/server/src/models/User.ts index 33aeaea8..5a3d552c 100644 --- a/server/src/models/User.ts +++ b/server/src/models/User.ts @@ -10,10 +10,6 @@ interface UserAttributes { proxy_url?: string | null; proxy_username?: string | null; proxy_password?: string | null; - google_sheets_email?: string | null; - google_sheet_id?: string | null; - google_access_token?: string | null; - google_refresh_token?: string | null; } interface UserCreationAttributes extends Optional { } @@ -27,10 +23,6 @@ class User extends Model implements User public proxy_url!: string | null; public proxy_username!: string | null; public proxy_password!: string | null; - public google_sheets_email!: string | null; - public google_sheet_id?: string | null; - public google_access_token!: string | null; - public google_refresh_token!: string | null; } User.init( @@ -80,22 +72,6 @@ User.init( type: DataTypes.STRING, allowNull: true, }, - google_sheets_email: { - type: DataTypes.STRING, - allowNull: true, - }, - google_sheet_id: { - type: DataTypes.STRING, - allowNull: true, - }, - google_access_token: { - type: DataTypes.STRING, - allowNull: true, - }, - google_refresh_token: { - type: DataTypes.STRING, - allowNull: true, - }, }, { sequelize, From 700d302ea4ea6287bfb692c431c8d16deb683f4c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 16 Oct 2024 23:52:03 +0530 Subject: [PATCH 21/72] feat: use robot instead of user --- server/src/routes/auth.ts | 34 ++++++++++++++++++++++++++-------- server/src/routes/storage.ts | 22 +++++++++++----------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 42c463b6..6775c442 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -1,5 +1,6 @@ import { Router, Request, Response } from 'express'; import User from '../models/User'; +import Robot from '../models/Robot'; import jwt from 'jsonwebtoken'; import { hashPassword, comparePassword } from '../utils/auth'; import { requireSignIn } from '../middlewares/auth'; @@ -187,10 +188,15 @@ router.get('/google', (req, res) => { }); // Step 2: Handle Google OAuth callback -router.get('/google/callback', requireSignIn, async (req, res) => { +router.post('/google/callback', requireSignIn, async (req, res) => { const { code } = req.query; + const { robotId } = req.body; try { + if (!robotId) { + return res.status(400).json({ message: 'Robot ID is required' }); + } + // Get access and refresh tokens if (typeof code !== 'string') { return res.status(400).json({ message: 'Invalid code' }); @@ -213,8 +219,13 @@ router.get('/google/callback', requireSignIn, async (req, res) => { return res.status(400).json({ message: 'User not found' }); } - // Update the current user's Google Sheets email and tokens - user = await user.update({ + let robot = await Robot.findOne({ where: { 'recording_meta.id': robotId } }); + + if (!robot) { + return res.status(400).json({ message: 'Robot not found' }); + } + + robot = await robot.update({ google_sheets_email: email, google_access_token: tokens.access_token, google_refresh_token: tokens.refresh_token, @@ -236,13 +247,14 @@ router.get('/google/callback', requireSignIn, async (req, res) => { const jwtToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' }); res.cookie('token', jwtToken, { httpOnly: true }); + res.redirect('http://localhost:3000'); + res.json({ message: 'Google authentication successful', - google_sheet_email: user.google_sheets_email, + google_sheet_email: robot.google_sheets_email, jwtToken, files }); - res.redirect('http://localhost:3000'); } catch (error: any) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); } @@ -250,17 +262,23 @@ router.get('/google/callback', requireSignIn, async (req, res) => { // Step 3: Get data from Google Sheets router.post('/gsheets/data', requireSignIn, async (req, res) => { - const { spreadsheetId } = req.body; + const { spreadsheetId, robotId } = req.body; const user = await User.findOne({ where: { id: req.user.id } }); if (!user) { return res.status(400).json({ message: 'User not found' }); } + const robot = await Robot.findOne({ where: { 'recording_meta.id': robotId } }); + + if (!robot) { + return res.status(400).json({ message: 'Robot not found' }); + } + // Set Google OAuth credentials oauth2Client.setCredentials({ - access_token: user.google_access_token, - refresh_token: user.google_refresh_token, + access_token: robot.google_access_token, + refresh_token: robot.google_refresh_token, }); const sheets = google.sheets({ version: 'v4', auth: oauth2Client }); diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index e7e92e64..9e09c8fa 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -12,7 +12,7 @@ import { requireSignIn } from '../middlewares/auth'; import Robot from '../models/Robot'; import Run from '../models/Run'; import { BinaryOutputService } from '../storage/mino'; -import { workflowQueue } from '../worker'; +// import { workflowQueue } from '../worker'; export const router = Router(); @@ -282,16 +282,16 @@ router.put('/schedule/:id/', requireSignIn, async (req, res) => { const runId = uuid(); const userId = req.user.id; - await workflowQueue.add( - 'run workflow', - { id, runId, userId }, - { - repeat: { - pattern: cronExpression, - tz: timezone - } - } - ); + // await workflowQueue.add( + // 'run workflow', + // { id, runId, userId }, + // { + // repeat: { + // pattern: cronExpression, + // tz: timezone + // } + // } + // ); res.status(200).json({ message: 'success', From 137bac49a01be0b8c09bf0e8efcd61a1259bdabd Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 00:15:28 +0530 Subject: [PATCH 22/72] feat: get google_sheet_mail + post request for google auth callback --- src/components/molecules/IntegrationSettings.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index a7c68c96..3c411045 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -36,9 +36,12 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I // Function to handle Google OAuth callback and fetch spreadsheets const handleOAuthCallback = async () => { try { - const response = await axios.get('http://localhost:8080/auth/google/callback'); - const { email, files } = response.data; - setUserInfo({ email }); + const response = await axios.post('http://localhost:8080/auth/google/callback', { + // code: new URLSearchParams(window.location.search).get('code'), + robotId: '' + }); + const { google_sheet_email, files } = response.data; + setUserInfo({ email:google_sheet_email }); setSpreadsheets(files); setIsAuthenticated(true); } catch (error) { From 3817f65c3310884bed76eb6474fea4cda311e1d6 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 00:32:16 +0530 Subject: [PATCH 23/72] feat: pass recordingId --- src/components/organisms/Recordings.tsx | 6 ++++-- src/components/organisms/RightSidePanel.tsx | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/Recordings.tsx b/src/components/organisms/Recordings.tsx index 06af02fd..ec215b98 100644 --- a/src/components/organisms/Recordings.tsx +++ b/src/components/organisms/Recordings.tsx @@ -9,7 +9,7 @@ interface RecordingsProps { handleEditRecording: (id: string, fileName: string) => void; handleRunRecording: (settings: RunSettings) => void; handleScheduleRecording: (settings: ScheduleSettings) => void; - handleIntegrateRecording: (settings: IntegrationSettings) => void; + handleIntegrateRecording: (id: string, settings: IntegrationSettings) => void; setRecordingInfo: (id: string, name: string) => void; } @@ -20,6 +20,8 @@ export const Recordings = ({ handleEditRecording, handleRunRecording, setRecordi const [params, setParams] = useState([]); const [selectedRecordingId, setSelectedRecordingId] = useState(''); + console.log(`Selected reocrding id: ${selectedRecordingId}`); + const handleSettingsAndIntegrate = (id: string, name: string, params: string[]) => { if (params.length === 0) { setIntegrateSettingsAreOpen(true); @@ -94,7 +96,7 @@ export const Recordings = ({ handleEditRecording, handleRunRecording, setRecordi /> handleIntegrateRecording(settings)} + handleStart={(settings) => handleIntegrateRecording(selectedRecordingId, settings)} /> diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 96f0269d..ad50c68a 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -346,6 +346,10 @@ export const RightSidePanel: React.FC = ({ onFinishCapture } }); resetListState(); + setShowPaginationOptions(false); + setShowLimitOptions(false); + setCaptureStage('initial'); + setConfirmedListTextFields({}); notify('error', 'Capture List Discarded'); }, [browserSteps, stopGetList, deleteBrowserStep, resetListState]); From 3cf084b102e3412ae4e885c80093044c759b0665 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 12:32:08 +0530 Subject: [PATCH 24/72] feat: get robotId from query --- server/src/routes/auth.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 6775c442..5a0df9ee 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -188,10 +188,8 @@ router.get('/google', (req, res) => { }); // Step 2: Handle Google OAuth callback -router.post('/google/callback', requireSignIn, async (req, res) => { - const { code } = req.query; - const { robotId } = req.body; - +router.get('/google/callback', requireSignIn, async (req, res) => { + const { code, robotId } = req.query; try { if (!robotId) { return res.status(400).json({ message: 'Robot ID is required' }); From 3ff7a4c417ecbecb8dd45c48a1d37df31b7d32f4 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 13:28:06 +0530 Subject: [PATCH 25/72] fix: store robotId in state --- server/src/routes/auth.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 5a0df9ee..406448fe 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -174,6 +174,10 @@ const oauth2Client = new google.auth.OAuth2( // Step 1: Redirect to Google for authentication router.get('/google', (req, res) => { + const { robotId } = req.query; + if (!robotId) { + return res.status(400).json({ message: 'Robot ID is required' }); + } const scopes = [ 'https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/userinfo.email', @@ -183,18 +187,21 @@ router.get('/google', (req, res) => { access_type: 'offline', prompt: 'consent', // Ensures you get a refresh token on first login scope: scopes, + state: robotId.toString(), }); res.redirect(url); }); // Step 2: Handle Google OAuth callback router.get('/google/callback', requireSignIn, async (req, res) => { - const { code, robotId } = req.query; + const { code, state } = req.query; try { - if (!robotId) { + if (!state) { return res.status(400).json({ message: 'Robot ID is required' }); } + const robotId = state + // Get access and refresh tokens if (typeof code !== 'string') { return res.status(400).json({ message: 'Invalid code' }); From 6c7bf385d0a1fbcde0c2eb25fc9191381eddbb2e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 13:28:30 +0530 Subject: [PATCH 26/72] fix: remove redirect --- server/src/routes/auth.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 406448fe..ec62404f 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -252,8 +252,6 @@ router.get('/google/callback', requireSignIn, async (req, res) => { const jwtToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' }); res.cookie('token', jwtToken, { httpOnly: true }); - res.redirect('http://localhost:3000'); - res.json({ message: 'Google authentication successful', google_sheet_email: robot.google_sheets_email, From ed1225df3ef2a59ec1e0944b32b80e9fbf52d3b7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 14:21:22 +0530 Subject: [PATCH 27/72] feat: check for code --- .../molecules/IntegrationSettings.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 3c411045..5145fe2b 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -4,6 +4,7 @@ import { MenuItem, Typography, CircularProgress } from "@mui/material"; import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import axios from 'axios'; +import { useGlobalInfoStore } from '../../context/globalInfo'; interface IntegrationProps { isOpen: boolean; @@ -28,18 +29,17 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I const [error, setError] = useState(null); const [isAuthenticated, setIsAuthenticated] = useState(false); + const { recordingId } = useGlobalInfoStore(); + // Function to trigger Google OAuth authentication const authenticateWithGoogle = () => { - window.location.href = 'http://localhost:8080/auth/google'; // Redirect to backend Google OAuth route + window.location.href = `http://localhost:8080/auth/google?robotId=${recordingId}`; }; // Function to handle Google OAuth callback and fetch spreadsheets const handleOAuthCallback = async () => { try { - const response = await axios.post('http://localhost:8080/auth/google/callback', { - // code: new URLSearchParams(window.location.search).get('code'), - robotId: '' - }); + const response = await axios.get(`http://localhost:8080/auth/google/callback`); const { google_sheet_email, files } = response.data; setUserInfo({ email:google_sheet_email }); setSpreadsheets(files); @@ -55,11 +55,13 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I }; useEffect(() => { - // Simulate handling OAuth callback here after redirect - if (window.location.pathname === 'http://localhost:8080/auth/google/callback') { + // Check if we're on the callback URL + const urlParams = new URLSearchParams(window.location.search); + const code = urlParams.get('code'); + if (code) { handleOAuthCallback(); } - }, [isOpen]); + }, []); return ( Date: Thu, 17 Oct 2024 14:21:36 +0530 Subject: [PATCH 28/72] chore: lint --- src/components/molecules/IntegrationSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 5145fe2b..644642e0 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -41,7 +41,7 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I try { const response = await axios.get(`http://localhost:8080/auth/google/callback`); const { google_sheet_email, files } = response.data; - setUserInfo({ email:google_sheet_email }); + setUserInfo({ email: google_sheet_email }); setSpreadsheets(files); setIsAuthenticated(true); } catch (error) { From 6817a3c0df3b860f10b61c003670fc3217319166 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 14:33:51 +0530 Subject: [PATCH 29/72] feat: get a recording --- server/src/routes/storage.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index 9e09c8fa..9b6b9a75 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -37,6 +37,20 @@ router.get('/recordings', requireSignIn, async (req, res) => { } }); +router.get('/recordings/:id', requireSignIn, async (req, res) => { + try { + const data = await Robot.findOne({ + where: { 'recording_meta.id': req.params.id }, + raw: true + } + ); + return res.send(data); + } catch (e) { + logger.log('info', 'Error while reading recordings'); + return res.send(null); + } +}) + /** * DELETE endpoint for deleting a recording from the storage. */ From 04274d8f700766fec3c4b27acaa4a4ffaaa902c4 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 14:34:25 +0530 Subject: [PATCH 30/72] chore: docs --- server/src/routes/storage.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index 9b6b9a75..675fec7f 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -37,6 +37,9 @@ router.get('/recordings', requireSignIn, async (req, res) => { } }); +/** + * GET endpoint for getting a recording. + */ router.get('/recordings/:id', requireSignIn, async (req, res) => { try { const data = await Robot.findOne({ From 06c22f1d239f77e51e72077915d15e3a525138f0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 14:36:04 +0530 Subject: [PATCH 31/72] feat: get stored recording --- src/api/storage.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/api/storage.ts b/src/api/storage.ts index 2ff1e82a..b4d09ec8 100644 --- a/src/api/storage.ts +++ b/src/api/storage.ts @@ -32,6 +32,20 @@ export const getStoredRuns = async (): Promise => { } }; +export const getStoredRecording = async (id: string): Promise => { + try { + const response = await axios.get(`http://localhost:8080/storage/recordings/${id}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't retrieve stored recording ${id}`); + } + } catch(error: any) { + console.log(error); + return null; + } +} + export const deleteRecordingFromStorage = async (id: string): Promise => { try { const response = await axios.delete(`http://localhost:8080/storage/recordings/${id}`); From 95c830e10f8ef126bc47acef242d668bfe90799b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 14:43:45 +0530 Subject: [PATCH 32/72] chore: remove comments --- src/components/molecules/IntegrationSettings.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 644642e0..81279f85 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -5,6 +5,7 @@ import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import axios from 'axios'; import { useGlobalInfoStore } from '../../context/globalInfo'; +import { getStoredRecording } from '../../api/storage'; interface IntegrationProps { isOpen: boolean; @@ -31,12 +32,12 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I const { recordingId } = useGlobalInfoStore(); - // Function to trigger Google OAuth authentication + // trigger Google OAuth authentication const authenticateWithGoogle = () => { window.location.href = `http://localhost:8080/auth/google?robotId=${recordingId}`; }; - // Function to handle Google OAuth callback and fetch spreadsheets + // handle Google OAuth callback and fetch spreadsheets const handleOAuthCallback = async () => { try { const response = await axios.get(`http://localhost:8080/auth/google/callback`); @@ -49,7 +50,6 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I } }; - // Handle spreadsheet selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { setSettings({ ...settings, spreadsheetId: e.target.value }); }; From b66e9260f093eefba0d685131628c9a9f68c2e3b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 15:28:22 +0530 Subject: [PATCH 33/72] feat: handle state as per robot --- .../molecules/IntegrationSettings.tsx | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 81279f85..a258ded1 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -28,28 +28,28 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I const [userInfo, setUserInfo] = useState<{ email: string } | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const [isAuthenticated, setIsAuthenticated] = useState(false); + const [googleSheetsEmail, setGoogleSheetsEmail] = useState(null); // To store the Google Sheets email const { recordingId } = useGlobalInfoStore(); - // trigger Google OAuth authentication + // Function to trigger Google OAuth authentication const authenticateWithGoogle = () => { window.location.href = `http://localhost:8080/auth/google?robotId=${recordingId}`; }; - // handle Google OAuth callback and fetch spreadsheets + // Function to handle Google OAuth callback and fetch spreadsheets const handleOAuthCallback = async () => { try { const response = await axios.get(`http://localhost:8080/auth/google/callback`); const { google_sheet_email, files } = response.data; setUserInfo({ email: google_sheet_email }); setSpreadsheets(files); - setIsAuthenticated(true); } catch (error) { setError('Error authenticating with Google'); } }; + // Handle spreadsheet selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { setSettings({ ...settings, spreadsheetId: e.target.value }); }; @@ -61,7 +61,18 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I if (code) { handleOAuthCallback(); } - }, []); + + // Fetch the stored recording to check the Google Sheets email + const fetchRecordingInfo = async () => { + if (!recordingId) return; + const recording = await getStoredRecording(recordingId); + if (recording) { + setGoogleSheetsEmail(recording.google_sheets_email); // Assuming this is how the email is stored + } + }; + + fetchRecordingInfo(); + }, [recordingId]); return ( Google Sheets Integration - {/* If user is not authenticated, show Google OAuth button */} - {!isAuthenticated ? ( + {/* If Google Sheets email is empty, show Google OAuth button */} + {!googleSheetsEmail ? ( + {/* Display selected spreadsheet name */} {settings.spreadsheetId && ( From 4b14cfedda67dd3abd31964f7ea2ea1fe83348a8 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 17:22:40 +0530 Subject: [PATCH 41/72] feat: remove user code --- server/src/routes/auth.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index b818852f..35b3d160 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -301,12 +301,6 @@ router.post('/gsheets/data', requireSignIn, async (req, res) => { // Step 4: Get user's Google Sheets files (new route) router.get('/gsheets/files', requireSignIn, async (req, res) => { try { - // const user = await User.findByPk(req.user.id, { raw: true }); - - // if (!user) { - // return res.status(400).json({ message: 'User not found' }); - // } - const robotId = req.query.robotId; const robot = await Robot.findOne({ where: { 'recording_meta.id': robotId }, raw:true }); From 7fd8dcb8b482fe729e8e3d1c93a3e5b5c93b1b92 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 17:28:44 +0530 Subject: [PATCH 42/72] feat: update google sheet id --- server/src/routes/auth.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 35b3d160..910c7531 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -330,4 +330,27 @@ router.get('/gsheets/files', requireSignIn, async (req, res) => { console.log('Error fetching Google Sheets files:', error); res.status(500).json({ message: `Error retrieving Google Sheets files: ${error.message}` }); } -}); \ No newline at end of file +}); + +// Step 5: Update robot's google_sheet_id when a Google Sheet is selected +router.post('/gsheets/update', requireSignIn, async (req, res) => { + const { spreadsheetId, robotId } = req.body; + + if (!spreadsheetId || !robotId) { + return res.status(400).json({ message: 'Spreadsheet ID and Robot ID are required' }); + } + + try { + let robot = await Robot.findOne({ where: { 'recording_meta.id': robotId }, raw:true }); + + if (!robot) { + return res.status(404).json({ message: 'Robot not found' }); + } + + await robot.update({ google_sheet_id: spreadsheetId }); + + res.json({ message: 'Robot updated with selected Google Sheet ID' }); + } catch (error) { + res.status(500).json({ message: `Error updating robot: ${error.message}` }); + } +}); From 4fbd3783856c35aa02cac626a2829ce15feb07fc Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 17:29:56 +0530 Subject: [PATCH 43/72] feat: send the selected google sheet id --- src/components/molecules/IntegrationSettings.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index af17c79e..9ce3690e 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -60,6 +60,20 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I console.error('Error fetching spreadsheet files:', error.response?.data?.message || error.message); } }; + + // Function to send the selected sheet ID to the backend to update the robot's google_sheet_id +const updateGoogleSheetId = async () => { + try { + const response = await axios.post( + `http://localhost:8080/auth/gsheets/update`, + { spreadsheetId: settings.spreadsheetId, robotId: recordingId }, + { withCredentials: true } + ); + console.log('Google Sheet ID updated:', response.data); + } catch (error) { + console.error('Error updating Google Sheet ID:', error.response?.data?.message || error.message); + } +}; // Handle spreadsheet selection From 297acf7e86b22618d0e63e527f66aebba0620630 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 17:31:14 +0530 Subject: [PATCH 44/72] feat: set error as any --- src/components/molecules/IntegrationSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 9ce3690e..0a472397 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -70,7 +70,7 @@ const updateGoogleSheetId = async () => { { withCredentials: true } ); console.log('Google Sheet ID updated:', response.data); - } catch (error) { + } catch (error: any) { console.error('Error updating Google Sheet ID:', error.response?.data?.message || error.message); } }; From 6401fae8f735ac96c45d9c7db18e76d9cb05f46f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 17:35:33 +0530 Subject: [PATCH 45/72] chore: lint --- .../molecules/IntegrationSettings.tsx | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 0a472397..2a8fce8b 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -62,19 +62,18 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I }; // Function to send the selected sheet ID to the backend to update the robot's google_sheet_id -const updateGoogleSheetId = async () => { - try { - const response = await axios.post( - `http://localhost:8080/auth/gsheets/update`, - { spreadsheetId: settings.spreadsheetId, robotId: recordingId }, - { withCredentials: true } - ); - console.log('Google Sheet ID updated:', response.data); - } catch (error: any) { - console.error('Error updating Google Sheet ID:', error.response?.data?.message || error.message); - } -}; - + const updateGoogleSheetId = async () => { + try { + const response = await axios.post( + `http://localhost:8080/auth/gsheets/update`, + { spreadsheetId: settings.spreadsheetId, robotId: recordingId }, + { withCredentials: true } + ); + console.log('Google Sheet ID updated:', response.data); + } catch (error: any) { + console.error('Error updating Google Sheet ID:', error.response?.data?.message || error.message); + } + }; // Handle spreadsheet selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { @@ -129,9 +128,9 @@ const updateGoogleSheetId = async () => { {/* Show user info and allow spreadsheet selection once authenticated */} {userInfo && ( <> - - Logged in as: {userInfo.email} - + + Logged in as: {userInfo.email} + )} @@ -158,8 +157,8 @@ const updateGoogleSheetId = async () => {
+ Fetch Google Spreadsheets + {/* Display selected spreadsheet name */} {settings.spreadsheetId && ( From 4d684780113602b9ba81332ab8c11827cbf2e000 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 18:38:43 +0530 Subject: [PATCH 46/72] fix: set error type --- server/src/routes/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 910c7531..6ca305bb 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -350,7 +350,7 @@ router.post('/gsheets/update', requireSignIn, async (req, res) => { await robot.update({ google_sheet_id: spreadsheetId }); res.json({ message: 'Robot updated with selected Google Sheet ID' }); - } catch (error) { + } catch (error: any) { res.status(500).json({ message: `Error updating robot: ${error.message}` }); } }); From 957ce77bffbc344b242afd90e9baa2b26359af2b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 18:57:03 +0530 Subject: [PATCH 47/72] fix: remove raw true --- server/src/routes/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 6ca305bb..07b4a50a 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -341,7 +341,7 @@ router.post('/gsheets/update', requireSignIn, async (req, res) => { } try { - let robot = await Robot.findOne({ where: { 'recording_meta.id': robotId }, raw:true }); + let robot = await Robot.findOne({ where: { 'recording_meta.id': robotId } }); if (!robot) { return res.status(404).json({ message: 'Robot not found' }); From c91cb2871654bb4fb338aeefa58d86f28f42a2e2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 18:57:24 +0530 Subject: [PATCH 48/72] fix: call updateGoogleSheet id --- src/components/molecules/IntegrationSettings.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 2a8fce8b..6a41191d 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -172,7 +172,10 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I - {/* Display selected spreadsheet name */} {settings.spreadsheetId && ( Selected Spreadsheet ID: {settings.spreadsheetId} From 3102fb8e5b4f181dad8468167ff63c2aa60b6a19 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:23:28 +0530 Subject: [PATCH 50/72] refactor: remove extra S in column names --- server/src/models/Robot.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/src/models/Robot.ts b/server/src/models/Robot.ts index a5baf369..a48b9af1 100644 --- a/server/src/models/Robot.ts +++ b/server/src/models/Robot.ts @@ -19,8 +19,8 @@ interface RobotAttributes { id: string; recording_meta: RobotMeta; recording: RobotWorkflow; - google_sheets_email?: string | null; - google_sheets_name?: string | null; + google_sheet_email?: string | null; + google_sheet_name?: string | null; google_sheet_id?: string | null; google_access_token?: string | null; google_refresh_token?: string | null; @@ -32,8 +32,8 @@ class Robot extends Model implements R public id!: string; public recording_meta!: RobotMeta; public recording!: RobotWorkflow; - public google_sheets_email!: string | null; - public google_sheets_name?: string | null; + public google_sheet_email!: string | null; + public google_sheet_name?: string | null; public google_sheet_id?: string | null; public google_access_token!: string | null; public google_refresh_token!: string | null; @@ -54,11 +54,11 @@ Robot.init( type: DataTypes.JSONB, allowNull: false, }, - google_sheets_email: { + google_sheet_email: { type: DataTypes.STRING, allowNull: true, }, - google_sheets_name: { + google_sheet_name: { type: DataTypes.STRING, allowNull: true, }, From 410ec57e8de032d8462010c6164bb4f12c5046a7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:23:42 +0530 Subject: [PATCH 51/72] refactor: remove extra S in column names --- server/src/routes/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 07b4a50a..b0f5b429 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -231,7 +231,7 @@ router.get('/google/callback', requireSignIn, async (req, res) => { } robot = await robot.update({ - google_sheets_email: email, + google_sheet_email: email, google_access_token: tokens.access_token, google_refresh_token: tokens.refresh_token, }); @@ -254,7 +254,7 @@ router.get('/google/callback', requireSignIn, async (req, res) => { res.json({ message: 'Google authentication successful', - google_sheet_email: robot.google_sheets_email, + google_sheet_email: robot.google_sheet_email, jwtToken, files }); From 746c3b5ea194e0a50a4e64475824fa474ebdf18a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:24:03 +0530 Subject: [PATCH 52/72] refactor: remove extra S in column names --- .../molecules/IntegrationSettings.tsx | 172 +++++++++--------- 1 file changed, 87 insertions(+), 85 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 34c4911c..ef0645eb 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -28,7 +28,9 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I const [userInfo, setUserInfo] = useState<{ email: string } | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const [googleSheetsEmail, setGoogleSheetsEmail] = useState(null); // To store the Google Sheets email + const [googleSheetsEmail, setGoogleSheetsEmail] = useState(null); + const [googleSheetId, setGoogleSheetId] = useState(null); // Store the integrated sheet ID + const [googleSheetName, setGoogleSheetName] = useState(null); // Store the integrated sheet name const { recordingId } = useGlobalInfoStore(); @@ -43,7 +45,6 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I const response = await axios.get(`http://localhost:8080/auth/google/callback`); const { google_sheet_email, files } = response.data; setUserInfo({ email: google_sheet_email }); - //setSpreadsheets(files); } catch (error) { setError('Error authenticating with Google'); } @@ -55,28 +56,18 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I withCredentials: true, }); setSpreadsheets(response.data); - console.log(`Fetched spreadsheets:`, response.data); } catch (error: any) { console.error('Error fetching spreadsheet files:', error.response?.data?.message || error.message); } }; - // Function to send the selected sheet ID to the backend to update the robot's google_sheet_id - const updateGoogleSheetId = async () => { - try { - const response = await axios.post( - `http://localhost:8080/auth/gsheets/update`, - { spreadsheetId: settings.spreadsheetId, robotId: recordingId }, - { withCredentials: true } - ); - console.log('Google Sheet ID updated:', response.data); - } catch (error: any) { - console.error('Error updating Google Sheet ID:', error.response?.data?.message || error.message); - } - }; - // Handle spreadsheet selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { + const selectedSheet = spreadsheets.find(sheet => sheet.id === e.target.value); + if (selectedSheet) { + setGoogleSheetId(selectedSheet.id); + setGoogleSheetName(selectedSheet.name); + } setSettings({ ...settings, spreadsheetId: e.target.value }); }; @@ -88,12 +79,14 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I handleOAuthCallback(); } - // Fetch the stored recording to check the Google Sheets email + // Fetch the stored recording to check the Google Sheets email and google_sheet_id const fetchRecordingInfo = async () => { if (!recordingId) return; const recording = await getStoredRecording(recordingId); if (recording) { - setGoogleSheetsEmail(recording.google_sheets_email); + setGoogleSheetsEmail(recording.google_sheet_email); + setGoogleSheetId(recording.google_sheet_id); + setGoogleSheetName(recording.google_sheet_id); } }; @@ -101,83 +94,92 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I }, [recordingId]); return ( - -
+ +
Google Sheets Integration - {!googleSheetsEmail ? ( - + {/* Check if Google Sheet is already integrated */} + {googleSheetId ? ( + + Google Sheet Integrated Successfully! +
+ Sheet Name: {googleSheetName} +
+ Sheet ID: {googleSheetId} +
) : ( <> - {userInfo && ( - <> - - Logged in as: {userInfo.email} - - - )} - - {loading ? ( - - ) : error ? ( - {error} + {/* If Google Sheets email is empty, show Google OAuth button */} + {!googleSheetsEmail ? ( + ) : ( <> - - {spreadsheets.map(sheet => ( - - {sheet.name} - - ))} - - - - - {settings.spreadsheetId && ( + {/* Show user info and allow spreadsheet selection once authenticated */} + {userInfo && ( - Selected Spreadsheet ID: {settings.spreadsheetId} + Logged in as: {userInfo.email} )} + + {loading ? ( + + ) : error ? ( + {error} + ) : spreadsheets.length === 0 ? ( + + ) : ( + <> + {/* Dropdown for selecting the Google Sheet */} + + {spreadsheets.map(sheet => ( + + {sheet.name} + + ))} + + + {/* Display selected spreadsheet name and ID */} + {settings.spreadsheetId && ( + + Selected Sheet: {googleSheetName} (ID: {settings.spreadsheetId}) + + )} + + + + )} )} - - )}
From b4f2ca0f9c877edae0f0fdab10b0bfb7ec224d40 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:25:03 +0530 Subject: [PATCH 53/72] feat: set google sheet name --- server/src/routes/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index b0f5b429..697a1129 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -334,7 +334,7 @@ router.get('/gsheets/files', requireSignIn, async (req, res) => { // Step 5: Update robot's google_sheet_id when a Google Sheet is selected router.post('/gsheets/update', requireSignIn, async (req, res) => { - const { spreadsheetId, robotId } = req.body; + const { spreadsheetId, spreadsheetName, robotId } = req.body; if (!spreadsheetId || !robotId) { return res.status(400).json({ message: 'Spreadsheet ID and Robot ID are required' }); @@ -347,7 +347,7 @@ router.post('/gsheets/update', requireSignIn, async (req, res) => { return res.status(404).json({ message: 'Robot not found' }); } - await robot.update({ google_sheet_id: spreadsheetId }); + await robot.update({ google_sheet_id: spreadsheetId, google_sheet_name: spreadsheetName }); res.json({ message: 'Robot updated with selected Google Sheet ID' }); } catch (error: any) { From 520308e27880bbab5a6dd31d59f1e064f8e60b92 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:26:08 +0530 Subject: [PATCH 54/72] feat: use recording. google_sheet_name --- src/components/molecules/IntegrationSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index ef0645eb..9961bdc7 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -86,7 +86,7 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I if (recording) { setGoogleSheetsEmail(recording.google_sheet_email); setGoogleSheetId(recording.google_sheet_id); - setGoogleSheetName(recording.google_sheet_id); + setGoogleSheetName(recording.google_sheet_name); } }; From 21098c75716fc762950277581bcd2f6bfcba0845 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:52:53 +0530 Subject: [PATCH 55/72] feat: proper checks for recording gsheet details --- .../molecules/IntegrationSettings.tsx | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 9961bdc7..0dad7650 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -25,26 +25,20 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I }); const [spreadsheets, setSpreadsheets] = useState<{ id: string, name: string }[]>([]); - const [userInfo, setUserInfo] = useState<{ email: string } | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const [googleSheetsEmail, setGoogleSheetsEmail] = useState(null); - const [googleSheetId, setGoogleSheetId] = useState(null); // Store the integrated sheet ID - const [googleSheetName, setGoogleSheetName] = useState(null); // Store the integrated sheet name const { recordingId } = useGlobalInfoStore(); + const [recording, setRecording] = useState(null); - // Function to trigger Google OAuth authentication const authenticateWithGoogle = () => { window.location.href = `http://localhost:8080/auth/google?robotId=${recordingId}`; }; - // Function to handle Google OAuth callback and fetch spreadsheets const handleOAuthCallback = async () => { try { const response = await axios.get(`http://localhost:8080/auth/google/callback`); const { google_sheet_email, files } = response.data; - setUserInfo({ email: google_sheet_email }); } catch (error) { setError('Error authenticating with Google'); } @@ -61,14 +55,11 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I } }; - // Handle spreadsheet selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { const selectedSheet = spreadsheets.find(sheet => sheet.id === e.target.value); if (selectedSheet) { - setGoogleSheetId(selectedSheet.id); - setGoogleSheetName(selectedSheet.name); + setSettings({ ...settings, spreadsheetId: selectedSheet.id }); } - setSettings({ ...settings, spreadsheetId: e.target.value }); }; useEffect(() => { @@ -79,14 +70,11 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I handleOAuthCallback(); } - // Fetch the stored recording to check the Google Sheets email and google_sheet_id const fetchRecordingInfo = async () => { if (!recordingId) return; const recording = await getStoredRecording(recordingId); if (recording) { - setGoogleSheetsEmail(recording.google_sheet_email); - setGoogleSheetId(recording.google_sheet_id); - setGoogleSheetName(recording.google_sheet_name); + setRecording(recording); } }; @@ -98,19 +86,17 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I
Google Sheets Integration - {/* Check if Google Sheet is already integrated */} - {googleSheetId ? ( + {recording && recording.google_sheet_id ? ( Google Sheet Integrated Successfully!
- Sheet Name: {googleSheetName} + Sheet Name: {recording.google_sheet_name}
- Sheet ID: {googleSheetId} + Sheet ID: {recording.google_sheet_id}
) : ( <> - {/* If Google Sheets email is empty, show Google OAuth button */} - {!googleSheetsEmail ? ( + {!recording?.google_sheet_email ? ( ) : ( <> - {/* Dropdown for selecting the Google Sheet */} - {/* Display selected spreadsheet name and ID */} {settings.spreadsheetId && ( - Selected Sheet: {googleSheetName} (ID: {settings.spreadsheetId}) + Selected Sheet: {spreadsheets.find(s => s.id === settings.spreadsheetId)?.name} (ID: {settings.spreadsheetId}) )} From 2aa9cfccf1e3592d4920cf380f32866f2d3c3137 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:57:09 +0530 Subject: [PATCH 56/72] feat: send selected google sheet id --- src/components/molecules/IntegrationSettings.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 0dad7650..73cc6f7e 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -62,6 +62,19 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I } }; + const updateGoogleSheetId = async () => { + try { + const response = await axios.post( + `http://localhost:8080/auth/gsheets/update`, + { spreadsheetId: settings.spreadsheetId, robotId: recordingId }, + { withCredentials: true } + ); + console.log('Google Sheet ID updated:', response.data); + } catch (error: any) { + console.error('Error updating Google Sheet ID:', error.response?.data?.message || error.message); + } + }; + useEffect(() => { // Check if we're on the callback URL const urlParams = new URLSearchParams(window.location.search); From defa502b6149a13db6ce6b7959d93f0c868dddea Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 19:58:26 +0530 Subject: [PATCH 57/72] feat: include spreadsheet name in settings --- src/components/molecules/IntegrationSettings.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 73cc6f7e..601ea8b4 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -15,12 +15,14 @@ interface IntegrationProps { export interface IntegrationSettings { spreadsheetId: string; + spreadsheetName: string; data: string; } export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: IntegrationProps) => { const [settings, setSettings] = useState({ spreadsheetId: '', + spreadsheetName: '', data: '', }); @@ -66,7 +68,7 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I try { const response = await axios.post( `http://localhost:8080/auth/gsheets/update`, - { spreadsheetId: settings.spreadsheetId, robotId: recordingId }, + { spreadsheetId: settings.spreadsheetId, spreadsheetName: settings.spreadsheetName, robotId: recordingId }, { withCredentials: true } ); console.log('Google Sheet ID updated:', response.data); From 693a6eb987e86ccb921b48ee44e0eeb5947accad Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 17 Oct 2024 20:02:04 +0530 Subject: [PATCH 58/72] feat: call google sheet update id --- src/components/molecules/IntegrationSettings.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index 601ea8b4..36049423 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -168,7 +168,10 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I