-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: update user routes and enhance workflow UI components #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Refactored user routes to improve middleware usage and logging. - Updated TypeScript configurations for better module resolution. - Introduced new UI components for workflow management, including a modal for creating workflows and a placeholder node for actions. - Enhanced existing components with improved styling and functionality. - Added new node configurations for Gmail and Google Sheets actions. - Improved error handling and logging in workflow execution. - Updated API integration for creating and managing workflows and nodes.
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive workflow canvas interface with node-based configuration system. Changes include a new React Flow-based workflow editor, backend route type adjustments, database schema updates for node positioning, node configuration registry, API client wrapper, and worker executor enhancements for error handling and credential context. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Canvas as Workflow Canvas
participant API as Backend API
participant DB as Database
User->>Canvas: Click "Add Trigger"
Canvas->>API: GET /user/getAvailableTriggers
API->>DB: Query available triggers
DB-->>API: Return trigger list
API-->>Canvas: Return trigger options
Canvas->>User: Show trigger selector
User->>Canvas: Select trigger
Canvas->>API: POST /user/create/trigger
API->>DB: Create trigger node
DB-->>API: Return created trigger
API-->>Canvas: Trigger created
Canvas->>Canvas: Add trigger node + action placeholder
User->>Canvas: Click "Add Action" on placeholder
Canvas->>API: GET /user/getAvailableNodes
API->>DB: Query available nodes
DB-->>API: Return node list
API-->>Canvas: Return node options
Canvas->>User: Show action selector
User->>Canvas: Select action
Canvas->>API: POST /user/create/node
API->>DB: Create action node
DB-->>API: Return created node
API-->>Canvas: Node created
Canvas->>Canvas: Add action node + new placeholder, rewire edges
User->>Canvas: Configure action node
Canvas->>Canvas: Open ConfigModal
User->>Canvas: Submit configuration
Canvas->>API: PUT /user/update/node
API->>DB: Update node config
DB-->>API: Update confirmed
API-->>Canvas: Config saved
Canvas->>Canvas: Close modal, update node display
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/http-backend/src/routes/userRoutes/userRoutes.ts (2)
76-109: Security concern: Authentication disabled on trigger creation endpoint.The
userMiddlewarehas been commented out, allowing unauthenticated users to create available triggers. If this is intentional (e.g., for admin seeding), consider:
- Protecting this with an admin-specific middleware or API key
- Adding a comment explaining why authentication is bypassed
- Moving this to an admin-only route
🔒 Suggested fix to re-enable authentication
router.post( "/createAvaliableTriggers", - // userMiddleware, + userMiddleware, - async (req: Request, res: Response) => { + async (req: AuthRequest, res: Response) => {
354-356: Typo in response key:meesageshould bemessage.This typo could break client-side error handling that expects a
messagekey.✏️ Fix typo
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", });apps/web/app/types/workflow.types.ts (2)
23-28: RenameAvailabeActiontoAvailableAction(missing 'l' in "Available").The interface is imported and used in
apps/web/app/hooks/useActions.tsat least once, so the rename will need to be coordinated across both files.
12-16: Fix typos in interface names:Edage→EdgeandAvailabeAction→AvailableAction.Both interfaces contain typos:
Edage(line 12) appears unused and can be safely renamed.AvailabeAction(line 23) is imported and used inapps/web/app/hooks/useActions.tsand must be updated there as well.
🤖 Fix all issues with AI agents
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts`:
- Around line 437-439: The PUT route handler declared as
router.put("/workflow/update", userMiddleware, (req: AuthRequest, res: Response)
=> { }) is empty and will hang requests; either implement the handler or remove
it. To fix, add a concrete implementation inside that arrow function: validate
req.body, call the appropriate service/utility (e.g., updateUserWorkflow or
whatever existing workflow update function is used elsewhere), handle errors
with try/catch and return an appropriate response status (200 with updated data
or 204 on success, and 4xx/5xx on errors), or if the feature isn't ready return
a clear short response such as res.status(501). Ensure the handler uses the
AuthRequest user info provided by userMiddleware and always sends a response in
all code paths.
In `@apps/http-backend/tsconfig.tsbuildinfo`:
- Line 1: Remove all tracked TypeScript incremental build artifacts and add a
comprehensive ignore rule: run git rm --cached on every tracked tsbuildinfo file
(e.g., apps/hooks/tsconfig.tsbuildinfo, apps/processor/tsconfig.tsbuildinfo,
apps/worker/tsconfig.tsbuildinfo, packages/nodes/tsconfig.tsbuildinfo and the
root apps/http-backend/tsconfig.tsbuildinfo) and update the root .gitignore to
include a complete pattern like **/*.tsbuildinfo (or explicit entries for each
package/app) so these autogenerated files are no longer committed.
In `@apps/web/app/lib/nodeConfigs/googleSheet.action.ts`:
- Around line 36-48: The form field name is incorrectly set to "action" in the
dropdown config (object with name: "action"); it must match the rest of the code
which expects "operation". Change the field key from "action" to "operation" in
the dropdown definition (and keep the same options/defaultValue/description) so
submitted data aligns with SaveConfigFormData and updateConfigData; verify any
references to this field in the same module use "operation" as well.
In `@apps/web/app/workflows/`[id]/components/ConfigModal.tsx:
- Around line 127-145: The form inputs in ConfigModal.tsx are uncontrolled so
user edits are lost; initialize a local state object (e.g., formValues) keyed by
nodeConfig.fields' name (use useState, populated from nodeConfig or default
placeholders) and update it in an onChange handler attached to the rendered
<input> for each field; set each input's value to formValues[field.name];
finally update the call in handleSave to include the collected formValues (merge
with existing HOOKS_URL behavior) so all dynamic field values are passed back
when saving.
- Around line 23-24: The code is using userAction.setUserId (an action creator)
as if it were the userId; replace this with a Redux selector: import and use
useSelector (from react-redux) to read the userId from state (e.g., call
useSelector(state => state.user.id) or the existing selector function like
selectUserId) inside the ConfigModal component, remove the incorrect cast of
userAction.setUserId, and update any console/log references to use the
selector-derived userId instead of the action creator reference.
In `@apps/web/app/workflows/`[id]/page.tsx:
- Around line 59-88: handleNodesChange currently fires api.triggers.update and
api.nodes.update without awaiting or handling errors, causing unhandled
rejections and possible UI/backend divergence; modify handleNodesChange to await
(or at least attach .catch) the update promises for api.triggers.update and
api.nodes.update, log/report errors via your logger or show a toast, and on
failure revert the UI change by calling onNodesChange with the previous node
state from nodes to keep UI/backend consistent; additionally wrap rapid position
updates with a debounce (e.g., per-node debounce) so you only send the final
position after dragging to reduce API churn.
- Around line 425-445: The onSave handler currently expects (nodeId, config) but
ConfigModal calls onSave(config, userId); update the handler signature to
onSave={async (config, userId) => { const nodeId = selectedNode?.id; if
(!nodeId) return; /* then use nodeId below */ if (nodes.find(n => n.id ===
nodeId)?.data.nodeType === "trigger") await api.triggers.update({ TriggerId:
nodeId, Config: config }); else await api.nodes.update({ NodeId: nodeId, Config:
config }); setNodes(prev => prev.map(node => node.id === nodeId ? { ...node,
data: { ...node.data, config, isConfigured: true } } : node)); }} so it matches
ConfigModal calls and uses selectedNode.id as the node identifier (or
validate/throw if missing).
In `@apps/worker/src/engine/executor.ts`:
- Around line 54-64: When handling the failed execute result, ensure the error
is serialized to a string before saving and add the failing node identifier to
both the DB update and logs: convert execute.error to a reliable string (e.g.,
use execute.error?.message || String(execute.error) or JSON.stringify with a
fallback) and pass that into prismaClient.workflowExecution.update for the error
field; also include the current node identifier from the executor scope (e.g.,
nodeId, currentNode.id or node.name — whatever symbol is used in this file) as a
new field (e.g., failedNodeId/failedNodeName) and emit a processLogger or logger
call that records the workflowExecutionId, the failing node identifier, and the
serialized error.
In `@packages/common/src/index.ts`:
- Around line 4-5: BACKEND_URL and HOOKS_URL are hardcoded to localhost which
breaks deployments; change them to read from environment variables with sensible
defaults. Update the constants BACKEND_URL and HOOKS_URL in
packages/common/src/index.ts to use process.env.BACKEND_URL and
process.env.HOOKS_URL (or use BACKEND_URL as the single source if both should
match), falling back to "http://localhost:3002" when the env vars are absent,
and ensure exports remain unchanged so callers keep working.
- Around line 28-34: NodeSchema was renamed to use stage but frontend still
sends Position, causing validation failures; change the outgoing payload keys so
they send stage instead of Position. Specifically, update the places that
currently set "Position: 1" and "Position: context.position" to use "stage: 1"
and "stage: context.position" respectively, and ensure any related types/objects
(payload builders or serialize functions) that reference Position are updated to
stage to match NodeSchema.
In `@packages/db/prisma/schema.prisma`:
- Around line 109-110: The completedAt field currently has a db-generated
default which sets a timestamp on creation; remove the
`@default`(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) from the completedAt
field (leave it as DateTime? `@db.Timestamptz`(6) and nullable) so it stays null
until explicitly updated, while keeping startedAt's default intact; after
changing the schema run the Prisma migration to update the DB schema and ensure
application code sets completedAt when the execution finishes.
- Around line 68-71: AvailableNodeID is non-nullable but the relation
AvailableNode is optional, causing a schema mismatch; decide intended
cardinality and make them consistent: if the relation must be required, change
AvailableNode? to AvailableNode (remove the question mark) so AvailableNodeID
stays String and the relation is required; otherwise, make the foreign key
nullable by changing AvailableNodeID to String? so the optional relation
AvailableNode? aligns with a nullable FK. Update the Prisma model accordingly
and regenerate the client.
🟡 Minor comments (15)
apps/worker/src/engine/executor.ts-52-52 (1)
52-52: Object interpolation in template string will print[object Object].Using an object directly in a template string produces unhelpful output. Use
JSON.stringifyfor readable logging:- console.log(`Executing with context: ${context}`); + console.log(`Executing with context: ${JSON.stringify(context)}`);apps/worker/src/engine/executor.ts-37-37 (1)
37-37: Incorrect error check on Prisma update result.
update.errorrefers to theerrorfield on theWorkflowExecutionrecord, not a Prisma operation error. Prisma throws exceptions on failure rather than returning an error property. This check will always pass if the record has noerrorfield value.Either wrap the update in a try/catch block or simply log unconditionally:
- if (!update.error) console.log("updated the workflow execution"); + console.log("updated the workflow execution");Alternatively, if you want to handle Prisma errors:
+ try { const update = await prismaClient.workflowExecution.update({ where: { id: workflowExecutionId, }, data: { status: "InProgress", }, }); - if (!update.error) console.log("updated the workflow execution"); + console.log("updated the workflow execution"); + } catch (err) { + console.error("Failed to update workflow execution status:", err); + return; + }apps/http-backend/src/routes/userRoutes/userRoutes.ts-389-391 (1)
389-391: Same typo:meesageshould bemessage.✏️ Fix typo
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", });apps/http-backend/src/routes/userRoutes/userRoutes.ts-617-621 (1)
617-621: Same issue: use200 OKinstead of201 Createdfor updates.🔧 Fix status code
if (updatedTrigger) - return res.status(statusCodes.CREATED).json({ + return res.status(statusCodes.OK).json({ message: "Trigger updated", data: updatedTrigger, });apps/http-backend/src/routes/userRoutes/userRoutes.ts-578-582 (1)
578-582: Incorrect HTTP status code for update operation.PUT/update operations should return
200 OK, not201 Created. HTTP 201 is semantically reserved for resource creation.🔧 Fix status code
if (updateNode) - return res.status(statusCodes.CREATED).json({ + return res.status(statusCodes.OK).json({ message: "Node updated", data: updateNode, });apps/http-backend/src/routes/userRoutes/userRoutes.ts-259-274 (1)
259-274: Missing response for edge cases.The
findManyalways returns an array (possibly empty), so theif (creds)check will always be truthy. However, if somehow the block doesn't execute or an unexpected path is taken, no response is sent. This could leave requests hanging.🔧 Suggested fix to ensure response is always returned
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, - }); - } + return res.status(statusCodes.OK).json({ + message: "Fetched all credentials of the User!", + data: creds, + }); } catch (e) {apps/web/app/lib/nodeConfigs/googleSheet.action.ts-20-27 (1)
20-27: Fix field name casing:spreadsheetIdin config should matchspreadSheetIdin GoogleSheetFormClientProps interface, or vice versa.There is a confirmed inconsistency: the field is defined as
spreadsheetId(lowercase) in this config file and throughout the backend services, but the GoogleSheetFormClient component interface expectsspreadSheetId(capital S). This mismatch causes initialData not to populate correctly into the form (see GoogleSheetFormClient.tsx line 34 accessinginitialData?.spreadSheetId). Either rename this field tospreadSheetIdfor consistency with the component props, or update the component interface to usespreadsheetIdto match the backend and action config. The backend and service layer use lowercase consistently, so updating the component interface would be the preferred approach.apps/web/app/components/ui/Design/WorkflowCard.tsx-21-21 (1)
21-21: Description field is captured but never sent to the API.The
descriptionstate (line 21) is collected from user input but not passed toapi.workflows.create()(line 31). Either include it in the API call or remove the field if it's not needed.💡 If description should be sent
-const create = await api.workflows.create(name, []); +const create = await api.workflows.create(name, [], description);This assumes the API accepts description as a third parameter. Verify the API signature.
Also applies to: 30-31
apps/web/app/components/ui/Design/WorkflowCard.tsx-33-33 (1)
33-33: Add defensive check for deeply nested response property.Accessing
create.data.Data.idwill throw if the response structure is unexpected. Consider adding optional chaining or validation.💡 Suggested defensive access
-const id = create.data.Data.id; +const id = create.data?.Data?.id; +if (!id) { + throw new Error("Invalid response: missing workflow ID"); +}packages/common/src/index.ts-2-2 (1)
2-2: Remove unused import.The
numberimport fromzod/v4is not used anywhere in this file.🧹 Proposed fix
import z from "zod"; -import { number } from "zod/v4";apps/web/app/components/ui/Design/WorkflowCard.tsx-51-55 (1)
51-55: Prevent overlay click from closing modal during loading.Clicking the backdrop calls
onCloseeven whenloadingis true, which could interrupt the submission.💡 Proposed fix
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/30 backdrop-blur-sm transition-all" - onClick={onClose} + onClick={loading ? undefined : onClose} >apps/web/app/workflows/[id]/components/ConfigModal.tsx-30-35 (1)
30-35: Modal closes on error, preventing user from seeing failure and retrying.The
onClose()call in thefinallyblock causes the modal to close even when save fails. Users lose context and must reopen the modal to retry. Consider only closing on success.💡 Suggested fix
} catch (error) { console.error("Save failed:", error); + // Optionally set error state to display to user + return; // Don't close on error } finally { setLoading(false); - onClose(); } + onClose(); // Only close on success };apps/web/app/workflows/[id]/page.tsx-49-49 (1)
49-49: Fix typo in error state initialization.The initial state contains a stray single quote character which will cause the error state to always be non-empty.
- const [error, setError] = useState<string>("'"); + const [error, setError] = useState<string>("");apps/web/app/lib/api.ts-35-40 (1)
35-40:triggers.updatedoesn't return the axios response.Unlike other methods that implicitly return the axios promise, this method awaits without returning, meaning callers cannot access the response data.
update: async (data: z.infer<typeof TriggerUpdateSchema>) => { - await axios.put(`${BACKEND_URL}/user/update/trigger`, data, { + return await axios.put(`${BACKEND_URL}/user/update/trigger`, data, { withCredentials: true, headers: { "Content-Type": "application/json" }, }); },apps/web/app/components/NODES/BaseNode.tsx-102-106 (1)
102-106: Bug: "Configured" badge logic is incorrect.The badge displays when
data.onConfigurecallback exists, but this callback is typically always present (for opening settings). It should likely checkdata.isConfiguredor verify thatconfighas meaningful values.- {data.onConfigure && ( + {data.isConfigured && ( <span className="ml-2 px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full"> ✓ Configured </span> )}Note: This requires adding
isConfigured?: booleanto theBaseNodeProps.datainterface.
🧹 Nitpick comments (24)
packages/db/tsconfig.tsbuildinfo (1)
1-1: Consider excluding.tsbuildinfofiles from version control.TypeScript build information files are auto-generated compiler artifacts intended for incremental compilation. Committing them can lead to merge conflicts and build inconsistencies when team members use different TypeScript versions or have different build states.
Add
*.tsbuildinfoto your.gitignorefile and remove this file from the repository.packages/db/src/seed.ts (1)
59-60: Minor formatting inconsistency.The spacing around colons and commas is inconsistent with typical JavaScript/TypeScript conventions.
Suggested fix
- stage: 1, - position : {x : 250 , y : 200}, + stage: 1, + position: { x: 250, y: 200 },apps/http-backend/src/routes/userRoutes/userRoutes.ts (3)
535-539: Redundant conditional check.
prismaClient.node.create()either returns the created node or throws an exception (caught by the catch block). Theif (createdNode)check is unnecessary and removing it simplifies the code.♻️ Simplify by removing redundant check
}); - if (createdNode) - return res.status(statusCodes.CREATED).json({ - message: "Node created", - data: createdNode, - }); + return res.status(statusCodes.CREATED).json({ + message: "Node created", + data: createdNode, + }); } catch (e) {
218-218: Consider logging a warning ifBACKEND_URLis not configured.The
localhost:3002fallback is fine for development, but in production, a missingBACKEND_URLcould result in returning an incorrect auth URL to clients. Consider adding a startup check or warning log.
511-511: Remove or comment out debug logging.Debug
console.logstatements should be removed or commented out before merging to avoid cluttering production logs.🧹 Remove debug log
const data = req.body; - console.log(" from http-backeden" , data); + // console.log("from http-backend", data);apps/web/app/types/workflow.types.ts (1)
3-6: Consider using a more specific type forconfig.Using
anydefeats TypeScript's type safety. Consider defining a proper type or usingRecord<string, unknown>for better type checking, similar to what's used inAvailableTriggerandAvailabeAction.Suggested improvement
data: { Type: "Trigger" | "Action"; SelectedType: string; - label : string - config? : any + label: string; + config?: Record<string, unknown>; };apps/web/app/components/nodes/TriggerNode.tsx (1)
12-20: Consider adding a fallback for wheniconis undefined.Since
iconis now optional, rendering{data.icon}when undefined will display nothing, potentially leaving a visual gap in the UI. Consider providing a default icon or conditional rendering.Suggested improvement
export const TriggerNode = ({ data }: TriggerNodeProps) => { return ( <div className="flex flex-col items-center p-4 bg-gray-800 rounded-lg border border-gray-600"> - <div className="text-3xl">{data.icon}</div> + <div className="text-3xl">{data.icon ?? '⚡'}</div> <div className="text-white font-bold mt-2">{data.name}</div> <div className="text-gray-400 text-sm">{data.type}</div> <Handle type="source" position={Position.Right} /> </div> ); };apps/web/app/workflows/[id]/components/nodes/PlaceholderNode.tsx (2)
14-26: Consider improving keyboard accessibility.The clickable
divelement lacks keyboard accessibility. Users navigating with keyboards won't be able to interact with this element. Consider addingtabIndex,role, and keyboard event handlers, or use a<button>element.Suggested improvement
<div onClick={data.onClick} + onKeyDown={(e) => e.key === 'Enter' && data.onClick?.()} + role="button" + tabIndex={0} className="border-2 border-dashed border-gray-300 rounded-lg bg-gray-50 hover:bg-gray-100 hover:border-blue-400 cursor-pointer transition-all min-w-[280px]" >
21-23: Consider using an icon component instead of emoji.The emoji
➕may render inconsistently across different platforms and browsers. Consider using a proper icon component (e.g., from a library likelucide-reactor@heroicons/react) for consistent visual appearance.packages/ui/src/components/card.tsx (1)
31-48: Consider using semantic HTML elements for better accessibility.Using
<div>forCardTitleandCardDescriptionmisses the opportunity for semantic meaning. Consider using heading elements (<h3>,<h4>) for titles and<p>for descriptions, which improves accessibility and SEO.Suggested improvement
-function CardTitle({ className, ...props }: React.ComponentProps<"div">) { +function CardTitle({ className, ...props }: React.ComponentProps<"h3">) { return ( - <div + <h3 data-slot="card-title" className={cn("leading-none font-semibold", className)} {...props} /> ) } -function CardDescription({ className, ...props }: React.ComponentProps<"div">) { +function CardDescription({ className, ...props }: React.ComponentProps<"p">) { return ( - <div + <p data-slot="card-description" className={cn("text-muted-foreground text-sm", className)} {...props} /> ) }apps/web/app/page.tsx (1)
25-29: Consider using Tailwind margin classes instead of<br />for spacing.Using
<br />for layout spacing is a legacy HTML pattern. Prefer margin or padding utilities (e.g.,mt-4) on theParentComponentwrapper for better control and consistency with Tailwind-based styling.Also, "ParentComponent" is a generic name—consider renaming to something more descriptive like
WorkflowButtonorCreateWorkflowButtonin the import alias for clarity.✨ Suggested improvement
- <br /> - <ParentComponent/> + <div className="mt-4"> + <ParentComponent /> + </div>apps/web/app/components/ui/Design/WorkflowButton.tsx (1)
7-17: RenameParentComponentto a more descriptive name.The name
ParentComponentis generic and doesn't convey purpose. Consider renaming toCreateWorkflowButtonorWorkflowButtonWithModalto better reflect its functionality.✨ Suggested rename
-export default function ParentComponent() { +export default function CreateWorkflowButton() {apps/web/app/lib/nodeConfigs/index.ts (1)
9-19: Consider adding collision detection for the registry.The dual-mapping by
labelandidis flexible, but if two different configs share the same label or id, one will silently overwrite the other. At scale, consider adding a build-time or runtime check to detect collisions.apps/web/app/lib/nodeConfigs/gmail.action.ts (1)
29-45: InconsistentdependsOnusage across fields.The
tofield hasdependsOn: "credentialId", butsubjectandbodydon't. If the intent is to allow typing subject/body before selecting an account, this is fine. However, if all fields should be disabled until a credential is selected, adddependsOn: "credentialId"to all fields for consistency.apps/web/app/workflows/[id]/components/ConfigModal.tsx (1)
136-141: Form only renders text inputs regardless of field type.The
ConfigFieldinterface supports multiple types (dropdown, textarea, number, checkbox, password), but this code only renders<input type="text">. Consider rendering appropriate controls based onfield.type.apps/web/app/lib/nodeConfigs/webhook.trigger.ts (1)
1-33: Remove large block of commented-out code.This 33-line commented block clutters the file. If the previous implementation is needed for reference, consider storing it in version control history or documentation instead.
♻️ Proposed fix
-// import { NodeConfig } from '../types/node.types'; - -// export const webhookTriggerConfig: NodeConfig = { -// id: "webhook", -// type: "trigger", -// label: "Webhook", -// icon: "📡", -// description: "Trigger workflow on HTTP request", -// fields: [ -// { -// name: "path", -// label: "Webhook Path", -// type: "text", -// required: true, -// placeholder: "/api/webhook/12345", -// description: "The HTTP path where this webhook will listen. Must be unique per workflow." -// }, -// { -// name: "method", -// label: "HTTP Method", -// type: "dropdown", -// required: true, -// options: [ -// { label: "POST", value: "POST" }, -// { label: "GET", value: "GET" } -// ], -// defaultValue: "POST", -// description: "The HTTP method to accept (typically POST)." -// } -// ], -// summary: "Listen for HTTP requests on a unique webhook URL.", -// helpUrl: "https://docs.example.com/webhook-trigger" -// }; - import { NodeConfig } from "../types/node.types";packages/common/src/index.ts (1)
36-40: Consider stricter typing forpositionfield.Using
z.any().optional()forpositionprovides no validation. If position has a known structure (e.g.,{ x: number, y: number }), consider defining it explicitly:const PositionSchema = z.object({ x: z.number(), y: z.number(), }).optional(); export const NodeUpdateSchema = z.object({ NodeId: z.string(), Config: z.any().optional(), position: PositionSchema, });apps/web/app/workflows/[id]/page.tsx (3)
3-3: Remove unused importact.The
actimport from React is not used anywhere in this component.-import { useState, useEffect, act } from "react"; +import { useState } from "react";Note:
useEffectalso appears unused in this file.
89-210: Remove large block of commented-out code.This 120+ line commented block is dead code that should be removed. It clutters the file and makes maintenance harder. Version control preserves history if needed.
301-306: Remove unused variablecurrentActionNodes.
currentActionNodesis computed but never used, adding unnecessary overhead.- // Find all non-placeholder action nodes in the CURRENT state (after newNode is added) - const currentActionNodes = [ - ...nodes.filter( - (n) => n.data.nodeType === "action" && !n.data.isPlaceholder - ), - newNode, - ]; - // Find all existing action nodes (NOT including newNode yet)apps/web/app/components/NODES/BaseNode.tsx (2)
19-29: Consider using or removing unused propsidandtype.The
idandtypeprops are destructured from the component parameters but are never used in the component body. If they're not needed, simplify the signature. If they might be needed for debugging or accessibility, consider adding them as data attributes.
108-116: Remove commented-out code block.Dead code should be removed. Version control preserves history if restoration is needed.
apps/web/app/lib/api.ts (2)
59-70: Inconsistent naming:Credentialsuses PascalCase.For consistency with
workflows,triggers, andnodes, consider renaming tocredentials.- Credentials: { + credentials: { getCredentials: async (type: string) =>
7-71: Consider stronger typing and centralized axios configuration.The API wrapper uses
anyfor several payloads (Config,datain create methods). Consider:
- Defining proper TypeScript interfaces for request/response shapes
- Creating a shared axios instance with default config to reduce repetition of
withCredentialsand headers♻️ Example: Centralized axios instance
const apiClient = axios.create({ baseURL: BACKEND_URL, withCredentials: true, headers: { "Content-Type": "application/json" }, }); export const api = { workflows: { create: async (name: string, config: WorkflowConfig) => apiClient.post("/user/create/workflow", { Name: name, Config: config }), // ... }, // ... };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
apps/http-backend/src/routes/userRoutes/userMiddleware.tsapps/http-backend/src/routes/userRoutes/userRoutes.tsapps/http-backend/tsconfig.tsbuildinfoapps/web/app/components/NODES/BaseNode.tsxapps/web/app/components/nodes/TriggerNode.tsxapps/web/app/components/ui/Design/WorkflowButton.tsxapps/web/app/components/ui/Design/WorkflowCard.tsxapps/web/app/lib/api.tsapps/web/app/lib/nodeConfigs/gmail.action.tsapps/web/app/lib/nodeConfigs/googleSheet.action.tsapps/web/app/lib/nodeConfigs/index.tsapps/web/app/lib/nodeConfigs/webhook.trigger.tsapps/web/app/lib/types/node.types.tsapps/web/app/page.tsxapps/web/app/types/workflow.types.tsapps/web/app/workflows/[id]/components/ConfigModal.tsxapps/web/app/workflows/[id]/components/nodes/PlaceholderNode.tsxapps/web/app/workflows/[id]/page.tsxapps/worker/src/engine/executor.tspackages/common/src/index.tspackages/db/prisma/schema.prismapackages/db/src/seed.tspackages/db/tsconfig.tsbuildinfopackages/ui/src/components/button.tsxpackages/ui/src/components/card.tsx
🧰 Additional context used
🧬 Code graph analysis (10)
apps/web/app/lib/nodeConfigs/googleSheet.action.ts (3)
apps/web/app/lib/types/node.types.ts (1)
NodeConfig(3-22)apps/web/app/components/nodes/actions.ts (2)
updateConfigData(19-27)SaveConfigFormData(4-17)apps/web/app/components/nodes/GoogleSheetFormClient.tsx (2)
config(223-268)GoogleSheetFormClientProps(17-29)
apps/web/app/lib/nodeConfigs/gmail.action.ts (4)
apps/web/app/lib/types/node.types.ts (1)
NodeConfig(3-22)packages/nodes/src/gmail/gmail.node.ts (2)
GmailNode(4-43)register(33-38)packages/nodes/src/gmail/gmail.executor.ts (3)
GmailExecutor(17-72)NodeExecutionContext(4-9)execute(25-71)packages/nodes/src/gmail/gmail.service.ts (3)
GmailService(11-111)GmailCredentials(4-9)constructor(14-31)
apps/web/app/components/ui/Design/WorkflowButton.tsx (2)
apps/web/app/components/ui/Design/WorkflowCard.tsx (1)
CardDemo(18-126)apps/web/app/components/ui/Button.tsx (1)
props(14-81)
packages/ui/src/components/button.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/worker/src/engine/executor.ts (4)
apps/worker/src/engine/registory.ts (3)
execute(11-33)register(7-9)register(47-47)packages/nodes/src/gmail/gmail.executor.ts (1)
execute(25-71)packages/nodes/src/google-sheets/google-sheets.executor.ts (1)
execute(120-170)packages/db/src/index.ts (1)
prismaClient(17-18)
apps/web/app/lib/nodeConfigs/webhook.trigger.ts (1)
apps/web/app/lib/types/node.types.ts (1)
NodeConfig(3-22)
apps/web/app/workflows/[id]/page.tsx (5)
apps/web/app/components/NODES/BaseNode.tsx (1)
BaseNode(19-164)apps/web/app/lib/api.ts (1)
api(7-71)apps/web/app/workflows/[id]/components/ConfigModal.tsx (1)
ConfigModal(14-174)apps/web/app/components/nodes/TriggerSidebar.tsx (1)
TriggerSideBar(22-66)apps/web/app/components/Actions/ActionSidebar.tsx (1)
ActionSideBar(28-77)
apps/web/app/workflows/[id]/components/ConfigModal.tsx (3)
apps/web/store/slices/userSlice.ts (1)
userAction(41-41)apps/web/app/lib/nodeConfigs/index.ts (1)
getNodeConfig(25-27)packages/nodes/src/google-sheets/token.js (1)
code(10-10)
packages/ui/src/components/card.tsx (1)
packages/ui/src/lib/utils.ts (1)
cn(4-6)
apps/web/app/page.tsx (1)
apps/web/app/components/ui/Design/WorkflowButton.tsx (1)
ParentComponent(7-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Agent
🔇 Additional comments (17)
packages/db/prisma/schema.prisma (2)
22-29: LGTM!The formatting alignment changes improve readability. The commented-out relation is acceptable for tracking schema evolution.
65-66: LGTM!The
positionfield change fromInttoJsonsupports the new{x, y}coordinate format required by the workflow canvas UI. The newstagefield appropriately handles node ordering as an integer.apps/worker/src/engine/executor.ts (1)
65-76: LGTM!The output chaining between nodes and the completion status update are correctly implemented.
apps/http-backend/src/routes/userRoutes/userMiddleware.ts (1)
33-42: LGTM!Good practice to comment out debug logging that could leak sensitive user payload information in production logs.
apps/http-backend/src/routes/userRoutes/userRoutes.ts (1)
50-74: LGTM!Route formatting improved for readability while maintaining proper authentication.
packages/ui/src/components/button.tsx (1)
1-62: LGTM! Formatting changes applied consistently.Semicolons have been added throughout the file for style consistency. No functional changes.
packages/ui/src/components/card.tsx (1)
1-92: LGTM! Well-structured card component system.The card components follow a consistent pattern with proper className merging, data-slot attributes for styling hooks, and clean component composition. Good use of the shared
cnutility.apps/web/app/components/ui/Design/WorkflowButton.tsx (1)
1-17: LGTM on the overall pattern.The modal toggle pattern using
useStateis clean, and the conditional rendering approach for the modal is appropriate. The close callback properly resets state.apps/web/app/lib/nodeConfigs/googleSheet.action.ts (1)
11-49: Consider adding arangefield.The backend node definition (
packages/nodes/src/google-sheets/google-sheets.node.ts) and theSaveConfigFormDatainterface include arangefield. Depending on the operations supported, this may be needed for specifying which cells to read/write.apps/web/app/lib/nodeConfigs/index.ts (1)
1-27: LGTM on the registry pattern.The centralized registry with lookup by both label and id is a clean approach for node configuration management. The
getNodeConfighelper provides a safe fallback withnull.apps/web/app/lib/nodeConfigs/gmail.action.ts (1)
3-49: LGTM on the Gmail action config.The field names (
to,subject,body) align with theGmailExecutorexpectations inpackages/nodes/src/gmail/gmail.executor.ts. The config structure follows the establishedNodeConfigpattern correctly.apps/web/app/lib/types/node.types.ts (2)
3-22: LGTM! Well-structured interface with good documentation.The
NodeConfiginterface provides a comprehensive schema for node configurations with appropriate required and optional fields. The inline comments serve as effective documentation.
24-39: LGTM! ConfigField interface covers common form field types.The interface supports a good variety of field types and includes useful metadata like
dependsOnfor conditional field visibility.apps/web/app/lib/nodeConfigs/webhook.trigger.ts (1)
37-49: LGTM! Clean webhook trigger configuration.The empty
fieldsarray with the explanatory comment makes the design intent clear. The configuration correctly implements theNodeConfiginterface.Note: Update
helpUrlfrom the placeholderdocs.example.comwhen real documentation is available.apps/web/app/components/ui/Design/WorkflowCard.tsx (1)
25-49: Good error handling with appropriate fallbacks.The error handling logic comprehensively checks multiple error shapes (
error.response.data.message, string errors,error.message) with a sensible fallback. The form structure and loading states are well-implemented.apps/web/app/workflows/[id]/page.tsx (1)
403-415: LGTM on ReactFlow integration.The ReactFlow canvas setup with custom node types, background, and controls is well-structured. The
fitViewprop ensures proper initial viewport.apps/web/app/components/NODES/BaseNode.tsx (1)
36-92: Clean separation between placeholder and configured node rendering.The conditional rendering based on
isPlaceholderis well-structured. Handle placement logic correctly differentiates between trigger (source-only) and action (source + target) nodes for both placeholder and regular states.Also applies to: 94-163
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| router.put("/workflow/update" , userMiddleware , (req : AuthRequest , res : Response) => { | ||
|
|
||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete route handler will cause requests to hang.
This endpoint has no implementation and returns no response, which will cause client requests to timeout. Either implement the handler or remove it until ready.
🔧 Suggested placeholder implementation
-router.put("/workflow/update" , userMiddleware , (req : AuthRequest , res : Response) => {
-
-})
+router.put("/workflow/update", userMiddleware, async (req: AuthRequest, res: Response) => {
+ // TODO: Implement workflow update logic
+ return res.status(501).json({
+ message: "Not implemented",
+ });
+});📝 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.
| router.put("/workflow/update" , userMiddleware , (req : AuthRequest , res : Response) => { | |
| }) | |
| router.put("/workflow/update", userMiddleware, async (req: AuthRequest, res: Response) => { | |
| // TODO: Implement workflow update logic | |
| return res.status(501).json({ | |
| message: "Not implemented", | |
| }); | |
| }); |
🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts` around lines 437 -
439, The PUT route handler declared as router.put("/workflow/update",
userMiddleware, (req: AuthRequest, res: Response) => { }) is empty and will hang
requests; either implement the handler or remove it. To fix, add a concrete
implementation inside that arrow function: validate req.body, call the
appropriate service/utility (e.g., updateUserWorkflow or whatever existing
workflow update function is used elsewhere), handle errors with try/catch and
return an appropriate response status (200 with updated data or 204 on success,
and 4xx/5xx on errors), or if the feature isn't ready return a clear short
response such as res.status(501). Ensure the handler uses the AuthRequest user
info provided by userMiddleware and always sends a response in all code paths.
| @@ -1 +1 @@ | |||
| {"root":["./src/index.ts","./src/routes/google_callback.ts","./src/routes/nodes.routes.ts","./src/routes/userroutes/usermiddleware.ts","./src/routes/userroutes/userroutes.ts","./src/scheduler/token-scheduler.ts","./src/services/token-refresh.service.ts"],"version":"5.7.3"} No newline at end of file | |||
| {"root":["./src/index.ts","./src/routes/google_callback.ts","./src/routes/nodes.routes.ts","./src/routes/userRoutes/userMiddleware.ts","./src/routes/userRoutes/userRoutes.ts","./src/scheduler/token-scheduler.ts","./src/services/token-refresh.service.ts"],"version":"5.7.3"} No newline at end of file | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if tsbuildinfo files are ignored in .gitignore
echo "=== Checking .gitignore for tsbuildinfo ==="
if [ -f .gitignore ]; then
grep -i "tsbuildinfo" .gitignore && echo "Found in .gitignore" || echo "Not found in .gitignore"
else
echo ".gitignore not found"
fi
# Check for any other tsbuildinfo files in the repo
echo -e "\n=== All tsbuildinfo files in repo ==="
fd -e tsbuildinfo 2>/dev/null || find . -name "*.tsbuildinfo" -type f 2>/dev/null
# Also check if there's a root .gitignore and any nested .gitignore files
echo -e "\n=== All .gitignore files ==="
find . -name ".gitignore" -type f 2>/dev/nullRepository: Dev-Pross/BuildFlow
Length of output: 580
🏁 Script executed:
#!/bin/bash
# Check exact patterns in .gitignore related to tsbuildinfo
echo "=== Content of root .gitignore ==="
cat .gitignore | grep -i tsbuildinfo
echo -e "\n=== Checking git status for tsbuildinfo files ==="
git status --porcelain | grep -i tsbuildinfo || echo "No tsbuildinfo files in git status"
# Check if tsbuildinfo files are tracked by git
echo -e "\n=== Checking if tsbuildinfo files are tracked in git ==="
git ls-files | grep tsbuildinfo || echo "No tracked tsbuildinfo files"
# Check app-level .gitignore files
echo -e "\n=== Checking http-backend .gitignore ==="
if [ -f apps/http-backend/.gitignore ]; then
cat apps/http-backend/.gitignore | grep -i tsbuildinfo && echo "Found in http-backend/.gitignore" || echo "Not in http-backend/.gitignore"
fiRepository: Dev-Pross/BuildFlow
Length of output: 733
Remove tsbuildinfo build artifacts from version control and update .gitignore with complete coverage.
tsconfig.tsbuildinfo files are auto-generated incremental build caches that should not be committed. Currently, all tsbuildinfo files are tracked in git despite partial .gitignore entries. Files like apps/hooks/tsconfig.tsbuildinfo, apps/processor/tsconfig.tsbuildinfo, apps/worker/tsconfig.tsbuildinfo, and packages/nodes/tsconfig.tsbuildinfo lack .gitignore coverage entirely.
To resolve: Run git rm --cached on all tracked *.tsbuildinfo files, then ensure the root .gitignore includes a complete pattern such as **/*.tsbuildinfo or individual entries for all affected packages and apps.
🤖 Prompt for AI Agents
In `@apps/http-backend/tsconfig.tsbuildinfo` at line 1, Remove all tracked
TypeScript incremental build artifacts and add a comprehensive ignore rule: run
git rm --cached on every tracked tsbuildinfo file (e.g.,
apps/hooks/tsconfig.tsbuildinfo, apps/processor/tsconfig.tsbuildinfo,
apps/worker/tsconfig.tsbuildinfo, packages/nodes/tsconfig.tsbuildinfo and the
root apps/http-backend/tsconfig.tsbuildinfo) and update the root .gitignore to
include a complete pattern like **/*.tsbuildinfo (or explicit entries for each
package/app) so these autogenerated files are no longer committed.
| { | ||
| name: "action", | ||
| label: "Action", | ||
| type: "dropdown", | ||
| options: [ | ||
| { label: "Read Rows", value: "read_rows" }, | ||
| { label: "Append Row", value: "append_row" }, | ||
| { label: "Update Row", value: "update_row" } | ||
| ], | ||
| required: true, | ||
| defaultValue: "read_rows", | ||
| description: "What operation to perform on the sheet" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field name mismatch: action vs operation.
The existing codebase interfaces (SaveConfigFormData, updateConfigData in apps/web/app/components/nodes/actions.ts) and the backend node definition use operation, not action. This mismatch will likely cause runtime issues when submitting form data.
🐛 Proposed fix
{
- name: "action",
- label: "Action",
+ name: "operation",
+ label: "Operation",
type: "dropdown",
options: [
{ label: "Read Rows", value: "read_rows" },
{ label: "Append Row", value: "append_row" },
{ label: "Update Row", value: "update_row" }
],
required: true,
defaultValue: "read_rows",
- description: "What operation to perform on the sheet"
+ description: "What operation to perform on the sheet"
}📝 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.
| { | |
| name: "action", | |
| label: "Action", | |
| type: "dropdown", | |
| options: [ | |
| { label: "Read Rows", value: "read_rows" }, | |
| { label: "Append Row", value: "append_row" }, | |
| { label: "Update Row", value: "update_row" } | |
| ], | |
| required: true, | |
| defaultValue: "read_rows", | |
| description: "What operation to perform on the sheet" | |
| } | |
| { | |
| name: "operation", | |
| label: "Operation", | |
| type: "dropdown", | |
| options: [ | |
| { label: "Read Rows", value: "read_rows" }, | |
| { label: "Append Row", value: "append_row" }, | |
| { label: "Update Row", value: "update_row" } | |
| ], | |
| required: true, | |
| defaultValue: "read_rows", | |
| description: "What operation to perform on the sheet" | |
| } |
🤖 Prompt for AI Agents
In `@apps/web/app/lib/nodeConfigs/googleSheet.action.ts` around lines 36 - 48, The
form field name is incorrectly set to "action" in the dropdown config (object
with name: "action"); it must match the rest of the code which expects
"operation". Change the field key from "action" to "operation" in the dropdown
definition (and keep the same options/defaultValue/description) so submitted
data aligns with SaveConfigFormData and updateConfigData; verify any references
to this field in the same module use "operation" as well.
| const userId =userAction.setUserId as unknown as string; | ||
| console.log("we are getting this userId from ConfigModal" , userId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: userAction.setUserId is an action creator, not the user ID value.
userAction.setUserId is a Redux action creator function, not the actual userId string. This code will pass the function reference (or [object Object]) as the userId, which is incorrect.
You need to use a Redux selector (e.g., useSelector) to read the userId from state:
🐛 Proposed fix
import { getNodeConfig } from "@/app/lib/nodeConfigs";
-import { useState } from "react";
+import { useState } from "react";
+import { useSelector } from "react-redux";
import { HOOKS_URL } from "@repo/common/zod";
-import { userAction } from "@/store/slices/userSlice";
+// Import the selector for userId from your store
+// import { selectUserId } from "@/store/slices/userSlice";Then inside the component:
- const userId = userAction.setUserId as unknown as string;
- console.log("we are getting this userId from ConfigModal", userId);
+ const userId = useSelector((state: any) => state.user.userId); // adjust selector path as needed🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/components/ConfigModal.tsx around lines 23 - 24,
The code is using userAction.setUserId (an action creator) as if it were the
userId; replace this with a Redux selector: import and use useSelector (from
react-redux) to read the userId from state (e.g., call useSelector(state =>
state.user.id) or the existing selector function like selectUserId) inside the
ConfigModal component, remove the incorrect cast of userAction.setUserId, and
update any console/log references to use the selector-derived userId instead of
the action creator reference.
| <div className="space-y-4"> | ||
| {nodeConfig.fields.map((field) => ( | ||
| <div key={field.name} className="form-group"> | ||
| <label className="block text-sm font-medium text-white mb-1"> | ||
| {field.label}{" "} | ||
| {field.required && ( | ||
| <span className="text-red-400">*</span> | ||
| )} | ||
| </label> | ||
| {/* Render field based on type - only basic input for now */} | ||
| <input | ||
| type="text" | ||
| placeholder={field.placeholder} | ||
| className="w-full p-3 border border-gray-900 bg-black text-white rounded-md focus:ring-2 focus:ring-white focus:border-white placeholder-gray-400" | ||
| /> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Form inputs are uncontrolled and values are not captured on save.
The dynamically rendered fields have no value or onChange handlers, so user input is lost. The handleSave function (line 29) only passes { HOOKS_URL } regardless of what the user enters.
Consider managing field values in state:
🐛 Proposed fix outline
+const [formValues, setFormValues] = useState<Record<string, any>>({});
+
+const handleFieldChange = (fieldName: string, value: any) => {
+ setFormValues(prev => ({ ...prev, [fieldName]: value }));
+};
// In handleSave:
-await onSave({ HOOKS_URL }, userId);
+await onSave({ HOOKS_URL, ...formValues }, userId);
// In the input rendering:
<input
type="text"
placeholder={field.placeholder}
+ value={formValues[field.name] ?? field.defaultValue ?? ""}
+ onChange={(e) => handleFieldChange(field.name, e.target.value)}
className="w-full p-3 border border-gray-900 bg-black text-white rounded-md..."
/>🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/components/ConfigModal.tsx around lines 127 -
145, The form inputs in ConfigModal.tsx are uncontrolled so user edits are lost;
initialize a local state object (e.g., formValues) keyed by nodeConfig.fields'
name (use useState, populated from nodeConfig or default placeholders) and
update it in an onChange handler attached to the rendered <input> for each
field; set each input's value to formValues[field.name]; finally update the call
in handleSave to include the collected formValues (merge with existing HOOKS_URL
behavior) so all dynamic field values are passed back when saving.
| if (!execute.success) { | ||
| await prismaClient.workflowExecution.update({ | ||
| where: { id: workflowExecutionId }, | ||
| data: { | ||
| status: "Failed", | ||
| error: execute.error, | ||
| completedAt: new Date(), | ||
| }, | ||
| }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure error is serialized to string and log the failing node.
Based on the registry code, execute.error could be an Error object when exceptions are caught. Storing a raw Error object in a string field may result in [object Object]. Additionally, logging which node failed would help debugging.
if (!execute.success) {
+ const errorMessage = execute.error instanceof Error
+ ? execute.error.message
+ : String(execute.error);
+ console.error(`Node "${node.name}" (position ${node.position}) failed: ${errorMessage}`);
await prismaClient.workflowExecution.update({
where: { id: workflowExecutionId },
data: {
status: "Failed",
- error: execute.error,
+ error: errorMessage,
completedAt: new Date(),
},
});
return;
}🤖 Prompt for AI Agents
In `@apps/worker/src/engine/executor.ts` around lines 54 - 64, When handling the
failed execute result, ensure the error is serialized to a string before saving
and add the failing node identifier to both the DB update and logs: convert
execute.error to a reliable string (e.g., use execute.error?.message ||
String(execute.error) or JSON.stringify with a fallback) and pass that into
prismaClient.workflowExecution.update for the error field; also include the
current node identifier from the executor scope (e.g., nodeId, currentNode.id or
node.name — whatever symbol is used in this file) as a new field (e.g.,
failedNodeId/failedNodeName) and emit a processLogger or logger call that
records the workflowExecutionId, the failing node identifier, and the serialized
error.
| export const BACKEND_URL = "http://localhost:3002"; | ||
| export const HOOKS_URL = "http://localhost:3002"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded localhost URLs will fail in deployed environments.
Both BACKEND_URL and HOOKS_URL are hardcoded to http://localhost:3002. These should be configurable via environment variables for different deployment environments.
🐛 Proposed fix
-export const BACKEND_URL = "http://localhost:3002";
-export const HOOKS_URL = "http://localhost:3002";
+export const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:3002";
+export const HOOKS_URL = process.env.HOOKS_URL ?? "http://localhost:3002";Also, if these URLs will always be the same, consider using a single constant:
export const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:3002";
export const HOOKS_URL = BACKEND_URL; // If they're always the same🤖 Prompt for AI Agents
In `@packages/common/src/index.ts` around lines 4 - 5, BACKEND_URL and HOOKS_URL
are hardcoded to localhost which breaks deployments; change them to read from
environment variables with sensible defaults. Update the constants BACKEND_URL
and HOOKS_URL in packages/common/src/index.ts to use process.env.BACKEND_URL and
process.env.HOOKS_URL (or use BACKEND_URL as the single source if both should
match), falling back to "http://localhost:3002" when the env vars are absent,
and ensure exports remain unchanged so callers keep working.
| export const NodeSchema = z.object({ | ||
| Name: z.string(), | ||
| AvailableNodeId: z.string(), | ||
| Config: z.any(), | ||
| Position: z.number(), | ||
| Config: z.any().optional(), | ||
| stage: z.number().optional(), | ||
| WorkflowId: z.string(), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for usages of Position field in node-related code
rg -n -C2 '\bPosition\b' --type ts --type tsx -g '!node_modules'
# Search for stage field usage to verify migration
rg -n -C2 '\.stage\b' --type ts -g '!node_modules'Repository: Dev-Pross/BuildFlow
Length of output: 597
🏁 Script executed:
#!/bin/bash
# Check the full NodeSchema definition
cat -n packages/common/src/index.ts | sed -n '25,40p'Repository: Dev-Pross/BuildFlow
Length of output: 529
🏁 Script executed:
#!/bin/bash
# Get more context around the userRoutes.ts line where position appears
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '520,540p'Repository: Dev-Pross/BuildFlow
Length of output: 1031
🏁 Script executed:
#!/bin/bash
# Search for all references to 'position' (lowercase) in node/schema related code
rg -n -C3 '\bposition\b' --type ts -g '!node_modules'Repository: Dev-Pross/BuildFlow
Length of output: 30850
🏁 Script executed:
#!/bin/bash
# Search for API calls that send Position field
rg -n 'Position\s*:' --type ts -g '!node_modules' -B2 -A2Repository: Dev-Pross/BuildFlow
Length of output: 1333
Fix incomplete field rename: Position → stage is still referenced in frontend API calls.
The schema was updated to use stage, but frontend code still sends the old Position field name, causing validation failures:
- apps/web/app/workflows/[id]/page.tsx:226 — Sends
Position: 1; should bestage: 1 - apps/web/app/workflow/lib/config.ts:234 — Sends
Position: context.position; should bestage: context.position
The schema validation will reject these requests since NodeSchema expects stage, not Position.
🤖 Prompt for AI Agents
In `@packages/common/src/index.ts` around lines 28 - 34, NodeSchema was renamed to
use stage but frontend still sends Position, causing validation failures; change
the outgoing payload keys so they send stage instead of Position. Specifically,
update the places that currently set "Position: 1" and "Position:
context.position" to use "stage: 1" and "stage: context.position" respectively,
and ensure any related types/objects (payload builders or serialize functions)
that reference Position are updated to stage to match NodeSchema.
| AvailableNodeID String | ||
| // CredentialsID String | ||
| credentials Credential[] | ||
| AvailableNode AvailableNode? @relation(fields: [AvailableNodeID], references: [id]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistency between required foreign key and optional relation.
AvailableNodeID is defined as String (required/non-nullable), but the AvailableNode relation is marked as optional (AvailableNode?). This is contradictory:
- If a Node must always have an
AvailableNodeID, the relation should beAvailableNode(not optional). - If the relation is truly optional, the FK should be
String?.
This mismatch can cause confusion in generated types and unexpected behavior.
Suggested fix (if relation should be required)
AvailableNodeID String
- AvailableNode AvailableNode? `@relation`(fields: [AvailableNodeID], references: [id])
+ AvailableNode AvailableNode `@relation`(fields: [AvailableNodeID], references: [id])Alternative fix (if relation should be optional)
- AvailableNodeID String
+ AvailableNodeID String?
AvailableNode AvailableNode? `@relation`(fields: [AvailableNodeID], references: [id])📝 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.
| AvailableNodeID String | |
| // CredentialsID String | |
| credentials Credential[] | |
| AvailableNode AvailableNode? @relation(fields: [AvailableNodeID], references: [id]) | |
| AvailableNodeID String | |
| // CredentialsID String | |
| credentials Credential[] | |
| AvailableNode AvailableNode `@relation`(fields: [AvailableNodeID], references: [id]) |
| AvailableNodeID String | |
| // CredentialsID String | |
| credentials Credential[] | |
| AvailableNode AvailableNode? @relation(fields: [AvailableNodeID], references: [id]) | |
| AvailableNodeID String? | |
| // CredentialsID String | |
| credentials Credential[] | |
| AvailableNode AvailableNode? `@relation`(fields: [AvailableNodeID], references: [id]) |
🤖 Prompt for AI Agents
In `@packages/db/prisma/schema.prisma` around lines 68 - 71, AvailableNodeID is
non-nullable but the relation AvailableNode is optional, causing a schema
mismatch; decide intended cardinality and make them consistent: if the relation
must be required, change AvailableNode? to AvailableNode (remove the question
mark) so AvailableNodeID stays String and the relation is required; otherwise,
make the foreign key nullable by changing AvailableNodeID to String? so the
optional relation AvailableNode? aligns with a nullable FK. Update the Prisma
model accordingly and regenerate the client.
| startedAt DateTime @default(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) @db.Timestamptz(6) | ||
| completedAt DateTime? @default(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) @db.Timestamptz(6) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
completedAt should not have a default value.
Setting @default(dbgenerated(...)) on completedAt means it will be populated with a timestamp at record creation, even though the execution hasn't completed yet. This breaks the semantic meaning of the field:
startedAtwith a default is correct (marks when execution record is created/started).completedAtshould remainnulluntil the execution actually finishes and be explicitly set by application code.
Suggested fix
startedAt DateTime `@default`(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) `@db.Timestamptz`(6)
- completedAt DateTime? `@default`(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) `@db.Timestamptz`(6)
+ completedAt DateTime? `@db.Timestamptz`(6)📝 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.
| startedAt DateTime @default(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) @db.Timestamptz(6) | |
| completedAt DateTime? @default(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) @db.Timestamptz(6) | |
| startedAt DateTime `@default`(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) `@db.Timestamptz`(6) | |
| completedAt DateTime? `@db.Timestamptz`(6) |
🤖 Prompt for AI Agents
In `@packages/db/prisma/schema.prisma` around lines 109 - 110, The completedAt
field currently has a db-generated default which sets a timestamp on creation;
remove the `@default`(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) from the
completedAt field (leave it as DateTime? `@db.Timestamptz`(6) and nullable) so it
stays null until explicitly updated, while keeping startedAt's default intact;
after changing the schema run the Prisma migration to update the DB schema and
ensure application code sets completedAt when the execution finishes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request refactors user routes and enhances workflow UI components with new node management functionality. The changes introduce a workflow canvas with React Flow, new modal components, and update the database schema to support position-based node layouts.
Changes:
- Refactored database schema to separate node positioning (Json) from execution ordering (stage Int field)
- Added new workflow UI components including workflow creation modal, config modal, and placeholder nodes
- Updated API routes with improved formatting and added position/stage field handling
- Introduced node configuration system with support for Gmail and Google Sheets integrations
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 37 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/src/components/card.tsx | New card component for UI consistency |
| packages/ui/src/components/button.tsx | Updated semicolon style for consistency |
| packages/db/tsconfig.tsbuildinfo | TypeScript version updated to 5.9.2 |
| packages/db/src/seed.ts | Updated seed data with new position and stage fields |
| packages/db/prisma/schema.prisma | Schema changes: position now Json, added stage Int field, updated NodeExecution timestamps |
| packages/common/src/index.ts | Updated schemas to support optional config and position fields, added HOOKS_URL constant |
| apps/worker/src/engine/executor.ts | Re-enabled error handling and improved logging |
| apps/web/app/workflows/[id]/page.tsx | New workflow canvas page with React Flow integration |
| apps/web/app/workflows/[id]/components/nodes/PlaceholderNode.tsx | New placeholder node component |
| apps/web/app/workflows/[id]/components/ConfigModal.tsx | New configuration modal for nodes |
| apps/web/app/types/workflow.types.ts | Fixed typo in Type field, updated config to optional |
| apps/web/app/page.tsx | Added WorkflowButton component to home page |
| apps/web/app/lib/types/node.types.ts | New type definitions for node configurations |
| apps/web/app/lib/nodeConfigs/*.ts | New node configuration files for webhook, Gmail, and Google Sheets |
| apps/web/app/lib/api.ts | New centralized API client module |
| apps/web/app/components/ui/Design/WorkflowCard.tsx | New workflow creation modal component |
| apps/web/app/components/ui/Design/WorkflowButton.tsx | Button component to trigger workflow creation |
| apps/web/app/components/nodes/TriggerNode.tsx | Updated to support optional icon and config fields |
| apps/web/app/components/NODES/BaseNode.tsx | New unified base node component for workflow canvas |
| apps/http-backend/tsconfig.tsbuildinfo | Updated file paths for userRoutes |
| apps/http-backend/src/routes/userRoutes/userRoutes.ts | Major refactoring with improved formatting, added position/stage support, fixed empty route handler |
| apps/http-backend/src/routes/userRoutes/userMiddleware.ts | Commented out debug log |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| isOpen: boolean; | ||
| selectedNode: any | null; | ||
| onClose: () => void; | ||
| onSave: (config: any, userId: string) => Promise<void>; |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onSave callback signature expects (config: any, userId: string) as parameters, but in the actual usage in page.tsx (line 425-445), the callback only accepts (nodeId, config). This mismatch will cause runtime errors. The callback signature should be consistent with how it's actually called.
| setLoading(true); | ||
| try { | ||
| // For now, just save empty config | ||
| await onSave({ HOOKS_URL }, userId); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onSave call passes { HOOKS_URL } as the config and userId as the second parameter, but the actual implementation expects (nodeId, config). This will fail when called. Additionally, HOOKS_URL is a constant string, not a field that should be in curly braces unless you intend to create an object with HOOKS_URL as a key.
| } catch (e) { | ||
| console.log( | ||
| "Error Fetching the credentials ", | ||
| e instanceof Error ? e.message : "Unkown reason" |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The word "Unkown" is misspelled. It should be "Unknown".
| e instanceof Error ? e.message : "Unkown reason" | |
| e instanceof Error ? e.message : "Unknown reason" |
| TriggerType: trigger.type, | ||
| }); | ||
| const triggerId = result.data.data.id as string; | ||
| console.log("The Trigger Id is : ", triggerId); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug console.log statement should be removed or replaced with proper logging in production code.
| ); | ||
| return res | ||
| .status(statusCodes.INTERNAL_SERVER_ERROR) | ||
| .json({ message: "Internal server from fetching the credentials" }); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message says "Internal server from fetching the credentials" which is grammatically incorrect. It should be "Internal server error from fetching the credentials" to match the pattern used in other error messages in this file.
| .json({ message: "Internal server from fetching the credentials" }); | |
| .json({ message: "Internal server error from fetching the credentials" }); |
| // onSelectAction: (action: { id: string; name: string; type: string; icon?: string }) => void; | ||
|
|
||
| // 1. Call API to create action in DB | ||
| console.log("This is node Id before log", action.id); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug console.log statements should be removed or replaced with proper logging in production code. This log appears to be debugging code that should be cleaned up.
| nodeId String? | ||
| node Node? @relation(fields: [nodeId], references: [id]) | ||
| user User @relation(fields: [userId], references: [id]) | ||
| // Node_Node_CredentialsIDToCredential Node[] @relation("Node_CredentialsIDToCredential") |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the Node model, there is commented-out code suggesting an incomplete relation design. Remove this commented code or implement the relation properly.
| // Node_Node_CredentialsIDToCredential Node[] @relation("Node_CredentialsIDToCredential") |
| onClose?: () => void; | ||
| } | ||
|
|
||
| export function CardDemo({ onClose }: CardDemoProps) { |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The component name CardDemo does not reflect its actual purpose. This component creates a workflow modal, so it should be named something like CreateWorkflowModal or WorkflowCreationCard for better clarity.
| label, | ||
| icon, | ||
| isPlaceholder, | ||
| config, |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable config.
| config, |
|
|
||
| router.put("/workflow/update" , userMiddleware , (req : AuthRequest , res : Response) => { | ||
|
|
||
| }) |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid automated semicolon insertion (94% of all statements in the enclosing script have an explicit semicolon).
| }) | |
| }); |
Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.