diff --git a/apps/http-backend/package.json b/apps/http-backend/package.json index 9749996..5dd2c40 100644 --- a/apps/http-backend/package.json +++ b/apps/http-backend/package.json @@ -31,6 +31,7 @@ "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", + "googleapis": "^166.0.0", "jsonwebtoken": "^9.0.2", "next-auth": "^4.24.13" } diff --git a/apps/http-backend/src/index.ts b/apps/http-backend/src/index.ts index b0d364a..f693f78 100644 --- a/apps/http-backend/src/index.ts +++ b/apps/http-backend/src/index.ts @@ -8,6 +8,8 @@ import { NodeRegistry } from "@repo/nodes/nodeClient"; import express from "express"; import { userRouter } from "./routes/userRoutes/userRoutes.js"; import cors from "cors" +import { sheetRouter } from "./routes/nodes.routes.js"; +import { googleAuth } from "./routes/google_callback.js"; const app = express() @@ -33,6 +35,8 @@ app.use(cookieParser()); // }); app.use("/user" , userRouter) +app.use('/node', sheetRouter) +app.use('/google', googleAuth) const PORT= 3002 async function startServer() { await NodeRegistry.registerAll() diff --git a/apps/http-backend/src/routes/google_callback.ts b/apps/http-backend/src/routes/google_callback.ts new file mode 100644 index 0000000..c291914 --- /dev/null +++ b/apps/http-backend/src/routes/google_callback.ts @@ -0,0 +1,42 @@ +import { google } from 'googleapis'; +import { GoogleOAuthService } from '@repo/nodes'; +import type { OAuthTokens } from '@repo/nodes'; +import { userMiddleware } from './userRoutes/userMiddleware.js'; +import { Router, Request, Response } from 'express'; + +export const googleAuth: Router = Router() + +googleAuth.get('/callback', async(req: Request, res: Response)=>{ + const code = req.query.code; + const state = req.query.state; // userId + const Oauth = new GoogleOAuthService; + if (!code || typeof code !== 'string') { + return res.json({ error: 'Missing or invalid authorization code' }); + } + + const oauth2 = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI + ); + + try { + const { tokens } = await oauth2.getToken(code); + + // Save tokens to database if userId (state) is provided + if (state && typeof state === 'string') { + + await Oauth.saveCredentials(state, tokens as OAuthTokens) + } + + // Redirect to success page + return res.redirect('http://localhost:3000/workflow'); + } + catch (err: any) { + console.error('Google token exchange error:', err); + return res.redirect( + `http://localhost:3000/workflow?google=error&msg=${encodeURIComponent(err?.message ?? 'Token exchange failed')}`); + } +}) + + diff --git a/apps/http-backend/src/routes/nodes.routes.ts b/apps/http-backend/src/routes/nodes.routes.ts index 066d5b5..5288a0f 100644 --- a/apps/http-backend/src/routes/nodes.routes.ts +++ b/apps/http-backend/src/routes/nodes.routes.ts @@ -1,109 +1,82 @@ -// import { Router } from 'express'; -// import { prismaClient } from '@repo/db/client' -// import { GoogleSheetNode } from '@repo/nodes/nodeClinet'; +import { Response, Router } from "express"; +import { AuthRequest, userMiddleware } from "./userRoutes/userMiddleware.js"; +import { statusCodes } from "@repo/common/zod"; +import { GoogleSheetsNodeExecutor } from "@repo/nodes"; -// const router: Router = Router() +export const sheetRouter: Router = Router(); +const sheetExecutor = new GoogleSheetsNodeExecutor() -// router.post('/test-execute', async(req, res)=>{ -// try{ -// const { userId, nodeType, config } = req.body; -// const workflow = await prismaClient.workflow.create({ -// data : { -// id: `workflow_test_${Date.now()}`, -// userId: userId, -// name: `Test ${nodeType} - ${new Date().toISOString()}`, -// description: 'Auto-created for testing', -// status: 'Start', -// config: {} -// } -// }); +sheetRouter.get("/getDocuments/:cred", userMiddleware, async(req: AuthRequest, res: Response)=>{ + try{ + if (!req.user) + return res + .status(statusCodes.BAD_GATEWAY) + .json({ message: "User isnot logged in /not authorized" }); + const credId = req.params.cred + if(!credId){ + return res + .status(statusCodes.BAD_REQUEST) + .json({message: "credentials id not provided"}) + } + const userId = req.user.sub + console.log("userid from node route: ", userId) + if(!userId) return res + .status(statusCodes.NOT_FOUND) + .json({message: "User id not provided"}) + const sheets = await sheetExecutor.getSheets({userId: userId, credId: credId}) + if ((sheets as any)?.success === false) { + return res.status(statusCodes.NOT_FOUND).json({ + message: "files not found", + files: sheets + }); + } + return res.status(statusCodes.OK).json({ + message: "sheets are fetched successfully", + files: (sheets as any)?.data?.data?.files || [] + }) + }catch(e){ + console.log("Error Fetching the credentials ", e instanceof Error ? e.message : "Unkown reason"); + return res + .status(statusCodes.INTERNAL_SERVER_ERROR) + .json({ message: "Internal server from fetching the credentials" }); + } +}) -// const availableNode = await prismaClient.availableNode.findUnique({ -// where:{ -// type: nodeType -// } -// }); - -// if(!availableNode){ -// return res.status(404).json({error:"NOde type not found"}) -// } - -// const node = await prismaClient.node.create({ -// data:{ -// id: `node_test_${Date.now()}`, -// workflowId: workflow.id, -// config: config, -// typeId: availableNode.id, -// position: 1, -// name: `${availableNode.name} Node` -// } -// }) - -// const workflowExecution = await prismaClient.workflowExecution.create({ -// data : { -// id: `exec_${Date.now()}`, -// workflowId: workflow.id, -// status: 'InProgress', -// metadata: {} - -// } -// }); - -// const nodeExecution = await prismaClient.nodeExecution.create({ -// data: { -// id: `node_exec_${Date.now()}`, -// workflowExecId: workflowExecution.id, -// status: 'InProgress', -// nodeId: node.id -// } -// }); - -// const executor = GoogleSheetNode.getExecutor() -// const result = await executor.execute({ -// nodeId: node.id, -// userId: userId, -// config: config, -// inputData: null -// }); - -// await prismaClient.nodeExecution.update({ -// where: { -// id: nodeExecution.id -// }, -// data:{ -// status: result.success ? 'Completed' : 'Failed', -// outputData: result.output ? result.output : null, -// error: result.error ? result.error : null, -// completedAt: new Date() -// } -// }); - -// await prismaClient.workflowExecution.update({ -// where:{ -// id: workflowExecution.id -// }, -// data: { -// status: result.success ? 'Completed' : 'Failed', -// completedAt: new Date() -// } -// }); - -// res.status(200).json({ -// success: true, -// workflowId: workflow.id, -// nodeId: node.id, -// workflowExecutionId: workflowExecution.id, -// nodeExecution: nodeExecution.id, -// result: result -// }); -// } -// catch(e){ -// res.status(500).json({ -// success: false, -// error: e instanceof Error ? e.message : 'Unknown error' -// }) -// } -// }); - -// export default router; \ No newline at end of file +sheetRouter.get('/getSheets/:cred/:sheetId', userMiddleware, async(req: AuthRequest, res: Response)=>{ + try{ + const userId = req.user?.sub + if (!userId) + return res + .status(statusCodes.BAD_GATEWAY) + .json({ message: "User isnot logged in /not authorized" }); + const credId = req.params.cred + const sheetId = req.params.sheetId + if(!credId || !sheetId){ + return res + .status(statusCodes.BAD_REQUEST) + .json({message: `credentials id not provided `}) + } + if(!sheetExecutor){ + return res.status(statusCodes.FORBIDDEN).json({ + message: "sheet executor not configured well" + }) + } + const sheets = await sheetExecutor.getSheetTabs({userId: userId, credId: credId}, sheetId) + + if((sheets as any)?.success === false) return res.status(statusCodes.NOT_FOUND).json({ + message: "files not found", + files: sheets + }); + + return res.status(statusCodes.OK).json({ + message: "sheets are fetched successfully", + files: sheets + }) + }catch(e){ + console.log("Error Fetching the credentials ", e instanceof Error ? e.message : "Unkown reason"); + return res + .status(statusCodes.INTERNAL_SERVER_ERROR) + .json({ message: "Internal server from fetching the credentials" }); + } +}) \ No newline at end of file diff --git a/apps/http-backend/src/routes/userRoutes/userRoutes.ts b/apps/http-backend/src/routes/userRoutes/userRoutes.ts index 3950327..2d6bccf 100644 --- a/apps/http-backend/src/routes/userRoutes/userRoutes.ts +++ b/apps/http-backend/src/routes/userRoutes/userRoutes.ts @@ -10,6 +10,7 @@ import { TriggerSchema, WorkflowSchema, } from "@repo/common/zod"; +import { GoogleSheetsNodeExecutor } from "@repo/nodes"; const router: Router = Router(); // router.post("/create", async (req, res) => { // // const Data = req.body; @@ -64,8 +65,7 @@ router.post("/createNode", async (req: AuthRequest, res: Response) => { } }); -router.get( - "/getAvailableNodes", +router.get("/getAvailableNodes", userMiddleware, async (req: AuthRequest, res: Response) => { if (!req.user) { @@ -90,8 +90,7 @@ router.get( } ); -router.post( - "/createTriggers", +router.post("/createTriggers", userMiddleware, async (req: AuthRequest, res: Response) => { try { @@ -125,8 +124,7 @@ router.post( } ); -router.get( - "/getAvailableTriggers", +router.get("/getAvailableTriggers", userMiddleware, async (req: AuthRequest, res: Response) => { try { @@ -150,8 +148,55 @@ router.get( } ); -router.post( - "/create/workflow", +router.get('/getCredentials/:type', + userMiddleware, + async (req: AuthRequest, res) =>{ + try{ + console.log("user from getcredentials: ",req.user) + if(!req.user){ + return res.status(statusCodes.BAD_REQUEST).json({ + message: "User is not Loggedin" + }) + } + const userId = req.user.sub; + const type = req.params.type + console.log(userId," -userid") + if(!type || !userId){ + return res.status(statusCodes.BAD_REQUEST).json({ + message: "Incorrect type Input", + }); + } + const executor = new GoogleSheetsNodeExecutor() + const response = await executor.getAllCredentials(userId,type) + // console.log( typeof(response)); + // console.log("response: ",response) + const authUrl = typeof response === 'string' ? response : null + // console.log(authUrl); + + const credentials = response instanceof Object ? response : null + // console.log(credentials) + if(authUrl){ + return res.status(statusCodes.OK).json({ + message: "Credentials not found create credentials using this auth url", + Data: authUrl, + }); + } + else return res.status(statusCodes.OK).json({ + message: "Credentials Fetched succesfully", + Data: credentials, + }); + } + catch(e){ + console.log("Error Fetching the credentials ", e instanceof Error ? e.message : "Unkown reason"); + return res + .status(statusCodes.INTERNAL_SERVER_ERROR) + .json({ message: "Internal server from fetching the credentials" }); + } + } +); + + +router.post("/create/workflow", userMiddleware, async (req: AuthRequest, res) => { try { @@ -240,8 +285,7 @@ router.post( } ); -router.get( - "/workflows", +router.get("/workflows", userMiddleware, async (req: AuthRequest, res: Response) => { try { @@ -269,8 +313,7 @@ router.get( } ); -router.get( - "/workflow/:workflowId", +router.get("/workflow/:workflowId", userMiddleware, async (req: AuthRequest, res: Response) => { try { diff --git a/apps/web/app/api/auth/google/callback/route.ts b/apps/web/app/api/auth/google/callback/route.ts deleted file mode 100644 index 4ce0641..0000000 --- a/apps/web/app/api/auth/google/callback/route.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { google } from 'googleapis'; -import { prismaClient } from '@repo/db/client'; -import { GoogleOAuthService } from '@repo/nodes'; -import type { OAuthTokens } from '@repo/nodes'; - -export async function GET(req: NextRequest) { - const url = new URL(req.url); - const code = url.searchParams.get('code'); - const state = url.searchParams.get('state'); // userId - const Oauth = new GoogleOAuthService; - if (!code) { - return NextResponse.json({ error: 'Missing authorization code' }, { status: 400 }); - } - - const oauth2 = new google.auth.OAuth2( - process.env.GOOGLE_CLIENT_ID, - process.env.GOOGLE_CLIENT_SECRET, - process.env.GOOGLE_REDIRECT_URI - ); - - try { - const { tokens } = await oauth2.getToken(code); - - // Save tokens to database if userId (state) is provided - if (state) { - // await prismaClient.credential.create({ - // data: { - // userId: state, - // type: 'google_oauth', - // config: tokens as any, - // }, - // }); - await Oauth.saveCredentials(state, tokens as OAuthTokens) - } - - // Redirect to success page - return NextResponse.redirect(new URL('/form?google=connected', req.url)); - } catch (err: any) { - console.error('Google token exchange error:', err); - return NextResponse.redirect( - new URL(`/form?google=error&msg=${encodeURIComponent(err?.message ?? 'Token exchange failed')}`, req.url) - ); - } -} diff --git a/apps/web/app/components/nodes/CreateWorkFlow.tsx b/apps/web/app/components/nodes/CreateWorkFlow.tsx index 7a8cded..f30b074 100644 --- a/apps/web/app/components/nodes/CreateWorkFlow.tsx +++ b/apps/web/app/components/nodes/CreateWorkFlow.tsx @@ -8,6 +8,8 @@ import { TriggerNode } from "./TriggerNode"; import { TriggerSideBar } from "./TriggerSidebar"; import ActionSideBar from "../Actions/ActionSidebar"; import ActionNode from "../Actions/ActionNode"; +import { GoogleSheetFormClient } from "./GoogleSheetFormClient"; +import { useCredentials } from "@/app/hooks/useCredential"; interface NodeType { id: string; @@ -31,6 +33,8 @@ interface EdgeType { export const CreateWorkFlow = () => { const [sidebarOpen, setSidebarOpen] = useState(false); const [actionSidebarOpen, setActionSidebarOpen] = useState(false); + const [credType, setCredType] = useState(""); + const [loadSheet, setLoadSheet] = useState(false) const [nodes, setNodes] = useState([ { @@ -103,6 +107,13 @@ export const CreateWorkFlow = () => { ]); }; + function getCredentials(type: string){ + // if(credData){ + // console.log(creds.nodeId) + // setNodeId(creds.nodeId) + // } + } + const handleSelectAction = (action: { id: string; name: string; @@ -180,7 +191,7 @@ export const CreateWorkFlow = () => { nodeTypes={nodeTypes} nodes={nodes} edges={edges} - onNodeClick={(event, node) => { + onNodeClick={async(event, node) => { if (node.type === "placeholder") { const hasTrigger = nodes.some((n) => n.type === "trigger"); if (hasTrigger) { @@ -189,6 +200,19 @@ export const CreateWorkFlow = () => { setSidebarOpen(true); } } + if(node.type === 'action' || node.type === 'trigger'){ + if(node.data.name === 'Google Sheet' ){ + console.log("sheet called") + console.log(node.id) + // setNodeId("550e8400-e29b-41d4-a716-446655440000") + // getCredentials(node.data.type ? node.data.type : "") + // setNodeId(node.id.split("trigger-")[1] || "") + // if(cred) setLoadSheet(true) + setCredType(node.data.type === "google_sheet" ? "google_oauth" : "") + setLoadSheet(!loadSheet) + console.log("hook called") + } + } }} /> @@ -203,6 +227,11 @@ export const CreateWorkFlow = () => { onClose={() => setActionSidebarOpen(false)} onSelectAction={handleSelectAction} /> + + {loadSheet && + + } ); }; diff --git a/apps/web/app/components/nodes/GoogleSheetForm.tsx b/apps/web/app/components/nodes/GoogleSheetForm.tsx deleted file mode 100644 index 036c1ba..0000000 --- a/apps/web/app/components/nodes/GoogleSheetForm.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import Card from '../ui/Card'; -import { GoogleSheetsNodeExecutor, GoogleOAuthService } from '@repo/nodes'; -import { GoogleSheetFormClient } from './GoogleSheetFormClient'; - -const GoogleSheetForm = async () => { - const userId = "f27185c9-da2e-4c1e-b489-746f4b811490"; - const nodeId = "550e8400-e29b-41d4-a716-446655440000"; - - const executor = new GoogleSheetsNodeExecutor(); - const oauthService = new GoogleOAuthService(); - - const credentials = await executor.getAllCredentials(userId); - const authUrl = oauthService.getAuthUrl(userId); - - return ( - -
-

Google Sheets Configuration

- 0 }} - userId={userId} - nodeId={nodeId} - /> -
-
- ); -} - -export default GoogleSheetForm \ No newline at end of file diff --git a/apps/web/app/components/nodes/GoogleSheetFormClient.tsx b/apps/web/app/components/nodes/GoogleSheetFormClient.tsx index 05d825a..9c309b1 100644 --- a/apps/web/app/components/nodes/GoogleSheetFormClient.tsx +++ b/apps/web/app/components/nodes/GoogleSheetFormClient.tsx @@ -8,6 +8,8 @@ import React, { useState, useTransition } from 'react'; import Link from 'next/link'; import { toast } from 'sonner'; import { handleSaveConfig } from './actions'; +import { useCredentials } from '@/app/hooks/useCredential'; +import { BACKEND_URL } from '@repo/common/zod'; interface GoogleSheetFormClientProps { initialData: { @@ -19,7 +21,7 @@ interface GoogleSheetFormClientProps { nodeId: string; } -export function GoogleSheetFormClient({ initialData, userId, nodeId }: GoogleSheetFormClientProps) { +export function GoogleSheetFormClient(type:{type: string}) { const [selectedCredential, setSelectedCredential] = useState(''); const [documents, setDocuments] = useState>([]); const [selectedDocument, setSelectedDocument] = useState(''); @@ -30,6 +32,17 @@ export function GoogleSheetFormClient({ initialData, userId, nodeId }: GoogleShe const [loading, setLoading] = useState(false); const [isPending, startTransition] = useTransition(); const [result, setResult] = useState(null); + const [credId, setCredId] = useState(); + // const [authUrl, setAuthUrl] = useState() + const userId = ""// get it from redux + const credType = type.type + + const {cred: response, authUrl} = useCredentials(credType) + console.log('response from form client', typeof(response)) + + console.log(response," response from client after hook") + console.log(authUrl," authurl") + // Fetch documents when credential is selected const handleCredentialChange = async (credentialId: string) => { @@ -40,17 +53,18 @@ export function GoogleSheetFormClient({ initialData, userId, nodeId }: GoogleShe setSelectedSheet(''); if (!credentialId || credentialId === 'create-new') return; - + setCredId(credentialId) setLoading(true); try { - const response = await fetch('/api/google-sheets/documents', { - method: 'POST', + const response = await fetch(`${BACKEND_URL}/node/getDocuments/${credentialId}`, { + method: 'GET', + credentials:"include", headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userId, nodeId, credentialId }) }); const data = await response.json(); - if (data.success) { - setDocuments(data.data || []); + + if (data.files.length >0) { + setDocuments(data.files || []); } } catch (error) { console.error('Failed to fetch documents:', error); @@ -69,14 +83,15 @@ export function GoogleSheetFormClient({ initialData, userId, nodeId }: GoogleShe setLoading(true); try { - const response = await fetch('/api/google-sheets/tabs', { - method: 'POST', + const response = await fetch(`${BACKEND_URL}/node/getSheets/${credId}/${documentId}`, { + method: 'GET', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userId, nodeId, spreadsheetId: documentId }) + credentials:"include", }); const data = await response.json(); - if (data.success) { - setSheets(data.data || []); + // console.log(data," from clint") + if (data.files.data.length > 0) { + setSheets(data.files.data || []); } } catch (error) { console.error('Failed to fetch sheet tabs:', error); @@ -88,7 +103,7 @@ export function GoogleSheetFormClient({ initialData, userId, nodeId }: GoogleShe const handleSaveClick = () => { const config = { userId, - nodeId, + // nodeId, credentialId: selectedCredential, spreadsheetId: selectedDocument, sheetName: selectedSheet, @@ -122,26 +137,21 @@ export function GoogleSheetFormClient({ initialData, userId, nodeId }: GoogleShe - {!initialData.hasCredentials && initialData.authUrl ? ( - - + {authUrl ? ( + // + + Create new credential - - + + // ) : ( <> - {initialData.credentials.map((cred) => ( + { + response?.map((cred: any) => ( Google Account ({cred.id.slice(0, 8)}...) - ))} - {initialData.authUrl && ( - - - + Add another account - - - )} + )) + } )} diff --git a/apps/web/app/components/nodes/actions.ts b/apps/web/app/components/nodes/actions.ts index 2250f64..44cf69d 100644 --- a/apps/web/app/components/nodes/actions.ts +++ b/apps/web/app/components/nodes/actions.ts @@ -4,7 +4,6 @@ import { GoogleSheetsNodeExecutor } from '@repo/nodes'; interface SaveConfigFormData { userId: string; - nodeId: string; credentialId: string; spreadsheetId: string; sheetName: string; @@ -17,13 +16,12 @@ export async function handleSaveConfig(formData: SaveConfigFormData) { const context = { userId: formData.userId, - nodeId: formData.nodeId, + credId: formData.credentialId, config: { operation: formData.operation, spreadsheetId: formData.spreadsheetId, range: formData.range, sheetName: formData.sheetName, - credentialId: formData.credentialId, }, }; diff --git a/apps/web/app/form/page.tsx b/apps/web/app/form/page.tsx deleted file mode 100644 index 1123d5d..0000000 --- a/apps/web/app/form/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import GoogleSheetForm from '../components/nodes/GoogleSheetForm' - -const page = () => { - return ( -
- ) -} - -export default page \ No newline at end of file diff --git a/apps/web/app/hooks/useActions.tsx b/apps/web/app/hooks/useActions.ts similarity index 100% rename from apps/web/app/hooks/useActions.tsx rename to apps/web/app/hooks/useActions.ts diff --git a/apps/web/app/hooks/useCredential.ts b/apps/web/app/hooks/useCredential.ts new file mode 100644 index 0000000..565a24b --- /dev/null +++ b/apps/web/app/hooks/useCredential.ts @@ -0,0 +1,33 @@ +"use client" +import { useEffect, useState } from "react" +import { getCredentials } from "../workflow/lib/config" + +export const useCredentials = (type: string): any=>{ + const [cred, setCred] = useState() + const [authUrl, setAuthUrl] = useState() + useEffect(()=>{ + const fetchCred = async()=>{ + try{ + if(!type) return {} + const response = await getCredentials(type) + if(response){ + console.log(typeof(response)) + if(typeof(response) === 'string') setAuthUrl(response) + else setCred(response) + + // console.log(response[0].nodeId) + console.log(response) + return cred + + } + else return {} + } + catch(e){ + console.log(e instanceof Error ? e.message : "unknow error from useCredentials hook") + } + } + + fetchCred() + },[type]) + return {cred, authUrl} +} \ No newline at end of file diff --git a/apps/web/app/workflow/lib/config.ts b/apps/web/app/workflow/lib/config.ts index b999a59..7fd1041 100644 --- a/apps/web/app/workflow/lib/config.ts +++ b/apps/web/app/workflow/lib/config.ts @@ -1,4 +1,5 @@ import { BACKEND_URL } from "@repo/common/zod"; +import { GoogleSheetsNodeExecutor } from "@repo/nodes"; import axios from "axios" export const getAvailableTriggers = async () => { try { @@ -22,3 +23,23 @@ export const getAvailableTriggers = async () => { throw error; } }; + +export const getCredentials = async(type: string)=>{ + try{ + + const response = await axios.get( + `${BACKEND_URL}/user/getCredentials/${type}`, + { + withCredentials: true, + } + ); + console.log("response from config: ",response); + + const Data = JSON.stringify(response.data.Data); + return response.data.Data; + } + catch(e){ + console.error("Error fetching credentials:", e); + throw e; + } +} diff --git a/packages/nodes/src/common/google-oauth-service.ts b/packages/nodes/src/common/google-oauth-service.ts index a33216f..3b7813a 100644 --- a/packages/nodes/src/common/google-oauth-service.ts +++ b/packages/nodes/src/common/google-oauth-service.ts @@ -74,20 +74,21 @@ class GoogleOAuthService{ } } - async getCredentials(userId: string, nodeId?: string): Promise<{id: string, tokens: OAuthTokens} | null>{ + async getCredentials(userId: string, credId: string): Promise<{id: string, tokens: OAuthTokens} | null>{ try{ + console.log("user id: ",userId," & ",credId," from oauth service") const credentials = await this.prisma.credential.findFirst({ where:{ - userId: userId, + id:credId, + // userId: userId, type:'google_oauth', - ...(nodeId && {nodeId}) }, orderBy:{ id: 'desc' } }); - + console.log("credentails from oauth service: ",credentials) if(!credentials) return null; return { id: credentials.id, @@ -99,21 +100,15 @@ class GoogleOAuthService{ } } - async getAllCredentials(userId: string): Promise>{ + async getAllCredentials(userId: string, type: string): Promise>{ try{ const credentials = await this.prisma.credential.findMany({ where:{ userId: userId, - type:'google_oauth' + type: type }, - orderBy:{ - id: 'desc' - }, - select: { - id: true - } }); - + console.log("logs from service - ",credentials) return credentials; } catch(err){ diff --git a/packages/nodes/src/google-sheets/google-sheets.executor.ts b/packages/nodes/src/google-sheets/google-sheets.executor.ts index 28b5c40..db360bf 100644 --- a/packages/nodes/src/google-sheets/google-sheets.executor.ts +++ b/packages/nodes/src/google-sheets/google-sheets.executor.ts @@ -3,7 +3,7 @@ import { GoogleOAuthService } from "../common/google-oauth-service.js"; import { GoogleSheetsService, GoogleSheetsCredentials } from "./google-sheets.service.js"; interface NodeExecutionContext{ - nodeId: string, + credId: string, userId: string, config?: any, //sheet id / range... inputData?: any // previous node data @@ -51,10 +51,16 @@ class GoogleSheetsNodeExecutor{ return this.ensureSheetService(context); } - async getAllCredentials(userId: string) { + async getAllCredentials(userId: string, type: string) { try { - return await this.oauthService.getAllCredentials(userId); + + const credentials = await this.oauthService.getAllCredentials(userId, type); + console.log("log from executor - ",credentials) + if(credentials.length > 0) return credentials + else return (this.oauthService.getAuthUrl(userId)) + } catch (e) { + console.log(`Error in fetching credentials: ${e}`); return []; } } @@ -82,8 +88,8 @@ class GoogleSheetsNodeExecutor{ private async ensureSheetService(context: NodeExecutionContext): Promise<{ credentialId: string } | NodeExecutionResult> { try { - const credentials = await this.oauthService.getCredentials(context.userId, context.nodeId); - + const credentials = await this.oauthService.getCredentials(context.userId, context.credId); + console.log("credentails from sheet.executor: ",credentials) if (!credentials) { return { success: false, diff --git a/packages/nodes/src/google-sheets/google-sheets.node.ts b/packages/nodes/src/google-sheets/google-sheets.node.ts index 70fb80b..612a4c1 100644 --- a/packages/nodes/src/google-sheets/google-sheets.node.ts +++ b/packages/nodes/src/google-sheets/google-sheets.node.ts @@ -33,6 +33,9 @@ export class GoogleSheetNode { static async register(){ await NodeRegistry.register(this.definition) + // console.log(`✅ Registered node: ${this.definition.name}`); + await NodeRegistry.registerTrigger(this.definition) + // console.log(`✅ Registered Trigger: ${this.definition.name}`); } static getExecutor(){ diff --git a/packages/nodes/src/registry/node-registry.ts b/packages/nodes/src/registry/node-registry.ts index 032c1aa..187f3af 100644 --- a/packages/nodes/src/registry/node-registry.ts +++ b/packages/nodes/src/registry/node-registry.ts @@ -12,6 +12,7 @@ interface NodeDefinition { class NodeRegistry { private static registered = new Set(); + private static triggers = new Set() static async register(definition: NodeDefinition) { if (this.registered.has(definition.type)) return; @@ -34,12 +35,37 @@ class NodeRegistry { }); this.registered.add(definition.type); - console.log(`✅ Registered node: ${definition}`); + console.log(`✅ Registered node: ${definition.name}`); } catch (e) { console.error(`❌ Failed to register ${definition.name}:`, e); } } + static async registerTrigger(definition: NodeDefinition) { + if (this.triggers.has(definition.type)) return; + + try { + await prismaClient.availableTrigger.upsert({ + create: { + name: definition.name, + type: definition.type, + config: definition.config, + }, + update: { + name: definition.name, + type: definition.type, + config: definition.config, + }, + + where: { type: definition.type }, + }); + + this.triggers.add(definition.type); + console.log(`✅ Registered Trigger: ${definition.name}`); + } catch (e) { + console.error(`❌ Failed to Trigger ${definition.name}:`, e); + } + } static async registerAll() { await GoogleSheetNode.register(); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f808a4..ceb00d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,9 @@ importers: express: specifier: ^5.1.0 version: 5.1.0 + googleapis: + specifier: ^166.0.0 + version: 166.0.0 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2