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
2 changes: 1 addition & 1 deletion apps/http-backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ app.use(cookieParser());

app.use("/user" , userRouter)
app.use('/node', sheetRouter)
app.use('/oauth/google', googleAuth) // ← CHANGED THIS LINE!
app.use('/auth/google', googleAuth) // ← CHANGED THIS LINE!
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's check the actual state of the file at line 36
head -40 apps/http-backend/src/index.ts | tail -10

Repository: Dev-Pross/BuildFlow

Length of output: 283


🏁 Script executed:

# Search for references to the old OAuth route path
rg -n '/oauth/google' --type ts --type tsx --type js --type json 2>/dev/null

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Search for references to the new OAuth route path to understand scope
rg -n '/auth/google' --type ts --type tsx --type js --type json 2>/dev/null

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Check if there are any client-side files that might reference OAuth endpoints
fd -e ts -e tsx -e js | xargs grep -l -i 'oauth\|google.*auth' 2>/dev/null | head -20

Repository: Dev-Pross/BuildFlow

Length of output: 960


🏁 Script executed:

# Check the useCredential hook for OAuth endpoint references
cat -n apps/web/app/hooks/useCredential.ts

Repository: Dev-Pross/BuildFlow

Length of output: 1948


🏁 Script executed:

# Check the google-oauth-service for endpoint configuration
cat -n packages/nodes/src/common/google-oauth-service.ts

Repository: Dev-Pross/BuildFlow

Length of output: 6420


🏁 Script executed:

# Check the google_callback routes to see what endpoints are defined
cat -n apps/http-backend/src/routes/google_callback.ts

Repository: Dev-Pross/BuildFlow

Length of output: 11196


Remove debug comment and fix incomplete route migration.

The inline comment // ← CHANGED THIS LINE! must be removed before merging.

More critically, the route path change from /oauth/google to /auth/google is incomplete. Frontend code still references the old paths and will fail:

  • apps/web/app/hooks/useCredential.ts (line 31) references /oauth/google/initiate
  • packages/nodes/src/common/google-oauth-service.ts (line 19) references /oauth/google/callback

Both frontend files must be updated to use /auth/google/initiate and /auth/google/callback respectively, or the OAuth flow will break.

🤖 Prompt for AI Agents
In `@apps/http-backend/src/index.ts` at line 36, Remove the inline debug comment
from the app.use('/auth/google', googleAuth) line and ensure the route migration
is complete: update any frontend references to the old '/oauth/google' prefix to
the new '/auth/google' prefix. Specifically, change occurrences of
'/oauth/google/initiate' in the useCredential hook (useCredential) to
'/auth/google/initiate' and change '/oauth/google/callback' in the
google-oauth-service module (google-oauth-service) to '/auth/google/callback' so
the OAuth flow matches the backend's app.use('/auth/google', googleAuth) route.


const PORT= 3002

Expand Down
2 changes: 1 addition & 1 deletion apps/http-backend/tsconfig.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -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"}
{"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"}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if tsbuildinfo is in gitignore
echo "=== Checking .gitignore for tsbuildinfo ==="
if [ -f .gitignore ]; then
  cat .gitignore | grep -i 'tsbuild' && echo "Found tsbuildinfo reference" || echo "No tsbuildinfo reference found"
else
  echo ".gitignore not found"
fi

echo ""
echo "=== Full .gitignore content ==="
cat .gitignore 2>/dev/null | head -50

echo ""
echo "=== Checking for *.tsbuildinfo files in repo ==="
find . -name "*.tsbuildinfo" -type f 2>/dev/null | head -20

echo ""
echo "=== Git status for tsbuildinfo files ==="
git ls-files | grep tsbuildinfo || echo "No tsbuildinfo files tracked in git"

Repository: Dev-Pross/BuildFlow

Length of output: 1677


Fix .gitignore to properly exclude all tsconfig.tsbuildinfo files from version control.

The .gitignore has incomplete and malformed entries for .tsbuildinfo files. While some specific entries exist, they don't cover all packages and some are incorrectly formatted (e.g., apps/http-backend/tsbuildinfo is missing the tsconfig. prefix). Additionally, these auto-generated build cache files are currently tracked in git.

Replace scattered entries with a single glob pattern **/*.tsbuildinfo to exclude all TypeScript build info files across the entire monorepo, then remove the tracked files from git history using git rm --cached.

🤖 Prompt for AI Agents
In `@apps/http-backend/tsconfig.tsbuildinfo` at line 1, The repo is tracking
TypeScript build cache files like apps/http-backend/tsconfig.tsbuildinfo because
.gitignore entries are malformed; update .gitignore to add a single glob rule
**/*.tsbuildinfo (replacing the broken entries such as
apps/http-backend/tsbuildinfo) and then remove already-tracked tsbuildinfo files
from git with git rm --cached <path> (or git rm --cached **/*.tsbuildinfo) and
commit the change so files like tsconfig.tsbuildinfo are no longer tracked.

18 changes: 18 additions & 0 deletions apps/web/app/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,22 @@ export const api = {
headers: { "Content-Type": "application/json" },
}),
},
google: {
getDocuments: async (CredentialId : string) => {
const data = await axios.get(`${BACKEND_URL}/node/getDocuments/${CredentialId}`,{
withCredentials: true,
headers: {"Content-Type" : "application/json"},
})

console.log(data.data.files)
return data.data.files
},
getSheets: async (documentId: string, CredentialId: string) => {
const data = await axios.get(`${BACKEND_URL}/node/getSheets/${CredentialId}/${documentId}`,{
withCredentials: true,
headers: {"Content-Type":"application/json"}
})
return data.data.files.data
},
}
};
25 changes: 16 additions & 9 deletions apps/web/app/lib/nodeConfigs/googleSheet.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const googleSheetActionConfig: NodeConfig = {
label: "Google Sheet",
icon: "📊",
description: "Read or write data to Google Sheets",
credentials: "google", // Requires Google OAuth
credentials: "google_oauth", // Requires Google OAuth

fields: [
{
Expand All @@ -18,20 +18,20 @@ export const googleSheetActionConfig: NodeConfig = {
description: "Choose which Google account to use"
},
{
name: "spreadsheetId",
label: "Spreadsheet",
name: "spreadsheetId",
type: "dropdown",
label: "Spreadsheet",
required: true,
description: "Select the Google Spreadsheet",
dependsOn: "credentialId" // Only show after credential is selected
dependsOn: "credentialId", // <-- This field depends on credentialId
fetchOptions: "google.getDocuments", // <-- API method to call
},
{
name: "sheetName",
label: "Sheet Name",
type: "dropdown",
type: "dropdown",
label: "Sheet",
required: true,
description: "Select the specific sheet within the spreadsheet",
dependsOn: "spreadsheetId" // Only show after spreadsheet is selected
dependsOn: "spreadsheetId",
fetchOptions: "google.getSheets",
},
{
name: "action",
Expand All @@ -45,6 +45,13 @@ export const googleSheetActionConfig: NodeConfig = {
required: true,
defaultValue: "read_rows",
description: "What operation to perform on the sheet"
},
{
name: "Range",
type: "text",
label: "range",
value: "A1:Z100",
required: true
}
Comment on lines +49 to 55
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix naming conventions and use defaultValue consistently.

The new Range field has several inconsistencies:

  1. Field name casing: name: "Range" uses PascalCase while all other fields use camelCase (e.g., credentialId, spreadsheetId, sheetName).

  2. Label/name appear swapped: Typically name is the internal key (lowercase) and label is the human-readable display text (capitalized). Here it's reversed.

  3. Use defaultValue instead of value: The action field uses defaultValue on line 46. For consistency, this field should also use defaultValue.

Suggested fix
     {
-      name: "Range",
+      name: "range",
       type: "text", 
-      label: "range",
-      value: "A1:Z100",
+      label: "Range",
+      defaultValue: "A1:Z100",
       required: true
     }
📝 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
{
name: "Range",
type: "text",
label: "range",
value: "A1:Z100",
required: true
}
{
name: "range",
type: "text",
label: "Range",
defaultValue: "A1:Z100",
required: true
}
🤖 Prompt for AI Agents
In `@apps/web/app/lib/nodeConfigs/googleSheet.action.ts` around lines 49 - 55, The
Range field object uses inconsistent casing and keys; change the object so its
internal key is camelCase name: "range", its human label is label: "Range"
(capitalized), and replace value: "A1:Z100" with defaultValue: "A1:Z100" to
match the pattern used by other fields (e.g., credentialId, spreadsheetId,
sheetName, and action) and keep naming/defaults consistent in
googleSheet.action.ts.

],

Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/lib/types/node.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export interface ConfigField {
required?: boolean;
defaultValue?: string | number | boolean; // Initial value if not set
placeholder?: string;

fetchOptions?: string,
value? : string,
options?: Array<{ label: string; value: string | number }>; // For dropdowns
dependsOn?: string; // Name of another field this depends on
description?: string; // Help text for this field
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/workflow/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const getCredentials = async(type: string)=>{
console.log("response from config: ",response);

const Data = JSON.stringify(response.data.Data);
return response.data.Data;
return response.data.data;
Comment on lines 39 to +40
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Read the file around lines 39-40 with context
cat -n apps/web/app/workflow/lib/config.ts | sed -n '30,50p'

Repository: Dev-Pross/BuildFlow

Length of output: 707


🏁 Script executed:

# Search for all usages of response.data.Data and response.data.data in the file
rg -n 'response\.data\.[Dd]ata' apps/web/app/workflow/lib/config.ts

Repository: Dev-Pross/BuildFlow

Length of output: 388


🏁 Script executed:

# Check if the Data variable on line 39 is used anywhere after its declaration
rg -n '\bData\b' apps/web/app/workflow/lib/config.ts | head -20

Repository: Dev-Pross/BuildFlow

Length of output: 595


🏁 Script executed:

# Get full context of the function containing lines 39-40
sed -n '25,55p' apps/web/app/workflow/lib/config.ts

Repository: Dev-Pross/BuildFlow

Length of output: 748


Remove unused variable and verify response field casing consistency.

The Data variable on line 39 is unused after the return statement was changed to use response.data.data. This should be removed.

Additionally, this function now uses response.data.data (lowercase) while other functions in this file use response.data.Data (uppercase) — e.g., getAvailableTriggers (lines 13, 16, 18, 19), getAllWorkflows (line 69), getEmptyWorkflow (lines 261, 275, 278). Verify whether the backend response structure is consistent, or if different endpoints use different casing.

Suggested fix
   console.log("response from config: ",response);
-    
-    const Data = JSON.stringify(response.data.Data);
     return response.data.data;
📝 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 Data = JSON.stringify(response.data.Data);
return response.data.Data;
return response.data.data;
return response.data.data;
🤖 Prompt for AI Agents
In `@apps/web/app/workflow/lib/config.ts` around lines 39 - 40, The snippet
declares an unused const Data (const Data = JSON.stringify(response.data.Data))
but returns response.data.data (lowercase), so remove the unused variable and
normalize the response field access; update the function to return the correct
payload by using a single source like response.data.Data if backend uses
uppercase, or safely handle both casings (e.g., prefer response.data.Data ||
response.data.data) to avoid breakage. Check and align other functions
(getAvailableTriggers, getAllWorkflows, getEmptyWorkflow) to the same casing or
apply the same tolerant access pattern so all endpoints are handled
consistently.

}
catch(e){
console.error("Error fetching credentials:", e);
Expand Down
62 changes: 44 additions & 18 deletions apps/web/app/workflows/[id]/components/ConfigModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HOOKS_URL } from "@repo/common/zod";
import { useAppSelector } from "@/app/hooks/redux";
import { toast } from "sonner";
import { useCredentials } from "@/app/hooks/useCredential";
import { api } from "@/app/lib/api";

interface ConfigModalProps {
isOpen: boolean;
Expand All @@ -22,8 +23,36 @@ export default function ConfigModal({
workflowId,
}: ConfigModalProps) {
const [config, setConfig] = useState<Record<string, any>>({});
const [dynamicOptions, setDynamicOptions] = useState<Record<string, any[]>>({});
const [loading, setLoading] = useState(false);
const userId = useAppSelector((state) => state.user.userId) as string;

const fetchOptionsMap: Record<string, (params: any) => Promise<any>> = {
"google.getDocuments" : ({credentialId}) => api.google.getDocuments(credentialId),
"google.getSheets" : ({spreadsheetId, credentialId}) => api.google.getSheets(spreadsheetId, credentialId)
}

const handleFieldChange = async (fieldName: string, value: string, nodeConfig: any) => {
// Update config with new value
const updatedConfig = ({ ...config, [fieldName]: value })
console.log(fieldName, " ", value, " ", nodeConfig)
console.log(config, "from handle field function - 1")
setConfig((prev) => ({ ...prev, [fieldName]: value }));
console.log(config, "from handle field fun - 2")
console.log({ ...config, [fieldName]: value }, "what we're setting")
// Find fields that depend on this field
const dependentFields = nodeConfig.fields.filter((f:any) => f.dependsOn === fieldName);

for (const depField of dependentFields) {
const fetchFn = depField.fetchOptions ? fetchOptionsMap[depField.fetchOptions] : undefined;
console.log(fetchFn, "fecth FN")
if (fetchFn) {
const options = await fetchFn(updatedConfig);
// console.log(({ ...config, [depField.name]: options }), "optiops setting")
setDynamicOptions((prev) => ({ ...prev, [depField.name]: options }));
}
Comment on lines +35 to +53
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle option fetch failures to prevent unhandled rejections.
handleFieldChange awaits fetchFn without try/catch; a network failure will reject and leave the UI in a broken state without feedback.

🛠️ Suggested fix
  for (const depField of dependentFields) {
    const fetchFn = depField.fetchOptions ? fetchOptionsMap[depField.fetchOptions] : undefined;
    console.log(fetchFn, "fecth FN")
    if (fetchFn) {
-      const options = await fetchFn(updatedConfig);
-      // console.log(({ ...config, [depField.name]: options }), "optiops setting")
-      setDynamicOptions((prev) => ({ ...prev, [depField.name]: options }));
+      try {
+        const options = await fetchFn(updatedConfig);
+        setDynamicOptions((prev) => ({ ...prev, [depField.name]: options }));
+      } catch (err) {
+        console.error("Failed to load dependent options", err);
+        toast.error(`Failed to load ${depField.label ?? depField.name} options`);
+        setDynamicOptions((prev) => ({ ...prev, [depField.name]: [] }));
+      }
    }
  }
📝 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 handleFieldChange = async (fieldName: string, value: string, nodeConfig: any) => {
// Update config with new value
const updatedConfig = ({ ...config, [fieldName]: value })
console.log(fieldName, " ", value, " ", nodeConfig)
console.log(config, "from handle field function - 1")
setConfig((prev) => ({ ...prev, [fieldName]: value }));
console.log(config, "from handle field fun - 2")
console.log({ ...config, [fieldName]: value }, "what we're setting")
// Find fields that depend on this field
const dependentFields = nodeConfig.fields.filter((f:any) => f.dependsOn === fieldName);
for (const depField of dependentFields) {
const fetchFn = depField.fetchOptions ? fetchOptionsMap[depField.fetchOptions] : undefined;
console.log(fetchFn, "fecth FN")
if (fetchFn) {
const options = await fetchFn(updatedConfig);
// console.log(({ ...config, [depField.name]: options }), "optiops setting")
setDynamicOptions((prev) => ({ ...prev, [depField.name]: options }));
}
const handleFieldChange = async (fieldName: string, value: string, nodeConfig: any) => {
// Update config with new value
const updatedConfig = ({ ...config, [fieldName]: value })
console.log(fieldName, " ", value, " ", nodeConfig)
console.log(config, "from handle field function - 1")
setConfig((prev) => ({ ...prev, [fieldName]: value }));
console.log(config, "from handle field fun - 2")
console.log({ ...config, [fieldName]: value }, "what we're setting")
// Find fields that depend on this field
const dependentFields = nodeConfig.fields.filter((f:any) => f.dependsOn === fieldName);
for (const depField of dependentFields) {
const fetchFn = depField.fetchOptions ? fetchOptionsMap[depField.fetchOptions] : undefined;
console.log(fetchFn, "fecth FN")
if (fetchFn) {
try {
const options = await fetchFn(updatedConfig);
setDynamicOptions((prev) => ({ ...prev, [depField.name]: options }));
} catch (err) {
console.error("Failed to load dependent options", err);
toast.error(`Failed to load ${depField.label ?? depField.name} options`);
setDynamicOptions((prev) => ({ ...prev, [depField.name]: [] }));
}
}
🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/components/ConfigModal.tsx around lines 35 - 53,
handleFieldChange currently awaits fetchFn(updatedConfig) without error
handling; wrap the fetch call in a try/catch around the await
fetchFn(updatedConfig) (inside the loop over dependentFields) to catch network
or runtime failures, log the error (include fetchFn and depField.name), and set
a safe fallback into setDynamicOptions (e.g., empty array or previous options)
so the UI doesn’t break; ensure fetchFn is obtained from fetchOptionsMap and
continue processing other dependent fields even if one fetch fails.

}
};
// console.log("This is the credential Data from config from backend" , config);
// Fetch credentials with hook based on node config (google, etc) if appropriate
let credType: string | null = null;
Expand Down Expand Up @@ -53,7 +82,7 @@ export default function ConfigModal({
}
};

const renderField = (field: any) => {
const renderField = (field: any, nodeConfig: any) => {
const fieldValue = config[field.name] || "";

if (field.type === "dropdown" && field.name === "credentialId") {
Expand All @@ -68,12 +97,10 @@ export default function ConfigModal({
<>
<select
value={fieldValue}
onChange={(e) =>
setConfig({
...config,
[field.name]: e.target.value,
})
}
onChange={async(e) => {
await handleFieldChange(field.name, e.target.value, nodeConfig);
console.log(field.name, nodeConfig, e.target.value)
}}
className="w-full p-3 border border-gray-900 bg-black text-white rounded-md"
required={field.required}
>
Expand Down Expand Up @@ -126,6 +153,8 @@ export default function ConfigModal({
}

if (field.type === "dropdown") {
// Use dynamicOptions if available, otherwise fall back to field.options
const options = dynamicOptions[field.name] || field.options || [];
return (
<div key={field.name} className="form-group">
<label className="block text-sm font-medium text-white mb-1">
Expand All @@ -134,27 +163,24 @@ export default function ConfigModal({
</label>
<select
value={fieldValue}
onChange={(e) =>
setConfig({
...config,
[field.name]: e.target.value,
})
}
onChange={async(e) => {
await handleFieldChange(field.name, e.target.value, nodeConfig);
}}
className="w-full p-3 border border-gray-900 bg-black text-white rounded-md"
required={field.required}
>
<option value="">Select {field.label.toLowerCase()}</option>
{(field.options || []).map((opt: any) => (
<option key={opt.value || opt} value={opt.value || opt}>
{opt.label || opt}
{options.map((opt: any) => (
<option key={opt.value || opt.id || opt} value={opt.value || opt.id || opt}>
{opt.label || opt.name || opt}
</option>
))}
</select>
</div>
);
}

if (field.type === "textarea") {
if (field.type === "text") {
return (
<div key={field.name} className="form-group">
<label className="block text-sm font-medium text-white mb-1">
Expand Down Expand Up @@ -330,7 +356,7 @@ export default function ConfigModal({
}
return (
<div className="space-y-4">
{nodeConfig.fields.map(renderField)}
{nodeConfig.fields.map((field) => renderField(field, nodeConfig))}
</div>
);
})()}
Expand Down
2 changes: 1 addition & 1 deletion packages/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ model Workflow {
updatedAt DateTime @updatedAt
status WorkflowStatus?
isEmpty Boolean? @default(true)
nodes Node[]
Edges Json?
nodes Node[]
Trigger Trigger?
user User @relation(fields: [userId], references: [id])
executions WorkflowExecution[]
Expand Down