From ce631bf526547f7f32a657a7622190c2ad2543f4 Mon Sep 17 00:00:00 2001 From: Vamsi_0 Date: Mon, 5 Jan 2026 15:57:49 +0530 Subject: [PATCH] Added Email Integration and complete worker --- README.md | 4 +- apps/http-backend/src/index.ts | 28 +- .../src/routes/google_callback.ts | 91 ++++-- .../src/routes/userRoutes/userRoutes.ts | 124 +++++--- apps/worker/package.json | 2 + apps/worker/src/engine/executor.ts | 72 +++++ apps/worker/src/engine/registory.ts | 45 +++ apps/worker/src/index.ts | 44 ++- apps/worker/src/tests/create-execution.ts | 17 + apps/worker/src/tests/setup-test.ts | 93 ++++++ apps/worker/src/tests/test.ts | 42 +++ apps/worker/src/tests/update-node.ts | 17 + apps/worker/src/types.ts | 16 + apps/worker/tsconfig.json | 17 +- apps/worker/tsconfig.tsbuildinfo | 2 +- packages/db/src/seed.ts | 97 ++++++ packages/db/tsconfig.tsbuildinfo | 2 +- packages/nodes/.gitignore | 32 ++ packages/nodes/package.json | 3 +- .../nodes/src/common/google-oauth-service.ts | 295 +++++++++--------- packages/nodes/src/gmail/gmail.executor.ts | 76 +++++ packages/nodes/src/gmail/gmail.service.ts | 112 +++++++ .../google-sheets/google-sheets.service.ts | 238 +++++++------- packages/nodes/src/google-sheets/test.ts | 2 +- packages/nodes/src/index.ts | 6 +- packages/nodes/tsconfig.json | 3 +- packages/nodes/tsconfig.tsbuildinfo | 2 +- pnpm-lock.yaml | 9 +- 28 files changed, 1109 insertions(+), 382 deletions(-) create mode 100644 apps/worker/src/engine/executor.ts create mode 100644 apps/worker/src/engine/registory.ts create mode 100644 apps/worker/src/tests/create-execution.ts create mode 100644 apps/worker/src/tests/setup-test.ts create mode 100644 apps/worker/src/tests/test.ts create mode 100644 apps/worker/src/tests/update-node.ts create mode 100644 apps/worker/src/types.ts create mode 100644 packages/db/src/seed.ts create mode 100644 packages/nodes/.gitignore create mode 100644 packages/nodes/src/gmail/gmail.executor.ts create mode 100644 packages/nodes/src/gmail/gmail.service.ts diff --git a/README.md b/README.md index 5013735..ef960db 100644 --- a/README.md +++ b/README.md @@ -488,11 +488,11 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on: ## ๐Ÿ“„ License -[Add your license here] +[MIT license] ## ๐Ÿ‘ฅ Authors -- [Your Name/Team] +- [Vamsi , Teja] --- diff --git a/apps/http-backend/src/index.ts b/apps/http-backend/src/index.ts index f1920ae..84e9077 100644 --- a/apps/http-backend/src/index.ts +++ b/apps/http-backend/src/index.ts @@ -1,9 +1,4 @@ -// import {prismaClient} from '@repo/db/index.js' -// import axios from "axios"; -import { prismaClient } from "@repo/db/client"; import cookieParser from 'cookie-parser' - - import { NodeRegistry } from "@repo/nodes/nodeClient"; import express from "express"; import { userRouter } from "./routes/userRoutes/userRoutes.js"; @@ -21,30 +16,19 @@ app.use(cors({ app.use(express.json()) app.use(cookieParser()); -// const main = async () => { -// try { -// const users = await prismaClient.user.findMany(); -// console.log("Users from DB:", users); -// } catch (err) { -// console.error("Error fetching users:", err); -// } -// }; - -// main().then(() => { -// console.log("This log is from http Backend"); -// }); app.use("/user" , userRouter) app.use('/node', sheetRouter) -app.use('/google', googleAuth) +app.use('/auth/google', googleAuth) // โ† CHANGED THIS LINE! + const PORT= 3002 + async function startServer() { await NodeRegistry.registerAll() app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); - }) + console.log(`Server running on port ${PORT}`); + }) } - - startServer() +startServer() diff --git a/apps/http-backend/src/routes/google_callback.ts b/apps/http-backend/src/routes/google_callback.ts index f920cf2..568de2e 100644 --- a/apps/http-backend/src/routes/google_callback.ts +++ b/apps/http-backend/src/routes/google_callback.ts @@ -1,42 +1,73 @@ -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'; +import { google } from "googleapis"; +import { GoogleOAuthService } from "@repo/nodes"; +import type { OAuthTokens } from "@repo/nodes"; +import { AuthRequest, userMiddleware } from "./userRoutes/userMiddleware.js"; +import { Router, Request, Response } from "express"; -export const googleAuth: Router = Router() +export const googleAuth: Router = Router(); +googleAuth.get( + "/initiate", + userMiddleware, + async (req: AuthRequest, res: Response) => { + // const userId = req.user?.id || "test_user"; // Get from auth middleware + const userId = req.user?.id; -googleAuth.get('/callback', async(req: Request, res: Response)=>{ + const oauth2 = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI || + "http://localhost:3002/auth/google/callback" + ); + + const authUrl = oauth2.generateAuthUrl({ + access_type: "offline", + scope: [ + "https://www.googleapis.com/auth/spreadsheets", + "https://www.googleapis.com/auth/drive.readonly", + "https://www.googleapis.com/auth/gmail.send", + "https://www.googleapis.com/auth/gmail.readonly", + ], + state: userId, + prompt: "consent", + }); + + return res.redirect(authUrl); + } +); + +googleAuth.get( + "/callback", + userMiddleware, + 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 state = req.query.state; + 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 + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI || + "http://localhost:3002/auth/google/callback" ); try { - const { tokens } = await oauth2.getToken(code); - - // Save tokens to database if userId (state) is provided - if (state && typeof state === 'string') { + const { tokens } = await oauth2.getToken(code); - await Oauth.saveCredentials(state, tokens as OAuthTokens) - } + // 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')}`); + // 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/userRoutes/userRoutes.ts b/apps/http-backend/src/routes/userRoutes/userRoutes.ts index 498eaa1..eeb6c9a 100644 --- a/apps/http-backend/src/routes/userRoutes/userRoutes.ts +++ b/apps/http-backend/src/routes/userRoutes/userRoutes.ts @@ -151,51 +151,105 @@ router.get("/getAvailableTriggers", } ); +// //------------------------------ GET CREDENTIALS ----------------------------- + +// 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" }); +// } +// } +// ); + //------------------------------ GET CREDENTIALS ----------------------------- 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", - }); + 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", + }); + } + + // Check if credentials exist in database + const credentials = await prismaClient.credential.findMany({ + where: { + userId: userId, + type : type } - 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({ + }); + + if (credentials.length === 0) { + // No credentials found - return the correct auth URL + const authUrl = `${process.env.BACKEND_URL || 'http://localhost:3002'}/auth/google/initiate`; + 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"); + } + + // Credentials found - return them + return res.status(statusCodes.OK).json({ + message: "Credentials Fetched successfully", + Data: credentials, + }); + + } catch (e) { + console.log("Error Fetching the credentials ", e instanceof Error ? e.message : "Unknown reason"); return res .status(statusCodes.INTERNAL_SERVER_ERROR) - .json({ message: "Internal server from fetching the credentials" }); + .json({ message: "Internal server error from fetching the credentials" }); } } ); diff --git a/apps/worker/package.json b/apps/worker/package.json index f99e9d7..c444b01 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -5,12 +5,14 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc -b", "dev": "tsc -b && node dist/index.js" }, "type": "module", "keywords": [], "devDependencies": { "@repo/db": "workspace:*", + "@repo/nodes": "workspace:*", "@workspace/typescript-config": "workspace:*" }, "author": "", diff --git a/apps/worker/src/engine/executor.ts b/apps/worker/src/engine/executor.ts new file mode 100644 index 0000000..e058e71 --- /dev/null +++ b/apps/worker/src/engine/executor.ts @@ -0,0 +1,72 @@ +import { prismaClient } from "@repo/db/client"; +import { register } from "./registory.js"; +export async function executeWorkflow( + workflowExecutionId: string +): Promise { + console.log(`workflowExecutionId is ${workflowExecutionId}`); + const data = await prismaClient.workflowExecution.findUnique({ + where: { id: workflowExecutionId }, + include: { + workflow: { + include: { + nodes: { + include: { + AvailableNode: true, + credentials: true, + }, + orderBy: { position: "asc" }, + }, + }, + }, + }, + }); + let currentInputData = data?.metadata; + if (!data) { + console.log(`No workflow execution found for id ${workflowExecutionId}`); + return; + } + + const update = await prismaClient.workflowExecution.update({ + where: { + id: workflowExecutionId, + }, + data: { + status: "InProgress", + }, + }); + + const nodes = data?.workflow.nodes; + + for (const node of nodes) { + const nodeType = node.AvailableNode.type; + const context = { + nodeId: node.id, + userId: data.workflow.userId, + credentialId: node.credentials[0]?.id, + config: node.config as Record, + inputData: currentInputData, + }; + + const execute = await register.execute(nodeType, context); + if (!execute.success) { + await prismaClient.workflowExecution.update({ + where: { id: workflowExecutionId }, + data: { + status: "Failed", + error: execute.error, + completedAt: new Date(), + }, + }); + return; + } + currentInputData = execute.output; + } + const updatedStatus = await prismaClient.workflowExecution.update({ + where: { id: workflowExecutionId }, + data: { + status: "Completed", + completedAt: new Date(), + }, + }); + console.log(updatedStatus); +} diff --git a/apps/worker/src/engine/registory.ts b/apps/worker/src/engine/registory.ts new file mode 100644 index 0000000..16954bb --- /dev/null +++ b/apps/worker/src/engine/registory.ts @@ -0,0 +1,45 @@ +import { ExecutionContext, ExecutionResult, NodeExecutor } from "../types.js"; +import { GmailExecutor } from "@repo/nodes/nodeClient"; +class ExecutionRegistry { + private executors = new Map(); + + register(nodeType: string, executor: NodeExecutor) { + this.executors.set(nodeType, executor); + } + + async execute( + nodeType: string, + context: ExecutionContext + ): Promise { + const executor = this.executors.get(nodeType); + if (!executor) { + return { + success: false, + error: `No Executor found for ${nodeType}`, + }; + } + try { + const result = await executor.execute(context); + console.log("Execute result:", result); + + return result; + } catch (error: any) { + return { + success: false, + error: error, + }; + } + } + initialize() { + // TODO: Ensure GmailExecutor implements NodeExecutor and is compatible with ExecutionContext + // If needed, adapt/extract a compatible Executor for registration. + // For now, this cast suppresses the type error. Refactor as soon as possible! + + + //wehen visits this next time make sure chang gmail executor implements NodeExecutor + this.register("gmail", new GmailExecutor() as unknown as NodeExecutor); + console.log(`The current Executors are ${this.executors.size}`); + } +} + +export const register = new ExecutionRegistry(); diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index c36ef78..de8e826 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -1,7 +1,8 @@ import { Kafka } from "kafkajs"; - +import { executeWorkflow } from "./engine/executor.js"; const kafka = new Kafka({ - clientId: "Processing App", + // clientId: "Processing App", + clientId: "BuildFlow-Worker", brokers: ["localhost:9092"], }); const TOPIC_NAME = "First-Client"; @@ -17,27 +18,46 @@ async function main() { await consumer.run({ autoCommit: false, eachMessage: async ({ topic, partition, message }) => { + const workflowExecutionId = message.value?.toString(); console.log({ partition, offset: message.offset, value: message.value?.toString(), - topic + topic, }); - await new Promise((r) => setTimeout(r, 5000)); + if (!workflowExecutionId) { + console.warn("message not found skipping"); + return ; + } + console.log(`Recieved workflowExecutionId ${workflowExecutionId}`); + console.log("The value we get from processor", message.value); + console.log( + "The value we get from processor to make it string", + message.value?.toString() + ); + const result = await executeWorkflow(workflowExecutionId) + console.log("This is the result fo the execuitng" , result) + // await new Promise((r) => setTimeout(r, 5000)); console.log("Processing Job Done"); - await consumer.commitOffsets([ - { - topic: TOPIC_NAME, - partition: partition, + try { + await consumer.commitOffsets([ + { + topic: TOPIC_NAME, + partition: partition, - offset: (parseInt(message.offset) + 1).toString() - }, - ]); + offset: (parseInt(message.offset) + 1).toString(), + }, + ]); + } catch (error) { + console.log(`Failed for ${workflowExecutionId}`, error); + } console.log(typeof message.offset); }, }); } -main(); +main().catch((error) => { + console.error("failed , fatal error", error); +}); diff --git a/apps/worker/src/tests/create-execution.ts b/apps/worker/src/tests/create-execution.ts new file mode 100644 index 0000000..ee5e618 --- /dev/null +++ b/apps/worker/src/tests/create-execution.ts @@ -0,0 +1,17 @@ +import { prismaClient } from "@repo/db/client"; + +async function createExecution() { + const execution = await prismaClient.workflowExecution.create({ + data: { + workflowId: 'f5d1ae7c-104c-44ab-ab49-917f4716e2f3', + status: 'Pending', + metadata: { trigger: 'manual', test: 'gmail-api-enabled' } + } + }); + console.log('โœ… New execution created!'); + console.log('Execution ID:', execution.id); + console.log('\n๐Ÿ“ Update test.ts with:'); + console.log(`await executeWorkflow("${execution.id}");`); +} + +createExecution(); diff --git a/apps/worker/src/tests/setup-test.ts b/apps/worker/src/tests/setup-test.ts new file mode 100644 index 0000000..d7fce67 --- /dev/null +++ b/apps/worker/src/tests/setup-test.ts @@ -0,0 +1,93 @@ +import { prismaClient } from "@repo/db/client"; + +async function setupTestWorkflow() { + const userId = "b3be6569-f174-4ea6-b2b9-05a62b75b280"; // Your user ID + + console.log("๐Ÿ”ง Setting up test workflow...\n"); + + // 1. Find your credential + const credential = await prismaClient.credential.findFirst({ + where: { + userId: userId, + type: "google_oauth" + } + }); + + if (!credential) { + console.log("โŒ No credential found! Create one from frontend first."); + return; + } + console.log("โœ… Found credential:", credential.id); + + // 2. Find AvailableNode for Gmail + const availableNode = await prismaClient.availableNode.findFirst({ + where: { type: "gmail" } + }); + + if (!availableNode) { + console.log("โŒ Gmail node not found in AvailableNode table!"); + console.log("Available node types:"); + const nodes = await prismaClient.availableNode.findMany(); + nodes.forEach(n => console.log("-", n.type)); + return; + } + console.log("โœ… Found AvailableNode:", availableNode.type); + + // 3. Create workflow + const workflow = await prismaClient.workflow.create({ + data: { + name: "Test Gmail Workflow", + userId: userId, + description : "First Workflow Creating for Testing" , + config : {} + } + }); + console.log("โœ… Created workflow:", workflow.id); + + // 4. Create node + const node = await prismaClient.node.create({ + data: { + name: "Send Test Email", + workflowId: workflow.id, + AvailableNodeID: availableNode.id, + position: 0, + config: { + to: "iamvamsi0@gmail.com", + subject: "Test from BuildFlow Worker", + body: "This is a test email sent from the worker!" + } + } + }); + console.log("โœ… Created node:", node.id); + + // 5. Link credential to node + await prismaClient.credential.update({ + where: { id: credential.id }, + data: { + nodeId: node.id + } + }); + console.log("โœ… Linked credential to node"); + + // 6. Create workflow execution + const execution = await prismaClient.workflowExecution.create({ + data: { + workflowId: workflow.id, + status: "Pending", + metadata: { + trigger: "manual", + from: "Test setup script" + } + } + }); + + console.log("\n๐ŸŽ‰ Test workflow setup complete!\n"); + console.log("๐Ÿ“‹ Details:"); + console.log(" - Workflow ID:", workflow.id); + console.log(" - Node ID:", node.id); + console.log(" - WorkflowExecution ID:", execution.id); + console.log("\nโ–ถ๏ธ Run this command to test:"); + console.log(` Update test.ts with: await executeWorkflow("${execution.id}")`); +} + +setupTestWorkflow().catch(console.error); diff --git a/apps/worker/src/tests/test.ts b/apps/worker/src/tests/test.ts new file mode 100644 index 0000000..0183313 --- /dev/null +++ b/apps/worker/src/tests/test.ts @@ -0,0 +1,42 @@ +import { executeWorkflow } from "../engine/executor.js"; +import { register } from "../engine/registory.js"; + +async function testDirect() { + console.log("๐Ÿงช Testing Gmail integration directly...\n"); + + // Initialize registry + register.initialize(); + + // Use the NEW execution ID from setup + await executeWorkflow("83b1e9ef-c8f2-45a6-9f91-ef48d29fbb4c"); + + console.log("\nโœ… Test complete! Check your email inbox."); +} + +testDirect().catch(console.error); + + + +import { prismaClient } from "@repo/db/client"; + +async function debug() { + const executionId = "ca4210e3-c830-408e-a86d-961508e9b325"; + + // Step 1: Get execution only + const execution = await prismaClient.workflowExecution.findUnique({ + where: { id: executionId } + }); + console.log("Execution:", execution); + + if (execution?.workflowId) { + // Step 2: Get workflow + const workflow = await prismaClient.workflow.findUnique({ + where: { id: execution.workflowId }, + include: { nodes: true } + }); + console.log("Workflow:", workflow); + console.log("Number of nodes:", workflow?.nodes?.length); + } +} + +debug(); diff --git a/apps/worker/src/tests/update-node.ts b/apps/worker/src/tests/update-node.ts new file mode 100644 index 0000000..a4b23ee --- /dev/null +++ b/apps/worker/src/tests/update-node.ts @@ -0,0 +1,17 @@ +import { prismaClient } from "@repo/db/client"; + +async function updateNode() { + await prismaClient.node.update({ + where: { id: 'f058ac5a-f329-4d6f-986a-e9a1f65d0b14' }, + data: { + config: { + to: 'iamvamsi0@gmail.com', + subject: 'Test from BuildFlow - Self Send', + body: 'This email is sent from and to the same account: iamvamsi0@gmail.com' + } + } + }); + console.log('โœ… Updated node config'); +} + +updateNode(); diff --git a/apps/worker/src/types.ts b/apps/worker/src/types.ts new file mode 100644 index 0000000..dfb0927 --- /dev/null +++ b/apps/worker/src/types.ts @@ -0,0 +1,16 @@ +export interface ExecutionContext { + nodeId: string; + userId: string; + credentialsId?: string; + config: Record; + inputData: any; +} +export interface ExecutionResult { + success: boolean; + output?: any; + error?: string; + metadata?: Record; +} +export interface NodeExecutor { + execute(context: ExecutionContext): Promise; +} diff --git a/apps/worker/tsconfig.json b/apps/worker/tsconfig.json index 133fe0f..4e5371d 100644 --- a/apps/worker/tsconfig.json +++ b/apps/worker/tsconfig.json @@ -1,7 +1,14 @@ { - "extends" : "@workspace/typescript-config/base.json", - "compilerOptions" : { - "rootDir" : "./src", - "outDir" : "./dist" - } + "extends": "@workspace/typescript-config/base.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "lib": ["ES2020"], + "moduleResolution": "nodenext", + "module": "nodenext", + "target": "ES2020" + }, + "include": [ + "src" + ] } \ No newline at end of file diff --git a/apps/worker/tsconfig.tsbuildinfo b/apps/worker/tsconfig.tsbuildinfo index 44940f3..bab5e89 100644 --- a/apps/worker/tsconfig.tsbuildinfo +++ b/apps/worker/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/index.ts"],"version":"5.7.3"} \ No newline at end of file +{"root":["./src/index.ts","./src/test.ts","./src/types.ts","./src/engine/executor.ts","./src/engine/registory.ts"],"version":"5.7.3"} \ No newline at end of file diff --git a/packages/db/src/seed.ts b/packages/db/src/seed.ts new file mode 100644 index 0000000..63ff3d4 --- /dev/null +++ b/packages/db/src/seed.ts @@ -0,0 +1,97 @@ +// import { prismaClient } from "@repo/db/client"; +import { prismaClient } from "."; +async function setupTestWorkflow() { + console.log("๐Ÿ”ง Setting up test workflow..."); + + // Step 1: Create AvailableNode for Gmail + const availableNode = await prismaClient.availableNode.upsert({ + where: { id: "gmail_node_001" }, + update: {}, + create: { + id: "gmail_node_001", + name: "Gmail", + type: "gmail", + description: "Send emails via Gmail", + // category: "Communication", + config: { + to: { type: "string", required: true }, + subject: { type: "string", required: true }, + body: { type: "string", required: true }, + }, + }, + }); + console.log("โœ… AvailableNode created:", availableNode.id); + + // Step 2: Check for existing credential + const credential = await prismaClient.credential.findFirst({ + where: { type: "google_oauth" }, + }); + + if (!credential) { + console.error("โŒ No Gmail credential found!"); + console.log("๐Ÿ‘‰ Connect Gmail via your frontend first"); + return; + } + console.log("โœ… Found credential:", credential.id); + + // Step 3: Create Workflow + const workflow = await prismaClient.workflow.upsert({ + where: { id: "test_workflow_001" }, + update: {}, + create: { + id: "test_workflow_001", + name: "Test Gmail Workflow", + description: "Testing Gmail integration", + userId: credential.userId, + config: {}, // Add required 'config' field + status: "InProgress" + }, + }); + console.log("โœ… Workflow created:", workflow.id); + + // Step 4: Create Node with credential + const node = await prismaClient.node.upsert({ + where: { id: "test_node_001" }, + update: {}, + create: { + id: "test_node_001", + name: "Send Test Email", + position: 1, + workflowId: workflow.id, + AvailableNodeID: availableNode.id, + config: { + to: "iamvamsi0@gmail.com", // โ† CHANGE THIS! + subject: "BuildFlow Test Email", + body: "This is a test email from BuildFlow worker!", + }, + credentials: { + connect: { id: credential.id }, + }, + }, + }); + console.log("โœ… Node created:", node.id); + + // Step 5: Create WorkflowExecution + const execution = await prismaClient.workflowExecution.create({ + data: { + id: "test_exec_001", + workflowId: workflow.id, + status: "Pending", + metadata: { + trigger: "manual", + timestamp: new Date().toISOString(), + }, + }, + }); + console.log("โœ… Execution created:", execution.id); + + console.log("\n๐ŸŽ‰ Test workflow ready!"); + console.log("๐Ÿ“ง Email will be sent to: your-email@gmail.com"); +} + +setupTestWorkflow() + .then(() => process.exit(0)) + .catch((error) => { + console.error("โŒ Error:", error); + process.exit(1); + }); diff --git a/packages/db/tsconfig.tsbuildinfo b/packages/db/tsconfig.tsbuildinfo index 44940f3..968d9e9 100644 --- a/packages/db/tsconfig.tsbuildinfo +++ b/packages/db/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/index.ts"],"version":"5.7.3"} \ No newline at end of file +{"root":["./src/index.ts","./src/seed.ts"],"version":"5.9.2"} \ No newline at end of file diff --git a/packages/nodes/.gitignore b/packages/nodes/.gitignore new file mode 100644 index 0000000..cce2241 --- /dev/null +++ b/packages/nodes/.gitignore @@ -0,0 +1,32 @@ +# build output +dist/ +build/ + +# TypeScript +*.tsbuildinfo + +# node modules +node_modules/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# IDE & editor folders +.vscode/ +.idea/ +*.swp + +# environment files +.env +.env.* + +# OS files +.DS_Store +Thumbs.db + +# Others +coverage/ +*.log + diff --git a/packages/nodes/package.json b/packages/nodes/package.json index a87dcbd..22871f8 100644 --- a/packages/nodes/package.json +++ b/packages/nodes/package.json @@ -8,7 +8,6 @@ "scripts": { "build": "tsc -b", "dev": "npm run build && node dist/index.js", - "clean": "rm -rf dist" }, "keywords": [], @@ -20,12 +19,12 @@ "license": "ISC", "packageManager": "pnpm@10.4.1", "dependencies": { + "@types/dotenv": "^8.2.3", "dotenv": "^17.2.3", "google-auth-library": "^10.5.0", "googleapis": "^166.0.0" }, "devDependencies": { - "@types/node": "^20.19.9", "@repo/db": "workspace:*", "@workspace/typescript-config": "workspace:*" } diff --git a/packages/nodes/src/common/google-oauth-service.ts b/packages/nodes/src/common/google-oauth-service.ts index 3b7813a..7bf52c4 100644 --- a/packages/nodes/src/common/google-oauth-service.ts +++ b/packages/nodes/src/common/google-oauth-service.ts @@ -1,153 +1,164 @@ -import { google } from 'googleapis'; +import { google } from "googleapis"; import { prismaClient } from "@repo/db"; -interface OAuthTokens{ - access_token: string, - refresh_token: string, - token_type: string, - expiry_date: number, - scope: string +interface OAuthTokens { + access_token: string; + refresh_token: string; + token_type: string; + expiry_date: number; + scope: string; } -class GoogleOAuthService{ - private oauth2Client; - private prisma; - - constructor(){ - this.prisma = prismaClient; - this.oauth2Client = new google.auth.OAuth2( - process.env.GOOGLE_CLIENT_ID, - process.env.GOOGLE_CLIENT_SECRET, - process.env.GOOGLE_REDIRECT_URI - ); +class GoogleOAuthService { + private oauth2Client; + private prisma; + + constructor() { + this.prisma = prismaClient; + this.oauth2Client = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI + ); + } + + getAuthUrl(userId: string): string { + const scopes = [ + "https://www.googleapis.com/auth/spreadsheets", + "https://www.googleapis.com/auth/drive.readonly", + "https://www.googleapis.com/auth/gmail.send", + "https://www.googleapis.com/auth/gmail.readonly", + ]; + + return this.oauth2Client.generateAuthUrl({ + access_type: "offline", + scope: scopes, + state: userId, + prompt: "consent", + }); + } + + async getTokens(code: string): Promise { + try { + const { tokens } = await this.oauth2Client.getToken(code); + + return { + access_token: tokens.access_token || "", + refresh_token: tokens.refresh_token || "", + expiry_date: tokens.expiry_date || 0, + token_type: tokens.token_type || "", + scope: tokens.scope || "", + }; + } catch (error) { + throw new Error( + `Failed to exchange code for token: ${error instanceof Error ? error.message : "Unknow error"}` + ); } - - getAuthUrl(userId: string):string { - const scopes = [ - 'https://www.googleapis.com/auth/spreadsheets', - 'https://www.googleapis.com/auth/drive.readonly' - ] - - return this.oauth2Client.generateAuthUrl({ - access_type: 'offline', - scope: scopes, - state: userId, - prompt: 'consent' - }) - } - - async getTokens(code: string): Promise { - - try - { - const {tokens} = await this.oauth2Client.getToken(code); - - return{ - access_token : tokens.access_token || '', - refresh_token : tokens.refresh_token || '', - expiry_date : tokens.expiry_date|| 0, - token_type : tokens.token_type || '', - scope : tokens.scope || '' - } - } - catch(error){ - throw new Error(`Failed to exchange code for token: ${error instanceof Error ? error.message : "Unknow error"}`) - } - } - - async saveCredentials(userId: string, tokens: OAuthTokens, nodeId?: string): Promise { - try{ - // const credentialId = `cred_google_${userId}_${Date.now()}` - - await this.prisma.credential.create({ - data:{ - // id:credentialId, - userId: userId, - type: 'google_oauth', - config: JSON.parse(JSON.stringify(tokens)), - nodeId: nodeId || null - } - }) - } - catch(error){ - throw new Error(`failed to store data in Credentials: ${error instanceof Error ? error.message : "Unknown Error"}`) - } + } + + async saveCredentials( + userId: string, + tokens: OAuthTokens, + nodeId?: string + ): Promise { + try { + // const credentialId = `cred_google_${userId}_${Date.now()}` + + await this.prisma.credential.create({ + data: { + // id:credentialId, + userId: userId, + type: "google_oauth", + config: JSON.parse(JSON.stringify(tokens)), + nodeId: nodeId || null, + }, + }); + } catch (error) { + throw new Error( + `failed to store data in Credentials: ${error instanceof Error ? error.message : "Unknown Error"}` + ); } - - 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:{ - id:credId, - // userId: userId, - type:'google_oauth', - }, - orderBy:{ - id: 'desc' - } - }); - console.log("credentails from oauth service: ",credentials) - if(!credentials) return null; - return { - id: credentials.id, - tokens: credentials.config as unknown as OAuthTokens - } - } - catch(err){ - throw new Error(`Failed to get credentials: ${err instanceof Error ? err.message : "unknown error"}`) - } + } + + 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: { + id: credId, + // userId: userId, + type: "google_oauth", + }, + orderBy: { + id: "desc", + }, + }); + console.log("credentails from oauth service: ", credentials); + if (!credentials) return null; + return { + id: credentials.id, + tokens: credentials.config as unknown as OAuthTokens, + }; + } catch (err) { + throw new Error( + `Failed to get credentials: ${err instanceof Error ? err.message : "unknown error"}` + ); } - - async getAllCredentials(userId: string, type: string): Promise>{ - try{ - const credentials = await this.prisma.credential.findMany({ - where:{ - userId: userId, - type: type - }, - }); - console.log("logs from service - ",credentials) - return credentials; - } - catch(err){ - throw new Error(`Failed to get all credentials: ${err instanceof Error ? err.message : "unknown error"}`) - } + } + + async getAllCredentials(userId: string, type: string): Promise> { + try { + const credentials = await this.prisma.credential.findMany({ + where: { + userId: userId, + type: type, + }, + }); + console.log("logs from service - ", credentials); + return credentials; + } catch (err) { + throw new Error( + `Failed to get all credentials: ${err instanceof Error ? err.message : "unknown error"}` + ); } - - async updateCredentials(credentialId: string, tokens: Partial): Promise { - try{ - const existing = await this.prisma.credential.findUnique({ - where:{ - id: credentialId - } - }); - - if(!existing) throw new Error(`No Credential found`); - - const updatedConfig = { - ...(existing.config as object), - ...(tokens) - } - - await this.prisma.credential.update({ - where:{ - id:credentialId - }, - data:{ - config: updatedConfig as any - } - }); - - } - catch(e){ - throw new Error(`Failed to update Credentials: ${e instanceof Error ? e.message : "unknown error"}`) - } + } + + async updateCredentials( + credentialId: string, + tokens: Partial + ): Promise { + try { + const existing = await this.prisma.credential.findUnique({ + where: { + id: credentialId, + }, + }); + + if (!existing) throw new Error(`No Credential found`); + + const updatedConfig = { + ...(existing.config as object), + ...tokens, + }; + + await this.prisma.credential.update({ + where: { + id: credentialId, + }, + data: { + config: updatedConfig as any, + }, + }); + } catch (e) { + throw new Error( + `Failed to update Credentials: ${e instanceof Error ? e.message : "unknown error"}` + ); } - + } } -export { GoogleOAuthService } -export type { OAuthTokens } - +export { GoogleOAuthService }; +export type { OAuthTokens }; diff --git a/packages/nodes/src/gmail/gmail.executor.ts b/packages/nodes/src/gmail/gmail.executor.ts new file mode 100644 index 0000000..7241e6a --- /dev/null +++ b/packages/nodes/src/gmail/gmail.executor.ts @@ -0,0 +1,76 @@ +import { GoogleOAuthService } from "../common/google-oauth-service.js"; +import { GmailService, GmailCredentials } from "./gmail.service.js"; + +interface NodeExecutionContext { + credId: string; + userId: string; + config?: any; + inputData?: any; +} + +interface NodeExecutionResult { + success: boolean; + output?: any; + error?: string; +} + +class GmailExecutor { + private oauthService: GoogleOAuthService; + private gmailService: GmailService | null = null; + + constructor() { + this.oauthService = new GoogleOAuthService(); + } + + async execute(context: NodeExecutionContext): Promise { + try { + // Get credentials + const credentials = await this.oauthService.getCredentials( + context.userId, + context.credId + ); + + if (!credentials) { + return { + success: false, + error: "Gmail authorization required", + }; + } + + // Initialize service + this.gmailService = new GmailService( + credentials.tokens as GmailCredentials + ); + + // Check token expiry and refresh if needed + if (this.gmailService.isTokenExpired()) { + const refreshResult: any = await this.gmailService.refreshAccessToken(); + if (refreshResult.success) { + await this.oauthService.updateCredentials( + credentials.id, + refreshResult.data + ); + } + } + + // Send email + const { to, subject, body } = context.config; + const result = await this.gmailService.sendEmail(to, subject, body); + + return { + success: result.success, + output: result.data, + error: result.error, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + } +} + +export default GmailExecutor; +// export { default as GmailExecutor } from "./gmail.executor.js"; +// export { GmailService } from "./gmail.service.js"; diff --git a/packages/nodes/src/gmail/gmail.service.ts b/packages/nodes/src/gmail/gmail.service.ts new file mode 100644 index 0000000..347a093 --- /dev/null +++ b/packages/nodes/src/gmail/gmail.service.ts @@ -0,0 +1,112 @@ +import { google, gmail_v1 } from "googleapis"; +import { OAuth2Client } from "google-auth-library"; + +export interface GmailCredentials { + access_token: string; + refresh_token: string; + token_type: string; + expiry_date: number; +} + +export class GmailService { + private gmail: gmail_v1.Gmail; + private auth: OAuth2Client; + constructor(credentials: GmailCredentials) { + this.auth = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI + ); + + // FIX 1: Set credentials + this.auth.setCredentials({ + access_token: credentials.access_token, + refresh_token: credentials.refresh_token, + token_type: credentials.token_type, + expiry_date: credentials.expiry_date, + }); + + // FIX 2: Initialize gmail + this.gmail = google.gmail({ version: "v1", auth: this.auth }); + } + + async sendEmail(to: string, subject: string, body: string) { + try { + // Build the raw email string + const emailLines = [ + `To: ${to}`, + `Subject: ${subject}`, + "Content-Type: text/html; charset=utf-8", + "", + body, + ]; + const email = emailLines.join("\r\n"); + + // Encode the email to base64url + const encodedEmail = Buffer.from(email) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); + + // Initialize gmail if needed + if (!this.gmail) { + this.gmail = google.gmail({ version: "v1", auth: this.auth }); + } + + // Send email + const result = await this.gmail.users.messages.send({ + userId: "me", + requestBody: { + raw: encodedEmail, + }, + }); + + return { + success: true, + data: result.data, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error), + }; + } + } + + isTokenExpired(): boolean { + // Check if the token is expired, similar to GoogleSheetsService + // this.auth.credentials.expiry_date is in ms + if ( + !this.auth || + !this.auth.credentials || + !this.auth.credentials.expiry_date + ) { + return true; + } + const now = Date.now(); + // Give a 1-minute buffer + return now >= this.auth.credentials.expiry_date - 60000; + } + + async refreshAccessToken() { + // Refresh access token using OAuth2Client, similar to GoogleSheetsService + if (!this.auth) { + throw new Error("OAuth2 client not initialized"); + } + try { + const tokens = await this.auth.refreshAccessToken(); + this.auth.setCredentials(tokens.credentials); + return { + success: true, + data: tokens.credentials, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error), + }; + } + } +} + diff --git a/packages/nodes/src/google-sheets/google-sheets.service.ts b/packages/nodes/src/google-sheets/google-sheets.service.ts index 761d6c9..0f3daa2 100644 --- a/packages/nodes/src/google-sheets/google-sheets.service.ts +++ b/packages/nodes/src/google-sheets/google-sheets.service.ts @@ -1,130 +1,128 @@ -import { google, sheets_v4, drive_v3 } from 'googleapis'; -import { OAuth2Client } from 'google-auth-library'; -import { OAuthTokens } from '../common/google-oauth-service'; - -interface GoogleSheetsCredentials{ - access_token: string, - refresh_token: string, - token_type: string, - expiry_date: number +import { google, sheets_v4, drive_v3 } from "googleapis"; +import { OAuth2Client } from "google-auth-library"; +// import { OAuthTokens } from "../common/google-oauth-service.js"; + +interface GoogleSheetsCredentials { + access_token: string; + refresh_token: string; + token_type: string; + expiry_date: number; } -interface ReadRowsParams{ - spreadsheetId: string, - range: string +interface ReadRowsParams { + spreadsheetId: string; + range: string; } -class GoogleSheetsService{ - private sheets : sheets_v4.Sheets; - private auth: OAuth2Client; - private drive: drive_v3.Drive; - constructor(credentials: GoogleSheetsCredentials){ - this.auth = new google.auth.OAuth2( - process.env.GOOGLE_CLIENT_ID, - process.env.GOOGLE_CLIENT_SECRET, - process.env.GOOGLE_REDIRECT_URI - ); - - this.auth.setCredentials({ - access_token: credentials.access_token, - refresh_token: credentials.refresh_token, - token_type: credentials.token_type, - expiry_date: credentials.expiry_date - }); - - this.sheets = google.sheets({ - version: 'v4', - auth: this.auth - }); - - this.drive = google.drive({ - version: 'v3', - auth: this.auth - }) - - } - - async getSheets(): Promise{ - const files = await this.drive.files.list({ - q: "mimeType='application/vnd.google-apps.spreadsheet'", - spaces: 'drive', - pageSize: 10, - fields: 'files(id, name, createdTime)', - }) - - if(files){ - return { - success: true, - data: files - } - } - return { - success: false, - data: null - } - } - - async getSheetTabs(spreadsheetId: string): Promise{ - try{ - const response = await this.sheets.spreadsheets.get({ - spreadsheetId: spreadsheetId, - fields: 'sheets.properties' - }); - - const tabs = response.data.sheets?.map(sheet => ({ - id: sheet.properties?.sheetId, - name: sheet.properties?.title - })) || []; - - return { - success: true, - data: tabs - }; - } - catch(error){ - return { - success: false, - error: error instanceof Error ? error.message : 'Failed to fetch sheet tabs' - }; - } +class GoogleSheetsService { + private sheets: sheets_v4.Sheets; + private auth: OAuth2Client; + private drive: drive_v3.Drive; + constructor(credentials: GoogleSheetsCredentials) { + this.auth = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI + ); + + this.auth.setCredentials({ + access_token: credentials.access_token, + refresh_token: credentials.refresh_token, + token_type: credentials.token_type, + expiry_date: credentials.expiry_date, + }); + + this.sheets = google.sheets({ + version: "v4", + auth: this.auth, + }); + + this.drive = google.drive({ + version: "v3", + auth: this.auth, + }); + } + + async getSheets(): Promise { + const files = await this.drive.files.list({ + q: "mimeType='application/vnd.google-apps.spreadsheet'", + spaces: "drive", + pageSize: 10, + fields: "files(id, name, createdTime)", + }); + + if (files) { + return { + success: true, + data: files, + }; } - - async readRows(params: ReadRowsParams): Promise{ - try{ - const response = await this.sheets.spreadsheets.values.get({ - spreadsheetId: params.spreadsheetId, - range: params.range - }); - return response.data.values || [] - } - catch(error){ - throw new Error(`Failed to fetch the rows: ${error}`) - } + return { + success: false, + data: null, + }; + } + + async getSheetTabs(spreadsheetId: string): Promise { + try { + const response = await this.sheets.spreadsheets.get({ + spreadsheetId: spreadsheetId, + fields: "sheets.properties", + }); + + const tabs = + response.data.sheets?.map((sheet) => ({ + id: sheet.properties?.sheetId, + name: sheet.properties?.title, + })) || []; + + return { + success: true, + data: tabs, + }; + } catch (error) { + return { + success: false, + error: + error instanceof Error ? error.message : "Failed to fetch sheet tabs", + }; } - - isTokenExpired():boolean { - const credentials = this.auth.credentials; - if( !credentials.expiry_date) return false; - - return Date.now() >= credentials.expiry_date - (5 *60 * 1000); + } + + async readRows(params: ReadRowsParams): Promise { + try { + const response = await this.sheets.spreadsheets.values.get({ + spreadsheetId: params.spreadsheetId, + range: params.range, + }); + return response.data.values || []; + } catch (error) { + throw new Error(`Failed to fetch the rows: ${error}`); } - - async refreshAccessToken(): Promise { - try{ - const {credentials} = await this.auth.refreshAccessToken(); - - return { - access_token: credentials.access_token || '', - refresh_token: credentials.refresh_token || '', - token_type: credentials.token_type || '', - expiry_date: credentials.expiry_date || 0 - } - } - catch (error){ - throw new Error(`Falied to refresh token: ${error}`) - } + } + + isTokenExpired(): boolean { + const credentials = this.auth.credentials; + if (!credentials.expiry_date) return false; + + return Date.now() >= credentials.expiry_date - 5 * 60 * 1000; + } + + async refreshAccessToken(): Promise { + try { + const { credentials } = await this.auth.refreshAccessToken(); + + return { + access_token: credentials.access_token || "", + refresh_token: credentials.refresh_token || "", + token_type: credentials.token_type || "", + expiry_date: credentials.expiry_date || 0, + }; + } catch (error) { + throw new Error(`Falied to refresh token: ${error}`); } + } } -export { GoogleSheetsService } -export type { GoogleSheetsCredentials, ReadRowsParams } \ No newline at end of file +export { GoogleSheetsService }; +export type { GoogleSheetsCredentials, ReadRowsParams }; diff --git a/packages/nodes/src/google-sheets/test.ts b/packages/nodes/src/google-sheets/test.ts index 4fd6555..cff9294 100644 --- a/packages/nodes/src/google-sheets/test.ts +++ b/packages/nodes/src/google-sheets/test.ts @@ -1,4 +1,4 @@ -import { GoogleSheetsCredentials, GoogleSheetsService } from "./google-sheets.service"; +import { GoogleSheetsCredentials, GoogleSheetsService } from "./google-sheets.service.js"; import dotenv from 'dotenv'; import path from 'path'; diff --git a/packages/nodes/src/index.ts b/packages/nodes/src/index.ts index 84672c1..bc1ed03 100644 --- a/packages/nodes/src/index.ts +++ b/packages/nodes/src/index.ts @@ -1,10 +1,10 @@ // Central export for all major modules // export { default as NodeRegistry } from './registry/node-registry.js'; -// Fixed lint error: Use correct export name 'GoogleSheetNode' +// Fixed lint error: Use correct export name 'GoogleSheetNode'\ export { GoogleSheetNode } from './google-sheets/google-sheets.node.js'; export type { OAuthTokens } from './common/google-oauth-service.js' export { default as GoogleSheetsNodeExecutor } from './google-sheets/google-sheets.executor.js'; export { GoogleOAuthService } from './common/google-oauth-service.js'; export { default as NodeRegistry } from './registry/node-registry.js'; - -console.log("Hello World From node / index.ts"); +export { default as GmailExecutor } from '../src/gmail/gmail.executor.js'; +console.log("server running perfectly from node ") \ No newline at end of file diff --git a/packages/nodes/tsconfig.json b/packages/nodes/tsconfig.json index a45eafb..d033d98 100644 --- a/packages/nodes/tsconfig.json +++ b/packages/nodes/tsconfig.json @@ -3,8 +3,7 @@ "compilerOptions": { "rootDir": "./src", "outDir": "./dist", - "module": "ESNext", - "moduleResolution": "Node" + }, "include": ["src"] diff --git a/packages/nodes/tsconfig.tsbuildinfo b/packages/nodes/tsconfig.tsbuildinfo index 30107f7..57f96b1 100644 --- a/packages/nodes/tsconfig.tsbuildinfo +++ b/packages/nodes/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/index.ts","./src/common/google-oauth-service.ts","./src/google-sheets/google-sheets.executor.ts","./src/google-sheets/google-sheets.node.ts","./src/google-sheets/google-sheets.service.ts","./src/google-sheets/test.ts","./src/registry/node-registry.ts"],"version":"5.7.3"} \ No newline at end of file +{"root":["./src/index.ts","./src/common/google-oauth-service.ts","./src/gmail/gmail.executor.ts","./src/gmail/gmail.service.ts","./src/google-sheets/google-sheets.executor.ts","./src/google-sheets/google-sheets.node.ts","./src/google-sheets/google-sheets.service.ts","./src/google-sheets/test.ts","./src/registry/node-registry.ts"],"version":"5.7.3"} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 578d59f..51efa89 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -235,6 +235,9 @@ importers: '@repo/db': specifier: workspace:* version: link:../../packages/db + '@repo/nodes': + specifier: workspace:* + version: link:../../packages/nodes '@workspace/typescript-config': specifier: workspace:* version: link:../../packages/typescript-config @@ -311,6 +314,9 @@ importers: packages/nodes: dependencies: + '@types/dotenv': + specifier: ^8.2.3 + version: 8.2.3 dotenv: specifier: ^17.2.3 version: 17.2.3 @@ -324,9 +330,6 @@ importers: '@repo/db': specifier: workspace:* version: link:../db - '@types/node': - specifier: ^20.19.9 - version: 20.19.9 '@workspace/typescript-config': specifier: workspace:* version: link:../typescript-config