Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 96 additions & 48 deletions apps/http-backend/src/routes/userRoutes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ router.post("/createAvaliableNode", async (req: AuthRequest, res: Response) => {
}
});

router.get(
"/getAvailableNodes",
router.get("/getAvailableNodes",
userMiddleware,
async (req: AuthRequest, res: Response) => {
if (!req.user) {
Expand All @@ -94,8 +93,7 @@ router.get(
}
);

router.post(
"/createTriggers",
router.post("/createAvaliableTriggers",
userMiddleware,
async (req: AuthRequest, res: Response) => {
try {
Expand Down Expand Up @@ -129,8 +127,7 @@ router.post(
}
);

router.get(
"/getAvailableTriggers",
router.get("/getAvailableTriggers",
userMiddleware,
async (req: AuthRequest, res: Response) => {
try {
Expand Down Expand Up @@ -158,56 +155,79 @@ router.get(

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",
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({
}
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"
);
}
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" });
}
}
);
Comment on lines 156 to 201

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Inconsistent user ID extraction.

Line 166 uses req.user.sub while other routes (lines 79, 210, 241, 290, 316, 346) consistently use req.user.id. This inconsistency could cause authentication or data retrieval issues.

Please verify which property contains the user ID in the JWT payload. Run the following script to check all usages:

#!/bin/bash
# Search for req.user property access patterns in route handlers
rg -n -C3 'req\.user\.(id|sub)' --type=ts
🤖 Prompt for AI Agents
In apps/http-backend/src/routes/userRoutes/userRoutes.ts around lines 156 to
201, the handler uses req.user.sub while the rest of the codebase uses
req.user.id; verify which property the JWT payload actually sets (run the
provided rg script or inspect token parsing), then replace req.user.sub with
req.user.id for consistency, update the userId assignment and related
null/validation checks accordingly, and re-run tests/lint to ensure no other
handlers expect .sub.


router.post(
"/create/workflow",
router.get('/getAllCreds', userMiddleware, async(req: AuthRequest, res:Response) =>{
try{
if(!req.user){
return res.status(statusCodes.BAD_REQUEST).json({
message: "User is not Loggedin"
})
}
const userId = req.user.sub;
const creds = await prismaClient.credential.findMany({
where:{ userId: userId}
})
if(creds){
return res.status(statusCodes.OK).json({
message: "Fetched all credentials of the User!",
data: creds
})
}
}
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" });
}
})
Comment on lines +203 to +227

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add missing return statement and use consistent user ID property.

Line 210 uses req.user.sub instead of req.user.id (inconsistent with other routes). More critically, there's no return statement after the successful response (line 218), and the catch block (lines 221-226) is outside the try block scope.

🔎 Proposed fix
 router.get('/getAllCreds', userMiddleware, async(req: AuthRequest, res:Response) =>{
   try{
       if(!req.user){
           return res.status(statusCodes.BAD_REQUEST).json({
             message: "User is not Loggedin"
           })
         }
-        const userId = req.user.sub;
+        const userId = req.user.id;
         const creds = await prismaClient.credential.findMany({
           where:{ userId: userId}
         })
         if(creds){
           return res.status(statusCodes.OK).json({
             message: "Fetched all credentials of the User!",
             data: creds
           })
         } 
-      }
-      catch(e){
+    } 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" });
-      }
+    }
 })
🤖 Prompt for AI Agents
In apps/http-backend/src/routes/userRoutes/userRoutes.ts around lines 203-227,
fix the try/catch scope, user ID property, and ensure the handler always
returns: make sure the try block correctly wraps the credential lookup and
response (move the catch so it directly follows the try block), change
req.user.sub to req.user.id to match other routes, and ensure you return after
sending the successful response (or add an explicit return after the if block)
so execution doesn't continue past the response; keep the error catch
immediately after the try and return the internal-server response there.

// ----------------------------------- CREATE WORKFLOW ---------------------------------

router.post("/create/workflow",
userMiddleware,
async (req: AuthRequest, res) => {
try {
Expand Down Expand Up @@ -257,8 +277,9 @@ router.post(
}
);

router.get(
"/workflows",
// ------------------------------------ FETCHING WORKFLOWS -----------------------------------

router.get("/workflows",
userMiddleware,
async (req: AuthRequest, res: Response) => {
try {
Expand Down Expand Up @@ -286,8 +307,35 @@ router.get(
}
);

router.get(
"/workflow/:workflowId",
router.get('/empty/workflow', userMiddleware, async(req:AuthRequest, res: Response)=>{
try{
if (!req.user)
return res
.status(statusCodes.UNAUTHORIZED)
.json({ message: "User is not logged in /not authorized" });
const userId = req.user.id;
const workflow = await prismaClient.workflow.findFirst({
where:{
userId: userId,
isEmpty: true
},
orderBy: {
createdAt: 'desc'
}
})
return res
.status(statusCodes.OK)
.json({ message: "Workflow fetched succesful", Data: workflow });

}catch(e){
console.log("The error is from getting wrkflows", e instanceof Error ? e.message : "UNKNOWN ERROR");

return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
meesage: "Internal Server Error From getting workflows for the user",
});
}
})
Comment on lines +310 to +337

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo in error response.

Line 334 has a typo: "meesage" should be "message". Note: The same typo also appears on line 304 in the /workflows route.

🔎 Proposed fix
     return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
-      meesage: "Internal Server Error From  getting workflows for the user",
+      message: "Internal Server Error From getting workflows for the user",
     });

Also fix line 304:

       return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
-        meesage: "Internal Server Error From  getting workflows for the user",
+        message: "Internal Server Error From getting workflows for the user",
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
router.get('/empty/workflow', userMiddleware, async(req:AuthRequest, res: Response)=>{
try{
if (!req.user)
return res
.status(statusCodes.UNAUTHORIZED)
.json({ message: "User is not logged in /not authorized" });
const userId = req.user.id;
const workflow = await prismaClient.workflow.findFirst({
where:{
userId: userId,
isEmpty: true
},
orderBy: {
createdAt: 'desc'
}
})
return res
.status(statusCodes.OK)
.json({ message: "Workflow fetched succesful", Data: workflow });
}catch(e){
console.log("The error is from getting wrkflows", e instanceof Error ? e.message : "UNKNOWN ERROR");
return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
meesage: "Internal Server Error From getting workflows for the user",
});
}
})
router.get('/empty/workflow', userMiddleware, async(req:AuthRequest, res: Response)=>{
try{
if (!req.user)
return res
.status(statusCodes.UNAUTHORIZED)
.json({ message: "User is not logged in /not authorized" });
const userId = req.user.id;
const workflow = await prismaClient.workflow.findFirst({
where:{
userId: userId,
isEmpty: true
},
orderBy: {
createdAt: 'desc'
}
})
return res
.status(statusCodes.OK)
.json({ message: "Workflow fetched succesful", Data: workflow });
}catch(e){
console.log("The error is from getting wrkflows", e instanceof Error ? e.message : "UNKNOWN ERROR");
return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
message: "Internal Server Error From getting workflows for the user",
});
}
})
🤖 Prompt for AI Agents
In apps/http-backend/src/routes/userRoutes/userRoutes.ts around lines 304 and
334, there is a typo in the JSON error response key "meesage" — change it to
"message" in both places so the error responses use the correct key; ensure the
surrounding response objects and status codes remain unchanged and run
tests/lint to confirm no other occurrences of the misspelled key.

router.get("/workflow/:workflowId",
userMiddleware,
async (req: AuthRequest, res: Response) => {
try {
Expand Down
96 changes: 62 additions & 34 deletions apps/web/app/components/nodes/CreateWorkFlow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import "@xyflow/react/dist/style.css";
import { Background, Controls, ReactFlow } from "@xyflow/react";
import { ReactFlow } from "@xyflow/react";
import PlaceholderNode from "./PlaceHolder";
import { TriggerNode } from "./TriggerNode";

Expand All @@ -15,6 +15,7 @@ import { createWorkflow, getEmptyWorkflow, getworkflowData } from "@/app/workflo
import { useAppSelector } from '@/app/hooks/redux';



interface NodeType {
id: string;
type: "placeholder" | "trigger" | "action";
Expand All @@ -38,7 +39,9 @@ export const CreateWorkFlow = () => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [actionSidebarOpen, setActionSidebarOpen] = useState(false);
const [credType, setCredType] = useState<string>("");
const [loadSheet, setLoadSheet] = useState<boolean>(false);
const [nodeIDType, setNodeIDType] = useState<string>('')
const [loadSheet, setLoadSheet] = useState<boolean>(false)
const [selectedNodeConfig, setSelectedNodeConfig] = useState<any>(undefined);
Comment on lines +42 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Replace any type with a proper interface.

Using any defeats TypeScript's type checking. Define a proper interface for the node configuration shape based on the properties you're accessing (lines 345-349).

🔎 Proposed refactor
+interface NodeConfig {
+  range: string;
+  operation: string;
+  sheetName: string;
+  spreadsheetId: string;
+  credId: string;
+}
+
 const [nodeIDType, setNodeIDType] = useState<string>('')
 const [loadSheet, setLoadSheet] = useState<boolean>(false)
-const [selectedNodeConfig, setSelectedNodeConfig] = useState<any>(undefined);
+const [selectedNodeConfig, setSelectedNodeConfig] = useState<NodeConfig | undefined>(undefined);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [nodeIDType, setNodeIDType] = useState<string>('')
const [loadSheet, setLoadSheet] = useState<boolean>(false)
const [selectedNodeConfig, setSelectedNodeConfig] = useState<any>(undefined);
interface NodeConfig {
range: string;
operation: string;
sheetName: string;
spreadsheetId: string;
credId: string;
}
const [nodeIDType, setNodeIDType] = useState<string>('')
const [loadSheet, setLoadSheet] = useState<boolean>(false)
const [selectedNodeConfig, setSelectedNodeConfig] = useState<NodeConfig | undefined>(undefined);
🤖 Prompt for AI Agents
In apps/web/app/components/nodes/CreateWorkFlow.tsx around lines 42 to 44, the
state selectedNodeConfig is typed as any; define a proper TypeScript interface
(e.g., NodeConfig) that includes the exact properties read at lines 345–349
(make fields optional or typed correctly: their names and types as used there:
string/number/boolean/objects/arrays), replace useState<any> with
useState<NodeConfig | undefined>, export or declare the interface in this file
(or import from a types file), and update any places that set or read
selectedNodeConfig to satisfy the stricter types (add null checks or optional
chaining where needed).

const dispatch = useDispatch();
const userId = useAppSelector(s=>s.user.userId)
const workflowId = useAppSelector(s=>s.workflow.workflow_id)
Expand Down Expand Up @@ -117,20 +120,35 @@ export const CreateWorkFlow = () => {
]);
};

useEffect(() => {
async function getEmptyWorkflowID() {
const workflow = await getEmptyWorkflow();

if (workflow) {
const { id, isEmpty } = workflow;
dispatch(workflowActions.setWorkflowId(id));
dispatch(workflowActions.setWorkflowStatus(isEmpty));
} else {
// const newWorkflow
useEffect(()=>{
async function getEmptyWorkflowID(){
const workflow = await getEmptyWorkflow()

if(workflow){
const {id, isEmpty} = workflow
dispatch(workflowActions.setWorkflowId(id))
dispatch(workflowActions.setWorkflowStatus(isEmpty))
}
else{
if (!userId) return
const newWorkflow = await createWorkflow()
dispatch(workflowActions.setWorkflowId(newWorkflow.id))
dispatch(workflowActions.setWorkflowStatus(newWorkflow.isEmpty))
}
}
getEmptyWorkflowID();
}, [dispatch]);
async function getWorkflowData(){
if(!workflowId) return
const workflow = await getworkflowData(workflowId)
if(workflow.success){
dispatch(workflowActions.setWorkflowStatus(false))
dispatch(workflowActions.setWorkflowNodes(workflow.data.nodes))
dispatch(workflowActions.setWorkflowTrigger(workflow.data.Trigger))
// console.log(`workfklow from redux: ${workflow.data}`)
}
}
// if(!workflowId) getEmptyWorkflowID()
getWorkflowData();
},[dispatch, userId, workflowId])
Comment on lines +123 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Incomplete workflow initialization logic.

The getEmptyWorkflowID() function is defined but never invoked (line 149 shows it's commented out). This means the workflow initialization flow is incomplete. Additionally, missing error handling could lead to silent failures that would confuse users.

🔎 Proposed fix
 useEffect(()=>{
   async function getEmptyWorkflowID(){
-     const workflow = await getEmptyWorkflow()
+     try {
+       const workflow = await getEmptyWorkflow()
       
-     if(workflow){
-       const {id, isEmpty} = workflow
-       dispatch(workflowActions.setWorkflowId(id))
-       dispatch(workflowActions.setWorkflowStatus(isEmpty))
-     }
-     else{
-       if (!userId) return
-       const newWorkflow = await createWorkflow()
-       dispatch(workflowActions.setWorkflowId(newWorkflow.id))
-       dispatch(workflowActions.setWorkflowStatus(newWorkflow.isEmpty))
+       if(workflow){
+         const {id, isEmpty} = workflow
+         dispatch(workflowActions.setWorkflowId(id))
+         dispatch(workflowActions.setWorkflowStatus(isEmpty))
+       }
+       else{
+         if (!userId) return
+         const newWorkflow = await createWorkflow()
+         dispatch(workflowActions.setWorkflowId(newWorkflow.id))
+         dispatch(workflowActions.setWorkflowStatus(newWorkflow.isEmpty))
+       }
+     } catch (error) {
+       console.error("Error initializing workflow:", error);
      }
    }
   async function getWorkflowData(){
+     try {
-     if(!workflowId) return
-     const workflow = await getworkflowData(workflowId)
-     if(workflow.success){
-       dispatch(workflowActions.setWorkflowStatus(false))
-       dispatch(workflowActions.setWorkflowNodes(workflow.data.nodes))
-       dispatch(workflowActions.setWorkflowTrigger(workflow.data.Trigger))
-       // console.log(`workfklow from redux: ${workflow.data}`)
+       if(!workflowId) return
+       const workflow = await getworkflowData(workflowId)
+       if(workflow.success){
+         dispatch(workflowActions.setWorkflowStatus(false))
+         dispatch(workflowActions.setWorkflowNodes(workflow.data.nodes))
+         dispatch(workflowActions.setWorkflowTrigger(workflow.data.Trigger))
+       }
+     } catch (error) {
+       console.error("Error fetching workflow data:", error);
      }
    }
-   // if(!workflowId) getEmptyWorkflowID()
+   if(!workflowId) getEmptyWorkflowID()
    getWorkflowData();
 },[dispatch, userId, workflowId])


useEffect(()=>{
// Guard: only rebuild nodes/edges if there's actual stored data
Expand Down Expand Up @@ -279,7 +297,8 @@ export const CreateWorkFlow = () => {
nodeTypes={nodeTypes}
nodes={nodes}
edges={edges}
onNodeClick={async (event, node) => {
onNodeClick={async(event, node) => {
console.log("Node clicked:", node.id, node.type, node.data);
if (node.type === "placeholder") {
const hasTrigger = nodes.some((n) => n.type === "trigger");
if (hasTrigger) {
Expand All @@ -288,26 +307,22 @@ 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");
if(node.type === 'action' || node.type === 'trigger'){
// Check by type instead of name (more reliable)
const nodeType = node.data.type?.toLowerCase() || '';
if(nodeType.includes('google_sheet') || nodeType.includes('google sheet')){
console.log("Google Sheet node clicked")
console.log("Node ID:", node.id)
setNodeIDType(node.id)
setCredType("google_oauth")
console.log("config from node: ", node.data.config)
setSelectedNodeConfig(node.data.config)
setLoadSheet(!loadSheet)
console.log("Form opened")
}
}
}
}}
>
<Background />
<Controls />
</ReactFlow>
/>

<TriggerSideBar
isOpen={sidebarOpen}
Expand All @@ -321,7 +336,20 @@ export const CreateWorkFlow = () => {
onSelectAction={handleSelectAction}
/>

{loadSheet && <GoogleSheetFormClient type={credType} />}
{loadSheet &&
<GoogleSheetFormClient
type={credType}
nodeType={nodeIDType}
position={existingNodes.length + 1}
initialData={selectedNodeConfig ? {
range: selectedNodeConfig.range as string,
operation: selectedNodeConfig.operation as string,
sheetName: selectedNodeConfig.sheetName as string,
spreadSheetId: selectedNodeConfig.spreadsheetId as string,
credentialId: selectedNodeConfig.credId as string,
} : undefined}
/>
}
</div>
);
};
Expand Down
Loading